Hook mono_compile_method导致Unity Mono游戏崩溃的排查求助
Unity Mono游戏Hook mono_compile_method崩溃问题排查与修复
问题描述
我正在给基于Unity Mono架构的游戏编写C++外挂,由于这类游戏的方法是运行时编译、无静态地址,所以选择Hookmono_compile_method来追踪编译的方法。但调用该函数时游戏会崩溃(先打印地址到控制台后崩溃),经Cheat Engine确认Hook位置正确,但Hook函数无返回地址,不确定是否正常。
原始代码
#include "pch.h" #include <Minhook.h> #include <Windows.h> #include <iostream> void* (__fastcall* mono_compile_method_o)(void* method, DWORD a1, DWORD a2); void* mono_compile_method_h(void* method, DWORD a1, DWORD a2) { std::cout << "a1: " << a1 << std::endl; std::cout << "a2: " << a2 << std::endl; std::cout << "method: " << method << std::endl; return mono_compile_method_o(method, a1, a2); } void InitHooks() { uintptr_t monoDll = (uintptr_t)GetModuleHandle(L"mono.dll"); uintptr_t mono_compile_method_address = (uintptr_t)GetProcAddress((HMODULE)monoDll, "mono_compile_method"); std::cout << mono_compile_method_address << std::endl; MH_Initialize(); MH_CreateHook(reinterpret_cast<LPVOID*>(mono_compile_method_address), &mono_compile_method_h, (LPVOID*)mono_compile_method_o); MH_EnableHook(MH_ALL_HOOKS); return; }
核心问题与修复方案
- MinHook参数传递错误:
MH_CreateHook的第三个参数需要传入原始函数指针变量的地址,你直接传递了mono_compile_method_o的初始NULL值,导致MinHook无法正确保存原始函数地址,调用时必然崩溃。修复:将(LPVOID*)mono_compile_method_o改为(LPVOID*)&mono_compile_method_o。 - 函数调用约定不匹配:Mono导出函数通常用
__cdecl调用约定(Windows平台),而非__fastcall。调用约定错误会破坏栈平衡,直接引发崩溃。修复:将函数指针和Hook函数的调用约定改为__cdecl。 - 参数类型错误:
DWORD是32位无符号整数,若游戏为64位,mono_compile_method的参数可能是64位类型,用DWORD会截断参数值;此外该函数的实际参数通常是int(优化级别)和unsigned int(编译标志),而非DWORD。修复:根据对应Mono版本的函数签名修正参数类型。 - 控制台输出线程冲突:游戏线程中调用
std::cout可能引发同步冲突,或游戏进程不支持标准输出环境,导致崩溃。修复:改用OutputDebugString输出日志,通过DebugView工具查看。 - 位数不匹配:外挂编译位数(32/64位)与游戏不一致时,指针长度不匹配会直接崩溃。修复:确保外挂编译位数与游戏完全一致。
修正后的代码
#include "pch.h" #include <Minhook.h> #include <Windows.h> #include <cstdio> // 匹配Mono官方的mono_compile_method签名(以常见版本为例) typedef void*(__cdecl* mono_compile_method_t)(void* method, int opt_level, unsigned int flags); mono_compile_method_t mono_compile_method_o = nullptr; void* __cdecl mono_compile_method_h(void* method, int opt_level, unsigned int flags) { // 用OutputDebugString输出,避免线程问题 char log_buf[256]; sprintf_s(log_buf, "method addr: %p | opt_level: %d | flags: %u\n", method, opt_level, flags); OutputDebugStringA(log_buf); // 安全调用原始函数 if (mono_compile_method_o) { return mono_compile_method_o(method, opt_level, flags); } return nullptr; } void InitHooks() { HMODULE mono_dll = GetModuleHandle(L"mono.dll"); if (!mono_dll) { OutputDebugStringA("Failed to load mono.dll\n"); return; } void* compile_method_addr = GetProcAddress(mono_dll, "mono_compile_method"); if (!compile_method_addr) { OutputDebugStringA("Failed to find mono_compile_method\n"); return; } char addr_buf[64]; sprintf_s(addr_buf, "mono_compile_method addr: %p\n", compile_method_addr); OutputDebugStringA(addr_buf); // 初始化MinHook并检查错误 if (MH_Initialize() != MH_OK) { OutputDebugStringA("MH_Initialize failed\n"); return; } // 正确创建Hook:传入原始函数指针的地址 if (MH_CreateHook(compile_method_addr, &mono_compile_method_h, reinterpret_cast<LPVOID*>(&mono_compile_method_o)) != MH_OK) { OutputDebugStringA("MH_CreateHook failed\n"); MH_Uninitialize(); return; } // 启用指定Hook,而非全部Hook if (MH_EnableHook(compile_method_addr) != MH_OK) { OutputDebugStringA("MH_EnableHook failed\n"); MH_RemoveHook(compile_method_addr); MH_Uninitialize(); return; } }
额外注意事项
- 务必确认目标游戏使用的Mono版本,不同版本的
mono_compile_method签名可能略有差异,可通过Mono源码或导出表验证。 - 始终检查MinHook的返回值,便于快速定位Hook创建/启用失败的原因。
- 避免在Hook函数中执行耗时操作,防止游戏线程卡顿或触发检测机制。
内容的提问来源于stack exchange,提问作者Gore Box
相关产品推荐
相关产品推荐

