Question

Je veux faire un détour EndScene d'une application DirectX 9 arbitraire pour créer une petite superposition. À titre d'exemple, vous pouvez prendre la superposition du compteur de trame de FRAPS, qui est représenté dans les jeux quand il est activé.

Je sais que les méthodes suivantes pour faire ceci:

  1. Création d'une nouvelle d3d9.dll , qui est ensuite copié sur le chemin des jeux. Étant donné que le dossier en cours est recherché d'abord, avant d'aller SYSTEM32 etc., ma DLL modifiée se charge, exécuter mon code supplémentaire.

    Inconvénient: Vous devez le mettre là avant de commencer le jeu

    .
    • Identique à la première méthode, mais en remplaçant la DLL dans system32 directement.

    Inconvénient: Vous ne pouvez pas ajouter jeu code spécifique. Vous ne pouvez pas exclure les applications où vous ne voulez pas que votre DLL à charger.

    • Obtenir le EndScene décalage directement à partir de la DLL à l'aide d'outils tels que l'IDA Pro 4.9 gratuit. Étant donné que la DLL se charge comme cela est, vous pouvez simplement ajouter ce décalage à l'adresse de départ DLL, quand il est mis en correspondance avec le jeu, pour obtenir le décalage réel, puis le brancher.

    Inconvénient: Le décalage est pas le même sur tous les systèmes

    .
    • Accrochage Direct3DCreate9 pour obtenir le D3D9, accrochage puis D3D9-> CreateDevice pour obtenir le pointeur de périphérique, puis accrocher Device-> EndScene à travers la table virtuelle.

    Inconvénient: La DLL ne peut pas être injecté, lorsque le processus est en cours d'exécution. Vous devez commencer le processus avec le drapeau de CREATE_SUSPENDED pour accrocher la première Direct3DCreate9 .

    • Création d'un nouveau périphérique dans une nouvelle fenêtre, dès que la DLL s'injecté. Ensuite, obtenir le EndScene décalé par rapport à ce dispositif et accrochant, entraînant un crochet pour le dispositif qui est utilisé par le jeu.

    Inconvénient:. que de certaines informations que j'ai lu, la création d'un second dispositif peut interférer avec le dispositif existant, et il peut bug avec fenêtré vs mode plein écran, etc

    • Identique à la troisième méthode. Cependant, vous allez faire un balayage de modèle pour obtenir EndScene.

    Inconvénient:. ne semble pas fiable

Comment puis-je brancher EndScene d'une DLL injectée, ce qui peut être chargé quand le jeu est déjà en cours d'exécution, sans avoir à faire face à différentes d3d9.dll 's sur d'autres systèmes, et une méthode qui est fiable? Comment peut-FRAPS par exemple exécuter DirectX est des crochets? La DLL ne doit pas appliquer à tous les jeux, juste à des processus spécifiques où j'injectent via CreateRemoteThread.

Était-ce utile?

La solution

Vous installez un système large crochet. (SetWindowsHookEx) Ceci fait, vous obtenez à charger dans tous les processus.

lorsque le crochet est appelé, vous recherchez un d3d9.dll chargé.

Si l'on est chargé, vous créez un objet D3D9 temporaire, et marcher le vtable pour obtenir l'adresse de la méthode EndScene.

Ensuite, vous pouvez patcher l'appel EndScene, avec votre propre méthode. (Remplacer la première instruction EndScene par un appel à votre méthode.

Lorsque vous avez terminé, vous devez patcher le rappel, pour appeler la méthode EndScene originale. Et puis réinstallez votre patch.

Ceci est la façon dont FRAPS le fait. ( lien )


Vous pouvez trouver une adresse de fonction de la vtable d'une interface.

Vous pouvez effectuer les opérations suivantes (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 ne contient maintenant un pointeur sur la fonction elle-même. Nous pouvons maintenant patcher soit tous les appels-sites ou nous pouvons patcher la fonction elle-même.

Gardez-vous que tout cela dépend de la connaissance de la mise en œuvre des COM-Interfaces dans Windows. Mais cela fonctionne sur toutes les versions de Windows (32 ou 64, pas les deux en même temps).

Autres conseils

Une question un peu vieux, je sais - mais dans le cas où quelqu'un est intéressé à le faire avec C #, voici mon exemple sur accrochage de l'API Direct3D 9 en utilisant C # . Ceci utilise EasyHook un ensemble de .NET open source qui vous permet de « en toute sécurité » installer les crochets de code managé en fonctions non gérés. (Note: EasyHook prend soin de toutes les questions entourant l'injection de DLL - par exemple CREATE_SUSPENDED, ACL, 32 vs et ainsi de suite 64 bits)

J'utilise une approche similaire VTable comme mentionné par Christopher via une petite dll aide C ++ pour déterminer dynamiquement l'adresse des fonctions IDirect3DDevice9 à l'hameçon. Ceci est réalisé en créant une poignée de fenêtre temporaire, et la création d'un IDirect3Device9 jetable dans l'ensemble injecté avant alors accrocher les fonctions souhaitées. Cela permet à votre application d'accrocher une cible qui est déjà en cours d'exécution. (Mise à jour: notez que cela est possible entièrement dans C # aussi - voir les commentaires sur la page liée)

Mise à jour : il y a aussi une version mise à jour pour accrochage Direct3D 9, 10 et 11 utilisant encore EasyHook et avec SharpDX au lieu de SlimDX

Je sais que cette question est vieux, mais cela devrait fonctionner pour tout programme en utilisant DirectX9, vous créez votre propre instance au fond, puis obtenir le pointeur sur le VTable, vous accrochez juste. Vous aurez besoin de détours 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();

Et puis votre fonction:

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

    //Call the original (if you want)
    return EndScene(pDevice);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top