关注

【Unity IL2CPP 项目逆向分析教程】从 Il2CppDumper 到 Ghidra

前言

  • 声明:本文仅用于 技术研究与学习目的,讨论的是 Unity 游戏打包结构、IL2CPP 原理、GameAssembly.dll 分析方法 等知识点,不涉及任何商业游戏资源的发布或破解。

  • 本文将使用Il2CppDumper 解析 GameAssembly.dllglobal-metadata.dat,结合 ILSpy 查看类与方法结构,再通过 Ghidra 对机器码函数进行反汇编和伪 C 代码分析,最终可以得到的效果:请添加图片描述

  • 大致的流程:

GameAssembly.dll + global-metadata.dat
        ↓
   Il2CppDumper → 生成 script.json / stringliteral.json / DummyDll
        ↓
   ILSpy → 查看 DummyDll 类结构
        ↓
   Ghidra → 导入 GameAssembly.dll + JSON 映射 → 查看函数实现

1 原理解析

1-1 Unity打包项目结构
  • 当 Unity 项目打包后,通常会生成类似如下的目录结构:
GameName
├─GameName.exe
├─GameName_Data
│  ├─Managed
│  ├─Plugins
│  ├─Resources
│  ├─StreamingAssets
│  └─globalgamemanagers
  • 不同脚本后端会导致 代码存储方式不同
  • 通常在 Mono 脚本后端 下:
GameName_Data  
└─Managed  
├─Assembly-CSharp.dll  
├─UnityEngine.dll  
└─其他程序集
  • 如果项目使用 IL2CPP
GameName
├─GameAssembly.dll
└─GameName_Data
   └─il2cpp_data
      └─Metadata
         └─global-metadata.dat

1-2 Assembly-CSharp.dll
  • 在 Unity 项目中:
    • Assembly-CSharp.dll默认的游戏脚本程序集
  • 所有用户编写的脚本,例如:
Player.cs  
Enemy.cs  
GameManager.cs
  • 都会被编译到:Assembly-CSharp.dll
  • 因此如果是Mono 脚本后端打包的项目,我们可以直接通过解包工具直接反汇编Assembly-CSharp.dll得到游戏的工程源码
  • 需要注意的是,在 Mono 脚本后端 下,Assembly-CSharp.dll 中存储的并不是机器码,而是 .NET 中间语言(IL,Intermediate Language)
  • IL 是一种 高级虚拟机指令,仍然保留了大量原始程序结构信息,例如:
    • 类结构
    • 方法名
    • 字段
    • 控制流程
    • 局部变量
  • 因此使用 ILSpydnSpy 等工具反编译时,可以较容易地恢复出 接近原始 C# 的代码结构
  • 例如:
public void Attack()  
{  
	if (hp <= 0)  
	{  
		Die();  
	}  
}

1-3 IL2CPP

在这里插入图片描述

  • IL2CPP(Intermediate Language To C++)是 Unity 提供的 AOT(Ahead-of-Time)编译方案
  • 其编译流程如下:
C# Script  
   ↓  
IL(Assembly-CSharp.dll)  
   ↓  
IL2CPP 转换  
   ↓  
C++ 代码  
   ↓  
C++ 编译器  
   ↓  
GameAssembly.dll
  • 与 Mono 不同,IL2CPP 在构建过程中会将 IL 转换为 C++ 再编译为机器码,因此最终生成的 GameAssembly.dll 中存储的是 原生机器指令,而不是 .NET 的 IL。
  • 这意味着:
    • GameAssembly.dll 中的方法已经被编译为 CPU 指令
    • 原始的 C# 语法结构已经丢失
    • 只能通过反汇编工具查看底层汇编代码
  • 同时 Unity 会将 类型信息 单独存储在:
global-metadata.dat
  • 该文件中包含:
    • 类名
    • 方法名
    • 字段信息
    • 参数类型
    • 命名空间
  • 因此在 IL2CPP 项目中:
    • GameAssembly.dll → 机器码逻辑
    • global-metadata.dat → 类型与方法信息
  • 正是因为 代码与类型信息被拆分存储,普通的 .NET 反编译工具(如 ILSpydnSpy)无法直接解析 IL2CPP 程序。
  • 如下就是解包后的一段例子:
[Token(Token = "0x6000531")]
[Address(RVA = "0x32B490", Offset = "0x329E90", VA = "0x18032B490")]
public void OnClick_BtnItem()
{

}
  • 因此需要借助 Il2CppDumper 等工具,将两者结合起来 重建程序集结构,从而恢复出类似 Assembly-CSharp.dll 的代码框架。
  • 这也是 IL2CPP 相比 Mono 更难逆向分析的主要原因

1-4 Il2CppDumper
  • 当然上有政策下有对策
  • Il2CppDumper 是一个常用的 IL2CPP 解析工具参考下载链接
  • 由于 IL2CPP 项目中:
GameAssembly.dll      → 机器码逻辑  
global-metadata.dat   → 类型信息
  • 代码逻辑与类型信息被 拆分存储,因此普通的 .NET 反编译工具无法直接解析。
  • Il2CppDumper 的核心作用就是 重新关联这两个文件中的信息,从而恢复程序的结构。
  • 具体来说,它会完成以下工作:
    • 解析 GameAssembly.dll 中的 IL2CPP 结构
    • 解析 global-metadata.dat 中的类型数据
  • 然后将方法地址与类型信息重新匹配,并重建程序集结构
  • 解析完成后,工具会生成如下文件:
DummyDll  
script.json  
stringliteral.json
dump.cs
  • DummyDll
    • 生成的 伪程序集 DLL,通常是 Assembly-CSharp.dll
    • 包含类结构、方法签名、字段信息
    • 方法体为空,但可以用于 理解游戏结构和类关系
  • script.json
    • 保存 方法、类型、字段等映射信息
    • 可以被 IL2CPP 分析工具或 IDA/Ghidra 脚本读取,用于快速定位方法或类型
  • stringliteral.json
    • 保存程序中 字符串字面量(string literal) 的信息
    • 对分析 UI 文本、配置或提示信息很有帮助
  • dump.cs
    • Il2CppDumper 自动生成的 C# 脚本
    • 作用是 帮助快速定位函数、字段和类型
    • 虽然方法体为空,但可以作为 辅助参考,结合反汇编工具进行逻辑分析

1-5 ILSpy
  • ILSpy 是一个 .NET 反编译工具。参考下载链接
  • 它可以:
    • 查看 .dll
    • 反编译 C# 代码
    • 导出 Visual Studio 项目
  • 在 Mono 项目中,ILSpy 可以直接反编译:
Assembly-CSharp.dll
  • 并恢复出接近原始的 C# 源代码
  • 但在 IL2CPP 项目中,ILSpy 的作用主要是:
    • 读取 Il2CppDumper 生成的 DummyDll
  • 通过 ILSpy 打开 DummyDll/Assembly-CSharp.dll 后,可以看到:
    • 游戏的命名空间结构
    • 类之间的继承关系
    • 方法签名
    • 字段定义
  • 因此 IL2CPP 的常见分析流程是:
GameAssembly.dll + global-metadata.dat  
          ↓  
      Il2CppDumper  
          ↓  
        DummyDll  
          ↓  
         ILSpy  
          ↓  
   Assembly-CSharp.csproj
  • 最终可以得到一个 可浏览的伪项目结构,从而更方便地理解程序的整体架构。

1-6 Mono vs IL2CPP 对比表
项目Mono 脚本后端IL2CPP 脚本后端
编译输出Assembly-CSharp.dll(IL 中间语言)GameAssembly.dll(机器码) + global-metadata.dat(类型信息)
代码可读性可直接使用 ILSpy / dnSpy 反编译成接近原始 C#方法体为空,需通过 Il2CppDumper + 反汇编工具分析
方法逻辑存储IL 代码包含方法实现机器码在 GameAssembly.dll,DummyDll 仅包含结构信息
类型信息存储存储在 DLL 中存储在 global-metadata.dat 中,独立于逻辑代码
工具支持ILSpy、dnSpy 直接反编译Il2CppDumper 生成 DummyDll + ILSpy 查看结构;IDA/Ghidra 查看逻辑
分析难度简单,可快速恢复源码较高,需要工具链 + 反汇编配合分析
优点反编译易,调试简单AOT 编译,性能和安全性更高
缺点性能较低,逆向容易逆向难度大,源码不可直接获取

1-7 Ghidra
  • Ghidra 是由美国国家安全局(NSA)开源的一款 反汇编与逆向分析工具官方链接
  • 功能特点:
    • 支持多种处理器架构(x86, x64, ARM, ARM64, MIPS 等)
    • 可以分析 PE、ELF、Mach-O 等可执行文件格式
    • 内置反编译器,可将汇编代码转换为 伪 C 代码,便于理解
    • 支持脚本扩展(Jython / Java / Python),可自动化批量标记符号与函数
  • 在 IL2CPP 项目中,Ghidra 的作用:
    • 分析 GameAssembly.dll(机器码逻辑)
    • Il2CppDumper 生成的 script.json/stringliteral.json 配合,将函数名、类型信息映射回机器码
    • 查看具体函数实现、内存布局、调用关系
    • 补充 IL2CPP 结构信息中 方法体为空 的情况
  • 使用流程:
GameAssembly.dll
      ↓
Ghidra 导入
      ↓
	 分析 
      ↓
使用 Il2CppDumper JSON 映射
      ↓
  查看函数实现

2 教程

2-1 准备文件
  • 首先找到两个核心文件:
    • GameAssembly.dll
    • global-metadata.dat
  • 一般位于
游戏目录  
├─GameAssembly.dll  
└─GameName_Data  
   └─il2cpp_data  
      └─Metadata  
         └─global-metadata.dat

2-2 使用 Il2CppDumper
  • 打开 Il2CppDumper.exe
  • 在文件选择窗口中选择:
GameAssembly.dll
global-metadata.dat
  • 工具会自动解析并生成输出文件,输出目录中会出现:在这里插入图片描述
  • 其中DummyDll是解包后的文件目录,里头有生成的 伪程序集 DLL,通常是 Assembly-CSharp.dll
  • script.jsonstringliteral.json则是这些程序集的方法、类型、字段等映射信息,一会我们会使用Ghidra 脚本读取,用于快速定位方法或类型

2-3 使用 ILSpy 读取 DummyDll
  • 打开 ILSpy
    1. 选择 File → Open
    2. 打开:
DummyDll/Assembly-CSharp.dll

请添加图片描述

  • 此时可以看到游戏的整个伪项目:请添加图片描述

  • 虽然方法体通常为空,但可以获得:

    • 类结构
    • 方法签名
    • 字段信息

2-4 (可选)导出 Visual Studio 项目
  • ILSpy 中:
    1. 右键 Assembly-CSharp
    2. 选择:Save Code
  • ILSpy 会自动生成:
Assembly-CSharp.csproj
  • 我们可以使用 Visual Studio 打开 Assembly-CSharp.csproj

2-5 使用Ghidra反汇编函数具体实现
2-5-1 准备 Ghidra 项目
  • 找到ghidra下载文件夹下的support打开pyghidraRun.bat请添加图片描述

  • 注意!!!这里必须使用pyghidraRun.bat打开,否则一会无法在ghidra中运行python脚本

  • 打开 Ghidra → File → New Project → 选择 Non-Shared Project请添加图片描述


2-5-2 导入 GameAssembly.dll
  • File→ Import File → 选择 GameAssembly.dll,Ghidra 会自动识别文件类型(PE / x86-64 / ARM64 等,根据平台选择)点击 OK 进行导入

  • 请添加图片描述

  • Project Window 中双击导入的 DLL,打开 CodeBrowser 窗口

  • 导入完成后,Ghidra 会弹出 “Analyze Imported File” 窗口请添加图片描述

  • 分析选项可以保持默认,点击 Analyze请添加图片描述

  • 现在已经可以看到具体实现了但是仍缺乏具体的函数名请添加图片描述


2-5-3 加载 Il2CppDumper JSON 数据还原函数名
  • Window → Script Manager, 我们点击新建脚本请添加图片描述

  • 类型我们选择PyGhidra

  • 请添加图片描述

  • Il2CppDumper 提供的 ghidra.py 脚本 拷贝到新建脚本

  • 注意在脚本开头加上

# @runtime jython
  • 这里也提供完整的代码实现
# -*- coding: utf-8 -*-
# @runtime jython
import json

processFields = [
	"ScriptMethod",
	"ScriptString",
	"ScriptMetadata",
	"ScriptMetadataMethod",
	"Addresses",
]

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 and "ScriptMethod" in processFields:
	scriptMethods = data["ScriptMethod"]
	monitor.initialize(len(scriptMethods))
	monitor.setMessage("Methods")
	for scriptMethod in scriptMethods:
		addr = get_addr(scriptMethod["Address"])
		name = scriptMethod["Name"].encode("utf-8")
		set_name(addr, name)
		monitor.incrementProgress(1)

if "ScriptString" in data and "ScriptString" in processFields:
	index = 1
	scriptStrings = data["ScriptString"]
	monitor.initialize(len(scriptStrings))
	monitor.setMessage("Strings")
	for scriptString in scriptStrings:
		addr = get_addr(scriptString["Address"])
		value = scriptString["Value"].encode("utf-8")
		name = "StringLiteral_" + str(index)
		createLabel(addr, name, True, USER_DEFINED)
		setEOLComment(addr, value)
		index += 1
		monitor.incrementProgress(1)

if "ScriptMetadata" in data and "ScriptMetadata" in processFields:
	scriptMetadatas = data["ScriptMetadata"]
	monitor.initialize(len(scriptMetadatas))
	monitor.setMessage("Metadata")
	for scriptMetadata in scriptMetadatas:
		addr = get_addr(scriptMetadata["Address"])
		name = scriptMetadata["Name"].encode("utf-8")
		set_name(addr, name)
		setEOLComment(addr, name)
		monitor.incrementProgress(1)

if "ScriptMetadataMethod" in data and "ScriptMetadataMethod" in processFields:
	scriptMetadataMethods = data["ScriptMetadataMethod"]
	monitor.initialize(len(scriptMetadataMethods))
	monitor.setMessage("Metadata Methods")
	for scriptMetadataMethod in scriptMetadataMethods:
		addr = get_addr(scriptMetadataMethod["Address"])
		name = scriptMetadataMethod["Name"].encode("utf-8")
		methodAddr = get_addr(scriptMetadataMethod["MethodAddress"])
		set_name(addr, name)
		setEOLComment(addr, name)
		monitor.incrementProgress(1)

if "Addresses" in data and "Addresses" in processFields:
	addresses = data["Addresses"]
	monitor.initialize(len(addresses))
	monitor.setMessage("Addresses")
	for index in range(len(addresses) - 1):
		start = get_addr(addresses[index])
		make_function(start)
		monitor.incrementProgress(1)

print 'Script finished!'

  • 点击运行按钮请添加图片描述

  • 如果运行python脚本报错:

    • 请确保打开ghidra的方式为pyghidraRun.bat
    • 且脚本开头添加了# @runtime jython
  • 运行脚本 → 弹出文件选择窗口 → 选择:

    • script.json(方法、类型映射)
    • stringliteral.json(字符串映射)
  • 脚本执行后:

    • 函数、类、字段、字符串会被自动标记和命名
    • 函数名和类型信息会与 IL2CPP 结构对应
    • 可在 Functions Window 中查看已映射的函数列表请添加图片描述

2-5-4 查看函数实现
  • Functions Window 中双击函数 → 打开 ListingDecompile 窗口请添加图片描述

  • 可以看到伪 C 代码(伪汇编)请添加图片描述

  • 注意:

    • 方法内部逻辑可读
    • 局部变量名通常无法还原,需人工命名分析
      请添加图片描述

2-6 流程总结
GameAssembly.dll + global-metadata.dat
        ↓
   Il2CppDumper → 生成 script.json / stringliteral.json / DummyDll
        ↓
   ILSpy → 查看 DummyDll 类结构
        ↓
   Ghidra → 导入 GameAssembly.dll + JSON 映射 → 查看函数实现

总结

  • 本文系统讲解了 Unity 游戏打包结构、Mono 与 IL2CPP 的差异、以及 IL2CPP 项目的逆向分析流程,重点介绍了如何利用 Il2CppDumper 解析 GameAssembly.dllglobal-metadata.dat,结合 ILSpy 查看类与方法结构,再通过 Ghidra 对机器码函数进行反汇编和伪 C 代码分析,从而在方法体不可直接还原的情况下理解游戏逻辑和调用关系,为学习和研究 Unity 游戏逆向提供了完整、可操作的技术方案。
  • 如有错误欢迎指出!感谢大家的观看

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/m0_73800387/article/details/159156747

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--