首页
论坛
课程
招聘
[原创]一个Unity游戏保护方案的分析和还原符号信息,偷学对global-metadata保护的思路
2020-6-14 00:43 9176

[原创]一个Unity游戏保护方案的分析和还原符号信息,偷学对global-metadata保护的思路

2020-6-14 00:43
9176

前言

我发现,似乎每当我因为手残玩不过游戏的时候,似乎都会遇到倒霉的事情。

 

上一个帖子是因为手残打不过音游,试图作弊走捷径,结果遇到一个超麻烦的dll保护方案

 

这一个帖子是因为手残打不过魂类游戏,同样试图作弊,结果又遇到了一个新的保护方案

 

游戏是新的某帕姓魂类手游,TapTap上就有卖,推荐大家玩一下,质量还是不错的,顺便支持一下国产,反正也不贵,25块钱。
PS:不是广告

 

接下来是正题,来说说我分析这个游戏的时候都遇到了些什么,过于啰嗦请慎入。

初见

首先当然是使用我们万能的GG修改器上去试一下了,结果发现这游戏并没有做内存保护,甚至没有检测GG,直接搜索数值改就成功了,顿时觉得索然无味。

 

但是怎么可以就这么简单地就结束呢?那多无聊,于是我把目标瞄准了游戏存档。

 

到数据目录下查看,发现了感兴趣的文件夹
数据文件

 

一个Save文件夹,一个android文件夹。Save文件夹里面为我们的目标:存档文件,而android文件夹里面是热更新的资源,一个典型的tolua框架。
lua文件

 

首先把lua资源拷贝出来,尝试用AssetStudio读取,发现读取失败。
这里是一个Unity3D读取资源打包资源的一个设置,它是可以设置偏移的。
这里在前面塞了一个tipsworks,这个好像是公司标识?
头部塞入字节

 

写个脚本,把前面的字节去掉就可读取了,直接把lua资源解压出来,尝试用文本编辑器打开:
luajit标识

 

熟悉的LJ标志,这里开发者将所有lua脚本全部编译成了luajit框架的二进制代码。

 

在这里感谢 NightNord 大佬开发的ljd反编译框架以及后续的各位参与维护的大佬,让luajit编译之后的二进制代码依旧可以被还原为可读代码。

 

ljd框架 github地址

 

把所有脚本用ljd框架转为可读的脚本之后,我们可以快速定位到Save相关部分,找到保存相关的代码:
lua保存部分

 

这里可以看到,它将存档路径和存档相关内容传入到了Recorder.write函数里面。也就是说我们找到这个Recorder类就行了。

 

然而事与愿违,这玩意不存在在Lua脚本之中,估计是使用了Wrap类在C#代码中实现了这个类。

 

那么就回到了我们熟悉的节奏了,逆向Unity3D游戏,不就是抱住 Prefare 大佬的大腿当一个脚本小子吗(雾)

global-metadata的保护与还原

在正式开始之前,先简单地说一下global-metadata文件(下称gm文件)的用处。

 

il2cpp技术是将C#转为C++代码的一种技术,然而和C++代码不同,C#之间的函数调用很多时候不是直接跳转,而是需要先通过符号查找函数地址,再进入函数。

 

因此C#在转为C++代码时,需要保留C#中的符号信息,比如函数定义,函数名称,类名称等等。

 

而Il2CppDumper的作用就是将gm文件里的信息提取出来,和il2cpp文件对应起来。

 

直接上Il2CppDumper,结果理所当然的出错了:
gm文件加密

 

从错误提示中可以看到,它没能识别出gm文件,用hex打开,发现连gm文件头的标识都没了:
gm文件Hex

 

正常的gm文件都是以AF 1B B1 FA字节开头的,这里没有,很明显游戏对gm文件进行了加密。

 

把ilbil2cpp.so文件拖入IDA,然后找到加载gm文件的函数,我们把它和原函数代码做一个对比:
gm文件加载函数
加载gm文件源代码

 

可以看到,两者之间非常相似,但是存在一定的区别。

 

对比着分析,大概知道了gm文件的解密流程。

 

首先读入gm文件,并且让一个指针指向它的头部。

 

再读取0x110大小字节数组,进行解密:
解密gm文件头

 

再将之后的内容进行解压缩解密,完毕。

 

在使用gm文件信息的时候,一般是通过gm文件指针加上gm头部结构的偏移值来指向需要的部分。在原函数中,gm文件指针和gm文件头部实际上指向的是同一个地址,因此直接使用一个指针就行了。

 

而在这里,gm文件头部和gm文件分开进行了解密,存在两个不同的位置,因此在使用gm文件信息时,会出现两个指针:
使用gm文件信息

 

指向这两个部分的是全局变量,因此直接靠偏移就可以在内存中找到这两个部分,dump下来之后,将头部信息的0x110个字节覆盖到解密之后的主体文件中,就获得到了解密之后的gm文件:
解密之后的gm

 

现在,再使用Il2CppDumper来尝试提取符号信息:
gm文件字符串加密

 

dump成功,但是创建dll失败,原因是不明字符串,这让我有了不祥的预感。

 

打开dump.cs文件,结果一片乱码……
字符串被加密

 

很明显,部分字符串被加密混淆了,dump出来的信息基本没用……

 

本来到这里我都想放弃了,毕竟如果没有这些符号信息,il2cpp的逆向将会比直接cpp的逆向复杂无数倍,让人心态爆炸。

 

但是细心(?)的我发现了一个问题,这个加密混淆的系统将一些关键词也混淆掉了,比如Start,Update,Awake……

 

这里就涉及到一个Unity3D引擎的原理问题,U3D引擎通过Start,Update之类的关键词函数来调用用户写的代码,实现诸如初始化,帧更新等功能。

 

如果它连这些关键词都给加密混淆处理了的话,那么U3D引擎将无法执行用户的代码。

 

所以,为了让程序正常运行,它必定在内存中解密了这些字符串。

 

那么这个解密的时机选取在哪里比较好呢?我们先来分析一下。

 

第一个时机在读取gm文件时,这里我们已经分析过了,并没有解密相关的部分。

 

第二个时机在函数初始化的时候。在il2cpp技术转化出来的Cpp函数开头会有这么一部分:
初始化函数信息

 

这里就是函数信息初始化的部分,在函数第一次被调用的时候,执行初始化函数。

 

追踪下去,可以看到最主要的部分:
初始化主体

 

分别对函数信息和类信息初始化的部分继续跟踪分析,借助原代码进行对比,很快就可找到解密字符串的部分。

 

这里为了保护一下开发者,不全部公开了,提示是将字符串每个字节分别与某个值进行异或处理,即可解密。

 

至于这个值是怎么获取的,大家有兴趣就自己找找吧。

 

接下来根据解密方式改造一下Il2CppDumper工具,再次解密:
修改的IL2CPPDUMPER

 

成功将信息dump出来,获取到了明文信息:
字符串被解密

 

接下来就是正常的分析过程了,最后得知存档文件的加密方式是通过AES加密,密钥为tiencikpncoanvsnauewjxzogtrdfkes,再base64编码即可。

 

解密得到的存档:
解密的存档

 

到这里,我们的目标就完全实现了。

总结

接下来就是神装走起,满级出门,然后被BOSS血虐。

 

这个故事说明了一个道理:在魂类游戏中手残不是靠装备和等级可以弥补的。

 

说说这次逆向分析的感谢吧,首先就是这个gm文件加密还是挺少见的。最常见的就是给so文件加壳,然后被动态dump,防御力只有5(说的就是那些卖保护机制十几万块每年还做的没啥防御力的公司)。

 

加密gm文件感觉其实比给so文件加壳更加有效,至少能够挡住大部分只依靠工具的脚本党(比如我,只会抱大佬大腿)

 

我一直以来的观点是与其想着怎么挡住那些抱着恶意的攻击者,倒不如想着怎么提高他们的破解成本,给他们制造麻烦,延长他们的破解时间,更加符合游戏保护的目的。

 

这次的保护方式还挺对我胃口的,算是从U3D引擎原理的层面来进行保护,需要对引擎的机制有一定了解才能更好地进行分析。可惜最后的混淆不是对非关键函数进行随机字符串替换,只是简单地加密,被发现了破绽。

 

分析这些保护方式真的是层层递进,像是剥洋葱一样,流着泪分析。在一堆的代码中间找和原版程序不一样的地方,再进行分析,算是个体力活吧,所幸最终成功了,还是挺有的成就感的。

题外话

最近我就要搞大三的生产实习了,找的工作是某公司的客户端安全,现在心情坎坷,不知道去了之后会不会拖团队的后腿,或者说某些方面不了解而犯错。

 

有没有那个大佬有兴趣说说实习都要干些啥啊,我就大二出去实习过一次,还不是搞安全,只算是长见识。

 

平时干啥都是自己瞎搞,现在担心我的各种不规范和外行行为会被大佬看穿,反思自己为啥找了个这么样的家伙进来……

 

作为当代废柴大学生代表,我现在慌得一批。


[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年6月班开始招生!!

收藏
点赞10
打赏
分享
最新回复 (19)
雪    币: 3916
活跃值: 活跃值 (11468)
能力值: ( LV9,RANK:216 )
在线值:
发帖
回帖
粉丝
0x指纹 活跃值 4 2020-6-14 12:00
2
1
楼主大二就出去实习了,我到现在还在家里蹲着,我才是当代废柴大学生代表(留下了没技术的眼泪
雪    币: 1360
活跃值: 活跃值 (468)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Amun 活跃值 2020-6-14 19:16
3
1
和 Unity 国内特供版自带 IL2CPP 加密类似,后面会遇到越来越多的。
雪    币: 8945
活跃值: 活跃值 (3566)
能力值: ( LV12,RANK:252 )
在线值:
发帖
回帖
粉丝
一半人生 活跃值 4 2020-6-15 08:27
4
0
不赖
雪    币: 101
活跃值: 活跃值 (284)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xxRea 活跃值 2020-6-15 09:33
5
0
很棒!
雪    币: 15599
活跃值: 活跃值 (14834)
能力值: (RANK:75 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-6-16 09:37
6
0
感谢分享~
雪    币: 1316
活跃值: 活跃值 (322)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
开花的水管 活跃值 2020-6-16 09:56
7
0
666 赞!
雪    币: 2020
活跃值: 活跃值 (374)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
whitehack 活跃值 2020-6-16 10:00
8
0
大佬.感谢分享.
雪    币: 256
活跃值: 活跃值 (177)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ZwCopyAll 活跃值 2020-6-16 15:40
9
0
666
雪    币: 3264
活跃值: 活跃值 (197)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
yezheyu 活跃值 2020-6-17 01:04
10
0
感谢分享
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
节奏39 活跃值 2020-6-17 05:40
11
0
有同类的需求,可以留个联系方式吗
雪    币: 36
活跃值: 活跃值 (223)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
芃杉 活跃值 2020-6-17 11:33
12
0
mark
雪    币: 0
活跃值: 活跃值 (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
江t 活跃值 2020-6-23 17:19
13
0
流啤
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
心动-Micahel 活跃值 2020-6-23 17:56
14
0
有兴趣来游戏公司的安全组实习嘛?可以私信个联系方式给我,帮你内推呀~
雪    币: 208
活跃值: 活跃值 (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
一只小猴子 活跃值 2020-6-24 22:56
15
0
mark
雪    币: 29
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
玉皇 活跃值 2020-7-16 15:34
16
0
大佬
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
逮虾户 活跃值 2020-7-31 09:55
17
0
我去试试崩坏3
雪    币: 8
活跃值: 活跃值 (39)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
清风而已 活跃值 2021-4-18 16:28
18
0
你好,大神,已经被加密的ab包怎么解开?我已经得到Assembly-CSharp.dll,求不吝赐教,帮忙找到客户端解密加载ab包的方法,我的qq1756816846
雪    币: 1552
活跃值: 活跃值 (1180)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
PlaneJun 活跃值 4 2021-4-24 00:05
19
0
最近在研究il2cpp,不知大哥可发一下初始化gm的那个函数源码及其他文件打包发给我一下,或者提供下载连接也是可以的。qq:543322463
雪    币: 1298
活跃值: 活跃值 (752)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
影………… 活跃值 1 2021-5-1 23:34
20
0
这么小,研究这个会掉头发吗??
游客
登录 | 注册 方可回帖
返回