d3d9-offsets.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <d3d9.h>
  4. #include "get-graphics-offsets.h"
  5. typedef HRESULT (WINAPI *d3d9createex_t)(UINT, IDirect3D9Ex**);
  6. struct d3d9_info {
  7. HMODULE module;
  8. HWND hwnd;
  9. IDirect3D9Ex *d3d9ex;
  10. IDirect3DDevice9Ex *device;
  11. IDirect3DSwapChain9 *swap;
  12. };
  13. static inline bool d3d9_init(d3d9_info &info)
  14. {
  15. d3d9createex_t create;
  16. HRESULT hr;
  17. info.hwnd = CreateWindowExA(0, DUMMY_WNDCLASS, "d3d9 get-offset window",
  18. WS_POPUP, 0, 0, 1, 1, nullptr, nullptr,
  19. GetModuleHandleA(nullptr), nullptr);
  20. if (!info.hwnd) {
  21. return false;
  22. }
  23. info.module = LoadLibraryA("d3d9.dll");
  24. if (!info.module) {
  25. return false;
  26. }
  27. create = (d3d9createex_t)GetProcAddress(info.module,
  28. "Direct3DCreate9Ex");
  29. if (!create) {
  30. return false;
  31. }
  32. hr = create(D3D_SDK_VERSION, &info.d3d9ex);
  33. if (FAILED(hr)) {
  34. return false;
  35. }
  36. D3DPRESENT_PARAMETERS pp = {};
  37. pp.Windowed = true;
  38. pp.SwapEffect = D3DSWAPEFFECT_FLIP;
  39. pp.BackBufferFormat = D3DFMT_A8R8G8B8;
  40. pp.BackBufferWidth = 2;
  41. pp.BackBufferHeight = 2;
  42. pp.BackBufferCount = 1;
  43. pp.hDeviceWindow = info.hwnd;
  44. pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
  45. hr = info.d3d9ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
  46. info.hwnd,
  47. D3DCREATE_HARDWARE_VERTEXPROCESSING |
  48. D3DCREATE_NOWINDOWCHANGES, &pp, nullptr, &info.device);
  49. if (FAILED(hr)) {
  50. return false;
  51. }
  52. hr = info.device->GetSwapChain(0, &info.swap);
  53. if (FAILED(hr)) {
  54. return false;
  55. }
  56. return true;
  57. }
  58. static inline void d3d9_free(d3d9_info &info)
  59. {
  60. if (info.swap)
  61. info.swap->Release();
  62. if (info.device)
  63. info.device->Release();
  64. if (info.d3d9ex)
  65. info.d3d9ex->Release();
  66. if (info.hwnd)
  67. DestroyWindow(info.hwnd);
  68. }
  69. #ifdef _WIN64
  70. #define MAX_CMP_SIZE 22
  71. static const uint8_t mask[][MAX_CMP_SIZE] = {
  72. {
  73. 0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  74. 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
  75. 0xFF, 0x00,
  76. 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00
  77. },
  78. {
  79. 0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  80. 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  81. 0xFF, 0x00,
  82. 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00
  83. },
  84. {
  85. 0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  86. 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  87. 0xFF, 0x00,
  88. 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
  89. },
  90. };
  91. static const uint8_t mask_cmp[][MAX_CMP_SIZE] = {
  92. /*
  93. * Windows 7
  94. * 48 8B 83 B8 3D 00 00 mov rax, [rbx+3DB8h]
  95. * 44 39 B8 68 50 00 00 cmp [rax+5068h], r15d
  96. * 75 12 jnz short loc_7FF7AA90530
  97. * 41 B8 F9 19 00 00 mov r8d, 19F9h
  98. */
  99. {
  100. 0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00,
  101. 0x44, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00,
  102. 0x75, 0x00,
  103. 0x40, 0xB8, 0x00, 0x00, 0x00, 0x00
  104. },
  105. /*
  106. * Windows ???+
  107. * 49 8B 87 78 41 00 00 mov rax, [r15+4178h]
  108. * 39 98 E0 51 00 00 cmp [rax+51E0h], ebx
  109. * 75 12 jnz short loc_1800AEC9C
  110. * 41 B9 C3 1A 00 00 mov r9d, 1AC3h
  111. */
  112. {
  113. 0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00,
  114. 0x39, 0x80, 0x00, 0x00, 0x00, 0x00,
  115. 0x75, 0x00,
  116. 0x40, 0xB8, 0x00, 0x00, 0x00, 0x00
  117. },
  118. /*
  119. * Windows 10 April 2018
  120. * 49 8B 87 58 40 00 00 mov rax, [r15+4058h]
  121. * 39 98 C0 53 00 00 cmp [rax+53C0h], ebx
  122. * 75 12 jnz short loc_1800A7FEC
  123. * 48 8D 15 7F B6 09 00 lea rdx, addrErrorMsg
  124. *
  125. * Note: different instructions, last byte skipped due to MAX_CMP_SIZE
  126. */
  127. {
  128. 0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00,
  129. 0x39, 0x80, 0x00, 0x00, 0x00, 0x00,
  130. 0x75, 0x00,
  131. 0x48, 0x8D, 0x00, 0x00, 0x00, 0x00
  132. }
  133. };
  134. // Offset into the code for the numbers we're interested in
  135. static const uint32_t code_offsets[][2] = {
  136. {3, 10},
  137. {3, 9},
  138. {3, 9},
  139. };
  140. #else
  141. #define MAX_CMP_SIZE 20
  142. static const uint8_t mask[][MAX_CMP_SIZE] = {
  143. {
  144. 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  145. 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  146. 0xFF, 0x00,
  147. 0xFF, 0x00, 0x00, 0x00, 0x00
  148. },
  149. {
  150. 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  151. 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF,
  152. 0xFF, 0x00,
  153. 0xFF, 0x00, 0x00, 0x00, 0x00
  154. },
  155. {
  156. 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
  157. 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF,
  158. 0xFF, 0x00,
  159. 0xFF, 0x00, 0x00, 0x00, 0x00
  160. },
  161. };
  162. static const uint8_t mask_cmp[][MAX_CMP_SIZE] = {
  163. /*
  164. * Windows 7+
  165. * 8B 83 E8 29 00 00 mov eax, [ebx+29E8h]
  166. * 39 B0 80 4B 00 00 cmp [eax+4B80h], esi
  167. * 75 14 jnz short loc_754CD9E1
  168. * 68 F9 19 00 00 push 19F9h
  169. */
  170. {
  171. 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00,
  172. 0x39, 0x80, 0x00, 0x00, 0x00, 0x00,
  173. 0x75, 0x00,
  174. 0x68, 0x00, 0x00, 0x00, 0x00
  175. },
  176. /* Windows 10 Creator's Update+
  177. * 8B 86 F8 2B 00 00 mov eax, [esi+2BF8h]
  178. * 83 B8 00 4D 00 00 00 cmp dword ptr [eax+4D00h], 0
  179. * 75 0F jnz short loc_100D793C
  180. * 68 C3 1A 00 00 push 1AC3h
  181. */
  182. {
  183. 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00,
  184. 0x83, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
  185. 0x75, 0x00,
  186. 0x68, 0x00, 0x00, 0x00, 0x00
  187. },
  188. /*
  189. * Windows 10 April 2018 Update
  190. * 8B 86 68 2B 00 00 mov eax, [esi+2B68h]
  191. * 83 B8 F4 4D 00 00 00 cmp dword ptr [eax+4DF4h], 0
  192. * 75 0F jnz short loc_100D9A9C
  193. * BA 08 71 01 10 mov edx, offset errMsg
  194. */
  195. {
  196. 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00,
  197. 0x83, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
  198. 0x75, 0x00,
  199. 0xBA, 0x00, 0x00, 0x00, 0x00
  200. }
  201. };
  202. // Offset into the code for the numbers we're interested in
  203. static const uint32_t code_offsets[][2] = {
  204. {2, 8},
  205. {2, 8},
  206. {2, 8},
  207. };
  208. #endif
  209. #define MAX_FUNC_SCAN_BYTES 200
  210. static inline bool pattern_matches(uint8_t *byte, uint32_t *offset1,
  211. uint32_t *offset2)
  212. {
  213. for (size_t j = 0; j < sizeof(mask) / sizeof(mask[0]); j++) {
  214. for (size_t i = 0; i < MAX_CMP_SIZE; i++) {
  215. if ((byte[i] & mask[j][i]) != mask_cmp[j][i])
  216. goto next_signature;
  217. }
  218. *offset1 = code_offsets[j][0];
  219. *offset2 = code_offsets[j][1];
  220. return true;
  221. next_signature:;
  222. }
  223. return false;
  224. }
  225. void get_d3d9_offsets(struct d3d9_offsets *offsets)
  226. {
  227. d3d9_info info = {};
  228. bool success = d3d9_init(info);
  229. if (success) {
  230. uint8_t **vt = *(uint8_t***)info.device;
  231. uint8_t *crr = vt[125];
  232. offsets->present = vtable_offset(info.module, info.device, 17);
  233. offsets->present_ex = vtable_offset(info.module, info.device,
  234. 121);
  235. offsets->present_swap = vtable_offset(info.module, info.swap,
  236. 3);
  237. uint32_t offset1, offset2;
  238. for (size_t i = 0; i < MAX_FUNC_SCAN_BYTES; i++) {
  239. if (pattern_matches(&crr[i], &offset1, &offset2)) {
  240. #define get_offset(x) *(uint32_t*)&crr[i + x]
  241. uint32_t off1 = get_offset(offset1);
  242. uint32_t off2 = get_offset(offset2);
  243. /* check to make sure offsets are within
  244. * expected values */
  245. if (off1 > 0xFFFF || off2 > 0xFFFF)
  246. break;
  247. /* check to make sure offsets actually point
  248. * toward expected data */
  249. #ifdef _MSC_VER
  250. __try {
  251. uint8_t *ptr = (uint8_t*)(info.device);
  252. uint8_t *d3d9_ptr =
  253. *(uint8_t**)(ptr + off1);
  254. if (d3d9_ptr != (uint8_t*)info.d3d9ex)
  255. break;
  256. BOOL &is_d3d9ex =
  257. *(BOOL*)(d3d9_ptr + off2);
  258. if (is_d3d9ex != TRUE)
  259. break;
  260. } __except(EXCEPTION_EXECUTE_HANDLER) {
  261. break;
  262. }
  263. #endif
  264. offsets->d3d9_clsoff = off1;
  265. offsets->is_d3d9ex_clsoff = off2;
  266. break;
  267. }
  268. }
  269. }
  270. d3d9_free(info);
  271. }