Il2CppDumper实战:Unity IL2CPP逆向符号恢复与IDA/Ghidra衔接指南

发布时间:2026/10/15 15:42:21
Il2CppDumper实战:Unity IL2CPP逆向符号恢复与IDA/Ghidra衔接指南 简介面向 Unity 游戏逆向与安全分析人员的 IL2CPP 元数据还原工具包专门解决 IL2CPP 编译产物在静态分析中难以直接获得类型与结构信息的问题。还原出的 DLL 文件虽然不包含实际代码却能用于提取 MonoBehaviour 与 MonoScript帮助使用者快速定位游戏逻辑、分析通信协议、修改玩法或开展安全研究是手游逆向、外挂分析与模组开发过程中的常用工具。该工具支持多种可执行文件格式包括 Linux 下的 ELF 与 ELF64、macOS 的 Mach-O、Windows 的 PE、任天堂 Switch 的 NSO 以及 WebAssembly 的 WASM并覆盖 Unity 5.3 至 2020 的多个版本兼容性较为完整。资源包整体约 10.7MB压缩包解压后共 11 个文件其中 8 个为 Python 辅助脚本分别负责生成 IDA 分析脚本、生成 Ghidra 分析脚本、输出结构体头文件、适配 WASM 与 Ghidra 场景等2 个为可执行程序承担核心解析工作1 个 JSON 配置文件用于按需调整导出参数。资源包内脚本命名清晰可根据实际目标灵活组合例如仅输出结构体头文件或只生成某一种分析脚本。已有 1048 人学习下载适合具备一定 IL2CPP 基础、正在处理实际逆向项目的读者。入手后可直接生成带结构信息的 IDA/Ghidra 脚本与结构体定义省去手工整理偏移和数据类型的繁琐过程还能将生成结果导入反编译工具提升代码可读性。针对从内存 dump 出来的 libil2cpp.so 或带简单 PE 保护的目标工具同样提供了对应选项能够顺利完成解析整体实用性较强。1. Unity 逆向的符号恢复利器为什么拿到 il2cpp 产物仍需先过这道坎做过 Unity 游戏分析的人大概都有过这种体验拿到一个 IL2CPP 打包的 APK 或 So丢进 IDA 一看导出表里只剩寥寥几个入口点函数名全是sub_xxxx类结构、字符串引用一概无从谈起。IL2CPP 把 C# 编译成 C 再编成原生码顺手把 metadata 也压进了全局元数据区传统 .NET 反编译工具对这套产物基本失效。Il2CppDumper-win-v6.7.46 解决的就是这个卡点它从libil2cpp.so和global-metadata.dat里恢复完整的类、方法、字段、属性与字符串信息输出可供 IDA、Ghidra 直接使用的符号文件和分析脚本。这工具适合三类人做手游安全评估的逆向工程师、研究 Unity 引擎机制的学习者以及需要快速定位关键函数做二次开发的 Mod 作者。它算不上傻瓜式一键出结果但掌握正确姿势后效率提升是立竿见影的。2. IL2CPP 产物的黑匣子先搞清 Dumper 在恢复什么2.1 IL2CPP 的编译链路从 C# 到原生指令信息丢在哪一步要理解 Il2CppDumper 的恢复原理得先看 IL2CPP 的产物结构。Unity 在构建 IL2CPP 工程时会经历两条并行链路第一条是 C# 源码经 Roslyn 编译成 IL 程序集再由 IL2CPP 前端翻译成 C 源码随后交给目标平台编译器编成原生指令最终链接进libil2cpp.soAndroid 平台或可执行文件Windows/桌面平台第二条是 IL 程序集的所有类型、方法、字段、字符串等声明信息被序列化进global-metadata.dat这个文件本质上是运行时反射数据的二进制快照。关键点在于C 翻译层生成了一堆Il2CppClass、MethodInfo结构体但这些结构体的字段值不是在编译期写死的而是运行时从global-metadata.dat里反序列化填充的。所以你在二进制里看到的类对象只是一堆指针和索引真实的类名、方法签名、字段偏移全在那个 metadata 文件里。2.2 Dumper 的工作方式静态解析 地址映射的配合Il2CppDumper 采用静态分析而非动态调试它不需要运行目标程序。核心逻辑分三块第一块读取global-metadata.dat的头结构校验版本号解析字符串表、类型定义表、方法定义表等区块第二块扫描libil2cpp.so的符号表如果没剥离或通过特征码匹配关键结构体如Il2CppCodeRegistration、Il2CppMetadataRegistration拿到方法代码的虚拟地址RVA第三块把两边的信息拼接生成最终的符号映射文件。它能恢复的典型内容包括完整的命名空间与类层级、方法名与参数列表、字段名与偏移量、字符串字面量如global-metadata.dat中的 UTF-8 串甚至包括泛型实例化的具体类型。以我常用的 v6.7.46 版本为例它支持的 metadata 版本范围较广从 Unity 5.3 到较新的 2021/2022 LTS 都能处理前提是版本号对得上。2.3 两种输入材料各自的脾性So 与 Dat 的匹配边界有个常被忽视的边界条件libil2cpp.so和global-metadata.dat必须来自同一次构建。Unity 每次打包都会重新生成 metadata其哈希校验与代码结构绑定。如果你把 A 包的 so 配 B 包的 datDumper 大概率直接报Magic version mismatch或解析出来的地址错到十万八千里。另外IL2CPP 的 metadata 版本号与 Unity 版本有关但并非一一对应。同一 Unity 版本可能使用不同 metadata 版本跨版本升级也有兼容情况。实际执行 Dumper 时它会先输出Global-Metadata Version与Unity Version信息如果显示Unknown但仍继续通常说明 metadata header 足够新但不在内置表里此时生成的脚本可用性会打折。提示优先保证 so 与 dat 的同源性这是所有后续步骤成立的前提。3. 实战在 Windows 上跑通 Il2CppDumper 的完整流程3.1 获取与前置准备文件清单和运行环境在 Windows 上使用这个工具首先要明确你拿到的是预编译版通常是 .NET 编写的控制台程序。释放压缩包后目录里应有Il2CppDumper.exe、配置文件、以及若干依赖库。运行环境需要 .NET Framework 4.5 或 .NET Core 运行时取决于发行版配置命令行窗口直接执行即可不需要图形界面。取材料时Android 平台主要关注libil2cpp.so在lib/armeabi-v7a、lib/arm64-v8a等目录下与assets/bin/Data/Managed/Metadata/global-metadata.dat。Windows 平台则直接找游戏根目录下的GameAssembly.dll和global-metadata.dat。如果是 iOS 版本libil2cpp.so通常位于 Mach-O 可执行文件内部需要先提取。这一步的原理在于Unity 将 C# 层对比原生层时两块文件存放路径是构建时写死的逆向时按这个路径取即可。3.2 命令行执行与参数含义三种模式的选型v6.7.46 支持三种运行模式通过交互式菜单选择。第一种是自动模式指定 so 与 dat 路径后它会自动分析并输出到同目录第二种是手动模式遇到版本识别失败时可强行指定 metadata 版本号和 Unity 版本号第三种是脚本模式适合后续需要批量处理或与自家工具链集成时使用。模式命令格式示例适用场景自动Il2CppDumper.exe libil2cpp.so global-metadata.dat常规分析版本能被正确识别手动Il2CppDumper.exe -m metadata_version so dat版本识别失败或识别结果异常脚本模式通过配置文件驱动非交互式执行批量处理多个目标或集成进 CI 流程我一般先用自动模式跑一次看输出的版本号是否合理。如果输出里Unity Version: 2021.3.0f1, Metadata Version: 27这种说明识别正常。手动模式里metadata_version填 24、26、27 这类数字需要你去查目标 Unity 版本对应的 metadata 表填错会直接导出失败。3.3 核心命令行操作从执行到产物验证以一个模拟项目 X 为例假设从某跨平台系统里取出了libil2cpp.so和global-metadata.dat放在D:\work\input下执行命令如下cd D:\work\input D:\tools\Il2CppDumper\Il2CppDumper.exe libil2cpp.so global-metadata.dat执行后终端会依次打印 metadata 读取状态、版本识别结果、代码注册信息扫描情况最后显示导出文件列表。整个流程通常在几秒内完成如果文件较大超过 100MB 的 so可能需要十几秒。成功后同目录下会出现四个关键产物文件内容用途dump.cs完整的类型、字段、方法声明C# 风格阅读类结构、定位关键方法script.json方法地址与名称映射含 RVA给 IDA/Ghidra 做符号导入il2cpp.h结构体定义头文件含偏移量C 逆向时的宏定义参考string.json字符串字面量地址映射定位关键字符串引用这里有个参数我每次都会留意如果 so 文件是 ELF 且带符号表Dumper 会优先用符号表匹配如果被剥离则退化为特征码扫描输出文件里可能少一部分方法。需要用file命令或 PE/ELF 查看器确认 so 的符号保留情况。执行后的第一件事是打开dump.cs搜索你想分析的关键类名比如登录逻辑、支付回调如果搜不到先别急着怀疑 Dumper查一下 so 是否加了壳或做了运行时解密。3.4 与 IDA Pro 的衔接把恢复的符号灌进反汇编视图拿到script.json后接下来要做的是把它导进 IDA。常见做法是用 IDA 的File - Script File执行一段 Python 脚本逐条读取 JSON 并调用set_name给对应地址重命名。以 IDA 7.x/8.x 的 Python 环境为例import json with open(rD:\work\input\script.json, r, encodingutf-8) as f: data json.load(f) for entry in data: address entry.get(Address) # 形如 0x1A2B3C4 name entry.get(Name) # 完整可见名称 if address and name: ida_name name.replace(::, _).replace(., _) ok idc.set_name(int(address, 16), ida_name, SN_NOWARN) if not ok: print(failed to set name at %s - %s % (address, ida_name))这段脚本做的事很简单把 JSON 里的 RVA 地址转成整形替换掉 C# 命名空间里的特殊字符再调用set_name写入 IDA 的符号表。注意SN_NOWARN标志它抑制重命名冲突时的警告弹窗避免脚本中断。IDAPython 默认把函数名表放在ida_funcs段如果目标地址不在函数起始位置set_name会失败此时可配合ida_funcs.add_func(address)先建函数。提示如果目标 so 是 64 位 ARM导入前先在 IDA 中确认加载基址script.json里的地址是 RVAIDA 显示的是 VA。若基址不为 0需要在脚本里加上get_imagebase()偏移。3.5 与 Ghidra 的衔接从 JSON 到符号表Ghidra 导入符号的路径是File - Parse C Source或直接写脚本。Il2CppDumper 生成的il2cpp.h包含结构体定义但 Ghidra 更常用的是通过脚本一次性补全函数名。参考实现from ghidra.program.model.symbol import SourceType import json json_path rD:\work\input\script.json base currentProgram.getImageBase().getOffset() with open(json_path, r, encodingutf-8) as f: data json.load(f) fm currentProgram.getFunctionManager() st currentProgram.getSymbolTable() for entry in data: rva int(entry[Address], 16) name entry[Name].replace(::, _) addr toAddr(base rva) try: func fm.getFunctionAt(addr) if func is None: func createFunction(addr, name) else: func.setName(name, SourceType.USER_DEFINED) except Exception as e: print(ffailed: {name}, {e})这段脚本处理了两种情况目标地址已有函数则直接改名没有则先创建。Ghidra 的createFunction在地址位于指令边界时会自动识别参数栈如果分析阶段已有自动创建的函数优先走改名分支速度更快。两种工具导入后dump.cs仍然作为“类结构字典”存在不是替代反汇编视图而是作为辅助阅读。4. 参数调优与格式边界让还原结果更完整的技巧4.1 理解script.json与dump.cs的格式差异script.json的每条记录包含AddressRVA 十六进制字符串、Name完整签名如Assembly-CSharp.dll:PlayerController:Update、Signature参数类型列表。而dump.cs是纯文本的 C# 风格声明含继承关系、字段偏移、虚方法表布局。我做分析时通常先查dump.cs定位类名再用script.json里同名的Address回 IDA 里看汇编。有个细节dump.cs里字段行后有Offset: 0x...注释这是字段在对象内存布局中的偏移。在分析类时非常有用可以直接推断对象的成员排列不必靠猜。如果你发现某字段的Offset为-1表示该字段是静态的不在对象实例内存内。4.2 处理带混淆或裁剪的 IL2CPP当标准产物不完整时Unity 的 Managed Stripping Level 与代码裁剪会影响 metadata 内容。裁剪后未被引用的类可能被移除Dumper 不会导出但这不代表它们不存在于二进制中只是 metadata 里没有对应项。遇到这种情况只能通过字符串交叉引用或调用链分析手工补。另一种常见干扰是商业保护方案如某些手游加固它们会篡改 metadata 头或对方法体做指令混淆。表现通常是 Dumper 能读出类名但地址对不上或者script.json里有大量低地址项。我处理这种目标时会先跑 Dumper 看是否报错再决定是否先脱壳。注意这里说的脱壳是二进制加固层不是网络代理层的操作。4.3 对比不同版本 Dumper 的差异为什么选 6.7.46选版本主要看 metadata 支持表。v6.7.46 支持较新的 Unity 2022 系列 metadata 版本同时保留了对老版本如 5.3 时代的兼容。同源工具链里某些 fork 版本支持更新的global-metadata.dat加密但也引入更多误报。我的经验是先用官方版跑通全流程再根据情况换 fork 版做对照确保dump.cs的类数量与二进制里实际代码规模大致匹配。4.4 字符串还原与硬编码偏移string.json 的使用string.json里每项含Address与Value记录了global-metadata.dat字符串池的地址映射。分析函数时按 XRef 找到字符串引用再从string.json定位具体内容比逐个看 ASCII 转储高效得多。尤其在搜索关键提示语如错误码、弹窗文本时非常有用。python search_string.py script.json combo_check_failed这是我自己写的检索脚本把需要定位的字符串关键词传进去从 JSON 里筛出所有匹配地址然后再到 IDA 里看交叉引用。比起在 IDA 里手动搜字符串再找引用这个顺序能省去大量重复操作。5. 避坑手册从报错到产出物错位五个高频坑的排查路径5.1Magic version mismatch报错同源性问题现象执行 Dumper 立即终止提示 metadata magic 与期待不符。原因global-metadata.dat与libil2cpp.so不是同一次构建或者 metadata 被外部工具改动过。解决换回同包导出的 dat 文件确认 APK 内路径无误有些渠道包会二次打包覆盖 dat。这个原因在我处理某个模拟项目 X 的某跨平台系统时遇到过重新从原包提取后解决。5.2 Dumper 成功但dump.cs类不全裁剪或加固干扰现象跑完流程打开dump.cs发现核心类缺失。原因Managed Stripping Level 设置为 Aggressive大量内部类被裁剪或 metadata 中某些区间被加固工具改写。解决检索string.json是否还有目标字符串若字符串在但类不在说明 metadata 被删改需要手工重建结构。另一个验证方式是看 APK 里global-metadata.dat的大小过小几 KB往往说明内容被清理过。5.3 IDA 脚本导入后函数名错位基址与重定位问题现象IDAPython 跑完F5 反编译的函数名对不上逻辑。原因script.json的地址是 RVA未加 image base或目标 so 在 IDA 中加载的基址与 Dumper 假设不一致。解决脚本里显式求基址再加偏移检查idc.get_imagebase()返回是否与 ELF 头里e_entry对齐。我通常在脚本开头打印get_imagebase()确认不是默认的 0。5.4 由加密的 metadata 导致的暴力跳过查壳与异常特征现象Dumper 输出版本号正常但地址大量为 0 或集中在低地址区间。原因目标工程对global-metadata.dat做了整体加密Dumper 在读取时只能看到部分内容结构被破坏。解决先检查文件熵值如果熵值接近 1.0说明高概率加密此时需要先解密 metadata 再跑 Dumper或在动态环境下直接读内存中的解密后内容。这步依赖具体目标无法用通用参数解决。5.5 Windows 平台 Dump 后 IDA 识别不到函数异常模块问题现象对 Windows 版程序执行 Dumper 后GameAssembly.dll在 IDA 里加载的地址与 JSON 中不匹配。原因Windows PE 有重定位表加载基址不一定等于默认基址Dumper 输出的 RVA 是相对于模块基址的。解决IDA 加载时选择Manual Load把基址设成与 Dumper 识别的一致或用脚本读取当前模块的ImageBase再做偏移计算。这个坑在 64 位程序上更常见因为 ASLR 默认开启。6. 进阶用 Python 批量验证符号与交叉引用校验还原质量拿到 Dumper 产物后我习惯做一次系统性校验确保还原质量配得上后续分析。校验思路是通过script.json的函数名分布反推二进制里的代码区域再用 IDA 的段信息反向确认是否有遗漏。简单实现如下import json with open(rD:\work\input\script.json, r, encodingutf-8) as f: data json.load(f) stat {total: len(data), with_ns: 0, assembly: {}} for e in data: name e[Name] if : in name: stat[with_ns] 1 asm_part name.split(:)[0] stat[assembly][asm_part] stat[assembly].get(asm_part, 0) 1 print(total methods:, stat[total]) print(with namespace:, stat[with_ns]) for asm, cnt in sorted(stat[assembly].items(), keylambda x: -x[1])[:10]: print(f{asm}: {cnt})这段脚本的用法是统计方法与程序集的分布如果Assembly-CSharp.dll的占比异常低说明 metadata 解析不完整如果with_ns比例极低可能是命名空间被混淆或裁剪。正常的 Unity 工程Assembly-CSharp.dll应占大头其次才是UnityEngine.CoreModule等。再进一步用 Python 检索dump.cs中某个关键类的方法偏移与script.json里的地址做一对一的映射验算import json, re with open(rD:\work\input\dump.cs, r, encodingutf-8) as f: dump_text f.read() with open(rD:\work\input\script.json, r, encodingutf-8) as f: script_data json.load(f) class_name PlayerController count_in_dump len(re.findall(r\bclass %s\b % class_name, dump_text)) count_in_script sum(1 for e in script_data if class_name in e[Name]) print(dump class count:, count_in_dump) print(script method count:, count_in_script)如果 class 声明只有 1 个但 method 计数有几十个说明该类的方法映射完整如果两边都是 0大概率是类名被混淆或裁剪。这是我验证符号还原的最常用套路。哪怕目标有加固、有字符串加密只要 Dumper 能跑通这套脚本就能在几分钟内确认还原质量。从那以后我每次拿到新目标都强制走一遍这个流程先跑自动模式看版本验dump.cs扫script.json分布再灌进 IDA。确认无误后才开始正式的逆向分析。步骤不多但每一步都能在关键时刻拉你一把希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询