首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
7
11
[原创]神庙逃亡(Temple Run)IL2CPP 逆向实战:从 APK 到 Frida 实现角色无敌
发表于: 2026-1-18 20:51
3792
[原创]神庙逃亡(Temple Run)IL2CPP 逆向实战:从 APK 到 Frida 实现角色无敌
我是jet
2026-1-18 20:51
3792
# Unity IL2CPP 逆向实战:从 APK 到 Frida 无敌 Hook 的完整流程 > - Unity IL2CPP 游戏 > - 无符号 / 无字符串 / 全是 sub_xxx 的 SO > - 目标:定位死亡逻辑 / 无敌 / 关键状态变量 本文以一个跑酷类神庙逃亡(Temple Run) Unity 游戏为例。 --- ### 游戏截图  ## 一、准备工具 & 我使用的设备 & 工具版本 - **apktool** - **Il2CppDumper** - **IDA Pro(ARM64)** - **Ubuntu / Linux 环境(本文以 Linux localhost 4.19.95-perf-g79255ac #1 SMP PREEMPT Thu Sep 2 03:29:24 CST 2021 aarch64 为例)** - **Android 10 真机(arm64)** - **Frida版本**:17.5.2 - **Frida Server版本**:`frida-server-17.5.1-android-arm64` - **Python版本**:Python 3.12.3 --- ## 二、解包 APK,提取关键文件 ```bash apktool d game.apk -o game_apk ```` 可以用 adb 查看手机 CPU 架构: ```bash adb shell getprop ro.product.cpu.abi ``` 常见输出: - arm64-v8a ✅(64 位 ARM) - armeabi-v7a ❌(32 位) - x86 / x86_64(模拟器或少数设备) > 如果同时存在,可以使用上面的命令查看自己对应的库目录: > > * arm64-v8a > * armeabi-v7a > > **优先选择 arm64-v8a** --- 重点关注两个位置`libil2cpp.so和global-metadata.dat这两个文件缺一不可。`: ### 1️⃣ libil2cpp.so ```text game_apk/lib/arm64-v8a/libil2cpp.so ``` ### 2️⃣ global-metadata.dat ```text game_apk/assets/bin/Data/Managed/Metadata/global-metadata.dat ``` --- ## 三、使用 Il2CppDumper工具还原 IL2CPP 结构 ```bash ./Il2CppDumper libil2cpp.so global-metadata.dat il2cpp_dump ``` 生成目录结构如下: ```text il2cpp_dump/ ├── dump.cs ├── script.json ├── stringliteral.json ├── DummyDll/ │ ├── Assembly-CSharp.dll │ └── ... ``` --- ## 四、从 dump.cs 找“真正有用的类” 在 `dump.cs` 中搜索查看关键信息 **游戏核心角色类**,例如: ```csharp public class CharacterPlayer : CommonPlayer { public bool IsDead; // 0xD0 public float TimeSinceDeath; // 0xD4 public DeathTypes DeathType; // 0xD8 public void Kill(DeathTypes type); public void Update(); public void StartInvcibility(float duration); } ``` 这一步非常重要: > **字段偏移 = 后续 Hook / 内存验证的锚点** --- ## 五、利用 script.json 精准定位 SO 中的函数 在 `script.json` 中可以找到方法对应关系: ```json { "Address": 17287964, "Name": "CharacterPlayer$$Kill", "Signature": "void CharacterPlayer__Kill (CharacterPlayer_o* __this, int32_t deathType, const MethodInfo* method);" } ``` 注意: * `Address` 是 **十进制** * 表示 **RVA(相对于 libil2cpp.so 基址)** --- ### 十进制 → 十六进制 ```text 17287964 (decimal) = 0x107CB1C (hex) ``` --- ## 六、在 IDA 中精准跳转函数 1️⃣ 用 IDA 打开 `libil2cpp.so` 2️⃣ 按下键盘 **G** 3️⃣ 输入: ```text 0x107CB1C ``` 4️⃣ 回车 IDA 会直接跳转到 `CharacterPlayer$$Kill` 的真实函数实现 --- ## 七、验证逻辑是否匹配 dump.cs IDA 反编译中可以看到: ```c *(_BYTE *)(a1 + 208) = 1; // IsDead = true *(_DWORD *)(a1 + 216) = a2;// DeathType ``` 而 dump.cs 中: ```csharp public bool IsDead; // 0xD0 (208) public DeathTypes DeathType; // 0xD8 (216) ``` ✅ **字段偏移完全一致** 说明: * metadata * dump.cs * IDA 反编译 三者已经完全对齐。 --- ## 八、为什么“字符串搜索 / 函数名搜索”基本没用? 在 IL2CPP 游戏中通常存在: * SO 全 strip * 函数名被抽掉 * 字符串清空 * Update / LateUpdate 刷屏 **靠猜函数 ≈ 大海捞针** 正确思路是: > **metadata 给你“地图”,SO 只是实现** --- ## 九、用 Frida Hook 而不是硬改 SO 目标:让角色始终处于无敌状态。 在script.json中发现: ```json { "Address": 17291184, "Name": "CharacterPlayer$$StartInvcibility", "Signature": "void CharacterPlayer__StartInvcibility (CharacterPlayer_o* __this, float duration, const MethodInfo* method);", "TypeSignature": "vifi" } ``` 或 dump.cs中发现: ```csharp // RVA: 0x107D7B0 Offset: 0x107C7B0 VA: 0x107D7B0 public void StartInvcibility(float duration); ``` 在 script.json 中查到的"Address": 17291184和dump.cs中查到的RVA: 0x107D7B0,两种都可以: ```json "Address": 0x107D7B0 ``` --- ## 十、最终 Frida Hook 实现(实战) 在script.json中发现: ```json { "Address": 17285176, "Name": "CharacterPlayer$$Update", "Signature": "void CharacterPlayer__Update (CharacterPlayer_o* __this, const MethodInfo* method);", "TypeSignature": "vii" } ``` 或 dump.cs中发现: ```csharp // RVA: 0x107C038 Offset: 0x107B038 VA: 0x107C038 Slot: 4 public override void Update(); ``` 在 script.json 中查到的"Address": 17285176和dump.cs中查到的RVA: 0x107C038,两种都可以: 函数Update游戏开始后会一直调用,每次调用就调用函数StartInvcibility(player, 9999.0, ptr(0));设置无敌 ### 神庙逃亡(Temple Run)的进程Frida Attach ```bash frida -U -p 19480 -l hook.js ``` --- ## hook.js 脚本(核心代码) ```js const base = Process.findModuleByName("libil2cpp.so").base; // CharacterPlayer::StartInvcibility const StartInvAddr = base.add(0x107D7B0); // CharacterPlayer::Update const UpdateAddr = base.add(0x107C038); const StartInvcibility = new NativeFunction( StartInvAddr, 'void', ['pointer', 'float', 'pointer'] ); Interceptor.attach(UpdateAddr, { onEnter(args) { const player = args[0]; // 每一帧刷新无敌时间 StartInvcibility(player, 9999.0, ptr(0)); } }); console.log("[+] Invincibility hook installed"); ``` 效果说明: * 普通碰撞、陷阱:不会死亡 * 游戏本身设计的“强制死亡”(掉深渊、被追上):仍然会死 说明这是**调用游戏原生无敌逻辑,不是暴力 patch** --- ## 十一、核心思维总结(非常重要) > **IL2CPP 逆向不是“找函数”,而是“算地址”。**
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
#逆向分析
#HOOK注入
#工具脚本
收藏
・
7
点赞
・
11
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
wx_晨梦
这个讨论对我很有帮助,谢谢!
2026-7-16 06:26
沐仁浴义
感谢你的积极参与,期待更多精彩内容!
2026-4-14 03:09
mb_dwpyujbe
非常支持你的观点!
2026-2-13 19:54
x_req
你的分享对大家帮助很大,非常感谢!
2026-1-24 15:40
木志本柯
+5
为你点赞!
2026-1-22 20:35
零_721409
感谢你的贡献,论坛因你而更加精彩!
2026-1-22 15:30
南行
+1
感谢你的贡献,论坛因你而更加精彩!
2026-1-20 15:34
git_80416402971561
感谢你的积极参与,期待更多精彩内容!
2026-1-20 09:50
mb_vlmccdbw
+1
这个讨论对我很有帮助,谢谢!
2026-1-19 18:49
doduhuang
感谢你分享这么好的资源!
2026-1-19 16:15
git_47499test-look
为你点赞!
2026-1-19 02:04
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
3
)
huangjw
雪 币:
6747
活跃值:
(11957)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
514
粉丝
2
关注
私信
huangjw
2
楼
2026-1-24 10:17
0
mb_ppryzjtf
雪 币:
80
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
11
粉丝
0
关注
私信
mb_ppryzjtf
3
楼
没加密的游戏改起来还是太方便了
2026-1-29 19:01
0
CheRevir
雪 币:
0
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
90
粉丝
0
关注
私信
CheRevir
4
楼
666
2026-7-8 16:32
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
我是jet
3
发帖
1
回帖
10
RANK
关注
私信
他的文章
[原创]神庙逃亡(Temple Run)IL2CPP 逆向实战:从 APK 到 Frida 实现角色无敌
3792
[原创]逆向实战:如何用机器码把 x64dbg 的汇编精准定位到 IDA / Ghidra 的 C 代码
3155
[原创]一次完整的 Unity Mono 安卓游戏逆向:Frida Hook 绕过碰撞死亡判定
19144
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部