总结一下IL2CPP相关学习
0x00 IL2CPP概念
I. 背景
Unity3D(后文简称U3D)想必大家都不陌生,独立游戏制作者们很多人都在用它,甚至一些大公司也用在很商业的游戏制作上。Unity3D 最大的一个特点是一次制作,多平台部署。本来这一核心功能是靠 Mono 实现的。在 2014 年年中的时候,Unity3D 官方博客上却发了一篇 “The future of scripting in unity” 的文章,引出了 IL2CPP 的概念,感觉有取代 Mono 之势。但Mono也不是水做的,发展了这么多年,短时间内也不会被取代。
所以到目前,U3D就同时存在着两种脚本后端了。所谓脚本后端(Scripting Background)就是指用于支持游戏脚本正常运行的系统,即游戏的逻辑经过脚本后端编译之后让游戏按预期的方向运行。
要学习IL2CPP,作为它前辈的Mono,自然也不能少。下面康一康两者的大致过程和区别吧
Mono编译游戏脚本过程
整个过程关键部分在于Mono VM,即Mono项目维护的虚拟机,游戏的脚本逻辑实际上放在名字里含有”Assembly-CSharp”的.Net
动态链接库里,执行过程中我们一般不会选择费力去分析Mono(非常难),而是把目标锁定到Assembly的脚本链接库里,当然游戏的保护主要也是围绕这个DLL进行的,能深入的地方也不少,我也在学习中。
IL2CPP编译游戏脚本过程
整个过程特点就是将脚本编译得到IL
之后,通过IL2CPP的模块将IL
转换为C++的代码
再进行编译 得到可执行代码 之后通过这个代码来支持游戏运行
和Mono相比 看起来过程挺复杂的 不过这么做当然有一定优势的
- C++的执行效率快
- 利用现成的在各个平台的C++编译器对代码执行编译期优化,进一步减小最终游戏的尺寸并提高游戏运行速度。
更具体的方面有待进一步深入
0x01 分析IL2CPP的游戏
本文只提供大致思路 具体的基础操作流程 可以参考这里
I. 分析流程
在游戏没有保护的情况下 游戏脚本的逻辑其实就暴露在我们的文件系统中 作为一个库文件(安卓为libil2cpp.so)
只不过游戏中使用的字符串和函数符号信息都被保存在了一个叫 global-metadata.dat
的资源文件里,只有在动态运行时才会将这些字符串读入内存。直接拖入IDA分析是看不到各种字符串和符号信息的,这样分析非常影响效率。
为了找到并分析我们的关键的代码,找到字符串信息还是非常重要的。
只需要先提取主逻辑文件的符号 找到文件``global-metadata.dat`(至于Unity为什么要创造出这么一个文件 还需要我进一步研究)
那么现在有一个方便开源的工具 il2cppDumper
能帮助我们从global-metadata.dat
中获取字符串和函数符号信息
顺便寻找主逻辑文件中的字符串和函数信息对应的两个表的位置 之后生成一个IDA符号恢复脚本Script.py
和 一个C#函数集合dump.cs
到这里已经可以将符号信息导入 并进行深入的分析了
II. 静态保护
这里提到的是简单的保护情景 想要深入可以参考TP和易盾的保护方式
关键的文件主要就是上述介绍的两个
- 主逻辑文件
- Global-Metadata
主要方式还是加密和混淆 本次就主要谈Global-Metadata加密
Global-Metadata
有时候发现Global-Metadata文件的内容跟原版的格式完全不一致,那么在加载的过程中一定会需要对Global-Metadata进行处理
首先最好对整个加载过程进行学习 这里有参考
一般会从 MetadataCache::Initialize()
开始跟进 对比源代码 看看加载的时候如何进行
一般有两种思路
静态解密
跟进操作 分析其算法 自行解密
内存取证(优先尝试)
Unity需要一次Global-Metadata将符号信息等加载完
那么在内存中某一时刻主逻辑会对其解密 甚至解密之后就不动了
这种时候就只需要从内存中寻找 一般通过Global-Metadata的Magic Number来寻找其位置
具体操作以后可能会总结一波
主逻辑
主逻辑被加密也是类似的思路
静态解密的方式我还没尝试过
动态内存取证依然是有效的一个方法
0x02 参考链接
https://www.nevermoe.com/2016/08/10/unity-metadata-loader/