2010-01-03 116 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