从注入的 DLL 中挂钩 DirectX EndScene

发布于 2024-08-17 03:42:56 字数 1262 浏览 2 评论 0原文

我想从任意 DirectX 9 应用程序绕行 EndScene 来创建一个小的覆盖层。例如,您可以使用 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
    缺点:看起来不太可靠。

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

I want to detour EndScene from an arbitrary DirectX 9 application to create a small overlay. As an example, you could take the frame counter overlay of FRAPS, which is shown in games when activated.

I know the following methods to do this:

  1. Creating a new d3d9.dll, which is then copied to the games path. Since the current folder is searched first, before going to system32 etc., my modified DLL gets loaded, executing my additional code.
    Downside: You have to put it there before you start the game.

  2. Same as the first method, but replacing the DLL in system32 directly.
    Downside: You cannot add game specific code. You cannot exclude applications where you don't want your DLL to be loaded.

  3. Getting the EndScene offset directly from the DLL using tools like IDA Pro 4.9 Free. Since the DLL gets loaded as is, you can just add this offset to the DLL starting address, when it is mapped to the game, to get the actual offset, and then hook it.
    Downside: The offset is not the same on every system.

  4. Hooking Direct3DCreate9 to get the D3D9, then hooking D3D9->CreateDevice to get the device pointer, and then hooking Device->EndScene through the virtual table.
    Downside: The DLL cannot be injected, when the process is already running. You have to start the process with the CREATE_SUSPENDED flag to hook the initial Direct3DCreate9.

  5. Creating a new Device in a new window, as soon as the DLL gets injected. Then, getting the EndScene offset from this device and hooking it, resulting in a hook for the device which is used by the game.
    Downside: as of some information I have read, creating a second device may interfere with the existing device, and it may bug with windowed vs. fullscreen mode etc.

  6. Same as the third method. However, you'll do a pattern scan to get EndScene.
    Downside: doesn't look that reliable.

How can I hook EndScene from an injected DLL, which may be loaded when the game is already running, without having to deal with different d3d9.dll's on other systems, and with a method which is reliable? How does FRAPS for example perform it's DirectX hooks?
The DLL should not apply to all games, just to specific processes where I inject it via CreateRemoteThread.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

踏月而来 2024-08-24 03:42:56

您安装了系统范围的挂钩。 (SetWindowsHookEx) 完成此操作后,您就可以加载到每个进程中。

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

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

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

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

这就是 FRAPS 的做法。(< a href="http://www.woodmann.com/forum/archive/index.php/t-11023.htm" rel="noreferrer">链接)


可以从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,不能同时使用)。

You install a system wide hook. (SetWindowsHookEx) With this done, you get to be loaded into every process.

Now when the hook is called, you look for a loaded d3d9.dll.

If one is loaded, you create a temporary D3D9 object, and walk the vtable to get the address of the EndScene method.

Then you can patch the EndScene call, with your own method. (Replace the first instruction in EndScene by a call to your method.

When you are done, you have to patch the call back, to call the original EndScene method. And then reinstall your patch.

This is the way FRAPS does it. (Link)


You can find a function address from the vtable of an interface.

So you can do the following (Pseudo-Code):

IDirect3DDevice9* pTempDev = ...;
const int EndSceneIndex = 26 (?);

typedef HRESULT (IDirect3DDevice9::* EndSceneFunc)( void );

BYTE* pVtable = reinterpret_cast<void*>( pTempDev );
EndSceneFunc = pVtable + sizeof(void*) * EndSceneIndex;

EndSceneFunc does now contain a pointer to the function itself. We can now either patch all call-sites or we can patch the function itself.

Beware that this all depends on the knowledge of the implementation of COM-Interfaces in Windows. But this works on all windows versions (either 32 or 64, not both at the same time).

你在我安 2024-08-24 03:42:56

我知道这个问题很老了,但这应该适用于任何使用 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);
}

I know this question is old, but this should work for any program using DirectX9, You are creating your own instance basically, and then getting the pointer to the VTable, then you just hook it. You will need detours 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();

And then your function:

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

    //Call the original (if you want)
    return EndScene(pDevice);
}
此刻的回忆 2024-08-24 03:42:56

我知道这是一个有点老的问题 - 但如果有人有兴趣使用 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

A slightly old question I know - but in case anyone is interested in doing this with C#, here is my example on hooking the Direct3D 9 API using C#. This utilizes EasyHook an open source .NET assembly that allows you to 'safely' install hooks from managed code into unmanaged functions. (Note: EasyHook takes care of all the issues surrounding DLL injection - e.g. CREATE_SUSPENDED, ACL's, 32 vs 64-bit and so on)

I use a similar VTable approach as mentioned by Christopher via a small C++ helper dll to dynamically determine the address of the IDirect3DDevice9 functions to hook. This is done by creating a temporary window handle, and creating a throw-away IDirect3Device9 within the injected assembly before then hooking the desired functions. This allows your application to hook a target that is already running (Update: note that this is possible entirely within C# also - see comments on the linked page).

Update: there is also an updated version for hooking Direct3D 9, 10 and 11 still using EasyHook and with SharpDX instead of SlimDX

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文