使用MinHook挂钩游戏函数后触发Hook却游戏崩溃的问题排查
问题描述
我写了一段用MinHook挂钩游戏函数的C++代码,目标是GameAssembly.dll里的Game.Managers.BuildingManager.BuildingManager$$RequestStartBuilding函数,偏移量设为0x341C20。代码如下:
#include "pch.h" #include <Windows.h> #include <cstdio> #include <MinHook.h> uintptr_t base = (uintptr_t)GetModuleHandle(NULL); uintptr_t GameAssembly = (uintptr_t)GetModuleHandle(L"GameAssembly.dll"); uintptr_t SetOffset = 0x341C20; void CreateConsole() { AllocConsole(); FILE* f; freopen_s(&f, "CONOUT$", "w", stdout); } void init() { MH_Initialize(); CreateConsole(); printf("started"); } bool(__fastcall* build)(DWORD*, DWORD*, DWORD*); bool __stdcall build_hook(DWORD* __this, DWORD* buildingDef, DWORD* methodinfo) { printf("buildingDef is called!!!"); return true; } void main() { init(); MH_CreateHook(reinterpret_cast<LPVOID*>(GameAssembly + SetOffset), &build_hook, (LPVOID*)&build); MH_EnableHook(reinterpret_cast<LPVOID*>(GameAssembly + SetOffset)); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CreateThread(0, 0, (LPTHREAD_START_ROUTINE)main, 0, 0, 0); case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
目标函数在dump中的信息:
{ "Address": 3415072, "Name": "Game.Managers.BuildingManager.BuildingManager$$RequestStartBuilding", "Signature": "bool Game_Managers_BuildingManager_BuildingManager__RequestStartBuilding (Game_Managers_BuildingManager_BuildingManager_o* __this, Game_Managers_BuildingManager_BuildingConfiguration_BuildingDef_o* buildingDef, const MethodInfo* method);", "TypeSignature": "iiii" }
现在代码能触发hook里的printf,但之后游戏直接崩溃,改返回值为true或false都没用,请问我用MinHook的时候哪里错了?
问题分析与修正
崩溃的核心原因是函数调用约定不匹配+未执行原函数破坏游戏逻辑,具体问题和修正方案如下:
1. 调用约定不匹配导致栈平衡破坏
从dump签名看,目标是C++成员函数,默认调用约定为thiscall,但你声明原函数指针用了__fastcall,hook函数用了__stdcall——不同调用约定的参数传递、栈清理规则完全不同,直接导致栈结构混乱,触发崩溃。
对于Unity IL2CPP导出的成员函数,实际调用约定通常为__fastcall,必须保证原函数指针和hook函数的调用约定完全一致。
2. 未调用原函数导致逻辑断裂
你直接返回true/false跳过了原函数执行,但游戏后续逻辑依赖原函数修改的内部状态,缺失这些状态必然引发崩溃,必须调用原函数维持逻辑完整性。
3. MinHook API使用错误
MH_CreateHook的第一个参数是目标函数的地址(LPVOID),而非LPVOID*,你的强制转换会导致地址解析错误;另外没有检查MinHook返回值,无法排查hook创建/启用是否成功。
修正后的代码
#include "pch.h" #include <Windows.h> #include <cstdio> #include <MinHook.h> uintptr_t GameAssembly = (uintptr_t)GetModuleHandle(L"GameAssembly.dll"); const uintptr_t SetOffset = 0x341C20; void CreateConsole() { AllocConsole(); FILE* f; freopen_s(&f, "CONOUT$", "w", stdout); } // 统一调用约定为__fastcall,匹配IL2CPP成员函数的实际调用规则 using BuildFunc = bool(__fastcall*)(void* __this, void* buildingDef, const void* methodInfo); BuildFunc originalBuild = nullptr; bool __fastcall build_hook(void* __this, void* buildingDef, const void* methodInfo) { printf("buildingDef is called!!!\n"); // 必须调用原函数,保证游戏内部逻辑正常执行 return originalBuild(__this, buildingDef, methodInfo); } void InitHook() { MH_STATUS status = MH_Initialize(); if (status != MH_OK) { printf("MH_Initialize failed: %d\n", status); return; } CreateConsole(); printf("started\n"); LPVOID targetAddr = reinterpret_cast<LPVOID>(GameAssembly + SetOffset); status = MH_CreateHook(targetAddr, &build_hook, reinterpret_cast<LPVOID*>(&originalBuild)); if (status != MH_OK) { printf("MH_CreateHook failed: %d\n", status); return; } status = MH_EnableHook(targetAddr); if (status != MH_OK) { printf("MH_EnableHook failed: %d\n", status); } } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)InitHook, nullptr, 0, nullptr); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
额外注意事项
- 确认偏移量正确性:dump中的
Address是3415072,转十六进制为0x341500,和你用的0x341C20存在差异,需确认是否是基址计算方式不同,确保GameAssembly + SetOffset指向正确的函数地址。 - 控制台输出建议加
\n,避免缓存导致输出不及时。 - IL2CPP的
MethodInfo参数尽量不要修改或忽略,部分函数依赖它做内部逻辑处理。
内容的提问来源于stack exchange,提问作者Blisa
相关产品推荐
相关产品推荐

