From: Dan Ginovker <danielginovker@gmail.com> --- dlls/d3d9/device.c | 32 ++++++++++++++++++++++ dlls/d3d9/tests/device.c | 57 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/dlls/d3d9/device.c b/dlls/d3d9/device.c index 20b7255f52f..d725ca831f0 100644 --- a/dlls/d3d9/device.c +++ b/dlls/d3d9/device.c @@ -4626,6 +4626,38 @@ static const struct IDirect3DDevice9ExVtbl d3d9_device_vtbl = d3d9_device_GetDisplayModeEx, }; +#ifdef __i386__ + +/* BFME Online Arena hooks the IDirect3DDevice9 vtbl. Instead of getting the + * vtbl address by doing something as simple as constructing a device, it + * apparently looks for the code sequence used by MSVC to initialize the device + * object with the vtbl address. Somehow this sequence has both remained stable + * and can be reliably used to identify *this* COM object specifically. It is: + * + * mov &d3d9_device_vtbl, %esi + * mov %eax, 0x########(%esi) + * mov %eax, 0x########(%esi) + * + * It doesn't really make sense to force using this sequence to initialize the + * d3d9_device, so instead just put it here. */ + +#include <pshpack1.h> + +static const __attribute__((used)) struct +{ + unsigned char op_mov_to_this[2]; + const struct IDirect3DDevice9ExVtbl *vtable_addr; + unsigned char op_mov_field_a[2]; + unsigned int disp_a; + unsigned char op_mov_field_b[2]; + unsigned int disp_b; +} +d3d9_overlay_scanner_marker = {{0xc7, 0x06}, &d3d9_device_vtbl, {0x89, 0x86}, 0, {0x89, 0x86}, 0}; + +#include <poppack.h> + +#endif + static inline struct d3d9_device *device_from_device_parent(struct wined3d_device_parent *device_parent) { return CONTAINING_RECORD(device_parent, struct d3d9_device, device_parent); diff --git a/dlls/d3d9/tests/device.c b/dlls/d3d9/tests/device.c index 9614a5b0a01..3dc9bff4dc2 100644 --- a/dlls/d3d9/tests/device.c +++ b/dlls/d3d9/tests/device.c @@ -28,6 +28,7 @@ #include <initguid.h> #include <d3d9on12.h> #include <dxgi1_4.h> +#include <psapi.h> static HMODULE d3d9_handle = 0; static HMODULE d3d12_handle = 0; @@ -15295,6 +15296,61 @@ out: DestroyWindow(window); } +/* BFME Online Arena hooks the IDirect3DDevice9 vtbl, getting its address by + * searching for this sequence. Test that this reliably works. */ +static void test_device_vtbl_initialization_assembly_hook(void) +{ +#ifdef __i386__ + const unsigned char *base, *p; + IDirect3DDevice9Vtbl *vtbl; + IDirect3DDevice9 *device; + BOOL found = FALSE; + IDirect3D9 *d3d; + ULONG refcount; + MODULEINFO mi; + HWND window; + BOOL ret; + + window = create_window(); + d3d = Direct3DCreate9(D3D_SDK_VERSION); + ok(!!d3d, "Failed to create a D3D object.\n"); + if (!(device = create_device(d3d, window, NULL))) + { + skip("Failed to create a D3D device, skipping tests.\n"); + goto done; + } + + ret = GetModuleInformation(GetCurrentProcess(), GetModuleHandleA("d3d9.dll"), &mi, sizeof(mi)); + ok(ret, "Failed to get module information, error %lu.\n", GetLastError()); + base = (const unsigned char *)mi.lpBaseOfDll; + + for (p = base; p + 14 <= base + mi.SizeOfImage; ++p) + { + if (p[0] == 0xc7 && p[1] == 0x06 + && p[6] == 0x89 && p[7] == 0x86 + && p[12] == 0x89 && p[13] == 0x86) + { + found = TRUE; + break; + } + } + ok(found, "Could not find device vtbl initialization sequence.\n"); + + memcpy(&vtbl, p + 2, sizeof(vtbl)); + + /* On modern Windows this seems to be similarly decoy code. + * The vtbl pointer doesn't match, and neither do multiple of the functions. + * At least one of the functions actually hooked, Present, still matches. */ + ok(vtbl->Present == device->lpVtbl->Present, "Got different Present() addresses.\n"); + + refcount = IDirect3DDevice9_Release(device); + ok(!refcount, "Device has %lu references left.\n", refcount); +done: + IDirect3D9_Release(d3d); + DestroyWindow(window); +#endif +} + START_TEST(device) { HMODULE d3d9_handle = GetModuleHandleA("d3d9.dll"); @@ -15432,6 +15488,7 @@ START_TEST(device) test_cursor_clipping(); test_window_position(); test_d3d9on12(); + test_device_vtbl_initialization_assembly_hook(); UnregisterClassA("d3d9_test_wc", GetModuleHandleA(NULL)); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10795