逆向记录(代号ayaranbu)
前言
爬取某页游CG的脚本突然崩了,今天花了一些时间研究了改动并重写了脚本,简单记录一下过程。
正文
小背景
这厂商的上一个游戏(代号UT)让我第一次接触解包。UT美工优秀,但优化很差,打副本闪退可谓是家常便饭。试想想打高难度本团灭,使用了抽卡用货币进行复活,复活玩打着打着闪退了,货币还消耗了不给返还…
所以我肯定是不玩的,每次更新都在某站对应的画廊等大佬更新CG。但是好景不长,大佬终于弃坑跑路了,作为伸手党的我就傻眼了。
尝试按F12查看已有角色的CG,结果发现资源URL是全明文而且没有任何防爬的验证措施(比如session token cookie之类的)。
这样只要在指定范围按URL pattern去遍历发送请求,服务器返回200的话保存下来就能拉取到所有CG了。
听起来非常暴力,but it works. 当时正好学了一天的Python,就按照以上的思路写了上古版本的资源爬取代码。
下载下来的数据是Unity的assetbundle,但当时显然是不知道这个“打不开”的二进制文件是什么东西,丢进hex editor看到了UnityFS的字样,然后才了解到相关知识。这个游戏实质是把mp4作为textasset打包的。
穷举id可能会触发大量403/404,可能会导致厂商检查服务器log时注意到。
关服后此厂商推出了新的页游,资源存储方式与这个一模一样…一样到老的脚本直接改一下cdn链接就能继续上岗了,就这样继续用了一年半。顺便一提游戏优化还是一言难尽,和前作一样卡得要死。
不久后的一次更新后(就在前几天),脚本跑了根本就没动静,一看log返回的全是404,心想着可能还在维护服务器,就等到晚上再跑了一遍,还是404。
进游戏一看完全没有在维护,爬下manifest文件表一看文件确实还是那种格式。最后就直接开了个角色剧情抓包一看,是一长串的hash。
开始研究
看一下 url 格式:https://***.******.com/AssetBundles/DmmR18Web/ui/73285980484e56353254c0089894d7c46b218a8f7d0fc87652eaf93cb16c7589.assetbundle
在manifest中的文件名是明文,在请求URL里却是一串巨长的字符(这个长度应该是sha256)
推测做了hash,至于如何做的,那就需要逆向分析了。
从安卓端掏出lib和metadata,处理一下丢进ida。趁着ida在跑打开dump.cs
寻觅一下变换函数的位置。
这个时候一般是从发送下载请求的函数入手,从关键字可以检索到AssetBundleDownloadUtility
类,简单扫一下结构,确实是我们要找的。
1 | // Namespace: App |
成员变量暗示可能对文件名进行了加盐操作,以及循环计算了一定次数的hash,目标函数应该为makeAssetBundleFileNameHash
。
静态分析
打开AssetBundleDownloadUtility
类,此类静态变量一般会在ctor或者cctor等constructor中进行值的初始化。
循环次数设为了14,根据StringLiteral_6415
对应的字符串我们也拿到了salt。
然后分析目标函数:
1 | // Namespace: GameFrame |
图中已经给临时变量进行更名操作方便分析,画圈处为关键代码。
功能是将URL中文件名主体(不含目录以及后缀)替换为makeSha256Hash
返回的值。
其中该函数的输入为文件名主体、循环次数以及盐。
清晰的do-while循环。(_ts
是 text 和 salt 拼接后的字符串,在上面没有截到。)
- A 区域表示在第一次循环之后,对该字符串的每个 byte 与
0x2C
相异或。 - B 区域对处理后的字符串计算 sha256。
- 执行顺序显然是 sha256->异或->sha256…异或->sha256
接下来就很简单了,StringLiteral_2016
对应的字符串是x2
,联想 url 的结构,以及 C#格式化字符串的参数,就是将最终的 bytearray 转为 Hex string。
分析完毕,测试代码如下:
1 | import hashlib |
与实际结果测试,结果无误,问题解决。
然后顺便写了一份根据文件表,仅下载更新档的脚本,自动化掉了之前繁琐的操作。
结语
总体感觉很适合新人静态分析入门)
可以frida插一下0x183DC14
拉到几个参数,不过要弄清过程还是静态分析更为适合一些,后面我们可能会用到frida这个强大的工具。