Imgui hook 注入 DirectX 和 OpenGL
Imgui hook 注入 DirectX 和 OpenGL
0x01 Imgui 工作流程
Imgui 的工作流程简单来说分为下面三步:
初始化
渲染
释放
下面以D3d11 Imgui Example为例解释需要获取的参数:
Imgui 初始化
1 | |
可以看出初始化需要 ID3D11Device 和 ID3D11DeviceContext
Imgui 渲染
1 | |
可以看出渲染将Imgui的数据写入 ID3D11DeviceContext 后 调用 IDXGISwapChain 的 Present 函数
Imgui 清理
1 | |
0x02 Imgui hook
如果要在界面上显示我们自己的 Imgui 内容, 那么必须要在渲染完毕之前注入我们自定义渲染逻辑
在Dx11中, 渲染完毕函数为
IDXGISwapChain::Present在Dx9中, 渲染完毕为
LPDIRECT3DDEVICE9::EndScene在OpenGL中, 渲染完毕为
SwapBuffers
只需要hook这几个函数, 将我们的渲染逻辑注入即可
DirectX hook
在DirectX中, Present 和 EndScene 均为类中的成员, 这种hook通常通过虚函数表的方式进行hook
为了获取函数的位置, 我们得手动创建一个D3d对象, 对于D3d11, 我们需要创建 IDXGISwapChain, 对于D3d9, 需要创建 LPDIRECT3DDEVICE9
D3d9
1 | |
D3d11
1 | |
他们都有三个参数
HWND: 当前窗口句柄v_table: 虚表列表size: 虚表大小
大小和偏移可由文档获取
获取 HWND 代码如下:
1 | |
获取到虚表后, 便可以定义方法的签名, 类中的方法有一个隐藏的参数 this, 则签名如下:
1 | |
随后就可以保存其记录的地址了
1 | |
获取到地址后即可对相应函数进行Hook, 其中 HookPresent 为要被替代的函数
1 | |
OpenGL hook
OpenGL的相对来说更简单, 因为 SwapBuffers 是一个全局函数, 可以直接获取其地址, 而不用找虚函数表
1 | |
0x03 Imgui 初始化和渲染
替换掉渲染函数后, 就可以对Imgui初始化了, 由于渲染函数会调用多次, 但是初始化只能初始化一次, 所以其流程为
判断是否初始化, 如果初始化则初始化
Imgui NewFrame
绘制
绘制结束
调用原函数完成绘制
D3d11
1 | |
D3d9
1 | |
OpenGL
1 | |
可以看到逻辑基本上一致, 只有不同平台的Imgui接口不同
至此, Imgui的dll被注入后就可以显示出基础ui了, 完整代码可以在 github 上查看到