C/C++ ImGUI劫持Dx9绘制窗体

2022-12-28 13:08:25 浏览数 (2)

ImGUI 是一个无任何第三方依赖的图形化界面组件,其支持多种绘图引擎,ImGUI可用于绘制辅助菜单功能,注入游戏内部方便快捷。

ImGUI下载:https://github.com/ocornut/imgui/releases/tag/v1.60

下载好以后用户需自行配置ImGUI到项目中,并配置D3Dx9开发工具包,此处的IMGUI需要取出imgui-1.60examplesdirectx9_example里面的imgui_impl_dx9并放入根目录。

并将其导入到项目目录下,添加现有项,导入所有的包。

注入测试代码如下:

代码语言:javascript复制
#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菜单。

0 人点赞