f53df7da64
Code submissions have continually suffered from formatting inconsistencies that constantly have to be addressed. Using clang-format simplifies this by making code formatting more consistent, and allows automation of the code formatting so that maintainers can focus more on the code itself instead of code formatting.
261 lines
7.1 KiB
C++
261 lines
7.1 KiB
C++
#include <stdio.h>
|
|
#include <windows.h>
|
|
#include <d3d9.h>
|
|
#include "get-graphics-offsets.h"
|
|
|
|
typedef HRESULT(WINAPI *d3d9createex_t)(UINT, IDirect3D9Ex **);
|
|
|
|
struct d3d9_info {
|
|
HMODULE module;
|
|
HWND hwnd;
|
|
IDirect3D9Ex *d3d9ex;
|
|
IDirect3DDevice9Ex *device;
|
|
IDirect3DSwapChain9 *swap;
|
|
};
|
|
|
|
static inline bool d3d9_init(d3d9_info &info)
|
|
{
|
|
d3d9createex_t create;
|
|
HRESULT hr;
|
|
|
|
info.hwnd = CreateWindowExA(0, DUMMY_WNDCLASS, "d3d9 get-offset window",
|
|
WS_POPUP, 0, 0, 1, 1, nullptr, nullptr,
|
|
GetModuleHandleA(nullptr), nullptr);
|
|
if (!info.hwnd) {
|
|
return false;
|
|
}
|
|
|
|
info.module = LoadLibraryA("d3d9.dll");
|
|
if (!info.module) {
|
|
return false;
|
|
}
|
|
|
|
create = (d3d9createex_t)GetProcAddress(info.module,
|
|
"Direct3DCreate9Ex");
|
|
if (!create) {
|
|
return false;
|
|
}
|
|
|
|
hr = create(D3D_SDK_VERSION, &info.d3d9ex);
|
|
if (FAILED(hr)) {
|
|
return false;
|
|
}
|
|
|
|
D3DPRESENT_PARAMETERS pp = {};
|
|
pp.Windowed = true;
|
|
pp.SwapEffect = D3DSWAPEFFECT_FLIP;
|
|
pp.BackBufferFormat = D3DFMT_A8R8G8B8;
|
|
pp.BackBufferWidth = 2;
|
|
pp.BackBufferHeight = 2;
|
|
pp.BackBufferCount = 1;
|
|
pp.hDeviceWindow = info.hwnd;
|
|
pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
|
|
hr = info.d3d9ex->CreateDeviceEx(
|
|
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, info.hwnd,
|
|
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_NOWINDOWCHANGES,
|
|
&pp, nullptr, &info.device);
|
|
if (FAILED(hr)) {
|
|
return false;
|
|
}
|
|
|
|
hr = info.device->GetSwapChain(0, &info.swap);
|
|
if (FAILED(hr)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline void d3d9_free(d3d9_info &info)
|
|
{
|
|
if (info.swap)
|
|
info.swap->Release();
|
|
if (info.device)
|
|
info.device->Release();
|
|
if (info.d3d9ex)
|
|
info.d3d9ex->Release();
|
|
if (info.hwnd)
|
|
DestroyWindow(info.hwnd);
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
|
|
#define MAX_CMP_SIZE 22
|
|
|
|
static const uint8_t mask[][MAX_CMP_SIZE] = {
|
|
{0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0xFF, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00},
|
|
{0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00,
|
|
0x00, 0x00, 0xFF, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00},
|
|
{0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00,
|
|
0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
|
};
|
|
|
|
static const uint8_t mask_cmp[][MAX_CMP_SIZE] = {
|
|
/*
|
|
* Windows 7
|
|
* 48 8B 83 B8 3D 00 00 mov rax, [rbx+3DB8h]
|
|
* 44 39 B8 68 50 00 00 cmp [rax+5068h], r15d
|
|
* 75 12 jnz short loc_7FF7AA90530
|
|
* 41 B8 F9 19 00 00 mov r8d, 19F9h
|
|
*/
|
|
{0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x44, 0x39, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x75, 0x00, 0x40, 0xB8, 0x00, 0x00, 0x00, 0x00},
|
|
/*
|
|
* Windows ???+
|
|
* 49 8B 87 78 41 00 00 mov rax, [r15+4178h]
|
|
* 39 98 E0 51 00 00 cmp [rax+51E0h], ebx
|
|
* 75 12 jnz short loc_1800AEC9C
|
|
* 41 B9 C3 1A 00 00 mov r9d, 1AC3h
|
|
*/
|
|
{0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0x80, 0x00, 0x00,
|
|
0x00, 0x00, 0x75, 0x00, 0x40, 0xB8, 0x00, 0x00, 0x00, 0x00},
|
|
/*
|
|
* Windows 10 April 2018
|
|
* 49 8B 87 58 40 00 00 mov rax, [r15+4058h]
|
|
* 39 98 C0 53 00 00 cmp [rax+53C0h], ebx
|
|
* 75 12 jnz short loc_1800A7FEC
|
|
* 48 8D 15 7F B6 09 00 lea rdx, addrErrorMsg
|
|
*
|
|
* Note: different instructions, last byte skipped due to MAX_CMP_SIZE
|
|
*/
|
|
{0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0x80, 0x00, 0x00,
|
|
0x00, 0x00, 0x75, 0x00, 0x48, 0x8D, 0x00, 0x00, 0x00, 0x00}};
|
|
|
|
// Offset into the code for the numbers we're interested in
|
|
static const uint32_t code_offsets[][2] = {
|
|
{3, 10},
|
|
{3, 9},
|
|
{3, 9},
|
|
};
|
|
#else
|
|
|
|
#define MAX_CMP_SIZE 20
|
|
|
|
static const uint8_t mask[][MAX_CMP_SIZE] = {
|
|
{0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
|
|
0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
|
{0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00,
|
|
0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
|
{0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00,
|
|
0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
|
};
|
|
|
|
static const uint8_t mask_cmp[][MAX_CMP_SIZE] = {
|
|
/*
|
|
* Windows 7+
|
|
* 8B 83 E8 29 00 00 mov eax, [ebx+29E8h]
|
|
* 39 B0 80 4B 00 00 cmp [eax+4B80h], esi
|
|
* 75 14 jnz short loc_754CD9E1
|
|
* 68 F9 19 00 00 push 19F9h
|
|
*/
|
|
{0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00,
|
|
0x75, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
/* Windows 10 Creator's Update+
|
|
* 8B 86 F8 2B 00 00 mov eax, [esi+2BF8h]
|
|
* 83 B8 00 4D 00 00 00 cmp dword ptr [eax+4D00h], 0
|
|
* 75 0F jnz short loc_100D793C
|
|
* 68 C3 1A 00 00 push 1AC3h
|
|
*/
|
|
{0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x83, 0x80, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x75, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
/*
|
|
* Windows 10 April 2018 Update
|
|
* 8B 86 68 2B 00 00 mov eax, [esi+2B68h]
|
|
* 83 B8 F4 4D 00 00 00 cmp dword ptr [eax+4DF4h], 0
|
|
* 75 0F jnz short loc_100D9A9C
|
|
* BA 08 71 01 10 mov edx, offset errMsg
|
|
*/
|
|
{0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x83, 0x80, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x75, 0x00, 0xBA, 0x00, 0x00, 0x00, 0x00}};
|
|
|
|
// Offset into the code for the numbers we're interested in
|
|
static const uint32_t code_offsets[][2] = {
|
|
{2, 8},
|
|
{2, 8},
|
|
{2, 8},
|
|
};
|
|
#endif
|
|
|
|
#define MAX_FUNC_SCAN_BYTES 200
|
|
|
|
static inline bool pattern_matches(uint8_t *byte, uint32_t *offset1,
|
|
uint32_t *offset2)
|
|
{
|
|
for (size_t j = 0; j < sizeof(mask) / sizeof(mask[0]); j++) {
|
|
for (size_t i = 0; i < MAX_CMP_SIZE; i++) {
|
|
if ((byte[i] & mask[j][i]) != mask_cmp[j][i])
|
|
goto next_signature;
|
|
}
|
|
|
|
*offset1 = code_offsets[j][0];
|
|
*offset2 = code_offsets[j][1];
|
|
|
|
return true;
|
|
next_signature:;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void get_d3d9_offsets(struct d3d9_offsets *offsets)
|
|
{
|
|
d3d9_info info = {};
|
|
bool success = d3d9_init(info);
|
|
|
|
if (success) {
|
|
uint8_t **vt = *(uint8_t ***)info.device;
|
|
uint8_t *crr = vt[125];
|
|
|
|
offsets->present = vtable_offset(info.module, info.device, 17);
|
|
offsets->present_ex =
|
|
vtable_offset(info.module, info.device, 121);
|
|
offsets->present_swap =
|
|
vtable_offset(info.module, info.swap, 3);
|
|
|
|
uint32_t offset1, offset2;
|
|
for (size_t i = 0; i < MAX_FUNC_SCAN_BYTES; i++) {
|
|
if (pattern_matches(&crr[i], &offset1, &offset2)) {
|
|
#define get_offset(x) *(uint32_t *)&crr[i + x]
|
|
uint32_t off1 = get_offset(offset1);
|
|
uint32_t off2 = get_offset(offset2);
|
|
|
|
/* check to make sure offsets are within
|
|
* expected values */
|
|
if (off1 > 0xFFFF || off2 > 0xFFFF)
|
|
break;
|
|
|
|
/* check to make sure offsets actually point
|
|
* toward expected data */
|
|
#ifdef _MSC_VER
|
|
__try {
|
|
uint8_t *ptr = (uint8_t *)(info.device);
|
|
|
|
uint8_t *d3d9_ptr =
|
|
*(uint8_t **)(ptr + off1);
|
|
if (d3d9_ptr != (uint8_t *)info.d3d9ex)
|
|
break;
|
|
|
|
BOOL &is_d3d9ex =
|
|
*(BOOL *)(d3d9_ptr + off2);
|
|
if (is_d3d9ex != TRUE)
|
|
break;
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
offsets->d3d9_clsoff = off1;
|
|
offsets->is_d3d9ex_clsoff = off2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
d3d9_free(info);
|
|
}
|