本篇文章已上传至bilibili【第玖期 REVERSE 分享会】直播录屏,空降50分钟可以观看讲解视频,题目讲解过程就不放在博客文章中了
https://www.bilibili.com/video/BV1U7igeZE5u/?spm_id_from=333.999.0.0
前言——unity相关基础
unity引擎与游戏文件
unity是一款游戏引擎,可以开发多平台,多系统下的2D 3D游戏,游戏平台上的一些小游戏,或者像我们熟悉的手游王者荣耀,原神。。。都是基于unity开发的游戏。
我们去打开一个unity游戏的文件夹时,我们可能会遇到下面这两种形式的文件形式,.exe便是我们的游戏启动器,无论是左边还是右边,都有一个后缀为_Data的文件夹
继续跟进目录后 我们就会发现两者有点细微的差别,左边名为il2cpp,右边名为管理的managed文件,想要进一步了解unity内部的原理,了解这两种的区别,就要接着往下去认识这两种语言。
C#和Mono
C sharp以.net框架作为基础一种面向对象的语言。我们知道unity有强大的多平台部署能力,而支撑这一能力的关键,就是包含了c#编译器等其他工具的开源项目Mono
Mono:一个由 Xamarin公司主持的自由开放源代码项目,目标是创建一系列符合ECMA标准(Ecma- 334和Ecma-335)的.NET工具包括C#编译器和通用语言架构。与微软的.NET Framework(共通语言运行平台)不同Mono项目不仅可以运行于Windows系统上,还可以运行于 Linux,FreeBSD,Unix,OS X和Solaris,甚至一些游戏平台。Mono使得C#这门语言有了很好的跨平台能力。
上右文件夹中的MonoBleedingEdge文件夹便是包含运行Unity服务器所需的所有Mono运行时库和依赖项。
在2014年,Unity3D官方博客上发布了“The future of scripting in unity”的文章,引入了比mono更为安全的il2cpp技术
IL语言
上文的il,全称Intermediate Language (中间语言),如何更好地理解他呢,我们可以把它理解为.NET框架下的“汇编”(低阶的人类可读编程语言)。我们实际在dnspy中去看一看他的结构
1 | public class PinHead : MonoBehaviour |
可以看出一段Csharp代码的对应IL中,出现了call,ldstr等类似与汇编语言中的命令,
通过一张图片我们可以更加清楚的理解上文提到的C#,高级语言如C# vb unityscript等语言经过编译,会先转为IL,IL再通过后续的编译,转为机器码使计算机去执行相应的指令。
正篇——逆向中的unity
出题
相关出题内容请移步下面的两篇博客
借着为学校招新赛出题的机会,尝试去自己开发一下unity游戏,经过自己的实际开发,我发现一款游戏产生就只有简单的三步,设计构思-素材设计-代码编写,在强大的unity引擎帮助下,很多类似物理效果,动画效果的实现都是有unity中自带的一些模块库就可以轻松实现(Boxcollider 2D就可以实现一个实体 Rigitbody 2D就能实现2的游戏的物理效果)
而我们需要去做的就是编写出结合游戏设计思路的关键脚本(比如游戏玩法,规则,人物的移动,关卡的转换逻辑之类的)
在这一部分主要想和大家分享一下在写c#脚本时的关键点,我们了解了正向的过程,能更好的去理解逆向的过程。我们在unity中新建一个C#脚本时,他会默认生成两个方法名Start和update
start叫做生命周期函数,就是他会在启动脚本时第一个去执行他,我们比如在这里就是实现了,在每次执行玩家c#代码时,先定义一个animator变量,再去执行下面的update代码。
update顾名思义就是更新,他是游戏画面逻辑更新的关键代码,很多玩家的行为,都写在这个方法之中,比如这里写到的就是一个简单的控制玩家行走跳跃的代码,以及按键的设置功能。
1 | void Start() |
那么作为一名逆向的思路,我们在寻找函数时,如果遇到的类似的2d游戏,在想去查找一些关键的执行逻辑在哪些方法中实现,可以直接去寻找update方法名的关键词,从中去获取到一些信息。
做题经历
在市面上普遍有两种unity逆向,他们分别使用不同的编译方式,解决的思路也不同。
Mono编译
这是原始的编译方式,也是最容易攻破,不太安全的一种编译方式,生成的游戏文件夹中,游戏的关键代码文件会直接生成在 _Data\Managed\Assembly-CSharp.dll的文件之中,出题人一般会想让我们通过破解游戏逻辑,或者在c#中进行解密来获取到最后的flag。
是男人就来扎针
牢大我想你了
il2cpp编译
这串怎么去读呢? IL二cpp吗?按照英文的读法,应该是 IL two(to)cpp,也就是从IL中间语言转换为cpp的过程,这一过程是如何实现的呢。
在上文的基础中我们已经知道,unity的代码在执行时,会先将高级语言转换为相应的IL中间语言,中间语言再通过Mono VM编译为机器码。刚才提到了The future of scripting in unity,便是使用了IL2CPP技术将原先的过程变为了,在代码转换为IL之后,不会通过Mono编译,而是通过IL2cpp转变为c++代码,然后再使用本地的c++编译器编译为ASM汇编代码,通过IL2cpp VM转为机器码执行。
图片可以很直观的看到改变的步骤
AOT(Ahead Of Time)编译而非JIT(Just In Time)编译https://gwb.tencent.com/community/detail/126815
而通过出题我们也很明显的看到了这一过程,将编译选择为IL2cpp后,生成项目时编译会出现“编译为c”“编译为ASM”这样的提示
我们的文件夹中也是提示不要伴随你的游戏文件的文件夹中,有il2cpp相应的输出文件,看到里面便是.cpp代码。也就是说,我们发布游戏的话,只用打包data中的文件,而不用把c#的dll直接像mono一样暴露在外面,所以说这种方式也就更为安全。
打开data文件夹中,我们发现只有少的可怜的global-metadata(元数据)但是这其中就包含着C#中的类名、方法名、属性名、字符串等地址信息,程序启动时会按需从中读取。可以说是il2cpp的关键逆向入口点。
XYCTF babyunity
Il2CppDumper
1 | Il2CppDumper.exe GameAssembly.dll global-metadata.dat output-out |
将游戏文件中的
结语——后期的项目
这几次的unity经历更多还是从基础的原理或者简单的题目中去学习的,还没有真正去实战的角度去分析,在整理资料的时候发现了一位师傅实战某手游的文章,弄完下一个项目之后计划再去搞一下类似的项目。
才疏学浅,希望今天的分享能够起到抛砖引玉的效果,所讲内容中有误的地方也希望师傅们可以批评指正,同时也希望能与更多感兴趣的师傅们一起继续讨论unity逆向相关的内容。
本文参考
[原创] XYCTF两道Unity IL2CPP题的出题思路与题解-CTF对抗-看雪-安全社区|安全招聘|kanxue.com
About this Post
This post is written by xianyuuuan, licensed under CC BY-NC 4.0.