C/C++ ImGUI劫持Dx9绘制窗体
ImGUI 是一个无任何第三方依赖的图形化界面组件,其支持多种绘图引擎,ImGUI可用于绘制辅助菜单功能,注入游戏内部方便快捷。
ImGUI下载:https://github.com/ocornut/imgui/releases/tag/v1.60
下载好以后用户需自行配置ImGUI到项目中,并配置D3Dx9开发工具包,此处的IMGUI需要取出imgui-1.60\examples\directx9_example
里面的imgui_impl_dx9
并放入根目录。
并将其导入到项目目录下,添加现有项,导入所有的包。
注入测试代码如下:
#include <Windows.h>
#include <d3dx9.h>
#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_internal.h"
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
#pragma pack(push)
#pragma pack(1)
#ifndef _WIN64
// 配置使用UTF8
#pragma execution_character_set("utf-8")
struct JmpCode
{
private:
const BYTE jmp;
DWORD address;
public:
JmpCode(DWORD srcAddr, DWORD dstAddr): jmp(0xE9)
{
setAddress(srcAddr, dstAddr);
}
void setAddress(DWORD srcAddr, DWORD dstAddr)
{
address = dstAddr - srcAddr - sizeof(JmpCode);
}
};
#else
struct JmpCode
{
private:
BYTE jmp[6];
uintptr_t address;
public:
JmpCode(uintptr_t srcAddr, uintptr_t dstAddr)
{
static const BYTE JMP[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 };
memcpy(jmp, JMP, sizeof(jmp));
setAddress(srcAddr, dstAddr);
}
void setAddress(uintptr_t srcAddr, uintptr_t dstAddr)
{
address = dstAddr;
}
};
#endif
#pragma pack(pop)
// 修改函数前面5个字节实现跳转
void hook(void* originalFunction, void* hookFunction, BYTE* oldCode)
{
JmpCode code((uintptr_t)originalFunction, (uintptr_t)hookFunction);
DWORD oldProtect, oldProtect2;
VirtualProtect(originalFunction, sizeof(code), PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(oldCode, originalFunction, sizeof(code));
memcpy(originalFunction, &code, sizeof(code));
VirtualProtect(originalFunction, sizeof(code), oldProtect, &oldProtect2);
}
void unhook(void* originalFunction, BYTE* oldCode)
{
DWORD oldProtect, oldProtect2;
VirtualProtect(originalFunction, sizeof(JmpCode), PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(originalFunction, oldCode, sizeof(JmpCode));
VirtualProtect(originalFunction, sizeof(JmpCode), oldProtect, &oldProtect2);
}
void* endSceneAddr = NULL;
BYTE endSceneOldCode[sizeof(JmpCode)];
// 窗口句柄
WNDPROC oWndProc = nullptr;
HWND Window = nullptr;
// 是否第一次初始化
BOOL is_init = FALSE;
// 单选框设置状态
bool show_another_window = false;
// imgui 窗体消息用的
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Imgui窗体消息循环
LRESULT CALLBACK hkWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
switch (wParam)
{
// 按下F9弹窗
case VK_F9:
MessageBox(0, 0, 0, 0);
break;
}
break;
}
//消息 操作imgui用的
if (ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam)) return 1;
return CallWindowProc(oWndProc, hWnd, uMsg, wParam, lParam);
}
HRESULT STDMETHODCALLTYPE MyEndScene(IDirect3DDevice9* thiz)
{
// 是否第一次执行
if (is_init == FALSE)
{
// 初始化ImGUI字体
is_init = TRUE;
ImGuiIO& io = ImGui::GetIO();
io.DeltaTime = 1.0f / 60.0f;
D3DDEVICE_CREATION_PARAMETERS d3dcp{ 0 };
thiz->GetCreationParameters(&d3dcp);
ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\simkai.ttf", 14.0f, NULL, io.Fonts->GetGlyphRangesChinese());
ImGui_ImplDX9_Init(d3dcp.hFocusWindow, thiz);
ImGui::StyleColorsDark();
// 挂钩EndScene函数
unhook(endSceneAddr, endSceneOldCode);
HRESULT hr = thiz->EndScene();
hook(endSceneAddr, MyEndScene, endSceneOldCode);
return hr;
}
// -------------------------------------------------------------
// Imgui绘制过程
// 定义静态变量
static float f = 0.0f;
static int counter = 0;
static char sz[256] = { 0 };
ImGui_ImplDX9_NewFrame();
ImGui::Begin("LyShark.com 辅助GUI主菜单");
ImGui::Checkbox("弹出子窗口", &show_another_window);
ImGui::SliderFloat("浮点条", &f, 0.0f, 1.0f);
ImGui::InputText("输入内容", sz, 256, 0, 0, 0);
if (ImGui::Button("点我触发"))
{
counter++;
}
ImGui::SameLine();
ImGui::Text("触发次数 = %d", counter);
ImGui::Text("当前FPS: %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
// 子窗体菜单
if (show_another_window)
{
ImGui::Begin("我是子窗体", &show_another_window);
ImGui::Text(" 您好,LyShark, 这是一个测试窗体 !");
// 按钮状态
if (ImGui::Button("关闭窗体"))
{
show_another_window = false;
}
ImGui::End();
}
ImGui::EndFrame();
// 把imgui东西刷新上去
ImGui::Render();
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
// 结束绘制,取消hook
unhook(endSceneAddr, endSceneOldCode);
HRESULT hr = thiz->EndScene();
hook(endSceneAddr, MyEndScene, endSceneOldCode);
return hr;
}
// 该函数作用是得到endSceneAddr地址
DWORD WINAPI initHookThread(LPVOID dllMainThread)
{
WaitForSingleObject(dllMainThread, INFINITE);
CloseHandle(dllMainThread);
WNDCLASSEX wc = {};
wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC;
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = L"Window";
if (RegisterClassEx(&wc) == 0)
{
return 0;
}
HWND hwnd = CreateWindowEx(0, wc.lpszClassName, L"", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, NULL, NULL, wc.hInstance, NULL);
if (hwnd == NULL)
{
return 0;
}
// 初始化D3D
IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
if (d3d9 == NULL)
{
DestroyWindow(hwnd);
return 0;
}
D3DPRESENT_PARAMETERS pp = {};
pp.Windowed = TRUE;
pp.SwapEffect = D3DSWAPEFFECT_COPY;
IDirect3DDevice9* device;
if (FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &pp, &device)))
{
d3d9->Release();
DestroyWindow(hwnd);
return 0;
}
// hook EndScene
// EndScene是IDirect3DDevice9第43个函数
endSceneAddr = (*(void***)device)[42];
hook(endSceneAddr, MyEndScene, endSceneOldCode);
// 释放
d3d9->Release();
device->Release();
DestroyWindow(hwnd);
return 0;
}
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
DWORD dwCurProcessId = *((DWORD*)lParam);
DWORD dwProcessId = 0;
GetWindowThreadProcessId(hwnd, &dwProcessId);
if (dwProcessId == dwCurProcessId && GetParent(hwnd) == NULL)
{
*((HWND *)lParam) = hwnd;
return FALSE;
}
return TRUE;
}
HWND GetMainWindow()
{
DWORD dwCurrentProcessId = GetCurrentProcessId();
if (!EnumWindows(EnumWindowsProc, (LPARAM)&dwCurrentProcessId))
{
return (HWND)dwCurrentProcessId;
}
return NULL;
}
// dll 入口
BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// 获取自身线程句柄
HANDLE curThread;
if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &curThread, SYNCHRONIZE, FALSE, 0))
return FALSE;
// 在另一个线程初始化COM组件
CloseHandle(CreateThread(NULL, 0, initHookThread, curThread, 0, NULL));
// 获取自身进程窗口句柄
Window = GetMainWindow();
// 设置窗口循环消息
oWndProc = (WNDPROC)SetWindowLongA(Window, GWL_WNDPROC, (LONG)hkWndProc);
if (oWndProc == nullptr)
return 1;
// 创建IMGUI上下文
ImGui::CreateContext();
break;
case DLL_PROCESS_DETACH:
if (endSceneAddr != NULL)
unhook(endSceneAddr, endSceneOldCode);
break;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
打开一个带有Dx9引擎的图形化界面,并将生成的DLL注入到游戏内,即可弹出内部GUI菜单。
文章出处:https://www.cnblogs.com/LyShark/p/9051751.html
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?