首页
社区
课程
招聘
看雪.安恒2020 KCTF春季赛 第十题 沐猴而冠
发表于: 2020-5-12 02:05 9184

看雪.安恒2020 KCTF春季赛 第十题 沐猴而冠

2020-5-12 02:05
9184

题目给出的是一个 APK,安装上试一试会发现是一个输入 flag,按一下按钮告诉你对不对的应用。

丢进 JEB 之类的东西看看可以看到 dex 里没有什么逻辑,且看起来没有代码被抽进壳了的迹象。该应用加载了两个 .so,libz****n.1.solibvv.so,然后直接调用 JNI 注册的 native 函数 com.zhuotong.mybox.MainActivity.eq 检查 flag 是否正确。

看 dex 里有的一些空类的包名,再把 libz****n.1.so 丢到 VirusTotal 之类的地方上看第一次提交的时间,可以判断出来这个 so 是原封原样没有修改过的一个某国内通讯应用里带的,太可怕了,我们还是不要看它了。

在此插入声明:本人没有以任何形式逆向工程该文件,以下相关描述如有涉及到的部分,纯属猜测及夜观天象得知。

分析 libvv.so 会发现该 so 带壳,不是常见的壳,看起来应该是作者自己写的。没有 init 函数,是在 JNI_OnLoad 里做了大量的事情。麻烦的一点是这个 so 带一些混淆,看起来跟 这里 描述的混淆类似,看 .comment 里面的内容可以发现是 Android clang version 5.0.300080 (based on LLVM 5.0.300080) 编译出来的,应该是什么小范围流传的基于 LLVM 的混淆器吧。

这个混淆不是很强,动态调试的话很容易就能摸清这个壳的逻辑,但试图调试这个程序会发现它会 crash,并且 crash 点不在 libvv.so 里,通过 夜观天象,可以知道大概是加载的另一个 so 里的反调试捣的鬼,解掉之后可以很容易 dump 出里面实际套的 so,ELF 头里大部分字段被抹了但直接参照 libvv.so 即可修复(因为是同一个 linker 生成的)。

然而由于我们只能 夜观天象,不能去逆向分析另一个 so,按照看雪比赛的规则,并不能写出来怎么过这个反调试,只好纯静态解这个混淆了,真累啊。

这个 binary 里的混淆主要有五部分:

处理完之后的 JNI_OnLoad 大概长这样:

JNI_OnLoad

可以看到就是保存了一下传入的 JavaVM*FindClass 找了下 MainActivity 这个 class 并取了一个 ref 存了起来,接下来开了个线程干坏事。
这个线程函数里面调用自己背着的一份代码,查字符串可以看出来似乎是作者把 bionic 里的 linker 代码直接拷过来编译进去了,调用的函数流程大概是 dlopen 自己,又开了一个线程(在 0x1D7FE 处,执行的线程函数是 0x18274),然后 dlsym 找自己的 JNI_OnLoad 的位置,再算了一通偏移,跳到了 0x13708 处的函数里去。

sub_13708

该函数调用 RegisterNatives 注册了 com.zhuotong.mybox.MainActivity.eq 函数在 0x12FC8。进去看看会发现它看起来是个检查函数,然而呃,夜观天象一下(或者你看上面开了个神秘线程点进去看看它干了什么)就可以发现这是个假的检查函数。

看上面又新开的线程的线程函数,0x18274,如下:

sub_18274

跟上面的逻辑一样,又把自己加载了一遍,卸载原来的自己,然后调用 0x18B2C,并把自己的 soinfo 和 dlclose 作为参数穿了进去,看这次调用的函数:

sub_18B2C

又开了一个线程,延迟1秒后再卸载自身,主要是调用进了 0x193B8,看看它干了啥:

sub_193B8_1
sub_193B8_2
sub_193B8_3

显然你不用还原控制流逻辑对着瞎猜也能猜出来,它把之前注册的 native 函数找出来,把里面的函数指针偷偷换掉了,真实的函数在 0x19FC4。

sub_19FC4

这个函数里调用了一个改过的 gzread (点进去看看里面的 inflate 特征很明显,认出 zlib 再认外面的 gz 就简单辣,看跳过了 10 字节的头和读了一个 flag,很明显),解压了 0x63750 处的共 107279 字节数据。改动点主要是把 magic 检查去掉了,于是对应的压缩过的数据开头的 1F 8B 08 也被调皮的换成了 89 50 4E,我们把它换回去,即可 gzip -d 解压。

解压出来的数据稍微看一看就可以知道是个 ELF,不过头部被搞坏了,参照 libvv.so 开头的内容直接修复即可。找了一下发现文件尾部的 section header 也都被清 0 了,shoff 就只能填 0 辣,不过并不影响分析。

上述函数接下来在内存中加载了这个 so,调用了 0x66C0 处的函数。

折腾了这么一长串,其实如果动态调试的话,真的可以直接跳过不看上面那些的。至于为什么不这么做,当然是因为我胆子小不敢去逆奇怪小厂的 so,夜观天象水平又不够高辣。

readelf 看下 dynamic section 可以看到:

这提示了我们里面是个什么东西。

(其实在壳的代码里也能看到诸如 D:/code/android_code/MyCtf/myaeswhitebox/src/main/jni/linker/libc_logging.cpp 这样的字符串)

内层 so 也有混淆,不过逻辑相对简单,并无大碍。
分析 0x66C0 处的函数,这次是真实的对应 com.zhuotong.mybox.MainActivity.eq 的 native 函数了:

可以看到就是把输入变换后 hex 编码,再和固定串 "0b6f04a3427089858041945e70082508" 比较,如果正确的话就手动弹个 toast。变换输入的函数特别短,应该是个没什么强度的白盒 AES(假装它是的话),相信作者的人品它真的是 AES 而不是什么仿 AES 之类的东西,直接重新实现一遍上 fault attack。用到的数据是用跟外层压缩 so 的方式一样压缩的(除了这次填的 magic 是 "elf" 了,真调皮),照样用 gzip -d 解压出来即可。

重写的程序见附件 wbox.cc,用 deadpool_dfa.py 可以直接全自动解掉:

得到 key 是 you get the key!,接下来直接解密答案即可。

真有意思,不如下次谁出 Windows 上的 CrackMe 的时候加载个某 P 进来反调试,然后让选手们写 writeup 教你过某 P,多好,还可以堂而皇之的说“我算法没用某 P 里面的东西啊,我没调用啊,不用看的啊”,呵呵呵。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2020-5-12 09:29 被kanxue编辑 ,原因:
上传的附件:
收藏
免费 2
支持
分享
最新回复 (3)
雪    币: 4752
活跃值: (2923)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
2
膜,夜观天象学到了,暗示CTF要晚上搞。
2020-5-12 10:37
0
雪    币: 259
活跃值: (283)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢大佬分析
2020-5-12 22:53
0
雪    币: 3
活跃值: (15)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
感谢大佬,大佬能给一份去完花指令的so 学习下不。
2020-5-20 16:32
0
游客
登录 | 注册 方可回帖
返回
//