Home > front end >  Hook DX11 on a game
Hook DX11 on a game

Time:08-27

I have found this example to hook DirectX 11:

void MainThread( void* pHandle )
    {
        // Hook d3d
        if (HookD3D())
        {
            // END key to unload
            while (!GetAsyncKeyState( VK_END ));
        }
    
        CleanupD3D();
        WriteMem( ogPresent, ogBytes, PRESENT_STUB_SIZE );
        VirtualFree( (void*)ogPresentTramp, 0x1000, MEM_RELEASE );
        CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, pHandle, 0, 0 );
    }
    
    BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved )
    {
        switch (fdwReason)
        {
            case DLL_PROCESS_ATTACH:
                DisableThreadLibraryCalls( hinstDLL );
                CreateThread( nullptr, 0, (LPTHREAD_START_ROUTINE)MainThread, hinstDLL, 0, nullptr );
                break;
            case DLL_PROCESS_DETACH:
    
                break;
        }
        return TRUE;
    }
    
    bool Hook( void* pSrc, void* pDst, size_t size )
    {
        DWORD dwOld;
        uintptr_t src = (uintptr_t)pSrc;
        uintptr_t dst = (uintptr_t)pDst;
    
        if (!VirtualProtect( pSrc, size, PAGE_EXECUTE_READWRITE, &dwOld ))
            return false;
    
        *(char*)src = (char)0xE9;
        *(int*)(src   1) = (int)(dst - src - 5);
    
        VirtualProtect( pSrc, size, dwOld, &dwOld );
        return true;
    }
    
    bool WriteMem( void* pDst, char* pBytes, size_t size )
    {
        DWORD dwOld;
        if (!VirtualProtect( pDst, size, PAGE_EXECUTE_READWRITE, &dwOld ))
            return false;
    
        memcpy( pDst, pBytes, PRESENT_STUB_SIZE );
    
        VirtualProtect( pDst, size, dwOld, &dwOld );
        return true;
    }
    
    bool HookD3D()

        D3D_FEATURE_LEVEL featLevel;
        DXGI_SWAP_CHAIN_DESC sd{ 0 };
        sd.BufferCount = 1;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.BufferDesc.Height = 800;
        sd.BufferDesc.Width = 600;
        sd.BufferDesc.RefreshRate = { 60, 1 };
        sd.OutputWindow = GetForegroundWindow();
        sd.Windowed = TRUE;
        sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        HRESULT hr = D3D11CreateDeviceAndSwapChain( nullptr, D3D_DRIVER_TYPE_REFERENCE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &sd, &pSwapchain, &pDevice, &featLevel, nullptr );
        if (FAILED( hr )) 
            return false;
    
        void** pVMT = *(void***)pSwapchain;
 
        ogPresent = (fnPresent)(pVMT[VMT_PRESENT]);
    

        safe_release( pSwapchain );
        safe_release( pDevice );
    
  jmp (important for x64)
        void* pLoc = (void*)((uintptr_t)ogPresent - 0x2000);
        void* pTrampLoc = nullptr;
        while (!pTrampLoc)
        {
            pTrampLoc = VirtualAlloc( pLoc, 1, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE );
            pLoc = (void*)((uintptr_t)pLoc   0x200);
        }
        ogPresentTramp = (fnPresent)pTrampLoc;
    
        memcpy( ogBytes, ogPresent, PRESENT_STUB_SIZE );
        memcpy( pTrampLoc, ogBytes, PRESENT_STUB_SIZE );
        
        pTrampLoc = (void*)((uintptr_t)pTrampLoc   PRESENT_STUB_SIZE);
        
        *(char*)pTrampLoc = (char)0xE9;
        pTrampLoc = (void*)((uintptr_t)pTrampLoc   1);
        uintptr_t ogPresRet = (uintptr_t)ogPresent   5;
        *(int*)pTrampLoc = (int)(ogPresRet - (uintptr_t)pTrampLoc - 4);
        

        pTrampoline = pTrampLoc = (void*)((uintptr_t)pTrampLoc   4);
    #ifdef _WIN64
        char pJmp[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 };
        WriteMem( pTrampLoc, pJmp, ARRAYSIZE( pJmp ) );
        pTrampLoc = (void*)((uintptr_t)pTrampLoc   ARRAYSIZE( pJmp ));
        *(uintptr_t*)pTrampLoc = (uintptr_t)hkPresent;
    #else
        *(char*)pTrampLoc = (char)0xE9;
        pTrampLoc = (void*)((uintptr_t)pTrampLoc   1);
        *(int*)pTrampLoc = (uintptr_t)hkPresent - (uintptr_t)pTrampLoc - 4;
    #endif
    
        return Hook(ogPresent, pTrampoline, PRESENT_STUB_SIZE);
    }
    

    bool InitD3DHook( IDXGISwapChain * pSwapchain )
    {
        HRESULT hr = pSwapchain->GetDevice( __uuidof(ID3D11Device), (void**)&pDevice );
        if (FAILED( hr ))
            return false;
    
        pDevice->GetImmediateContext( &pContext );
        pContext->OMGetRenderTargets( 1, &pRenderTargetView, nullptr );
    
        if (!pRenderTargetView)
        {
    
            ID3D11Texture2D* pBackbuffer = nullptr;
            hr = pSwapchain->GetBuffer( 0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pBackbuffer) );
            if (FAILED( hr ))
                return false;
   
            hr = pDevice->CreateRenderTargetView( pBackbuffer, nullptr, &pRenderTargetView );
            pBackbuffer->Release();
            if (FAILED( hr ))
                return false;
    
            pContext->OMSetRenderTargets( 1, &pRenderTargetView, nullptr );
        }
        
        ID3D10Blob* pBlob = nullptr;
    
        if (!CompileShader( szShadez, "VS", "vs_5_0", &pBlob ))
            return false;
    
        hr = pDevice->CreateVertexShader( pBlob->GetBufferPointer(), pBlob->GetBufferSize(), nullptr, &pVertexShader );
        if (FAILED( hr ))
            return false;
    
   
        D3D11_INPUT_ELEMENT_DESC layout[2] = {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}
        };
        UINT numElements = ARRAYSIZE( layout );
    
        hr = pDevice->CreateInputLayout( layout, numElements, pBlob->GetBufferPointer(), pBlob->GetBufferSize(), &pVertexLayout );
        if (FAILED( hr ))
            return false;
    
        safe_release( pBlob );
    
        if (!CompileShader( szShadez, "PS", "ps_5_0", &pBlob ))
            return false;
    
        hr = pDevice->CreatePixelShader( pBlob->GetBufferPointer(), pBlob->GetBufferSize(), nullptr, &pPixelShader );
        if (FAILED( hr ))
            return false;
    
        UINT numViewports = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
        float fWidth =  0;
        float fHeight = 0;
    

        pContext->RSGetViewports( &numViewports, pViewports );
        
    
        if (!numViewports || !pViewports[MAINVP].Width)
        {
        
            //HWND hWnd0 = FindWindowA( "W2ViewportClass", nullptr );
            HWND hWnd = FindMainWindow( GetCurrentProcessId() );
            RECT rc{ 0 };
            if (!GetClientRect( hWnd, &rc ))
                return false;
    
 
            fWidth = (float)rc.right;
            fHeight = (float)rc.bottom;
    
            pViewports[MAINVP].Width = (float)fWidth;
            pViewports[MAINVP].Height = (float)fHeight;
            pViewports[MAINVP].MinDepth = 0.0f;
            pViewports[MAINVP].MaxDepth = 1.0f;
    
            pContext->RSSetViewports( 1, pViewports );
        }
        else
        {
            fWidth = (float)pViewports[MAINVP].Width;
            fHeight = (float)pViewports[MAINVP].Height;
        }

        D3D11_BUFFER_DESC bd{ 0 };
        bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
        bd.ByteWidth = sizeof( ConstantBuffer );
        bd.Usage = D3D11_USAGE_DEFAULT;
        
        mOrtho = XMMatrixOrthographicLH( fWidth, fHeight, 0.0f, 1.0f );
        ConstantBuffer cb;
        cb.mProjection = mOrtho;
        
        D3D11_SUBRESOURCE_DATA sr{ 0 };
        sr.pSysMem = &cb;
        hr = pDevice->CreateBuffer( &bd, &sr, &pConstantBuffer );
        if (FAILED( hr ))
            return false;

        ZeroMemory( &bd, sizeof( bd ) );
        bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
        bd.Usage = D3D11_USAGE_DEFAULT;
        bd.ByteWidth = 3 * sizeof( Vertex );
        bd.StructureByteStride = sizeof( Vertex );
    
        float left = fWidth / -2;
        float top = fHeight / 2;
    
        float w = 50;
        float h = 50;

        float fPosX = -1 * left;
        float fPosY = top;
    
        Vertex pVerts[3] = {
            { XMFLOAT3( left   fPosX,           top - fPosY   h / 2,    1.0f ), XMFLOAT4( 1.0f, 0.0f, 0.0f, 1.0f ) },
            { XMFLOAT3( left   fPosX   w / 2,   top - fPosY - h / 2,    1.0f ), XMFLOAT4( 0.0f, 0.0f, 1.0f, 1.0f ) },
            { XMFLOAT3( left   fPosX - w / 2,   top - fPosY - h / 2,    1.0f ), XMFLOAT4( 0.0f, 1.0f, 0.0f, 1.0f ) },
        };    
    
        ZeroMemory( &sr, sizeof( sr ) );
        sr.pSysMem = &pVerts;
        hr = pDevice->CreateBuffer( &bd, &sr, &pVertexBuffer );
        if (FAILED( hr ))
            return false;
    
        ZeroMemory( &bd, sizeof( bd ) );
        ZeroMemory( &sr, sizeof( sr ) );
    
        UINT pIndices[3] = { 0, 1, 2 };
        bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
        bd.Usage = D3D11_USAGE_DEFAULT;
        bd.ByteWidth = sizeof( UINT ) * 3;
        bd.StructureByteStride = sizeof( UINT );
    
        sr.pSysMem = &pIndices;
        hr = pDevice->CreateBuffer( &bd, &sr, &pIndexBuffer );
        if (FAILED( hr ))
            return false;
    
        return true;
    }
    
    
    void Render()
    {   
        pContext->OMSetRenderTargets( 1, &pRenderTargetView, nullptr );
    
        ConstantBuffer cb;
        cb.mProjection = XMMatrixTranspose( mOrtho );
        pContext->UpdateSubresource( pConstantBuffer, 0, nullptr, &cb, 0, 0 );
        pContext->VSSetConstantBuffers( 0, 1, &pConstantBuffer );
    
        UINT stride = sizeof( Vertex );
        UINT offset = 0;
        pContext->IASetVertexBuffers( 0, 1, &pVertexBuffer, &stride, &offset );
        pContext->IASetInputLayout( pVertexLayout );
        pContext->IASetIndexBuffer( pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );
        pContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
        
        pContext->VSSetShader( pVertexShader, nullptr, 0 );
        pContext->PSSetShader( pPixelShader, nullptr, 0 );
    
        pContext->RSSetViewports( 1, pViewports );

        pContext->DrawIndexed( 3, 0, 0 );
    }
    
    HRESULT __stdcall hkPresent( IDXGISwapChain * pThis, UINT SyncInterval, UINT Flags )
    {
        pSwapchain = pThis;
    
        if (!pDevice)
        {
            if (!InitD3DHook( pThis ))
                return false;
        }
    
        Render();
        return ogPresentTramp( pThis, SyncInterval, Flags );
    }

the code show correctly the triangle at the center but make in pause the game until I press "end" key.

In short the game start normally show a presentation and after a few seconds it should show another presentation but dont't do nothing.

If I press "end" skip all and go directly to game menu.

// Hook
if (HookD3D())
{
   // END key to unload
   while (!GetAsyncKeyState( VK_END ));
}


CleanupD3D();
WriteMem( ogPresent, ogBytes, PRESENT_STUB_SIZE );
VirtualFree( (void*)ogPresentTramp, 0x1000, MEM_RELEASE );
CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, pHandle, 0, 0 );

The game after call "HookD3D" stop to work and only when the user press "end" and call "CleanupD3D()" the game continue to work normally.

My impression is that this code is context dependent, if it changes it doesn't work anymore.

I ask if there is a way to show the triangle without stop the game.

Thanks !

CodePudding user response:

// Hook
if (HookD3D())
{
   // END key to unload
   while (!GetAsyncKeyState( VK_END ));
}

This is a dead lock loop. It's just going to spin in the current thread until you press END which is exactly what you described.

  • Related