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

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

  1. 创建一个新的 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);
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top