zboralski/disunity
GitHub: zboralski/disunity
一款用于 Unity IL2CPP ARM64 二进制文件的静态元数据提取工具,无需运行程序即可还原方法与调用关系。
Stars: 3 | Forks: 0
# disunity: 静态 IL2CPP 元数据提取
## 概述
disunity 是一个用于 Unity IL2CPP ARM64 二进制文件的静态分析工具。
它从以下文件中提取方法名、类型层次结构和调用关系:
- `libil2cpp.so`
- `global-metadata.dat`
无需执行二进制文件。
输出是一个结构化的 JSON 文件,可被 Ghidra 或 IDA 读取,用于快速标注大型二进制文件。
这是一个概念验证项目。在我们测试的样本上效果良好,但对边缘情况和更新 IL2CPP 版本的支持取决于更多样本的可用性。
## 范围
- ARM64 ELF(Android Unity 应用)
- IL2CPP 元数据版本 24、27、29、31
- Unity 2017.1 至 Unity 6000.x
- 纯静态分析
版本 31 之后(新 Unity 构建中的 v35、v38、v39)暂不支持。欢迎提供这些版本的测试样本。
## 为什么要构建这个
现有工具虽然可用,但在三种压力下会失效:
- 规模
- 可重现性
- 安全性
对于每个包含 5 万到 10 万方法的二进制文件,瓶颈不是提取速度,而是如何快速定位关键部分以及在畸形或对抗性输入下管道的可靠性。
disunity 的设计目标是让管道更可预测,并减少提取后所需的人工分类量。
## 信号提取
大多数 IL2CPP 二进制文件以框架代码为主。
有用的行为集中在少量方法集中:
- 加密
- 网络
- 文件 I/O
- 本地互操作
- 动态加载
disunity 按行为对方法进行分类,并构建围绕这些信号的最小调用图。
这使你能够:
- 识别加密路径和密钥
- 定位网络端点
- 查找动态加载路径
无需逐步执行数万个函数。
参见 [docs/samples.md](docs/samples.md) 获取五个真实样本的演练,包括 AES/XXTEA/RSA 检测、区域隐藏、旧版 v24 回退以及加密元数据。
## 加密元数据
某些应用程序会加密 `global-metadata.dat`。disunity 通过检查魔数来检测加密元数据并报告加密方式。
代码库包含针对 XOR 加密元数据的实验性恢复支持,包括卡斯基分析、基于 IL2CPP 头部的已知明文攻击和有界暴力破解。这适用于重复 XOR 密钥。
我们测试样本集中唯一的加密样本使用了非 XOR 方案(可能是 AES 或自定义密码)。disunity 和 Il2CppDumper 都无法解密。需要带有实际 XOR 加密的样本来验证 XOR 恢复路径。
如果你有 XOR 加密的元数据样本(FairGuard、自定义 XOR 包装器),我们希望测试它们。
## 性能
从源码编译不到一秒。仅一个依赖。
在 23 个真实样本上与 Il2CppDumper 的提取对比:
| 指标 | disunity | Il2CppDumper |
| ------ | -------- | ------------ |
| 平均耗时 | 1.4s | 3.8s |
| 中位数 | 605ms | 2942ms |
| 更快的样本数 | 22 | 1 |
在小于 20K 方法的较小二进制文件上差距更大。
完整流程在 9.1 万方法的二进制文件上:
- 元数据解析
- 二进制分析
- 反汇编
- 调用图
- 信号分类
在 M 系列 Mac 上总耗时约 11 秒。
## Il2CppDumper 中的安全发现
在验证过程中,我们将 Il2CppDumper 视为输入表面,并应用了与 IL2CPP 解析相同的淘汰方法。
### 方法
1. 识别输入边界:
- PE 加载器
- ELF 加载器
- 元数据解析器
- ULEB128 解码器
- 压缩处理程序
2. 生成针对性变异:
- 截断头部
- 偏移溢出
- 畸形哈希表
- 无效重定位项
- 超大长度字段
3. 淘汰安全路径,直到仅剩不稳定行为。
这不是盲目模糊测试。每个变异都针对解析器中的特定假设。
### 结果
#### 通过 LoadLibrary 执行代码
Il2CppDumper 对 PE 输入调用 `LoadLibrary` 作为回退路径。
如果文件由攻击者控制,`DllMain` 会立即执行。
打开恶意的 `GameAssembly.dll` 会在任何解析之前运行代码。
#### dump.cs 中未过滤的标识符
元数据中的类型名和方法名直接写入生成的 `dump.cs` 文件,未进行过滤。
`il2cpp.h` 输出经过 `FixName()` 处理,仅允许 `[a-zA-Z0-9_]` 字符。而 `dump.cs` 路径没有此限制。
当 `dump.cs` 被除人类阅读者之外的任何内容消费时,这会成为问题。Modding 框架、代码生成器、AI 训练流水线、CI/CD 扫描器以及将 `dump.cs` 视为 C# 源或标识符列表的下游解析器都会收到元数据中包含的任何内容,包括嵌入在类型名中的语法有效的 C# 片段。
精心构造的类型名可能引入新声明、提前终止封闭类或注入属性。这是否重要完全取决于后续处理该文件的流程。`il2cpp.h`(已清理)与 `dump.cs`(未清理)之间的不一致表明这可能是无意的权衡,而非刻意设计。
#### 拒绝服务向量
我们确认了多种崩溃和挂起情况:
- 字符串偏移溢出
- 无限制字符串读取
- GNU 哈希链循环
- ULEB128 解码循环
- 超大分配
- 重定位处理越界
这些可通过小型畸形输入触发。
### 为何这很重要
如果你在不可信的二进制文件上使用这些工具,工具本身就会成为攻击面。
disunity 通过设计避免了这一点:
- 无动态加载
- 无代码生成
- 对所有表读取进行边界检查
畸形输入会导致错误,而非执行。
## Ghidra 集成
标准流程:
- 导入二进制文件
- 运行脚本
- 等待完整重新分析
在大型二进制文件上这需要 30 到 60 分钟。
disunity 改变了顺序:
- 禁用不必要的分析器
- 应用元数据
- 在全局重新分析前退出
导入时间缩短至几分钟。
## 架构

| 包 | 用途 |
| ------- | ------- |
| `cmd/disunity` | CLI |
| `internal/metadata` | global-metadata.dat 解析器 |
| `internal/binary` | CodeRegistration 和 MetadataRegistration 分析 |
| `internal/disasm` | ARM64 反汇编和调用图 |
| `internal/signal` | 行为分类 |
| `internal/output` | 导出格式 |
| `internal/pipeline` | 编排 |
## 兼容性
已在 1,067 个真实世界的 Unity IL2CPP Android 应用样本上测试。
1,063 个产生正确输出。4 个未通过。无崩溃。无错误输出。
| 版本 | Unity | 样本数 | 通过数 | 通过率 |
| --------- | ------------- | --------- | --------- | --------- |
| v24 | 2017.1-2019.4 | 39 | 37 | 94% |
| v27 | 2020.2-2021.1 | 45 | 45 | 100% |
| v29 | 2021.2+ | 176 | 175 | 99% |
| v31 | 2022.1+ | 806 | 806 | 100% |
| **总计** | **全部** | **1,067** | **1,063** | **99.6%** |
4 个失败样本:
- 2 个 v24 在大型旧式二进制文件上超时(pre-CodeGenModules 扫描较慢)
- 1 个损坏的元数据(字符串偏移量达数十亿,IlCppDumper 也失败)
- 1 个加密元数据(非 XOR 方案,disunity 和 Il2CppDumper 均无法解密)
IL2CPP 元数据版本 25、26、28 和 30 不存在。版本号从 24 跳至 27,再到 29,最后是 31。
更大的样本集(来自 Android 应用生产环境的 1,079 个元数据文件)显示版本分布高度集中于近期 Unity 版本:
| 版本 | 数量 | 占比 |
| --------- | ----- | ----- |
| v31 | 813 | 75% |
| v29 | 177 | 16% |
| v27 | 46 | 4% |
| v24 | 39 | 4% |
| v22 | 1 | <1% |
| 加密 | 3 | <1% |
大多数实际应用使用 v29 或 v31。旧版本越来越少见。v22(Unity 5.5.x)存在但目前不受支持。
版本 31 之后(v35、v38、v39)在新 Unity 构建中已观察到,但尚未出现在我们的测试样本中。
## 方法论
该工具采用淘汰法而非完整规范构建。

从一个版本的生效解析器开始,跨版本测试。移除假设,直到仅剩稳定结构。
每个版本差异都被视为约束,而非特殊情况。
相同方法应用于:
- 信号提取
- 元数据解密
- 漏洞发现
## 用法
```
disunity libil2cpp.so global-metadata.dat
disunity signal libil2cpp.so global-metadata.dat
disunity meta libil2cpp.so global-metadata.dat
disunity ghidra libil2cpp.so global-metadata.dat
```
## 构建
```
make build # build ./disunity
make test # run tests
make install # install to ~/.disunity/
```
单一依赖:`golang.org/x/arch` 用于 ARM64 指令解码。
## 相关项目
disunity 是一个用于移动应用逆向工程的工具包的一部分:
- [galago](https://github.com/nicloay/galago) - Cocos2d-x XXTEA 密钥提取和 JSC 解密
- [deflutter](https://github.com/nicloay/deflutter) - 从 libapp.so 恢复 Flutter/Dart 符号
- [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) - 成熟的 IL2CPP 提取器(C#)
- [Il2CppInspectorRedux](https://github.com/LukeFZ/Il2CppInspectorRedux) - 支持更新元数据版本(v35+)
- [Cpp2IL](https://github.com/SamboyCoding/Cpp2IL) - 另一种 IL2CPP 工具,集成 Mono Cecil
## 许可证
MIT
标签:Android, ARM64, DSL, EVTX分析, Ghidra插件, IDA插件, IL2CPP, Unity, URL提取, 二进制分析, 云安全监控, 云安全运维, 云资产清单, 信号提取, 元数据提取, 加密检测, 动态加载, 原生互操作, 反编译, 可重现分析, 安全管道, 文件I/O, 方法名提取, 日志审计, 样本分析, 框架代码, 类型层次, 结构化JSON, 网络行为, 误用抵抗, 调用图, 进程保护, 逆向工程, 静态分析, 静态提取