d3d9-offsets.cpp 8.4 KB

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