从出题到做题——浅探Unity游戏逆向

本篇文章已上传至 bilibili up主:水番正文-【第玖期 REVERSE 分享会】直播录屏,空降50分钟可以观看讲解视频,题目讲解过程就不放在博客文章中了

【第玖期 REVERSE 分享会】拨云见日之浅谈flutter逆向 & LLVM的超简易入门 & 从出题到做题——探Unity游戏逆向_哔哩哔哩_bilibili

前言——unity相关基础

(有过相关基础的师傅可以移步正篇内容)

unity引擎与游戏文件

unity是一款游戏引擎,可以开发多平台,多系统下的2D 3D游戏,游戏平台上的一些小游戏,或者像我们熟悉的手游王者荣耀,原神。。。都是基于unity开发的游戏。

我们去打开一个unity游戏的文件夹时,我们可能会遇到下面这两种形式的文件形式,.exe便是我们的游戏启动器,无论是左边还是右边,都有一个后缀为_Data的文件夹

img

继续跟进目录后 我们就会发现两者有点细微的差别,左边名为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
2
3
4
5
6
7
8
9
10
11
public class PinHead : MonoBehaviour
{
	// Token: 0x06000014 RID: 20 RVA: 0x0000221E File Offset: 0x0000041E
	private void OnTriggerEnter2D(Collider2D collision)
	{
		if (collision.tag != "PinHead")
		{
			GameObject.Find("GameManager").GetComponent<GameManager>().GameOver();
		}
	}
}

img

可以看出一段Csharp代码的对应IL中,出现了call,ldstr等类似与汇编语言中的命令,

通过一张图片我们可以更加清楚的理解上文提到的C#,高级语言如C# vb unityscript等语言经过编译,会先转为IL,IL再通过后续的编译,转为机器码使计算机去执行相应的指令。

img

正篇——逆向中的unity

出题

借着一次招新赛出题的机会,尝试去自己开发一下unity游戏,经过自己的实际开发,我发现一款游戏产生就只有简单的三步,设计构思-素材设计-代码编写,在强大的unity引擎帮助下,很多类似物理效果,动画效果的实现都是有unity中自带的一些模块库就可以轻松实现(Boxcollider 2D就可以实现一个实体 Rigitbody 2D就能实现2的游戏的物理效果)

img

而我们需要去做的就是编写出结合游戏设计思路的关键脚本(比如游戏玩法,规则,人物的移动,关卡的转换逻辑之类的)

在这一部分主要想和大家分享一下在写c#脚本时的关键点,我们了解了正向的过程,能更好的去理解逆向的过程。我们在unity中新建一个C#脚本时,他会默认生成两个方法名Start和update

start叫做生命周期函数,就是他会在启动脚本时第一个去执行他,我们比如在这里就是实现了,在每次执行玩家c#代码时,先定义一个animator变量,再去执行下面的update代码。

update顾名思义就是更新,他是游戏画面逻辑更新的关键代码,很多玩家的行为,都写在这个方法之中,比如这里写到的就是一个简单的控制玩家行走跳跃的代码,以及按键的设置功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void Start()
    {
        //获取animator组件
        animator = GetComponent<Animator>();
    }

void Update()
{
    //获取玩家的左右键按键 -1,0,1 (a和d)
    float h = Input.GetAxis("Horizontal");

    if (h != 0)
        {
            //移动.方向 * 上一帧花费的时间*玩家按键
            transform.Translate(transform.right*Time.deltaTime*h);
            //这里实现的是人物的方向,为了让人物行走更加真实,方向判断根据上面的h
            if (h<0)
            {
                GetComponent<SpriteRenderer>().flipX = false;
            }

            if(h>0)
            {
                GetComponent<SpriteRenderer>().flipX = true;
            }

            animator.SetBool("run",true);
        }
        else
        {
            animator.SetBool("run",false);
        }


        if(isOnGround && Input.GetKeyDown(KeyCode.W))
        {
            GetComponent<Rigidbody2D>().AddForce(Vector2.up*180);
        }

    }
}

那么作为一名逆向的思路,我们在寻找函数时,如果遇到的类似的2d游戏,在想去查找一些关键的执行逻辑在哪些方法中实现,可以直接去寻找update方法名的关键词,从中去获取到一些信息。

做题经历

在市面上普遍有两种unity逆向,他们分别使用不同的编译方式,解决的思路也不同。

Mono编译

这是原始的编译方式,也是最容易攻破,不太安全的一种编译方式,生成的游戏文件夹中,游戏的关键代码文件会直接生成在 _Data\Managed\Assembly-CSharp.dll的文件之中,出题人一般会想让我们通过破解游戏逻辑,或者在c#中进行解密来获取到最后的flag。

JEEK大挑战2023-是男人就来扎针
安洵ctf2023-牢大我想你了

(题目讲解请参考文章开头视频)

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转为机器码执行。

图片可以很直观的看到改变的步骤

img

AOT(Ahead Of Time)编译而非JIT(Just In Time)编译Unity之IL2CPP解析-腾讯游戏学堂

而通过出题我们也很明显的看到了这一过程,将编译选择为IL2cpp后,生成项目时编译会出现“编译为c”“编译为ASM”这样的提示

我们的文件夹中也是提示不要伴随你的游戏文件的文件夹中,有il2cpp相应的输出文件,看到里面便是.cpp代码。也就是说,我们发布游戏的话,只用打包data中的文件,而不用把c#的dll直接像mono一样暴露在外面,所以说这种方式也就更为安全。

img

打开data文件夹中,我们发现只有少的可怜的global-metadata(元数据)但是这其中就包含着C#中的类名、方法名、属性名、字符串等地址信息,程序启动时会按需从中读取。可以说是il2cpp的关键逆向入口点。

img

XYCTF babyunity

使用工具

Il2CppDumper

1
Il2CppDumper.exe GameAssembly.dll global-metadata.dat output-out

将游戏文件中的函数名恢复正常 进行解题

img

结语——后期的项目

这几次的unity经历更多还是从基础的原理或者简单的题目中去学习的,还没有真正去实战的角度去分析,在整理资料的时候发现了一位师傅实战某手游的文章,弄完下一个项目之后计划再去搞一下类似的项目。

才疏学浅,希望本篇能够起到抛砖引玉的效果,所讲内容中有误的地方也希望师傅们可以批评指正,同时也希望能与更多感兴趣的师傅们一起继续讨论unity逆向相关的内容。

本文参考

Unity之IL2CPP解析-腾讯游戏学堂

浅谈CTF中的unity游戏逆向 |

[原创] XYCTF两道Unity IL2CPP题的出题思路与题解-CTF对抗-看雪-安全社区|安全招聘|kanxue.com

Unity逆向

[原创]IL2CPP 逆向初探-软件逆向-看雪-安全社区|安全招聘|kanxue.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值