从注入的 DLL 中挂钩 DirectX EndScene
-
22-09-2019 - |
题
我想绕道 EndScene
从任意 DirectX 9 应用程序创建一个小的覆盖层。例如,您可以使用 FRAPS 的帧计数器叠加层,该叠加层在激活时会显示在游戏中。
我知道以下方法可以做到这一点:
创建一个新的 d3d9.dll, ,然后将其复制到游戏路径。由于首先搜索当前文件夹,然后再转到 system32 等,因此会加载我修改过的 DLL,并执行我的附加代码。
缺点: 您必须在开始游戏之前将其放在那里。
- 与第一种方法相同,但直接替换system32中的DLL。
缺点: 您无法添加游戏特定代码。您不能排除不希望加载 DLL 的应用程序。
- 使用 IDA Pro 4.9 Free 等工具直接从 DLL 获取 EndScene 偏移量。由于 DLL 是按原样加载的,因此当它映射到游戏时,您只需将此偏移量添加到 DLL 起始地址即可获取实际偏移量,然后挂钩它。
缺点: 每个系统上的偏移量并不相同。
- 挂钩 Direct3DCreate9 获取D3D9,然后挂接 D3D9->创建设备 获取设备指针,然后挂钩 设备->结束场景 通过虚拟表。
缺点: 当进程已经运行时,无法注入 DLL。您必须以以下方式开始该过程
CREATE_SUSPENDED
标记以挂钩初始 Direct3DCreate9.- 注入 DLL 后,立即在新窗口中创建一个新设备。然后,得到
EndScene
从该设备偏移并挂钩它,从而产生游戏使用的设备的挂钩。
缺点: 根据我读过的一些信息,创建第二个设备可能会干扰现有设备,并且可能会在窗口与窗口之间出现错误。全屏模式等
- 与第三种方法相同。但是,您将进行模式扫描以获得
EndScene
.
缺点: 看起来不太可靠。
我怎样才能挂钩 EndScene
来自注入的 DLL,可以在游戏运行时加载该 DLL,而无需处理不同的 d3d9.dll在其他系统上,并且使用的方法是可靠的?例如,FRAPS 如何执行其 DirectX 挂钩?该 DLL 不应适用于所有游戏,而应适用于我通过以下方式注入它的特定进程 CreateRemoteThread
.
解决方案
您安装了系统范围的挂钩。(SetWindowsHookEx) 完成此操作后,您就可以加载到每个进程中。
现在,当调用钩子时,您将查找已加载的 d3d9.dll。
如果加载了一个,则创建一个临时 D3D9 对象,并遍历 vtable 以获取 EndScene 方法的地址。
然后,您可以使用自己的方法修补 EndScene 调用。(将 EndScene 中的第一条指令替换为对您的方法的调用。
完成后,您必须修补回调,以调用原始的 EndScene 方法。然后重新安装你的补丁。
FRAPS 就是这样做的。(关联)
您可以从接口的 vtable 中找到函数地址。
所以你可以执行以下操作(伪代码):
IDirect3DDevice9* pTempDev = ...;
const int EndSceneIndex = 26 (?);
typedef HRESULT (IDirect3DDevice9::* EndSceneFunc)( void );
BYTE* pVtable = reinterpret_cast<void*>( pTempDev );
EndSceneFunc = pVtable + sizeof(void*) * EndSceneIndex;
EndSceneFunc 现在包含一个指向函数本身的指针。我们现在可以修补所有调用站点,也可以修补函数本身。
请注意,这一切都取决于 Windows 中 COM 接口实现的知识。但这适用于所有 Windows 版本(32 或 64,不能同时使用)。
其他提示
我知道一个有点老的问题 - 但如果有人有兴趣用 C# 来做这件事,这是我的例子 使用 C# 挂钩 Direct3D 9 API. 。这利用了 EasyHook,一个开源 .NET 程序集,允许您“安全”地将托管代码中的挂钩安装到非托管函数中。(笔记:EasyHook 处理与 DLL 注入相关的所有问题 - 例如CREATE_SUSPENDED、ACL、32 位与 64 位等)
我使用 Christopher 提到的类似 VTable 方法,通过一个小型 C++ 辅助 dll 来动态确定要挂钩的 IDirect3DDevice9 函数的地址。这是通过创建一个临时窗口句柄,并在注入的程序集中创建一个一次性的 IDirect3Device9 来完成的,然后再挂钩所需的函数。这允许您的应用程序挂钩已经运行的目标(更新:请注意,这在 C# 中也是完全可能的 - 请参阅链接页面上的注释)。
更新:还有一个更新版本 挂钩 Direct3D 9、10 和 11 仍然使用 EasyHook 并使用 SharpDX 而不是 SlimDX
我知道这个问题很老了,但这应该适用于任何使用 DirectX9 的程序,您基本上是在创建自己的实例,然后获取指向 VTable 的指针,然后您只需挂钩它即可。顺便说一句,你将需要 detours 3.X:
//Just some typedefs:
typedef HRESULT (WINAPI* oEndScene) (LPDIRECT3DDEVICE9 D3DDevice);
static oEndScene EndScene;
//Do this in a function or whatever
HMODULE hDLL=GetModuleHandleA("d3d9");
LPDIRECT3D9(__stdcall*pDirect3DCreate9)(UINT) = (LPDIRECT3D9(__stdcall*)(UINT))GetProcAddress( hDLL, "Direct3DCreate9");
LPDIRECT3D9 pD3D = pDirect3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE d3ddm;
HRESULT hRes = pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm );
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp));
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
WNDCLASSEX wc = { sizeof(WNDCLASSEX),CS_CLASSDC,TempWndProc,0L,0L,GetModuleHandle(NULL),NULL,NULL,NULL,NULL,("1"),NULL};
RegisterClassEx(&wc);
HWND hWnd = CreateWindow(("1"),NULL,WS_OVERLAPPEDWINDOW,100,100,300,300,GetDesktopWindow(),NULL,wc.hInstance,NULL);
hRes = pD3D->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT,
&d3dpp, &ppReturnedDeviceInterface);
pD3D->Release();
DestroyWindow(hWnd);
if(pD3D == NULL){
//printf ("WARNING: D3D FAILED");
return false;
}
pInterface = (unsigned long*)*((unsigned long*)ppReturnedDeviceInterface);
EndScene = (oEndScene) (DWORD) pInterface[42];
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)EndScene, newEndScene);
DetourTransactionCommit();
然后你的功能:
HRESULT WINAPI D3D9Hook::newEndScene(LPDIRECT3DDEVICE9 pDevice)
{
//Do your stuff here
//Call the original (if you want)
return EndScene(pDevice);
}