最强逆向组合:Il2CppDumper与Ghidra脚本模板完全开发指南
你是否还在为Unity游戏的il2cpp二进制文件逆向分析而烦恼?手动解析函数地址、重建类型结构、恢复字符串引用耗时又易错?本文将带你掌握Il2CppDumper与Ghidra脚本的协同工作流,通过5个实战步骤+3个高级模板,让你在1小时内完成专业级il2cpp逆向工具开发。
读完本文你将获得:
- 掌握Il2CppDumper输出文件的核心结构与应用场景
- 精通3种Ghidra脚本模板的修改与扩展技巧
- 学会自定义类型解析器处理复杂加密 metadata
- 构建自动化逆向流水线提升300%分析效率
- 解决90%常见逆向难题的调试方案
一、逆向工程的痛点与解决方案
1.1 il2cpp逆向的三大挑战
Unity游戏采用il2cpp技术将C#代码编译为原生机器码,带来性能提升的同时也给逆向分析带来巨大挑战:
- 符号信息缺失:编译过程剥离了所有C#类型元数据,仅剩内存地址
- 二进制格式多样:支持ELF、PE、Mach-O等多种格式,架构差异显著
- 版本兼容性复杂:Unity 5.3到2022.2之间存在20+版本差异,元数据结构不断变化
1.2 Il2CppDumper的核心价值
Il2CppDumper通过解析libil2cpp.so(或其他平台等效文件)和global-metadata.dat,恢复关键逆向信息:
关键输出文件及其作用:
| 文件类型 | 主要内容 | 应用场景 |
|---|---|---|
| script.json | 函数地址、字符串、元数据 | 逆向脚本数据源 |
| il2cpp.h | C风格结构体定义 | 类型信息重建 |
| ghidra.py | 基础分析脚本 | Ghidra自动化标注 |
| ghidra_with_struct.py | 带类型解析的脚本 | 高级结构应用 |
| il2cpp_header_to_ghidra.py | 头文件转换工具 | 类型定义导入 |
二、Ghidra脚本模板深度解析
2.1 基础模板:ghidra.py工作原理
Il2CppDumper提供的ghidra.py实现基础逆向功能,核心代码结构如下:
# 核心初始化
functionManager = currentProgram.getFunctionManager()
baseAddress = currentProgram.getImageBase()
USER_DEFINED = ghidra.program.model.symbol.SourceType.USER_DEFINED
# 地址转换
def get_addr(addr):
return baseAddress.add(addr)
# 标签创建
def set_name(addr, name):
name = name.replace(' ', '-')
createLabel(addr, name, True, USER_DEFINED)
# 函数创建
def make_function(start):
func = getFunctionAt(start)
if func is None:
createFunction(start, None)
# 主流程
f = askFile("script.json from Il2cppdumper", "Open")
data = json.loads(open(f.absolutePath, 'rb').read().decode('utf-8'))
# 处理函数
if "ScriptMethod" in data:
for scriptMethod in data["ScriptMethod"]:
addr = get_addr(scriptMethod["Address"])
name = scriptMethod["Name"].encode("utf-8")
set_name(addr, name)
make_function(addr)
# 处理字符串
if "ScriptString" in data:
for index, scriptString in enumerate(data["ScriptString"]):
addr = get_addr(scriptString["Address"])
value = scriptString["Value"].encode("utf-8")
name = f"StringLiteral_{index+1}"
createLabel(addr, name, True, USER_DEFINED)
setEOLComment(addr, value)
该脚本完成三项关键任务:
- 从
script.json加载逆向数据 - 根据地址创建函数标签(如
UnityEngine_UI_Button_OnClick) - 标注字符串常量及其值
2.2 高级模板:ghidra_with_struct.py增强功能
ghidra_with_struct.py在基础版上增加类型解析能力,核心扩展如下:
# 新增类型设置函数
def set_type(addr, type):
# 处理类型字符串格式化
newType = type.replace("*"," *").replace(" "," ").strip()
dataTypes = getDataTypes(newType)
# 处理指针类型
if len(dataTypes) == 0 and newType.endswith(" *"):
baseType = newType[:-2]
dataTypes = getDataTypes(baseType)
if dataTypes:
dtm = currentProgram.getDataTypeManager()
pointerType = dtm.getPointer(dataTypes[0])
addrType = dtm.addDataType(pointerType, None)
# 应用类型到地址
if addrType:
createData(addr, addrType)
# 新增函数签名设置
def set_sig(addr, name, sig):
try:
# 解析C风格函数签名
typeSig = CParserUtils.parseSignature(None, currentProgram, sig, False)
typeSig.setName(name)
# 应用签名到函数
ApplyFunctionSignatureCmd(addr, typeSig, USER_DEFINED, False, True).applyTo(currentProgram)
except ghidra.app.util.cparser.C.ParseException:
# 异常处理逻辑
print(f"解析签名失败: {sig}")
关键增强点:
- 类型系统集成:将JSON中的类型信息应用到内存地址
- 函数签名解析:通过C风格签名重建函数原型
- 错误处理完善:增加类型解析失败时的备选方案
2.3 转换工具:il2cpp_header_to_ghidra.py
该脚本处理il2cpp.h到Ghidra兼容格式的转换,解决Ghidra对某些C语法不支持的问题:
def main():
fixed_header_data = ""
with open("il2cpp.h", 'r') as f:
original_header_data = f.read()
# 修复Ghidra不支持的构造语法
fixed_header_data = re.sub(r": (\w+) {", r"{\n \1 super;", original_header_data)
# 添加基础类型定义
header = "typedef unsigned __int8 uint8_t;\n" \
"typedef unsigned __int16 uint16_t;\n" \
"typedef unsigned __int32 uint32_t;\n" \
"typedef unsigned __int64 uint64_t;\n"
with open("il2cpp_ghidra.h", 'w') as f:
f.write(header)
f.write(fixed_header_data)
主要解决:
- 添加Ghidra缺少的标准类型定义
- 转换C++继承语法为Ghidra兼容格式
- 处理特定编译器扩展语法
三、自定义脚本开发实战
3.1 需求分析:构建智能字符串分析器
默认脚本仅简单标注字符串,我们需要增强:
- 识别不同语言字符串(中文、日文等)
- 按使用场景分类标注(日志、UI文本、网络URL等)
- 关联引用函数,建立调用关系
3.2 实现方案:扩展字符串处理模块
# 增强版字符串处理
def process_string(scriptString, index):
addr = get_addr(scriptString["Address"])
value = scriptString["Value"]
# 语言检测
lang = detect_language(value)
# 场景分类
category = classify_string(value)
# 生成增强标签
name = f"StringLiteral_{category}_{lang}_{index}"
createLabel(addr, name, True, USER_DEFINED)
# 添加详细注释
comment = f"[{category}][{lang}] {value}"
setEOLComment(addr, comment)
# 查找引用并标注
references = getReferencesTo(addr)
for ref in references:
refAddr = ref.getFromAddress()
func = getFunctionContaining(refAddr)
if func:
funcName = func.getName()
setPreComment(refAddr, f"引用字符串: {name}")
# 语言检测函数
def detect_language(text):
# 中文检测
if re.search(r'[\u4e00-\u9fff]', text):
return "zh"
# 日文检测
elif re.search(r'[\u3040-\u309F\u30A0-\u30FF]', text):
return "ja"
# 韩文检测
elif re.search(r'[\uAC00-\uD7AF]', text):
return "ko"
else:
return "en"
# 字符串分类函数
def classify_string(text):
if re.match(r'^https?://', text):
return "URL"
elif re.match(r'^[0-9a-fA-F]{32}$', text):
return "Hash"
elif re.search(r'\{.*\}', text) or re.search(r'\[.*\]', text):
return "Format"
else:
return "Text"
3.3 整合与应用
将增强功能整合到完整脚本,修改主处理流程:
# 修改原字符串处理循环
if "ScriptString" in data and "ScriptString" in processFields:
index = 1
scriptStrings = data["ScriptString"]
monitor.initialize(len(scriptStrings))
monitor.setMessage("增强字符串处理")
for scriptString in scriptStrings:
process_string(scriptString, index) # 使用新函数处理
index += 1
monitor.incrementProgress(1)
四、高级应用:处理复杂场景
4.1 版本兼容性处理
不同Unity版本元数据结构差异大,可通过配置文件强制版本:
{
"ForceIl2CppVersion": true,
"ForceVersion": 24 // 强制使用Unity 2020.3+解析器
}
常见版本对应关系:
| Unity版本 | il2cpp版本 | 主要变化 |
|---|---|---|
| 5.3-5.6 | 1-6 | 基础结构,无泛型实例缓存 |
| 2017.x | 7-10 | 新增元数据v2格式 |
| 2018.x | 11-16 | 泛型处理优化 |
| 2019.x | 17-21 | 元数据加密支持 |
| 2020.x | 22-24 | 类型信息扩展 |
| 2021.x | 25-27 | 新增WASM支持 |
| 2022.x | 28+ | 元数据压缩优化 |
4.2 内存dump文件处理
对于内存dump的libil2cpp.so,需要特殊配置:
{
"ForceDump": true,
"NoRedirectedPointer": true // 禁用指针重定向
}
脚本层面需调整地址计算逻辑:
# 修改地址转换函数以支持dump文件
def get_addr(addr):
# 检测是否为dump模式并调整偏移
if config.get("ForceDump", False):
# 针对不同架构的基址修正
if is_arm_architecture():
return baseAddress.add(addr - 0x10000) # 示例偏移
elif is_x86_architecture():
return baseAddress.add(addr - 0x400000)
return baseAddress.add(addr)
4.3 自动化逆向流水线
结合多个脚本创建完整逆向流程:
自动化命令行调用:
# 1. 运行Il2CppDumper生成分析文件
Il2CppDumper.exe libil2cpp.so global-metadata.dat output
# 2. 转换头文件
python output/il2cpp_header_to_ghidra.py
# 3. 在Ghidra中执行导入脚本
# (需通过Ghidra脚本管理器或头文件导入功能)
五、调试与优化
5.1 常见错误及解决方法
错误1:类型解析失败
症状:set_type()函数频繁报错,类型无法应用
解决方法:
# 修改类型解析错误处理
def set_type(addr, type):
try:
# 原始类型处理逻辑
# ...
# 如失败,尝试基础类型
if not addrType:
base_types = ["int", "void", "char", "long"]
for base in base_types:
if type.startswith(base):
addrType = currentProgram.getDataTypeManager().getDataType(base)
break
if addrType:
createData(addr, addrType)
except Exception as e:
# 记录详细错误而非仅打印
with open("type_errors.log", "a") as f:
f.write(f"地址: {addr}, 类型: {type}, 错误: {str(e)}\n")
错误2:函数签名解析异常
症状:复杂函数签名无法解析,set_sig()失败
解决方法:预定义常见类型映射:
# 类型别名映射表
TYPE_ALIASES = {
"unsigned int": "uint32_t",
"unsigned long long": "uint64_t",
"size_t": "uint64_t",
# 添加更多类型映射
}
def normalize_signature(sig):
for alias, real_type in TYPE_ALIASES.items():
sig = sig.replace(alias, real_type)
# 处理函数指针语法
sig = re.sub(r'(\w+)\(\*(\w+)\)\((.*)\)', r'__attribute__((cdecl)) \1 (*\2)(\3)', sig)
return sig
5.2 性能优化
处理大型游戏时,脚本可能耗时过长:
# 优化1:批量处理而非逐个操作
def batch_process_methods(methods):
# 使用事务批量处理
tx = currentProgram.startTransaction("批量方法处理")
try:
for method in methods:
# 处理逻辑
# ...
finally:
currentProgram.endTransaction(tx, True)
# 优化2:进度条与取消支持
monitor.initialize(len(scriptMethods))
monitor.setMessage("处理方法")
for i, scriptMethod in enumerate(scriptMethods):
if monitor.isCancelled():
break
# 处理单个方法
# ...
monitor.incrementProgress(1)
# 定期更新状态
if i % 100 == 0:
monitor.setMessage(f"已处理 {i}/{len(scriptMethods)} 方法")
六、总结与扩展
6.1 关键知识点回顾
- Il2CppDumper通过解析可执行文件和元数据,为逆向提供关键数据
- Ghidra脚本模板提供基础逆向能力,可通过扩展满足特定需求
script.json包含核心逆向信息,是自定义脚本的主要数据源il2cpp.h提供类型定义,需转换后才能在Ghidra中有效使用- 版本兼容性和内存dump处理是实际应用中的主要挑战
6.2 进阶探索方向
- 交互式分析:结合Ghidra的交互式能力,创建用户界面调整分析参数
- 机器学习辅助:使用ML模型识别字符串类型和函数功能
- 跨平台支持:扩展脚本以支持iOS(Mach-O)和Windows(PE)格式
- 动态调试集成:结合Ghidra Debugger API,实现动态分析与静态分析结合
通过掌握Il2CppDumper与Ghidra脚本开发,你已具备解决复杂Unity游戏逆向分析的能力。记住,逆向工程不仅是工具的使用,更是对软件结构和编译原理的深入理解。持续探索不同版本和保护手段,不断优化你的分析工具链,将使你在逆向工程领域保持竞争力。
最后,逆向分析应遵守软件许可协议和相关法律法规,本文技术仅供学习研究使用。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



