Hooking DirectX endScene de una DLL inyectado
-
22-09-2019 - |
Pregunta
Quiero desvío EndScene
desde una aplicación arbitraria de DirectX 9 para crear una pequeña superposición. A modo de ejemplo, se puede tomar la superposición contador de cuadros de FRAPS, que se muestra en los juegos cuando se activa.
Yo sé los siguientes métodos para hacer esto:
-
Creación de un nuevo d3d9.dll , que se copia en la ruta de juegos. Desde la carpeta actual se busca en primer lugar, antes de ir a system32 etc., mi DLL modificada se carga, la ejecución de mi código adicional.
Desventaja: Tienes que ponerlo allí antes de iniciar el juego
.- Igual que el primer método, pero sustituyendo la DLL en system32 directamente.
Desventaja: No se puede agregar código específico de juego. No se puede excluir aplicaciones en las que no desea el archivo DLL se va a cargar.
- Obtener la endScene compensado directamente de la DLL usando herramientas como IDA Pro 4.9 gratis. Dado que la DLL se carga como es, puede simplemente añadir este desplazamiento a la DLL dirección de inicio, cuando se asigna al juego, para conseguir compensar el real, y luego conectarlo.
Desventaja:. El desplazamiento no es el mismo en todos los sistemas
- El enganchar Direct3DCreate9 para obtener el D3D9, después enganchando D3D9-> CreateDevice para obtener el puntero del dispositivo y, a continuación, conectar Device-> endScene a través de la mesa virtual.
Desventaja: El DLL no se puede inyectar, cuando el proceso ya se está ejecutando. Usted tiene que comenzar el proceso con la bandera
CREATE_SUSPENDED
para enganchar la inicial Direct3DCreate9 .- Creación de un nuevo dispositivo en una nueva ventana, tan pronto como el archivo DLL se inyecta. Entonces, conseguir el desplazamiento de este dispositivo
EndScene
y se enganche, lo que resulta en un gancho para el dispositivo que se utiliza por el juego.
Desventaja:. a partir de cierta información que he leído, la creación de un segundo dispositivo puede interferir con el dispositivo existente, y que puede fastidiar con el modo de pantalla completa vs. ventana etc
- Igual que el tercer método. Sin embargo, que va a hacer un patrón de escaneo para obtener
EndScene
.
Desventaja: no se ve tan confiable
.
¿Cómo puedo conectar EndScene
de una DLL inyectado, que puede ser cargado cuando el juego ya está en marcha, sin tener que lidiar con diferentes d3d9.dll 's en otros sistemas, y con un método que es fiable? ¿Cómo FRAPS por ejemplo realizan ganchos es DirectX?
La DLL no debe aplicarse a todos los juegos, sólo para procesos específicos en los que se inyecte a través de CreateRemoteThread
.
Solución
instalar un sistema de gancho ancho. (SetWindowsHookEx) Una vez hecho esto, se llega a ser cargado en cada proceso.
Ahora, cuando el gancho se llama, usted busca un d3d9.dll cargado.
Si uno está cargado, se crea un objeto D3D9 temporal, y caminar la viable para obtener la dirección del método endScene.
A continuación, se puede parchear la llamada endScene, con su propio método. (Vuelva a colocar la primera instrucción en endScene por una llamada a su método.
Cuando haya terminado, usted tiene que arreglar la parte posterior llamada, para llamar al método endScene originales. Y luego volver a instalar el parche.
Esta es la forma Fraps lo hace. ( Enlace )
Puede encontrar una dirección de función de la viable de una interfaz.
Así que usted puede hacer lo siguiente (Pseudo-Código):
IDirect3DDevice9* pTempDev = ...;
const int EndSceneIndex = 26 (?);
typedef HRESULT (IDirect3DDevice9::* EndSceneFunc)( void );
BYTE* pVtable = reinterpret_cast<void*>( pTempDev );
EndSceneFunc = pVtable + sizeof(void*) * EndSceneIndex;
EndSceneFunc hace ahora contiene un puntero a la función misma. Podemos ahora tampoco parchear todas las llamadas sitios o podemos arreglar la propia función.
Mira que todo esto depende de los conocimientos de la aplicación de COM-interfaces en Windows. Pero esto funciona en todas las versiones de Windows (32 o 64, no ambos al mismo tiempo).
Otros consejos
Un poco vieja pregunta que sé - pero en caso de que alguien está interesado en hacer esto con C #, aquí está mi ejemplo en enganchando la API Direct3D 9 usando C # . Esto se hace a EasyHook una fuente NET abierta que le permite 'segura' instalar ganchos de código administrado en funciones no administradas. (Nota: EasyHook se encarga de todas las cuestiones relacionadas con la inyección de DLL - por ejemplo, CREATE_SUSPENDED, ACL, 32 vs 64 bits y así sucesivamente)
I utilizar un enfoque Vtable similar a la mencionada por Christopher través de un pequeño C ++ helper DLL para determinar dinámicamente la dirección de las funciones IDirect3DDevice9 a gancho. Esto se hace mediante la creación de un identificador de ventana temporal, y la creación de un IDirect3Device9 de usar y tirar dentro de la asamblea antes de inyectarse después enganchando las funciones deseadas. Esto permite a su aplicación para enganchar un objetivo que ya se está ejecutando (Actualización: nota que esto es posible por completo dentro de C # también - véanse los comentarios sobre la página enlazada).
Actualizar : también hay una versión actualizada de enganchar Direct3D 9, 10 y 11 sigue utilizando EasyHook y con SharpDX en lugar de SlimDX
Sé que esta pregunta es antiguo, pero esto debería funcionar para cualquier programa usando DirectX 9, va a crear su propia instancia, básicamente, y luego conseguir el puntero a la viable, a continuación, sólo conectarlo. Necesitará desvíos 3.x por cierto:
//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();
Y entonces su función:
HRESULT WINAPI D3D9Hook::newEndScene(LPDIRECT3DDEVICE9 pDevice)
{
//Do your stuff here
//Call the original (if you want)
return EndScene(pDevice);
}