🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Hook DX11 on a game

Started by
2 comments, last by mmarcoluca 1 year, 10 months ago

Hi,

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 main 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 !

Advertisement

mmarcoluca said:
the code show correctly the triangle at the center but make in pause the game until I press

This is literally what the code you show does. It spins in an endless while-loop until you press that key. Not sure why the function is called “MainThread”, if you copied the code from an example, but you are supposed to eigther run the function in a thread, or the rest of your games-code. Or you restructure the code to not use a while-loop for waiting but define an entry-point and an end-point. You really only need to call the HookD3D-function when you want to start, and the cleanup-code when you are done.

@Juliean Thank you this what I have thinked the first time but when I have changed to:

void MainThread( void* pHandle )
{
// Hook d3d
HookD3D()
}

the situation not change the game stop and don't and it doesn't go forward.

So HookD3D show the triangle overlapped in the game but after the game don't continue to work.

I need to draw some lines and rectangle in realtime independently by the game.

This topic is closed to new replies.

Advertisement