[英]Hook DX11 on a game
我发现这个例子可以挂钩 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 );
}
代码正确显示了中心的三角形,但暂停游戏,直到我按下“结束”键。
简而言之,游戏开始通常会显示一个演示文稿,几秒钟后它应该显示另一个演示文稿,但不要什么都不做。
如果我按“结束”跳过所有 go 直接进入游戏菜单。
// 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 );
调用“HookD3D”后游戏停止运行,只有当用户按下“结束”并调用“CleanupD3D()”时,游戏才能继续正常运行。
我的印象是这段代码是依赖于上下文的,如果它改变它就不再起作用了。
我问是否有办法在不停止游戏的情况下显示三角形。
谢谢 !
// Hook
if (HookD3D())
{
// END key to unload
while (!GetAsyncKeyState( VK_END ));
}
这是一个死锁循环。 它只会在当前线程中旋转,直到您按 END ,这正是您所描述的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.