tgstc-2021-Android-复赛
分析
寻找游戏逻辑代码
上手发现是个unity游戏,首先找Assembly-Csharp.dll,没有,是个il2cpp打包了的游戏。那就找il2cpp.dll和global-metadata。找到了,但是都被加密了。
那么现在要做的有il2cpp.dll的解密和global-metadata的解密。
il2cpp.dll解密估计在libsec2021里,当然也可以直接dump,global-metadata可以动调断在il2cpp里跟或者直接GG搜内存dump。
去除反调试
考虑到之后还要修改,去除反调试应该是难以避免的。
打开就看到和初赛一样一样的字符串加解密算法,但是全部内联到了程序里面。
之前的方法不太好处理,也懒得分析它的算法,暂时先不管。
打开调试器启动,准备找下它的反调试的核心函数,发现自杀的代码基本长一个样。
通过异或产生一个不合法地址然后跳过去。可以用IDApython批量下断。
1 | def find_maybe_bad(start, end): |
可以断下来接近60个地方。。。看了下反调会影响函数流程,也不敢轻易乱改,只能让它跑起来再把撞上的断点一个个手动修了,肉眼看到的有检测ida和frida的文件和端口,手动过一下这些反调可以让你在程序里少patch几个地方。。。
最后只需要手动改不到10个地方就可以绕过反调了。
其实更应该去找上层一点反调试代码,比如启动反调试线程的地方,但是程序本身大量混淆非常难看,找起来很麻烦。
混淆类似ollvm的那种,感觉要去的话angr也挺好去的,符号执行到块结尾看BLX到哪里去,分支代码特征也很明显,都是ADDNE, MOVEQ这类的指令(口胡。
解密libunity和libil2cpp
有了上面的工作理论上已经可以调试了,但是实际上跑起来会死在libunity里面,而且跟过去也发现全是乱的,估计是libsec2021中用了校验码之类的东西去解密libunity.so,因为我们的patch导致解密失败,解出来的东西自然全是屎。
手动跟下libunity的init_proc,发现最终解密函数也是在libsec2021中。
而且发现这个地方很可疑。
其中用到的值被放在堆里,其指针放在
怀疑是key,直接GG挂上去看正常运行时的值,手动填进去试试,发现跑起来了。
解密程序其中取出了2个dword,但只使用了它们的异或值,所以这样patch程序也没问题:
好了,到这一步已经可以畅通无阻调试了。
恢复global-metadata
直接从内存中dump出来il2cpp.dll。global-metadata在il2cpp.so中下断后dump出来,断在下图第一个函数:
丢到il2cppDumper,报错了,也算意料之中。
找原因东找找西找找,最后对着源码看了下后面
偏移对不上,错位了。然后对着源码一个区段一个区段看,最后发现是把开头的3个string相关的区段挪到了中间去,所以其他区段全错位了,而且字符串区段还被加密了,加密就是个异或。
写个python脚本手动修了下:
1 | from pwn import * |
修复出来还是报错,手动跟il2cppDumper的源码调试,发现它有个区段认错了,怀疑是版本号有问题,因为这个区段在版本27中是没有的,手动把版本号改成24,成功dump出Assembly-Csharp.dll文件。
修改无敌版
剩下的工作就很简单了,用ida脚本恢复出来符号信息,找到关键函数。
判断是否障碍物
因为没有分析加解密具体算法,所以这里使用自修改的patch,修改libsec2021中的解密代码,让它解密结束之后将将对应位置改掉。