Files
reverse-skill/skills/field-journal/seed-014_unity-il2cpp-reverse.md
T
2026-08-08 18:32:33 +08:00

5.3 KiB
Raw Blame History

[种子] Unity IL2CPP 游戏逆向 → 还原元数据 + 修改逻辑

场景分类

游戏安全 / 移动逆向

目标概述

一个 Unity 打包的 Android 游戏(IL2CPP 模式),游戏内购或核心算法在 C# 编写但已编译成 native。需要还原方法名、定位关键逻辑、改动 / hook 实现修改。

完整执行链路

  1. 拆包 APK,确认是 IL2CPP
    unzip target.apk -d apk
    ls apk/lib/arm64-v8a/        # 看到 libil2cpp.so 即 IL2CPP
    ls apk/assets/bin/Data/Managed/Metadata/
    # 关键文件: global-metadata.dat
    
  2. 用 Il2CppDumper 还原元数据
    Il2CppDumper libil2cpp.so global-metadata.dat output/
    # 产物:DummyDll/ + script.json + il2cpp.h + dump.cs
    
  3. 把 IDA 的 IL2CPP 脚本(ida_with_struct.py)跑一遍
    • 加载 libil2cpp.so → File → Script File → 选 ida_with_struct.py → 选 script.json
    • IDA 现在能看到 C# 方法名、签名、字符串
  4. 在 dump.cs 里按业务关键字搜(AddCoin / OnPurchase / Verify / IsVip / CheckSign)
  5. 拿到关键方法的偏移 → IDA 跳过去看反汇编 / 反编译
  6. 选择修改方式:
    • 静态 patch:直接 IDA 把判断改成 mov w0, #1; ret
    • 动态 hook:Frida 接 il2cpp 方法(用 Frida-Il2CppBridge)
  7. 重打包验证 / 注入验证

踩坑记录

问题 原因 解决方案 耗时
Il2CppDumper 报 metadata 版本不支持 新版 Unity 改了 metadata 格式 升级 Il2CppDumper 到最新 / 用 Il2CppInspectorRedux 替代 30min
global-metadata.dat 加密了 用了 AntiCheatToolkit / 自定义加密 找游戏初始化时的解密函数(通常在 il2cpp_init 周围)→ Frida 在 mmap 后 dump 2h
dump.cs 看到方法名但 IDA 没匹配 script.json 与 so 不一致 必须用同一次 dump 的产物;换 IDA 时清缓存 20min
Frida hook IL2CPP 方法报错 IL2CPP 方法不是标准 Java/ObjC,要算 method offset 用 frida-il2cpp-bridge 库,不要硬写 Interceptor.attach 1h
Patch 后游戏闪退 校验文件 hash 或 anti-tamper 找 hash 校验逻辑也 patch 掉,或用 hook 不改文件 2h
重打包后启动崩溃 apksigner v2 签名不能改字节后再签 删 META-INF + apktool b + apksigner sign 一气 30min

工具链发现

  • Il2CppDumper 老牌但仍是默认选择
  • Il2CppInspectorRedux 更现代,支持新 Unity,能输出 IDA / Ghidra / Binary Ninja 多种插件脚本
  • frida-il2cpp-bridge 是 IL2CPP 上 hook 的事实标准,比裸 Frida 强 N 倍
  • DnSpy / dnSpyEx 用于看 DummyDll(dump 出来的伪 .NET assembly)
  • UnityCheat 系列辅助工具(GameGuardian 系不展开)

关键代码/命令

frida-il2cpp-bridge hook 示例:

// hook.ts
import "frida-il2cpp-bridge";

Il2Cpp.perform(() => {
    const Assembly = Il2Cpp.domain.assembly("Assembly-CSharp").image;

    // hook static method
    const PlayerData = Assembly.class("PlayerData");
    PlayerData.method("AddCoin").implementation = function (n: number) {
        console.log("[+] AddCoin called with:", n);
        return this.method("AddCoin").invoke(99999); // 改成 99999
    };

    // hook instance method
    const Purchase = Assembly.class("Purchase");
    Purchase.method("VerifyReceipt").implementation = function () {
        console.log("[+] VerifyReceipt → always true");
        return true;
    };
});
# 编译 + 注入
npm install frida-il2cpp-bridge
frida-compile hook.ts -o hook.js
frida -U -f com.target.game -l hook.js --no-pause

IDA 静态 patch:

1. 打开 libil2cpp.so,跑 il2cpp_load_metadata.py
2. 跳到 dump.cs 中 IsPurchaseValid 对应的偏移
3. 函数开头改成 MOV W0, #1; RET(ARM64)
4. Apply Patches → Save → 替换回 APK → 重签名

对本包的改进建议

  • reverse-engineering/SKILL.md 已覆盖 Unity,但缺 IL2CPP 完整工作链 案例
  • reverse-engineering/references/il2cpp-cheatsheet.md 单独成文:dump 工具对比、frida-bridge 模板、加密 metadata 处理
  • bootstrap manifest 增加 frida-il2cpp-bridge

可复用的模式/脚本片段

IL2CPP 标准流程:

1. 确认 IL2CPP(看 lib/abi 下有无 libil2cpp.so)
2. 找到 metadata(assets/bin/Data/Managed/Metadata/global-metadata.dat 或被加密)
3. Il2CppDumper / Inspector 还原
4. IDA + 脚本带回元信息
5. dump.cs 搜业务关键词
6. 选择 patch 还是 hook
7. 验证(启动 + 实际场景)

加密 metadata 处理:

1. Frida 在 fopen/open 系调用上挂钩,看谁读 global-metadata.dat
2. 在 mmap/read 后 dump 内存里已解密的元数据
3. 把 dump 出来的内存当 metadata 喂给 Il2CppDumper

进化动作

  • reverse-engineering/references 增加 il2cpp 完整章节
  • bootstrap-manifest 加入 frida-il2cpp-bridge / Il2CppInspectorRedux
  • 路由矩阵已含 Unity / IL2CPP

环境信息

  • Windows / macOS(运行 Il2CppDumper 用),目标设备 Android arm64
  • IDA Pro 7.7+ 或 Ghidra 11+
  • frida-tools 16.x, frida-il2cpp-bridge 0.9+
  • Unity 版本: 2019.x - 2022.x(不同版本 metadata 格式略异)

脱敏要求

本条目为种子数据,基于公开技术模式编写,不涉及任何真实游戏。包名 com.target.game 为占位符。