Pergunta

Eu quero desviar EndScene De um aplicativo arbitrário DirectX 9 para criar uma pequena sobreposição. Como exemplo, você pode pegar a sobreposição de contador de quadros de FRAPS, que é mostrada nos jogos quando ativada.

Eu sei os seguintes métodos para fazer isso:

  1. Criando um novo d3d9.dll, que é então copiado para o caminho dos jogos. Como a pasta atual é pesquisada primeiro, antes de ir para o System32 etc., minha DLL modificada é carregada, executando meu código adicional.

    Desvantagem: Você tem que colocá -lo lá antes de começar o jogo.

    • O mesmo que o primeiro método, mas substituindo a DLL no System32 diretamente.

    Desvantagem: Você não pode adicionar código específico do jogo. Você não pode excluir aplicativos onde não deseja que sua DLL seja carregada.

    • O compensando a cena final diretamente da DLL usando ferramentas como o IDA Pro 4.9 gratuitamente. Como a DLL é carregada como está, você pode apenas adicionar esse deslocamento ao endereço inicial da DLL, quando for mapeado para o jogo, para obter o deslocamento real e depois conectá -lo.

    Desvantagem: O deslocamento não é o mesmo em todos os sistemas.

    • Entrando DIRETO3DCREATE9 Para obter o D3D9, depois enganchar D3D9-> Createvice para obter o ponteiro do dispositivo e depois amarrar Dispositivo-> Endscene através da tabela virtual.

    Desvantagem: A DLL não pode ser injetada, quando o processo já estiver em execução. Você tem que iniciar o processo com o CREATE_SUSPENDED bandeira para conectar o inicial DIRETO3DCREATE9.

    • Criando um novo dispositivo em uma nova janela, assim que a DLL for injetada. Então, obtendo o EndScene Deslocamento deste dispositivo e conectá -lo, resultando em um gancho para o dispositivo usado pelo jogo.

    Desvantagem: A partir de algumas informações que li, a criação de um segundo dispositivo pode interferir no dispositivo existente e pode se embalar no modo de janela vs. tela cheia etc.

    • O mesmo que o terceiro método. No entanto, você fará uma varredura de padrão para obter EndScene.

    Desvantagem: não parece tão confiável.

Como posso conectar EndScene de uma DLL injetada, que pode ser carregada quando o jogo já estiver em execução, sem ter que lidar com diferentes d3d9.dllestá em outros sistemas e com um método que é confiável? Como o Fraps, por exemplo, executa seus ganchos diretos? A DLL não deve se aplicar a todos os jogos, apenas a processos específicos em que eu a injeto via CreateRemoteThread.

Foi útil?

Solução

Você instala um gancho amplo do sistema. (SetWindowshookex) Com isso feito, você pode ser carregado em todos os processos.

Agora, quando o gancho é chamado, você procura um d3d9.dll carregado.

Se um for carregado, você cria um objeto D3D9 temporário e anda a vtable para obter o endereço do método da cena final.

Em seguida, você pode corrigir a chamada final da cena, com seu próprio método. (Substitua a primeira instrução no Endscene por uma chamada para o seu método.

Quando terminar, você deve consertar a chamada de volta, para ligar para o método original da cena final. E depois reinstale seu patch.

É assim que Fraps faz isso. (Link)


Você pode encontrar um endereço de função a partir da vtable de uma interface.

Então você pode fazer o seguinte (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 agora contém um ponteiro para a própria função. Agora, podemos patorar todos os sites de chamada ou podemos corrigir a própria função.

Cuidado que tudo isso depende do conhecimento da implementação de interfaces COM no Windows. Mas isso funciona em todas as versões do Windows (32 ou 64, não ambas ao mesmo tempo).

Outras dicas

Uma pergunta um pouco antiga que eu conheço - mas caso alguém esteja interessado em fazer isso com C#, aqui está o meu exemplo sobre Concluindo a API Direct3D 9 usando C#. Isso utiliza o EasyHook um conjunto .NET de código aberto que permite 'instalar com segurança' ganchos do código gerenciado em funções não gerenciadas. (Nota: EasyHook cuida de todos os problemas em torno da injeção de DLL - por exemplo, create_suspended, ACL's, 32 vs 64 bits e assim por diante)

Eu uso uma abordagem vtable semelhante, como mencionado por Christopher por meio de uma pequena DLL de auxiliar C ++ para determinar dinamicamente o endereço das funções Idirect3DDevice9 para conectar. Isso é feito criando uma alça de janela temporária e criando um IDirect3Device9, dentro da montagem injetada antes de conectar as funções desejadas. Isso permite que seu aplicativo conecte um alvo que já está em execução (atualize: observe que isso é possível inteiramente dentro de C# também - veja os comentários na página vinculada).

Atualizar: Há também uma versão atualizada para Hooking Direct3d 9, 10 e 11 Ainda usando o EasyHook e com SharpDx em vez de Slimdx

Sei que essa pergunta é antiga, mas isso deve funcionar para qualquer programa usando o DirectX9, você está criando sua própria instância basicamente e, em seguida, obtendo o ponteiro para o VTable, então você apenas o conecta. Você precisará de desvios 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 então sua função:

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

    //Call the original (if you want)
    return EndScene(pDevice);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top