Unity游戏逆向实战 - Mono打包方式完全解析
字数 2406
更新时间 2026-02-06 04:49:52
Unity游戏逆向实战 - Mono打包方式完全解析
1. Unity框架与Mono技术基础
1.1 Unity跨平台架构
Unity3D最大的特点是"一次制作,多平台部署",这一核心功能通过Mono技术实现。Mono作为Unity3D的核心组件,是Unity跨平台能力的根本基础。
1.2 Mono技术原理
- Mono是一个开源的.NET框架实现
- 允许Unity在多种支持.NET的平台上运行
- 提供兼容的运行时环境和类库
- 将源代码编译成IL(中间语言)代码
1.3 安全考虑与IL2CPP
由于Mono将代码编译为IL中间语言,使得逆向工程相对容易。为增强安全性,Unity从2014年开始引入IL2CPP技术:
- 将IL代码转化为C++代码
- 再编译成机器码
- 提高代码安全性和性能
2. Mono打包游戏的文件结构分析
2.1 打包方式识别
Mono打包特征:
- 游戏目录下存在:
Data/Managed/Assembly-CSharp.dll - 主要逻辑代码集中在此文件中
IL2CPP打包特征:
- 游戏目录下存在大型
GameAssembly.dll文件 - 包含
Data/il2cpp_data文件夹
2.2 文件结构详解
游戏根目录/
└── Data/
└── Managed/
├── Assembly-CSharp.dll # 游戏主要逻辑代码
├── Assembly-CSharp-firstpass.dll # 第三方插件代码
├── UnityEngine.dll # Unity引擎库
└── 其他.NET框架库文件
3. dnSpy工具深度分析
3.1 基本操作流程
- 使用dnSpy打开
Assembly-CSharp.dll文件 - 浏览游戏的所有类和方法结构
- 重点关注特殊Unity组件类
3.2 Unity特殊类识别
MonoBehaviour派生类特征:
Start(): 脚本启动时调用一次Update(): 每帧调用一次OnClick(): 按钮点击事件处理- 这些方法通常包含游戏核心逻辑
3.3 关键API搜索技巧
常用Unity API关键词搜索:
PlayerPrefs: 游戏数据存储SceneManager: 场景管理Input: 输入处理Debug.Log: 调试信息输出- 验证相关:
flag、password、check、verify
4. 实战案例深度解析
4.1 案例一:BJDCTF2020 BJD hamburger competition
解题步骤:
-
定位关键代码
- 在
ButtonSpawnFruit类的Spawn方法中发现flag相关逻辑 - 找到SHA1密文:
DD01903921EA24941C26A48F2CEC24E0BB0E8CC7
- 在
-
解密过程
- 在线解密SHA1得到明文:
1001 - 分析MD5加密函数发现特殊处理:
- 结果转换为大写
- 只取前20个字符
- 在线解密SHA1得到明文:
-
Flag生成
# 计算过程 明文 = "1001" MD5("1001") = "B8C37E33DEFDE51CF91E1E1E1E1E1E1E" 取前20字符 = "B8C37E33DEFDE51CF91E" 最终flag = "BJDCTF{B8C37E33DEFDE51CF91E}"
4.2 案例二:SCTF2019 Who is he
加密逻辑分析:
-
发现加密机制
TestClick类中的EncryptData变量存储密文- 加解密流程:明文 → DES加密 → Base64编码
-
密钥分析
- 关键发现:C#采用Unicode字符存储
- 密钥和IV:
b"1\x002\x003\x004\x00"
-
解密脚本
import base64 from Crypto.Cipher import DES enc = "1Tsy0ZGotyMinSpxqYzVBWnfMdUcqCMLu0MA+22Jnp+MNwLHvYuFToxRQr0c+ONZc6Q7L0EAmzbycqobZHh4H23U4WDTNmmXwusW4E+SZjygsntGkO2sGA==" enc = base64.b64decode(enc.encode()) key = iv = b"1\x002\x003\x004\x00" des = DES.new(key, DES.MODE_CBC, iv) dec = des.decrypt(enc) print(dec.decode()) # 结果: He_P1ay_Basketball_Very_We11!Hahahahaha!
4.3 Cheat Engine在Unity逆向中的应用
CE的Mono功能详解:
-
进程附加
- 使用CE附加到Unity游戏进程
- 激活Mono功能菜单
-
类结构分析
- 使用"Dissect mono"功能列出所有类和对象实例
- 查看类字段、方法、继承关系
-
关键信息提取
- 查找加密密钥(如
encryptKey字段) - 查看对象属性值(玩家数据、游戏状态)
- 动态修改内存值
- 查找加密密钥(如
5. 高级案例:FlareOn5 Ultimate Minesweeper
5.1 程序结构分析
关键类识别:
Program: 程序入口点MainForm: 主窗口类MineField: 雷区逻辑封装SuccessPopup/FailurePopup: 成功/失败弹窗
5.2 核心逻辑逆向
AllocateMemory函数分析:
private void AllocateMemory(MineField mf)
{
for (uint num = 0U; num < MainForm.VALLOC_NODE_LIMIT; num += 1U)
{
for (uint num2 = 0U; num2 < MainForm.VALLOC_NODE_LIMIT; num2 += 1U)
{
bool flag = true;
uint r = num + 1U; // 行索引
uint c = num2 + 1U; // 列索引
// 地雷位置判断逻辑
if (this.VALLOC_TYPES.Contains(this.DeriveVallocType(r, c)))
{
flag = false; // 标记为地雷
}
mf.GarbageCollect[(int)num2, (int)num] = flag;
}
}
}
SquareRevealedCallback回调函数:
private void SquareRevealedCallback(uint column, uint row)
{
// 地雷检测逻辑(触发即失败)
if (this.MineField.BombRevealed)
{
// 失败处理代码...
}
// 记录已揭示格子
this.RevealedCells.Add(row * MainForm.VALLOC_NODE_LIMIT + column);
// 胜利条件检测
if (this.MineField.TotalUnrevealedEmptySquares == 0)
{
// 调用GetKey生成flag
new SuccessPopup(this.GetKey(this.RevealedCells)).ShowDialog();
}
}
5.3 Flag生成算法(GetKey函数)
算法步骤:
-
种子生成
revealedCells.Sort(); Random random = new Random(Convert.ToInt32( revealedCells[0] << 20 | revealedCells[1] << 10 | revealedCells[2])); -
密钥材料准备
- 生成32字节随机数组
- 预定义密文字节数组
-
异或解密
while ((ulong)num < (ulong)((long)array2.Length)) { byte[] array3 = array2; uint num2 = num; array3[(int)num2] = (array3[(int)num2] ^ array[(int)num]); num += 1U; }
5.4 两种解题方案
方案一:代码修改(推荐)
- 删除地雷检测逻辑
- 修改胜利条件检测
- 保存并运行修改后的程序
修改后的关键代码:
private void SquareRevealedCallback(uint column, uint row)
{
// 删除地雷检测代码块
this.RevealedCells.Add(row * MainForm.VALLOC_NODE_LIMIT + column);
// 修改条件:点击3个格子即触发
if (this.RevealedCells.Count >= 3)
{
new SuccessPopup(this.GetKey(this.RevealedCells)).ShowDialog();
}
}
方案二:动态调试
- 在GetKey函数返回处设置断点
- 启动调试模式运行程序
- 触发断点后查看解密结果
6. Mono游戏保护与绕过技术
6.1 常见保护措施
代码混淆
- 类名、方法名、变量名替换为无意义字符串
- 反制工具:de4dot等去混淆工具
字符串加密
- 运行时动态解密字符串常量
- 应对方法:动态调试或分析解密函数
反调试检测
- 检测调试器进程存在
- 检查调试标志位
- 时间差检测
- 绕过方法:修改检测代码或使用插件
完整性校验
- 文件哈希值验证
- 代码校验和检查
- 应对:NOP校验代码或修改校验逻辑
6.2 高级绕过技巧
IL代码修改
- 直接修改IL指令绕过检测
- 使用dnSpy的IL编辑功能
内存补丁
- 运行时修改关键内存区域
- 使用CE进行动态内存修改
7. 总结与最佳实践
7.1 逆向方法论
- 环境识别:首先判断打包方式(Mono/IL2CPP)
- 文件分析:定位关键dll文件和资源
- 静态分析:使用dnSpy进行代码分析
- 动态调试:结合CE进行运行时分析
- 代码修改:合理修改逻辑实现目标
7.2 工具链配置
- 主要工具:dnSpy、Cheat Engine
- 辅助工具:de4dot(去混淆)、PE工具
- 调试环境:虚拟机隔离测试
7.3 技能要求
- 熟悉C#和.NET框架
- 理解Unity引擎架构
- 掌握加密解密基础知识
- 具备基本的汇编语言理解能力
通过系统掌握上述技术和方法,可以有效分析和修改大多数使用Mono打包的Unity游戏,为安全研究、CTF竞赛和游戏修改提供坚实的技术基础。