https://www.cnblogs.com/iBinary/p/10805200.html
https://www.bilibili.com/video/BV1XyHLeyEgU/
1、编译MinHook库
vs2022对应vc17,分别x86,64编译
把libMinHook.x64.lib、libMinHook.x86.lib、MinHook.h放到程序目录(dllmain.cpp同级)
2、使用MinHook库
3、MinHook函数介绍
MH_STATUS WINAPI MH_Initialize(VOID); //初始化HOOK引擎
MH_STATUS WINAPI MH_Uninitialize(VOID); //反初始化
MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);//创建HOOK跳板
MH_STATUS WINAPI MH_CreateHookApi( //创建APIhook跳板
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);
MH_STATUS WINAPI MH_CreateHookApiEx(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget);
MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); //删除HOOK
MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); //启动HOOK
MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); //结束HOOK
MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
MH_STATUS WINAPI MH_ApplyQueued(VOID);
const char * WINAPI MH_StatusToString(MH_STATUS status);
4、MinHook使用步骤
1.初始化HOOK引擎
2.创建HOOK跳板函数
3.启用HOOK
4.结束HOOK
5.删除HOOK
6.反初始化HOOK引擎
特别注意创建HOOK跳板函数.我们可以用的接口有 MB_CreateHook MB_CreateHookApi MB_CreateHookApiEx
以第一个为例: 参数1: 你要HOOK的函数的函数指针(&LoadLibraryExW) 参数2:你自定义的函数 (&MyLoadLibraryExW) 参数3:跳板函数指针
参数3的意思就是 我们的函数内部调用参数3.相当于调用原函数.
通过HOOK 游戏的渲染函数 即Present函数,以达到在游戏内绘制的效果
函数原型如下
virtual HRESULT STDMETHODCALLTYPE Present(
/* [in] */ UINT SyncInterval,
/* [in] */ UINT Flags);
很明显Present函数是一个虚函数,那么既然是虚函数我们就可以通过虚函数表来获取Present函数的地址
Present函数虚表下的定义如下
DECLSPEC_XFGVIRT(IDXGISwapChain, GetDesc)
HRESULT ( STDMETHODCALLTYPE *GetDesc )(
IDXGISwapChain * This,
/* [annotation][out] */
_Out_ DXGI_SWAP_CHAIN_DESC *pDesc);
我们知道在内存下的有虚表对象的内存如下
虚表 -> 指针 |
---|
变量 |
变量 |
变量 |
好那我们要获得虚表就先得获得IDXGISwapChain的地址以拿到他的虚表
那我们怎么获得呢?
在Windows下dx11提供了一个函数D3D11CreateDeviceAndSwapChain他可以帮助我们获得IDXGISwapChain地址
其获取方法如下
//在这里其实不用过于去纠结参数里面的内容,感兴趣可以直接去Windows官方文档里面查看
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = GetForegroundWindow();
sd.SampleDesc.Count = 1;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
ID3D11Device* device = nullptr;
IDXGISwapChain* sc = nullptr;
const D3D_FEATURE_LEVEL level[] = { D3D_FEATURE_LEVEL_10_0,D3D_FEATURE_LEVEL_11_0 };
if (D3D11CreateDeviceAndSwapChain(
0,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
0,
level,
2,
D3D11_SDK_VERSION,
&sd,
&sc,
&device,
nullptr,nullptr) == S_OK) {
void** vtable = *reinterpret_cast<void***>(sc);//获得虚表 sc存着IDXGISwapChain本体指针 (指针后前8个字节) 存着虚表
sc->Release();//释放
device->Release();//释放
*Pointer = (PVOID)vtable[8];//虚表下的第9个元素就是Present函数
好那我们获得了Present地址我们地址了,那下一步我们就要HOOK了,怎么Hook呢,在Github上有一个功能强大的项目MinHook
他可以帮助我们方便的进行Inline Hook操作,这样我们就不用自己写Hook函数啦
Hook了之后我们需要获得一些必须的数据来初始化我们的ImGui
需要的数据有:
目标渲染窗口句柄
目标渲染视图
和Context Device
我们ImGui的初始化代码如下
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui_ImplWin32_Init(m_hwnd);//FindWindow
ImGui_ImplDX11_Init(device, context);
当然了这些数据都可以通过IDXGISwapChain和WindowsApi来获得
下面就一 一讲解下怎么去获得这些需要的数据:
获得目标渲染窗口句柄我们可以通过FindWindow来获得
获得目标渲染视图这个比较重要(因为它关乎着我们是否能正确的渲染到游戏中),那要怎么获得呢,我们可以通过获得后备缓冲区来获得
ID3D11Texture2D* pBackBuffer;
dx->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
device->CreateRenderTargetView(pBackBuffer, NULL, &mainRenderTargetView);
pBackBuffer->Release();
好那么下一步我们该如何获得Context和Device呢 其实通过IDXGISwapChain里面的成员函数就可以获得
ID3D11Device* device;
ID3D11DeviceContext* context;
dx->GetDevice(__uuidof(ID3D11Device), (void**)&device);
device->GetImmediateContext(&context);
好那么我们现在已经获取了我们需要的所有数据