最近在分析一款 极限摩托基于手机重力控制的 Unity 游戏:

本文完整记录了我 从 APK 分析 → 判断 Unity 架构 → Hook Mono Runtime → 精准拦截死亡函数 的全过程。
最终效果:
人物发生碰撞也不会死亡,游戏可正常继续运行。

上传到手机:
启动frida-server-17.5.1-android-arm64
重点关注:
在 lib/arm64-v8a/ 目录下发现:
同时:
这是一个 Unity Mono 架构游戏(非 IL2CPP)
在:
中可以看到:

说明:
所有 C# 方法最终都会经过 mono_runtime_invoke
这意味着:
Hook 一个函数,就能观察并控制所有 C# 方法调用。
输出示例:
日志中依次出现:
FailController::OnGUI
只是 UI 显示失败画面,不是死亡原因
FailController::Start
失败后的初始化逻辑
这正好符合游戏机制:
人物因重力翻转发生碰撞 → 立即失败
成功拦截后:
✅ 无需修改 APK
✅ 无需重打包
✅ 精准绕过失败判定
本文完整展示了一条 Unity Mono 游戏逆向的通用思路:
理解引擎执行模型,比盲目改代码更重要。
这套方法同样适用于:
后续我会继续分享更多 Unity / Mono / Frida 实战分析。
adb push frida-server-17.5.1-android-arm64 /data/local/tmp/
adb push frida-server-17.5.1-android-arm64 /data/local/tmp/
adb shell chmod +x /data/local/tmp/frida-server-17.5.1-android-arm64
adb shell chmod +x /data/local/tmp/frida-server-17.5.1-android-arm64
adb shell /data/local/tmp/frida-server-17.5.1-android-arm64
adb shell /data/local/tmp/frida-server-17.5.1-android-arm64
apktool d trial.apk
libmono.so
libu.so
assets/bin/Data/Managed/
Assembly-CSharp.dll
UnityEngine.dll
Assembly-CSharp.dll
UnityEngine.dll
Unity 物理 / 碰撞
↓
C
↓
IL Code
↓
mono_runtime_invoke
↓
Native 执行
Unity 物理 / 碰撞
↓
C
↓
IL Code
↓
mono_runtime_invoke
↓
Native 执行
adb shell ps -A | grep com.galapagossoft.trial
adb shell ps -A | grep com.galapagossoft.trial
u0_a236 19480 ... com.galapagossoft.trial
u0_a236 19480 ... com.galapagossoft.trial
frida -U -p 19480 -l mono_base.js
frida -U -p 19480 -l mono_base.js
console.log("[*] mono_base.js loaded");
var mono = Process.findModuleByName("libmono.so");
if (!mono) {
console.log("libmono.so not found");
return;
}
var mono_method_get_name_ptr = mono.getExportByName("mono_method_get_name");
var mono_runtime_invoke_ptr = mono.getExportByName("mono_runtime_invoke");
var mono_method_get_class_ptr = mono.getExportByName("mono_method_get_class");
var mono_class_get_name_ptr = mono.getExportByName("mono_class_get_name");
var mono_method_get_name = new NativeFunction(
mono_method_get_name_ptr, "pointer", ["pointer"]
);
var mono_method_get_class = new NativeFunction(
mono_method_get_class_ptr, "pointer", ["pointer"]
);
var mono_class_get_name = new NativeFunction(
mono_class_get_name_ptr, "pointer", ["pointer"]
);
var orig_mono_runtime_invoke = new NativeFunction(
mono_runtime_invoke_ptr,
"pointer",
["pointer", "pointer", "pointer", "pointer"]
);
Interceptor.replace(
mono_runtime_invoke_ptr,
new NativeCallback(function (method, obj, params, exc) {
var klass = mono_method_get_class(method);
var className = mono_class_get_name(klass).readCString();
var methodName = mono_method_get_name(method).readCString();
if (className === "Bone" && methodName === "OnCollisionEnter") {
console.log("[BLOCK] " + className + "::" + methodName);
return ptr(0);
}
return orig_mono_runtime_invoke(method, obj, params, exc);
}, "pointer", ["pointer", "pointer", "pointer", "pointer"])
);
console.log("[*] mono_base.js loaded");
var mono = Process.findModuleByName("libmono.so");
if (!mono) {
console.log("libmono.so not found");
return;
}
var mono_method_get_name_ptr = mono.getExportByName("mono_method_get_name");
var mono_runtime_invoke_ptr = mono.getExportByName("mono_runtime_invoke");
var mono_method_get_class_ptr = mono.getExportByName("mono_method_get_class");
var mono_class_get_name_ptr = mono.getExportByName("mono_class_get_name");
var mono_method_get_name = new NativeFunction(
mono_method_get_name_ptr, "pointer", ["pointer"]
);
var mono_method_get_class = new NativeFunction(
mono_method_get_class_ptr, "pointer", ["pointer"]
);
var mono_class_get_name = new NativeFunction(
mono_class_get_name_ptr, "pointer", ["pointer"]
);
var orig_mono_runtime_invoke = new NativeFunction(
mono_runtime_invoke_ptr,
"pointer",
["pointer", "pointer", "pointer", "pointer"]
);
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-12-28 13:11
被我是jet编辑
,原因: