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

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

2020-6-14 00:43
17844

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

上一个帖子是因为手残打不过音游,试图作弊走捷径,结果遇到一个超麻烦的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文件(下称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引擎原理的层面来进行保护,需要对引擎的机制有一定了解才能更好地进行分析。可惜最后的混淆不是对非关键函数进行随机字符串替换,只是简单地加密,被发现了破绽。

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

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

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


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 13
支持
分享
最新回复 (19)
雪    币: 4884
活跃值: (18895)
能力值: ( LV13,RANK:317 )
在线值:
发帖
回帖
粉丝
2
楼主大二就出去实习了,我到现在还在家里蹲着,我才是当代废柴大学生代表(留下了没技术的眼泪
2020-6-14 12:00
1
雪    币: 1110
活跃值: (3274)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
和 Unity 国内特供版自带 IL2CPP 加密类似,后面会遇到越来越多的。
2020-6-14 19:16
1
雪    币: 2157
活跃值: (12639)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
4
不赖
2020-6-15 08:27
0
雪    币: 120
活跃值: (1597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
很棒!
2020-6-15 09:33
0
雪    币: 26185
活跃值: (63307)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
6
感谢分享~
2020-6-16 09:37
0
雪    币: 1535
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
666 赞!
2020-6-16 09:56
0
雪    币: 5078
活跃值: (2856)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
大佬.感谢分享.
2020-6-16 10:00
0
雪    币: 259
活跃值: (283)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
666
2020-6-16 15:40
0
雪    币: 1841
活跃值: (1290)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2020-6-17 01:04
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
有同类的需求,可以留个联系方式吗
2020-6-17 05:40
0
雪    币: 36
活跃值: (1061)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
12
mark
2020-6-17 11:33
0
雪    币: 0
活跃值: (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
流啤
2020-6-23 17:19
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
有兴趣来游戏公司的安全组实习嘛?可以私信个联系方式给我,帮你内推呀~
2020-6-23 17:56
0
雪    币: 209
活跃值: (93)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
mark
2020-6-24 22:56
0
雪    币: 29
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
大佬
2020-7-16 15:34
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
我去试试崩坏3
2020-7-31 09:55
0
雪    币: 28
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
你好,大神,已经被加密的ab包怎么解开?我已经得到Assembly-CSharp.dll,求不吝赐教,帮忙找到客户端解密加载ab包的方法,我的qq1756816846
2021-4-18 16:28
0
雪    币: 141
活跃值: (7486)
能力值: ( LV9,RANK:335 )
在线值:
发帖
回帖
粉丝
19
最近在研究il2cpp,不知大哥可发一下初始化gm的那个函数源码及其他文件打包发给我一下,或者提供下载连接也是可以的。qq:543322463
2021-4-24 00:05
0
雪    币: 1342
活跃值: (1055)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
20
这么小,研究这个会掉头发吗??
2021-5-1 23:34
0
游客
登录 | 注册 方可回帖
返回
//