35

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

我知道以下方法可以做到这一点:

  1. 创建一个新d3d9.dll的 ,然后将其复制到游戏路径。由于首先搜索当前文件夹,在转到 system32 等之前,我修改后的 DLL 被加载,执行我的附加代码。
    缺点:你必须在开始游戏之前把它放在那里。

  2. 与第一种方法相同,但直接替换system32中的DLL。
    缺点:您无法添加游戏特定代码。您不能排除不希望加载 DLL 的应用程序。

  3. 使用 IDA Pro 4.9 Free 等工具直接从 DLL 获取 EndScene 偏移。由于 DLL 是按原样加载的,因此您只需将此偏移量添加到 DLL 起始地址,当它映射到游戏时,获取实际偏移量,然后将其挂钩。
    缺点:每个系统上的偏移量都不相同。

  4. 挂钩Direct3DCreate9获取D3D9,然后挂钩D3D9->CreateDevice获取设备指针,然后Device->EndScene通过虚拟表挂钩。
    缺点:当进程已经在运行时,无法注入 DLL。您必须使用标志开始该过程CREATE_SUSPENDED以挂钩初始Direct3DCreate9.

  5. 注入 DLL 后,在新窗口中创建新设备。然后,EndScene从该设备获取偏移量并将其挂钩,从而为游戏使用的设备生成挂钩。
    缺点:根据我阅读的一些信息,创建第二个设备可能会干扰现有设备,并且可能会出现窗口模式和全屏模式等问题。

  6. 同第三种方法。但是,您将进行模式扫描以获得EndScene.
    缺点:看起来不那么可靠。

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

4

3 回答 3

21

你安装了一个系统范围的钩子。(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,不能同时使用)。

于 2010-01-03T11:43:31.523 回答
6

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

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

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

于 2010-05-15T01:52:47.223 回答
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();

然后你的功能:

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

    //Call the original (if you want)
    return EndScene(pDevice);
}
于 2015-06-05T10:38:40.047 回答