神庙逃亡(Temple Run)IL2CPP 逆向实战:从 APK 到 Frida 实现角色无敌
好的,我现在要帮用户总结这篇文章的内容。用户的要求是用中文,控制在一百个字以内,不需要特定的开头,直接写描述。 首先,我通读了一遍文章。文章主要讲的是如何逆向分析一个使用Unity引擎开发的跑酷类游戏《神庙逃亡》。作者详细介绍了使用Il2CppDumper、IDA Pro和Frida等工具来提取游戏的IL2CPP结构,定位关键函数和变量,最终通过Frida Hook实现无敌状态。 接下来,我需要提取关键点:目标是定位死亡逻辑、无敌状态和关键变量;使用的工具包括Il2CppDumper、IDA Pro和Frida;步骤包括解包APK、提取文件、还原结构、定位函数、验证偏移以及Hook实现。 然后,我要把这些信息浓缩到100字以内。确保涵盖主要工具和方法,以及最终实现的效果。 最后,组织语言,确保简洁明了。避免使用复杂的术语,让总结易于理解。 </think> 文章介绍了一种通过逆向分析 Unity IL2CPP 游戏的方法,利用 Il2CppDumper 和 IDA Pro 工具提取游戏逻辑结构,并结合 Frida Hook 技术实现角色无敌状态的修改。该方法适用于无符号、无字符串的游戏 SO 文件,在跑酷类神庙逃亡游戏中成功实现了对死亡逻辑和无敌状态的精准定位与控制。 2026-1-21 02:13:55 Author: www.freebuf.com(查看原文) 阅读量:0 收藏

  • 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,提取关键文件

apktool d game.apk -o game_apk

可以用 adb 查看手机 CPU 架构:

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

game_apk/lib/arm64-v8a/libil2cpp.so

2、 global-metadata.dat

game_apk/assets/bin/Data/Managed/Metadata/global-metadata.dat

三、使用 Il2CppDumper工具还原 IL2CPP 结构

./Il2CppDumper libil2cpp.so global-metadata.dat il2cpp_dump

生成目录结构如下:

il2cpp_dump/
├── dump.cs
├── script.json
├── stringliteral.json
├── DummyDll/
│   ├── Assembly-CSharp.dll
│   └── ...

四、从 dump.cs 找“真正有用的类”

dump.cs中搜索查看关键信息 游戏核心角色类,例如:

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中可以找到方法对应关系:

{
  "Address": 17287964,
  "Name": "CharacterPlayer$$Kill",
  "Signature": "void CharacterPlayer__Kill (CharacterPlayer_o* __this, int32_t deathType, const MethodInfo* method);"
}

注意:

  • Address十进制

  • 表示 RVA(相对于 libil2cpp.so 基址)


十进制 → 十六进制

17287964 (decimal)
= 0x107CB1C (hex)

六、在 IDA 中精准跳转函数

1️用 IDA 打开 libil2cpp.so
2️ 按下键盘 G
3️ 输入:

0x107CB1C

4️ 回车

IDA 会直接跳转到 CharacterPlayer$$Kill的真实函数实现


七、验证逻辑是否匹配 dump.cs

IDA 反编译中可以看到:

*(_BYTE *)(a1 + 208) = 1;   // IsDead = true
*(_DWORD *)(a1 + 216) = a2;// DeathType

而 dump.cs 中:

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中发现:

{
  "Address": 17291184,
  "Name": "CharacterPlayer$$StartInvcibility",
  "Signature": "void CharacterPlayer__StartInvcibility (CharacterPlayer_o* __this, float duration, const MethodInfo* method);",
  "TypeSignature": "vifi"
}

或 dump.cs中发现:

// RVA: 0x107D7B0 Offset: 0x107C7B0 VA: 0x107D7B0
public void StartInvcibility(float duration);

在 script.json 中查到的"Address": 17291184和dump.cs中查到的RVA: 0x107D7B0,两种都可以:

"Address": 0x107D7B0

十、最终 Frida Hook 实现(实战)

在script.json中发现:

{
  "Address": 17285176,
  "Name": "CharacterPlayer$$Update",
  "Signature": "void CharacterPlayer__Update (CharacterPlayer_o* __this, const MethodInfo* method);",
  "TypeSignature": "vii"
}

或 dump.cs中发现:

// 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

frida -U -p 19480 -l hook.js

hook.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


文章来源: https://www.freebuf.com/articles/vuls/467436.html
如有侵权请联系:admin#unsafe.sh