将IL2CPP APK反编译为Unity工程的详细指南
⚠️ 重要声明:完全恢复原始Unity工程是不可能的,只能尽量还原代码逻辑和资源。
📋 整体流程图
text
APK文件
│
├─► 1. 解包APK
│ │
│ ├─► assets/bin/Data/ (Unity资源)
│ ├─► lib/arm64-v8a/libil2cpp.so (编译后的代码)
│ └─► assets/bin/Data/Managed/Metadata/global-metadata.dat (元数据)
│
├─► 2. 提取资源 (AssetStudio)
│ │
│ └─► 贴图、模型、音频、动画、场景等
│
├─► 3. 反编译代码 (Il2CppDumper + Cpp2IL)
│ │
│ └─► C#伪代码 / DLL
│
└─► 4. 手动重建Unity工程
🛠️ 所需工具
| 工具 | 用途 | 下载链接 |
|---|---|---|
| APKTool | 解包APK | https://ibotpeaches.github.io/Apktool/ |
| 7-Zip | 解压文件 | https://7-zip.org/ |
| Il2CppDumper | 提取IL2CPP元数据 | https://github.com/Perfare/Il2CppDumper |
| Cpp2IL | 重建IL代码 | https://github.com/SamboyCoding/Cpp2IL |
| AssetStudio | 提取Unity资源 | https://github.com/Perfare/AssetStudio |
| UABE | 编辑Unity资源 | https://github.com/SeriousCache/UABE |
| dnSpy | 查看C#代码 | https://github.com/dnSpy/dnSpy |
| IDA Pro/Ghidra | 分析二进制代码 | https://ghidra-sre.org/ |
📝 详细步骤
第一步:解包APK
Bash
# 方法1:使用APKTool
apktool d game.apk -o output_folder
# 方法2:直接用7-Zip解压(将.apk改为.zip)
7z x game.apk -o"output_folder"
解包后的关键文件:
text
output_folder/
├── lib/
│ ├── arm64-v8a/
│ │ └── libil2cpp.so # 64位编译代码 ⭐重要
│ └── armeabi-v7a/
│ └── libil2cpp.so # 32位编译代码
├── assets/
│ └── bin/
│ └── Data/
│ ├── Managed/
│ │ └── Metadata/
│ │ └── global-metadata.dat # IL2CPP元数据 ⭐重要
│ ├── level0 # 场景文件
│ ├── level1
│ ├── resources.assets # 资源文件
│ ├── sharedassets0.assets
│ └── ...
第二步:使用Il2CppDumper提取代码结构
Bash
# 运行Il2CppDumper
Il2CppDumper.exe libil2cpp.so global-metadata.dat output_dir
输出文件:
text
output_dir/
├── dump.cs # 所有类、方法、字段的C#定义 ⭐
├── script.json # 用于IDA/Ghidra的脚本数据
├── stringliteral.json # 字符串常量
├── DummyDll/ # 空壳DLL文件
│ ├── Assembly-CSharp.dll
│ ├── UnityEngine.dll
│ └── ...
└── il2cpp.h # C++头文件
dump.cs 示例内容:
csharp
// Namespace:
public class BossClawAi : MonoBehaviour
{
// Fields
public Vector3 _homePosition; // 0x18
public Vector2 patrolXz; // 0x24
public float patrolFrequency; // 0x2C
public NavMeshAgent _navAgent; // 0x30
// Methods
// RVA: 0x4EB1A0 Offset: 0x4E99A0 VA: 0x4EB1A0
private void AiTick() { }
// RVA: 0x4EB8C0 Offset: 0x4EA0C0 VA: 0x4EB8C0
public void SetActive() { }
}
📌 注意:这只给出了类结构,方法体是空的!
第三步:使用Cpp2IL重建IL代码
Bash
# 基本用法
Cpp2IL --game-path "path/to/extracted/apk" --experimental-enable-il-to-assembly-please
# Android APK专用命令
Cpp2IL --input-as "lib/arm64-v8a/libil2cpp.so" --metadata-path "assets/bin/Data/Managed/Metadata/global-metadata.dat" --experimental-enable-il-to-assembly-please
更详细的命令:
Bash
Cpp2IL.exe ^
--input-as "libil2cpp.so" ^
--metadata-path "global-metadata.dat" ^
--unity-version "2021.3.1" ^
--experimental-enable-il-to-assembly-please ^
--output-as "dummydll" ^
--output-to "./cpp2il_output"
输出:
text
cpp2il_output/
└── DummyDll/
├── Assembly-CSharp.dll # 包含反编译的代码
├── UnityEngine.CoreModule.dll
└── ...
第四步:用dnSpy查看反编译的代码
- 打开 dnSpy
- 拖入
Assembly-CSharp.dll - 浏览代码
可能看到的结果:
✅ 成功反编译的方法:
csharp
public void SetActive()
{
this._isActive = true;
this._lastAiTickTime = Time.time;
this.SwitchMentalState(MentalState.Patrol);
}
❌ 失败的方法:
csharp
private void AiTick()
{
throw new AnalysisFailedException("CPP2IL failed to recover any usable IL for this method.");
}
第五步:用IDA/Ghidra分析未能反编译的代码
对于Cpp2IL无法恢复的方法,需要手动分析汇编:
Bash
# 1. 打开Ghidra,创建新项目
# 2. 导入libil2cpp.so
# 3. 运行Il2CppDumper生成的ghidra脚本来重命名函数
在Ghidra中:
text
1. File → Import File → 选择 libil2cpp.so
2. 选择语言: ARM:LE:64:v8A (或 ARM:LE:32:v7 for 32位)
3. 分析完成后: Window → Script Manager
4. 运行 ghidra_with_struct.py (Il2CppDumper生成的)
手动分析汇编示例:
assembly
; BossClawAi.AiTick() at 0x4EB1A0
sub_4EB1A0:
stp x29, x30, [sp, #-0x10]!
mov x29, sp
ldr x8, [x0, #0x18] ; 加载 _homePosition
bl Random_get_value ; 调用 Random.value
fmul s0, s0, s1 ; 乘法运算
; ... 继续分析
第六步:提取Unity资源
使用AssetStudio:
- 打开AssetStudio
File → Load Folder→ 选择assets/bin/Data/- 等待加载完成
- 在Asset List中查看所有资源
可提取的资源类型:
text
├── Texture2D → .png, .jpg
├── Sprite → .png
├── AudioClip → .wav, .ogg
├── Mesh → .obj, .fbx
├── AnimationClip → .anim
├── TextAsset → .txt, .json, .xml
├── Shader → .shader (通常无法完全恢复)
├── Material → 部分信息
├── MonoBehaviour → 序列化数据
└── GameObject/Scene → 层级结构
导出资源:
text
1. 选择要导出的资源
2. Export → Selected assets
3. 选择导出格式和目录
第七步:重建Unity工程
这是最困难的部分,需要手动完成:
text
1. 创建新Unity项目(匹配原版本)
2. 导入提取的资源
Assets/
├── Textures/
├── Models/
├── Audio/
├── Animations/
└── ...
3. 根据反编译的代码重写脚本
Assets/
└── Scripts/
├── BossClawAi.cs
├── PlayerController.cs
└── ...
4. 重建场景
- 使用AssetStudio查看场景层级
- 手动在Unity中重建GameObject结构
5. 重建预制体(Prefab)
6. 配置项目设置
- Player Settings
- Quality Settings
- 等
🔧 实用脚本和命令
一键处理脚本 (Windows Batch)
batch
@echo off
set APK_PATH=game.apk
set OUTPUT=output
echo [1/4] 解压APK...
7z x %APK_PATH% -o"%OUTPUT%\apk_extracted"
echo [2/4] 运行Il2CppDumper...
Il2CppDumper.exe ^
"%OUTPUT%\apk_extracted\lib\arm64-v8a\libil2cpp.so" ^
"%OUTPUT%\apk_extracted\assets\bin\Data\Managed\Metadata\global-metadata.dat" ^
"%OUTPUT%\il2cpp_dump"
echo [3/4] 运行Cpp2IL...
Cpp2IL.exe ^
--input-as "%OUTPUT%\apk_extracted\lib\arm64-v8a\libil2cpp.so" ^
--metadata-path "%OUTPUT%\apk_extracted\assets\bin\Data\Managed\Metadata\global-metadata.dat" ^
--experimental-enable-il-to-assembly-please ^
--output-to "%OUTPUT%\cpp2il_output"
echo [4/4] 完成!
echo - 代码结构: %OUTPUT%\il2cpp_dump\dump.cs
echo - 反编译DLL: %OUTPUT%\cpp2il_output\DummyDll\
pause
Python辅助脚本(解析dump.cs)
Python
import re
import os
def parse_dump_cs(dump_path, output_dir):
"""将dump.cs分割成单独的类文件"""
with open(dump_path, 'r', encoding='utf-8') as f:
content = f.read()
# 匹配类定义
pattern = r'(// Namespace: .*?\n)?public class (\w+).*?^}'
matches = re.findall(pattern, content, re.MULTILINE | re.DOTALL)
os.makedirs(output_dir, exist_ok=True)
for namespace, class_name, class_body in matches:
file_path = os.path.join(output_dir, f"{class_name}.cs")
with open(file_path, 'w', encoding='utf-8') as f:
f.write(f"// Auto-generated from dump.cs\n")
f.write(f"using UnityEngine;\n\n")
if namespace:
f.write(namespace)
f.write(f"public class {class_name}\n{{\n{class_body}\n}}")
print(f"Created: {file_path}")
# 使用
parse_dump_cs("dump.cs", "output_scripts")
⚠️ 常见问题和解决方案
问题1:Il2CppDumper报错 "Metadata version not supported"
Bash
# 解决:使用最新版Il2CppDumper,或尝试不同版本
# 或手动指定Unity版本
Il2CppDumper.exe libil2cpp.so global-metadata.dat output --unity-version 2021.3
问题2:Cpp2IL成功率很低
Bash
# 尝试不同选项
Cpp2IL.exe \
--input-as libil2cpp.so \
--metadata-path global-metadata.dat \
--use-processor attributeinjector \
--output-as isil
问题3:找不到global-metadata.dat
text
可能的位置:
- assets/bin/Data/Managed/Metadata/global-metadata.dat
- assets/Managed/Metadata/global-metadata.dat
- 有时会被加密或混淆
问题4:libil2cpp.so被加密
一些游戏会加密libil2cpp.so,需要:
- 使用Frida在运行时dump解密后的so
- 分析加密算法并解密
JavaScript
// Frida脚本示例 - dump解密后的libil2cpp.so
Java.perform(function() {
var System = Java.use("java.lang.System");
System.loadLibrary.implementation = function(libName) {
console.log("Loading: " + libName);
this.loadLibrary(libName);
if(libName.includes("il2cpp")) {
// Dump memory
var module = Process.findModuleByName("libil2cpp.so");
// ... dump logic
}
};
});
📊 能恢复多少?
| 内容 | 恢复程度 | 说明 |
|---|---|---|
| 类/方法/字段名称 | ✅ 100% | 元数据完整保留 |
| 方法实现代码 | ⚠️ 30-70% | 取决于Cpp2IL成功率 |
| 资源文件 | ✅ 90%+ | 大部分可完整提取 |
| 场景结构 | ⚠️ 50-70% | 可查看,需手动重建 |
| Shader代码 | ❌ ~10% | 编译后难以恢复 |
| 项目设置 | ❌ 很少 | 需要手动配置 |
| 注释/原始格式 | ❌ 0% | 完全丢失 |
📚 推荐学习资源
- Il2CppDumper Wiki: https://github.com/Perfare/Il2CppDumper/wiki
- Cpp2IL文档: https://github.com/SamboyCoding/Cpp2IL/wiki
- Unity逆向教程: https://katyscode.wordpress.com/
- ARM汇编学习: https://azeria-labs.com/writing-arm-assembly-part-1/
如果需要更详细地了解某个具体步骤,请告诉我!
免责声明
本文档所有内容仅供安全研究、学术交流与技术学习使用,严禁用于任何未经授权的逆向破解、网络攻击、隐私窃取、恶意软件开发及其他违反《中华人民共和国网络安全法》《数据安全法》等法律法规的行为,使用者应确保已获得目标软件权利人的合法授权并自行承担因使用本文档内容所产生的一切法律责任与后果,作者不对任何直接或间接损害承担任何责任,继续阅读即视为您已知悉并同意上述全部条款。
浙公网安备 33010602011771号