2010-01-03 118 views
28

我想从任意DirectX 9应用程序绕行EndScene以创建一个小覆盖。举个例子,你可以采用FRAPS的帧计数器覆盖,当游戏被激活时显示。从注入的DLL中挂接DirectX EndScene

我知道下面的方法来做到这一点:

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

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

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

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

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

    缺点:每个系统的偏移量都不一样。

    • 挂钩Direct3DCreate9得到D3D9,然后钩D3D9-> CreateDevice的来获得设备指针,然后通过虚拟表钩住设备 - > EndScene

    缺点:当进程已经运行时,DLL不能被注入。您必须使用CREATE_SUSPENDED标志启动该过程,以挂钩最初的Direct3DCreate9

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

    缺点:作为一些信息我已阅读,创建第二装置可以与现有的设备干涉,并且它可以与错误窗口与全屏模式等

    • 相同第三种方法。但是,您将执行模式扫描以获得EndScene

    缺点:看起来并不可靠。

我怎样才能钩EndScene从注入的DLL,其可以当游戏已经运行被加载,而不必处理与其他系统不同d3d9.dll的,并用的方法,其是可靠的?例如,FRAPS如何执行它的DirectX挂钩? DLL不应该适用于所有游戏,只适用于我通过CreateRemoteThread注入的特定进程。

回答

15

您安装了一个系统挂钩。 (SetWindowsHookEx)完成后,您可以加载到每个进程中。

现在,当钩子被调用时,你会看到一个加载的d3d9.dll。

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

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

当你完成,你需要修补的回电,打电话给原来的EndScene方法。然后重新安装补丁。

这是FRAPS做它的方式(Link


你可以找到一个接口的虚函数表函数地址

所以,你可以做以下(伪代码):

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

退房此了解更多详情: Windows通过C/C++,第22章 - DLL注入和API挂钩,注入DLL使用Windows钩子 – whunmr 2010-01-03 11:56:35

+0

我想通过'CreateRemoteThread'注入DLL。我不想要一个全局钩子,只是针对特定的游戏。 – Etan 2010-01-03 12:35:33

+0

然后就放下第一步。 – Christopher 2010-01-03 13:02:16

4

我知道一个稍微老的问题 - 但如果有人有兴趣用C#做这件事,这里是我的例子hooking the Direct3D 9 API using C#。这利用了EasyHook一个开放源代码的.NET程序集,它允许你'安全地'将托管代码中的钩子安装到非托管函数中。 (注:EasyHook负责所有与DLL注入相关的问题 - 例如CREATE_SUSPENDED,ACL,32和64位等)

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

更新:有也是hooking Direct3D 9, 10 and 11的更新版本依然采用与SharpDX代替SlimDX

2

我知道这个问题是老EasyHook,但这应该使用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); 
} 
+0

为什么我得到“崩溃的应用程序”,当返回原始的EndScene? – Duracell 2016-05-11 16:40:10

+0

@Duracell它真的取决于你如何使用它。你是从主线程中挂钩吗?也许你的指针原来的EndScene是空的。我需要更多的信息才能够帮助你。 – Fredaikis 2016-05-14 02:46:57

+0

我注入的代码http://pastebin.com/ui0ezFyW应用程序(使用D3D9),并且我得到了控制台消息 “printf_s”( “大呼过瘾!);” 从功能 “HRESULT WINAPI newEndScene(LPDIRECT3DDEVICE9 pDevice)” ,但是当我调用“return EndScene(pDevice);” - 获取崩溃的应用程序,请检查我的错误。 – Duracell 2016-05-14 07:29:02