You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 04:07:29