2010-01-03 10 views
28

Voglio deviare EndScene da un'applicazione DirectX 9 arbitraria per creare un piccolo overlay. Ad esempio, è possibile eseguire la sovrapposizione del frame frame di FRAPS, che viene visualizzato nei giochi quando attivato.Collegamento di DirectX EndScene da una DLL iniettata

so i seguenti metodi per farlo:

  1. Creazione di un nuovo d3d9.dll, che viene poi copiato il percorso giochi. Poiché la cartella corrente viene cercata per prima, prima di andare a system32 ecc., La DLL modificata viene caricata, eseguendo il mio codice aggiuntivo.

    Lato negativo: Devi metterlo lì prima di iniziare il gioco.

    • Uguale al primo metodo, ma sostituzione diretta della DLL in system32.

    Downside: Non è possibile aggiungere codice specifico gioco. Non è possibile escludere applicazioni in cui non si desidera caricare la DLL.

    • Ottenere l'offset EndScene direttamente dalla DLL utilizzando strumenti come IDA Pro 4.9 gratuito. Poiché la DLL viene caricata così com'è, è possibile aggiungere questo offset all'indirizzo di origine della DLL, quando viene mappato sul gioco, per ottenere l'offset effettivo e quindi agganciarlo.

    Downside: L'offset non è la stessa su tutti i sistemi.

    • aggancia Direct3DCreate9 per ottenere il D3D9, quindi agganciando D3D9-> CreateDevice per ottenere il puntatore dispositivo, e quindi agganciando Device-> EndScene attraverso la tabella virtuale.

    Unico inconveniente: Il DLL non può essere iniettata, quando il processo è già in esecuzione. È necessario avviare il processo con il flag CREATE_SUSPENDED per agganciare l'iniziale Direct3DCreate9.

    • Creazione di un nuovo dispositivo in una nuova finestra, non appena la DLL viene iniettato. Quindi, ottenere l'offset EndScene da questo dispositivo e collegarlo, creando un gancio per il dispositivo utilizzato dal gioco.

    Unico inconveniente: come di un'informazione I letto, creando un secondo dispositivo può interferire con il dispositivo esistente, e può insetto con finestra vs. modalità a schermo intero ecc

    • Stessi terzo metodo. Tuttavia, esegui una scansione del modello per ottenere EndScene.

    Lato negativo: non sembra affidabile.

Come posso agganciare EndScene da un DLL iniettato, che può essere caricato quando il gioco è già in funzione, senza avere a che fare con diversi d3d9.dll 's su altri sistemi, e di un metodo che è affidabile? Come FRAPS ad esempio esegue i suoi hook DirectX? La DLL non dovrebbe essere applicata a tutti i giochi, solo a processi specifici in cui viene iniettata tramite CreateRemoteThread.

risposta

15

Si installa un gancio di sistema. (SetWindowsHookEx) Con questo fatto, si arriva a essere caricati in ogni processo.

Ora, quando viene chiamato l'hook, si cerca un file d3d9.dll caricato.

Se uno viene caricato, si crea un oggetto D3D9 temporaneo e si cammina il vtable per ottenere l'indirizzo del metodo EndScene.

Quindi è possibile applicare una patch alla chiamata EndScene, con il proprio metodo. (Sostituire la prima istruzione in EndScene da una chiamata al metodo.

Al termine, è necessario patchare la chiamata, per chiamare il metodo EndScene originale. E poi reinstallare il cerotto.

Questo è il modo in cui lo fa FRAPS (Link)


è possibile trovare un indirizzo di funzione dalla vtable di un'interfaccia

in modo da poter effettuare le seguenti operazioni (pseudo-codice):..

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

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

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

EndSceneFunc ora contiene un puntatore alla funzione stessa. Ora possiamo applicare una patch a tutti i siti di chiamata oppure possiamo applicare la patch alla funzione stessa.

Fare attenzione che tutto ciò dipende dalla conoscenza dell'implementazione delle interfacce COM in Windows. Ma questo funziona su tutte le versioni di Windows (32 o 64, non entrambe contemporaneamente).

+1

check-out questo per maggiori dettagli: Windows tramite C/C++, Capitolo 22 - DLL Injection e API Hooking, Iniettare una DLL utilizzando Windows Ganci – whunmr

+0

Voglio iniettare la DLL tramite 'CreateRemoteThread'. Non voglio un hook globale, solo per giochi specifici. – Etan

+0

Quindi basta rilasciare il primo passaggio. – Christopher

4

Una domanda un po 'vecchia che conosco - ma nel caso qualcuno sia interessato a farlo con C#, ecco il mio esempio su hooking the Direct3D 9 API using C#. Ciò utilizza EasyHook, un assembly .NET open source che consente di installare in modo "sicuro" i ganci dal codice gestito in funzioni non gestite. (Nota: EasyHook si prende cura di tutte le questioni che circondano DLL iniezione - per esempio CREATE_SUSPENDED, ACL, 32 vs 64 bit e così via)

Io uso un approccio VTable simile come detto da Christopher attraverso un piccolo C++ aiutante dll in modo dinamico determinare l'indirizzo delle funzioni IDirect3DDevice9 da agganciare. Ciò avviene creando un handle temporaneo della finestra e creando un IDirect3Device9 di eliminazione nell'assieme iniettato prima di agganciare le funzioni desiderate. Ciò consente alla tua applicazione di agganciare un obiettivo già in esecuzione (Aggiornamento: nota che questo è possibile anche interamente in C# - vedi i commenti sulla pagina collegata).

Aggiornamento: esiste anche una versione aggiornata per hooking Direct3D 9, 10 and 11 ancora utilizzando EasyHook e con SharpDX invece di SlimDX

2

So che questa domanda è vecchio, ma questo dovrebbe funzionare per qualsiasi programma utilizzando DirectX9, si sta creando il fondamentalmente la propria istanza, e quindi ottenere il puntatore al VTable, quindi basta agganciarlo. Avrai bisogno di deviazioni 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(); 

E poi la funzione:

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

    //Call the original (if you want) 
    return EndScene(pDevice); 
} 
+0

perché ottengo "crash app", quando restituisco l'EndScene originale? – Duracell

+0

@Duracell dipende davvero da come lo stai usando. Ti stai agganciando al thread principale? Forse il tuo puntatore all'originale EndScene è nullo. Ho bisogno di più informazioni per poterti aiutare. – Fredaikis

+0

Sto iniettando questo codice http://pastebin.com/ui0ezFyW nell'applicazione (uso d3d9) e ricevo il messaggio nella console "printf_s (" Hooked! ");" Dalla funzione "HRESULT WINAPI newEndScene (LPDIRECT3DDEVICE9 pDevice)" , ma poi quando chiamo "return EndScene (pDevice);" - ottieni l'app per il crash, per favore controlla il mio errore. – Duracell