Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

0x00 描述

给了你一个apk文件,看起来像某种CrackMe应用程序:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

0x01 解题

尝试输入一些字符,一直返回“无效的代码”。看样子我们应该逆向这个apk找到正确的输入。

用jeb打开这个apk,我们得到:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

很明显,dex(Android平台上可执行文件的类型。)代码被封装保护了。通过分析java代码我们可以找到真正的dex文件被隐藏在一个名为“zlaxyprctgauiqqdecamzbompcphoqmjg.jar”的源文件里了。

提取出来的函数用来执行一个名为“libaurorabridge.so”的动态链接库

通常这样的Android程序会在运行时释放dex文件到内存或磁盘上。所以我们应该首先检查应用程序的私有目录,为此你应该有一个已root了得Android设备。幸运的是我们发现:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

我们认为这是一个出题人的疏忽,现如今我见过的大多数都不会释放dex文件到文件系统中再提取,让我们看看这个解压后的dex,文件头告诉我们这是一个已优化odex(ODEX是安卓上的应用程序apk中提取出来的可运行文件,即将APK中的classes.dex文件通过dex优化过程将其优化生成一个.dex文件单独存放,原APK中的classes.dex文件会保留。附图classes.dex反编译和打包过程)文件:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

classes.dex反编译和打包过程

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

所以我们应该用smali或者baksmli 把它恢复到dex文件:

现在我们得到一个正常的dex文件,把它放到jeb里:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

我们可以看到java代码被某种模糊处理保护起来了,大部分的类信息,所有方法的信息都是混淆的。在这场比赛之后,作者告诉我这是一个自己开发定制的模糊处理器,通过阅读这些混淆代码,我们大致的理解了这个混淆框架“AuroraBridge”(这个名字让我想起了北欧神话中的Bifröst)。java和Android API调用都是通过代理执行本地函数:

一个混淆的Java API调用的样例:

我们猜想第一个int参数是一个代表了被调用的Java API的序号,而Object[]参数正是这个Java API所需的参数。现在是时候瞧一瞧这个"libaurorabridge.so"了。用IDA打开这个lib文件:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

哎,…完全一团糟啊,似乎所有的本地代码都被一些看起来貌似像LLVM(lowlevel virtual machine的简称,是一个编译器框架)的Control Flow Flattening混淆方法混淆了,并动态地生成多功能点。理解这些真是个苦活儿,我们尝试通过dd工具拷贝一份lib从Android设备初始化后这个程序的内存中,但是没有什么更多发现除了很多有加密的字符串:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

可能这些都是经加密过的Java API字符串,但是我们不知道如何解密。之后分析了一些可疑的方法:sub_FE7C, sub_10054, sub102F4, sub105B8(提取出的的dex文件,我们输入之后不会做什么处理),我们猜测的输入程序判断不在这里,它应该在java代码中来实现,因此我们返回去分析dex文件,尽管大多数是一些语义信息,我们还可以尝试找出一些有用的线索,我们定位在adb.crackme.b按钮的onClick(),这是代码:

我们可以做一些基本的预测借助一些Android应用程序开发经验:

1)vo是我们的输入

2)f.a(String) 是 String.length()

3)v0_1是通过Toast消息机制来显示结果字符串

4)vo_1的值取决于l.d(Class c, String)的返回值

根据这个模糊框架的模型,我们认为我们的输入会被Class c的三个函数处理和判断:

再次猜测:– Function c()检测输入的长度,应该是16,– Function a()将一个字符转换成二元序列,– Function b()检查二元序列的值是否正确,最核心的代码是:

但是缺少语义信息的m.a()和k.a()阻碍我们对这个应用程序app进行一个更深的理解,现在我们已做了我们所有能做的在现有的已给的材料上,在短时间内修复libaurorabridge似乎很难,但是这时候放弃还为时过早,我们需要更多的线索继续前进!我们用到自己的Android动态分析系统—Indroid(你可以得到更多关于这个工具的信息通过我们的papa DIAS:自动在线分析@CIT2014Android应用程序)来抓取这个程序的运行时的信息,我们向监控列表里添加AuroraBridge.a(), a.m.a(), a.k.a(), abc.crackme.a(), abc.crackme.b(), abc.crackme.c()之后运行这个app输入“1111111111111111”。从跟踪文件的末尾(你可以在这篇write-up的文末找到跟踪文件),我们发现一些数据可以证实我们之前的猜测混淆代码:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子 Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子 Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

除此之外,我们发现m.a(k.a(), v4, new double[]{-1}使用了许多Java.lang.Math.exp()来计算输入字符串,我们并不知道这意味着什么,但我们确定这是某种形式的比较,再次检查跟踪文件,我们发现一些像这种形式的可疑数据:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

我们发现这是一个128位长度数组的“1”或“0”:

这似乎是一个16为长度的字符串,原来是“S ^:D(007)skyFalm”后解码。哇!可能它就是flag!然而不幸的是,它并不是flag(╯▔皿▔)╯。但是这个字符串看起来有意义,一定是哪里错了。我们反复检查跟踪文件,找到另一个更可疑的数据:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

当你看到“1 f b8”你会怎么想?没错,它是一个GZIP文件。我们也可以找到Java API的调用Java / util / zip / GZIPInputStream在跟踪文件里。那么,让我们将其解压:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

得到的是127个长度为128数组和其中一些字符串看起来像命令和参数。我们写一个脚本来解密所有的数组:

Codegate 2015 逆向400 Dodocrackme2 WriteUp-安全盒子

现在我们终于明白这个应用程序真正是做什么的,它运行是以一种机器学习算法的方式,计算输入字符和那些字符的相差长度。在每个位置频繁出现的字符组成flag“S ^:D(007)SkyFall”

0x02 附录

apk文件

实际dex文件

libaurorabridge

libaurorabridge.so

Indroid跟踪文件

解密脚本