0%

beatmania IIDX ULTIMATE MOBILE 游戏资源解密

本博文之内容仅供技术交流、学习,请勿用于违法、盈利行为。博主对博文中内容的时效性不负责,也不保证能解答读者的一切问题。如需转载,请注明原作者信息,并附带本博文链接。继续阅读则默认您同意上述条款。若不同意,请立即关闭本页面。

游戏简介

众所周知,REFLEC BEAT plusjubeat plus 在一年前纷纷停止了曲包更新。而KONMAI居然在今年推出了将IIDX、SDVX、DDR三款街机音游移植到移动端的计划,令人惊讶(但我个人认为,移动端上这三款游戏的趣味远不如REFLEC BEAT和jubeat)。

beatmania IIDX ULTIMATE MOBILE在前几天正式上架,并在多个音游相关群引发了热烈的讨论。该游戏采取的是按月付费订阅制(980日元/月),并且在正式付费前可以体验7天的超级(ULTIMATE)会员权限。试用模式下,用户可以游玩的次数、曲目、难度均有限制,而超级会员可以无限制游玩任意难度的任意曲目,并且能自由播放SP/DP谱面和历代BEMANI音游的GST。

至于游戏的其他内容和特性,我在此不再赘述,毕竟本文的主要内容还是游戏资源的解密。

前言

这游戏刚上App Store,就马上有大佬下载了下来,不过好像服务器是过了一天才正式开放。我打开游戏试玩几盘之后,(出于新鲜感)也毫不犹豫地买了日区iTunes礼品卡充了超级会员(然而后来发现10级以上的曲目内含大量多押和楼梯元素,打不来,走了)。

后来在某音游自制群水群的时候,有人提到想提取游戏内置音乐播放器里DRS、钢琴机的GST,但内录音质非常差。于是,我便开始了对游戏资源的研究。

顺带一提,这也是我第一次单人独立逆向出加解密算法的经历,令我获得了很大的成就感,值得纪念~

出于各种原因,本人仅对iOS软件的逆向工程有所了解,本博文的逆向对象将仅限iOS版的beatmania IIDX ULTIMATE MOBILE。

砸壳&解包

用最方便的工具,给自己带来如丝般顺滑的逆向体验(然而事实上并不存在)。

把iPad连接到Mac,iproxy,启动!frida-ios-dump,启动!bm2dx.ipa,解压!

进到 .app 包里一看,发现Data/data.unity3d这么个东西,可以确定是基于Unity开发的游戏了。看来KONMAI的iOS开发程序员在这6年间还是学了点新技术的。

既然是Unity游戏,那么:AssetStudio,启动!把data.unity3d扔进去,惊喜地发现ipa包里自带的游戏资源是没有加密的,因此直接拿到了部分游戏贴图素材和音频(并在自制群秀了一下)。

可惜的是,用spek看了一下导出的wav文件的频谱,似乎只有mp3 128kbps的质量。

接下来就是游戏的下载数据了,Apps Manager + Filza打开App数据路径来一看,可以发现下载数据全部存放在Documents/ab这个文件夹里。

ablist.json一看就知道是存储资源信息列表的文件,戳开来一看,乱码……果然还是加密了吗,那不得不逆向解密了。

(注:当然还有另一种思路,就是Hook解密函数,在App运行时把解密好的文件从内存中Dump出来。由于我个人只做过Objective-C函数的Hook,而对C/C++函数Hook中的内存、文件读写不是很熟悉,就没有走这条路。)

还原符号

既然选择了远方,便只顾风雨兼程。IDA,启动!把ipa包里的可执行Mach-O文件bm2dxum扔进去分析一番,可以看到少数带Unity关键字的Objective-C函数,和一堆……sub_函数。搜索了一波crypt之类的关键字,除了几个Objective-C的hash函数以外没有结果。看来Unity偷偷把游戏中用到的很多函数名都隐藏掉了,真是个小机灵鬼!

那么,我们有方法恢复这些函数符号吗?答案当然是YES。Il2CppDumper,启动!把bm2dxum和global-metadata.dat放进去,随便填个2018.4之类的版本,让软件自动分析,就得到了dump.cs(内含类与函数的声明、地址信息)、stringliteral.json(内含字符串常量及其地址)、script.py(IDA专用脚本,用于还原函数名信息)。

在IDA中选择File – Script File,载入script.py,让它慢慢跑一会儿,之前那些被隐藏的函数名便重新浮出水面。至此,符号还原工作已经完成。

寻找加解密函数

既然函数名已经全部暴露,而且函数名完全没有混淆,接下来的逆向分析工作就简单不少了。直接搜索assetbundle、crypt、cipher之类的关键字,筛选出一些可能与资源加解密相关的函数(这里找AssetBundleList类的原因是,前面在资源文件夹中看到过ablist.json这个文件,还记得吗?):

为了确认,直接上动态调试,lldb,启动!配合Unc0ver越狱修改过权限的debugserver,体验简直不要太好。IDA里拿一下函数地址,lldb里输出一下ASLR偏移,在两者相加的地址下断点。

似乎是游戏在TAP TO START后,会验证一下本地已下载资源的完整性。数次网络通信后,很快就来到了断点。看一下函数的参数,我立刻面露微笑——ablist.json!这种存储资源信息列表的文件一般比较关键,值得从内存中提取出来。拿出来一看,果然和我预想的一样。小明玩音游,我解密音游资源,我们都有光明的未来!

接下来再结合IDA静态分析与lldb动态调试,终于找到用于加解密的类的构造函数AssetBundleCryptoStream$$.ctor_4306905664,并结合dump.cs还原一下函数参数名

分析加解密函数

继续IDA静态分析,会发现刚才的函数先调用了一个MakeSalt,再将一堆参数传进CreateAesStream。不知道它的salt是怎么make的,不妨再动态调试看看函数返回值,结果发现所谓的salt就是字符串的UTF-8编码:

好的,那么接下来可以看它具体是如何实现这个CreateAesStream的了。从IDA F5结果中可以看到,首先是调用了PasswordDeriveBytes$$.ctor这个函数构造出来一个类的实例,然后我们看看实例的交叉引用:(*(*pdb_self + 376))(pdb_self, (v14 >> 3), *(*pdb_self + 384));……

WTF!?还让不让人好好分析了?看dump.cs似乎只能找到类变量的偏移,而看不出这个+376到底是调用的啥函数。

失望、苦恼之际,我突然想到:直接动态调试一遍,选择进入函数,然后把lldb里的地址减掉ASLR,不就是真实的函数地址了吗?立刻行动,然后用类似的方法,把整个函数的逻辑找了出来,原来是先用PasswordDeriveBytes生成密钥,然后再使用AES_128_ECB模式进行加解密(做到这里我有点纳闷:为什么使用ECB这一不足够安全的模式呢?):

实现加解密函数(一)

接下来差不多可以实现加解密函数了吧。通过Google搜索,我发现,PasswordDeriveBytesAesManaged等都是微软的.NET Core里的类,这意味着,咱们可以滴滴偷代码了。我之前没有C#开发经验,于是这次花了几个小时安装.NET Core环境、学习C#基本语法、阅读微软官方文档,然后使用正确的key、iv和mode进行解密……

Boom!失败了。输出的文件依然是一团乱码。

噩梦才刚刚开始。我脑子里满是问号:为什么完全一样的参数,却不能成功解密?即使是padding的问题,根据ECB模式的算法,文件的前几个chunk也应该是明文才对啊?

实现加解密函数(二)

又重新读了一遍IDA F5出来的代码,我发现CreateAesStream也仅仅做了一个类似构造函数、初始化的工作,可能具体的加密方法并不如我所想的那么简单。

回到AssetBundleList$$Load函数去看一眼,有没有调用什么叫Decrypt之类的方法,很可惜,并没有:

看样子,CreateAesStream会直接返回一个具有Stream类特性的实例对象,并且能被StreamReader类实例直接读取成明文。从刚才的分析里可以看出,CreateAesStream似乎无需传入指定加密/解密操作的flag参数,难道这是一个流密码的实现吗?「Stream」这个词似乎也在暗示着什么……

果然,天无绝人之路!回到AssetBundleCryptoStream类去看看,马上找到一个可疑的函数cipher,配合dump.cs文件分析一下:

哈哈!这是一个AES的CTR模式的实现。接下来只要把IDA F5出来的代码改成对应的C#代码,就能实现正确的加解密了,事实也是如此。下面是我用游戏中素材进行拼接得到的一个动图:

总结&展望

逆向工程确实是一件非常费时、费神的事,它非常考验各方面的技巧(搜索、静态分析、动态调试、ARM汇编等),也需要一点好运气

成功来之不易,在这篇博文中,我将不会放出直接可用的解密程序。如果你也对beatmania IIDX ULTIMATE MOBILE的游戏资源感兴趣,某知名BEMANI相关PT站已有完整的解密文件可供下载。如果你也想探究它的加解密流程,希望本文能对你有所帮助。

此外,根据我在iOS设备上对beatmania IIDX ULTIMATE MOBILE进行的抓包测试,可以确认它开启了SSL Pinning(SSL Kill Switch对它无效),并且加密了与服务器的通信的所有内容。我还没有开始研究这个游戏的通信协议的加解密方式,或许在将来有空时会去看看,也或许不会再回来看,毕竟人家新出的游戏,还是付费订阅一下比较合适吧😉。