从注入的DLL中挂接DirectX EndScene

时间:2010-01-03 11:23:14

标签: c++ directx hook dll-injection

我想从任意DirectX 9应用程序中绕过EndScene以创建一个小的叠加层。例如,您可以采用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

    下行:看起来不那么可靠。

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

3 个答案:

答案 0 :(得分:15)

您安装系统范围的挂钩。 (SetWindowsHookEx)完成后,您将被加载到每个进程中。

现在,当调用钩子时,您会查找已加载的d3d9.dll。

如果加载了一个,则创建一个临时D3D9对象,然后遍历vtable以获取EndScene方法的地址。

然后,您可以使用自己的方法修补EndScene调用。 (通过调用方法替换EndScene中的第一条指令。

完成后,您必须修补回调,以调用原始的EndScene方法。然后重新安装补丁。

这是FRAPS的方式。 (Link


您可以从界面的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,而不是同时)。

答案 1 :(得分:4)

我知道一个稍微陈旧的问题 - 但是如果有人有兴趣用C#做这件事,这是我在hooking the Direct3D 9 API using C#上的例子。这利用EasyHook开源.NET程序集,允许您“安全地”将托管代码中的挂钩安装到非托管函数中。 (注意:EasyHook负责处理DLL注入的所有问题 - 例如CREATE_SUSPENDED,ACL,32对64位等等)

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

更新:还有hooking Direct3D 9, 10 and 11的更新版本仍在使用EasyHook和SharpDX而不是SlimDX

答案 2 :(得分:2)

我知道这个问题已经过时了,但这应该适用于使用DirectX9的任何程序,你基本上是在创建自己的实例,然后获取指向VTable的指针,然后你就可以将它挂钩。你需要绕路3.X btw:

//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);
}