从注入的DLL中挂接DirectX EndScene

Eta*_*tan 31 c++ directx hook dll-injection

我想绕过EndScene任意DirectX 9应用程序来创建一个小的叠加层.例如,您可以采用FRAPS的帧计数器覆盖,激活后会在游戏中显示.

我知道以下方法:

  1. 创建一个新的d3d9.dll,然后将其复制到游戏路径.由于首先搜索当前文件夹,然后转到system32等,我的修改后的DLL会被加载,执行我的附加代码.

    缺点:你必须在开始游戏之前把它放在那里.

    • 与第一种方法相同,但直接替换system32中的DLL.

    缺点:您无法添加游戏特定代码.您不能排除不希望加载DLL的应用程序.

    • 使用IDA Pro 4.9 Free等工具直接从DLL获取EndScene偏移量.由于DLL按原样加载,您可以将此偏移量添加到DLL起始地址,当它映射到游戏时,获取实际偏移量,然后将其挂钩.

    缺点:每个系统的偏移量都不相同.

    • 挂钩Direct3DCreate9以获取D3D9,然后挂钩D3D9-> CreateDevice以获取设备指针,然后通过虚拟表挂钩Device-> EndScene.

    缺点:当进程已经运行时,无法注入DLL.您必须使用CREATE_SUSPENDED标志启动进程以挂接初始Direct3DCreate9.

    • 一旦注入DLL,就在新窗口中创建新设备.然后,EndScene从该设备获取偏移量并将其挂钩,从而导致游戏使用的设备挂钩.

    缺点:从我读过的一些信息来看,创建第二个设备可能会干扰现有设备,并且可能会出现窗口与全屏模式等错误.

    • 与第三种方法相同.但是,您将进行模式扫描EndScene.

    缺点:看起来不那么可靠.

如何挂钩EndScene注入的DLL,可以在游戏运行时加载,而不必在其他系统上处理不同的d3d9.dll,并使用可靠的方法?例如,FRAPS如何执行它的DirectX挂钩?DLL不应该适用于所有游戏,只适用于我通过注入它的特定进程CreateRemoteThread.

Chr*_*her 15

您安装系统范围的挂钩.(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;
Run Code Online (Sandbox Code Playgroud)

EndSceneFunc现在包含指向函数本身的指针.我们现在可以修补所有呼叫站点,也可以修补功能本身.

请注意,这一切都取决于Windows中COM接口实现的知识.但这适用于所有Windows版本(32或64,而不是两者同时).


Jus*_*ing 6

我知道一个有点老的问题 - 但如果有人对用 C# 做这件事感兴趣,这里是我关于使用 C# 挂钩 Direct3D 9 API 的示例。这利用了 EasyHook 一个开源 .NET 程序集,它允许您“安全地”将钩子从托管代码安装到非托管函数中。(注意:EasyHook 处理所有与 DLL 注入相关的问题——例如 CREATE_SUSPENDED、ACL、32 位与 64 位等等)

我使用 Christopher 通过一个小的 C++ helper dll 提到的类似 VTable 方法来动态确定要挂钩的 IDirect3DDevice9 函数的地址。这是通过创建一个临时窗口句柄并在注入的程序集中创建一个一次性的 IDirect3Device9 来完成的,然后再挂钩所需的函数。这允许您的应用程序挂钩已经运行的目标(更新:请注意,这也完全可以在 C# 中实现 - 请参阅链接页面上的注释)。

更新:还有一个更新版本用于挂钩 Direct3D 9、10 和 11,仍然使用 EasyHook 和 SharpDX 而不是 SlimDX


小智 6

我知道这个问题很老,但这应该适用于任何使用 DirectX9 的程序,您基本上是在创建自己的实例,然后获取指向 VTable 的指针,然后您只需挂钩它。你需要绕道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();
Run Code Online (Sandbox Code Playgroud)

然后你的功能:

HRESULT WINAPI D3D9Hook::newEndScene(LPDIRECT3DDEVICE9 pDevice)
{   
    //Do your stuff here

    //Call the original (if you want)
    return EndScene(pDevice);
}
Run Code Online (Sandbox Code Playgroud)