Domanda

Voglio fare una deviazione EndScene da un'applicazione arbitraria DirectX 9 per creare un piccolo overlay. A titolo di esempio, si potrebbe prendere il contapose sovrapposizione di FRAPS, che viene mostrato nei giochi quando viene attivato.

Lo so i seguenti metodi per fare questo:

  1. La creazione di un nuovo d3d9.dll , che viene poi copiato il percorso giochi. Dal momento che la cartella corrente viene cercato prima, prima di andare a System32 ecc, il mio DLL modificato viene caricato, eseguendo il mio codice aggiuntivo.

    Downside: Devi mettere lì prima di iniziare il gioco

    .
    • Come il primo metodo, ma sostituendo la DLL in system32 direttamente.

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

    • Come l'offset direttamente dalla DLL EndScene utilizzando strumenti come IDA Pro 4.9 libero. Dal momento che la DLL viene caricata come è, si può semplicemente aggiungere questo offset alla DLL indirizzo di partenza, quando si è mappato il gioco, per ottenere compensare l'attuale, e poi agganciarlo.

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

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

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

    • Creazione di un nuovo dispositivo in una nuova finestra, non appena la DLL viene iniettato. Poi, ottenendo il EndScene compensato da questo dispositivo e agganciandolo, risultando in un gancio per il dispositivo che viene utilizzato dal gioco.

    Downside:. , come di alcune informazioni che ho letto, la creazione di un secondo dispositivo può interferire con il dispositivo esistente, e può bug con finestra vs. modalità a schermo intero etc

    • Come il terzo metodo. Tuttavia, si potrà fare una scansione modello per ottenere EndScene.

    Downside:. non sembra che affidabile

Come posso agganciare EndScene da un DLL iniettato, che può essere caricato quando il gioco è già in funzione, senza dover affrontare con diversi d3d9.dll s 'su altri sistemi, e con un metodo che è affidabile? Come si fa a FRAPS per esempio eseguire è ganci DirectX? La DLL non si dovrebbe applicare a tutti i giochi, solo per specifici processi dove iniettare tramite CreateRemoteThread.

È stato utile?

Soluzione

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

Ora, quando il gancio è chiamato, si cerca un d3d9.dll caricato.

Se uno è caricato, si crea un oggetto D3D9 temporanea, e camminare vtable per ottenere l'indirizzo del metodo EndScene.

Poi si può patchare la chiamata EndScene, con il proprio metodo. (Sostituire la prima istruzione in EndScene da una chiamata al metodo.

Quando si è fatto, è necessario patchare la chiamata, per chiamare il metodo EndScene originale. E quindi reinstallare il cerotto.

Questo è il modo in cui lo fa FRAPS. ( link )


È possibile trovare un indirizzo funzione dal vtable di un'interfaccia.

Così si può fare quanto segue (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 non ora contiene un puntatore alla funzione stessa. Ora possiamo o rattoppare tutti i call-siti o siamo in grado di patchare la funzione stessa.

Attenzione che tutto questo dipende dalla conoscenza della realizzazione di COM-interfacce in Windows. Ma questo funziona su tutte le versioni di Windows (32 o 64, non entrambi allo stesso tempo).

Altri suggerimenti

Un po 'vecchia questione che conosco - ma nel caso in cui qualcuno è interessato a fare questo con C #, ecco il mio esempio su agganciando l'API Direct3D 9 utilizzando C # . Questo utilizza EasyHook un assembly .NET open source che permette di 'sicurezza' installare ganci da 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)

Uso un approccio simile VTable come indicato da Christopher tramite una piccola dll C ++ helper per determinare dinamicamente l'indirizzo delle funzioni IDirect3DDevice9 per agganciare. Questo viene fatto creando maniglia finestra temporale, e creando un getta IDirect3Device9 all'interno dell'assieme iniettato prima di allora agganciando le funzioni desiderate. In questo modo l'applicazione per agganciare un bersaglio che è già in esecuzione. (Aggiornamento: si noti che questo è possibile interamente all'interno C # anche - vedere i commenti sulla pagina collegata)

Aggiorna : esiste anche una versione aggiornata per aggancio Direct3D 9, 10 e 11 ancora utilizzando EasyHook e con SharpDX anziché SlimDX

So che questa domanda è vecchio, ma questo dovrebbe funzionare per qualsiasi programma utilizzando DirectX9, si crea la propria istanza in fondo, e quindi ottenere il puntatore al VTable, poi basta agganciarlo. Avrete 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);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top