前言
第一次分析so层混淆的程序,ollvm真的是个神器。最先我还天真地看了半天汇编,ollvm可以让你分析时间翻几十倍。我并没有找到任何方法来抵抗这种混淆的方法,所以要分析只有硬刚。我先整理了一下网上关于ollvm的描述,然后根据我自己分析过程给出一些自己的理解。最后写了一下2016bctf的LostFlower的题解。
ollvm简介
ollvm是对于so层函数,c语言函数混淆的方法。之前在java层遇到过诸如proguard的一些混淆,但是大家对于他们的评价都是这种混淆只是混乱了一下逻辑,并没有什么作用,让分析时间增多,也只是略微增多一点。但是我想说ollvm是让人绝望地增多,当然也可能是我第一次接触这个东西。原本好好的代码经过ollvm处理后的结果就是这样的
具体原理(网上找的):正常的求和代码
复杂一点的表达方式:
再复杂一点的表达方式:
可以看到,函数执行的流程不再是由上到下的顺序流了,而是根据pc的值,动态得选择if块中的语句执行,同时把下一个if块的地址赋予pc指针,这个就是OLLVM大概的思路。
在做bctf这道题时,总结出ollvm的套路:定义一个变量,并不断使用这个变量,对他赋值对他大小进行比较、跳转。然后在这中间加入我们程序中真正有用的逻辑。我们在进行分析的时候需要不断地跟踪这个变量,一般来说这个变量是不会改变的。然后在中间找到代码真正的逻辑,由于代码不停的上下跳转也很难定到一个地方让他执行到这一步。所以我们需要特别仔细,特别关注if语句中判断是否相等条件下进行的处理。
LostFlower
打开这道题还是第一步扔到jeb中查看
逻辑很简单,将输入变为double类型,然后将这个值使用so中的stringFromJNI进行处理,返回结果如果等于6,那么就可以直接处理过后生成flag。ida打开so文件,由于没有什么反调试的措施,所以我们直接上动态调试。
跳过一堆判断函数后,找到了处理的关键check1函数,参数是我们输入数字的整数部分,我想吐槽,正常人会输小数?进入该函数后,又是一大堆烦人的跳转,我们从返回值逆向回去,也可以一步一步地走来看。其实在进行了几步后我们就可以看到他的规律,程序通过v2这个变量也就是我们寄存器r0的值,不断用它进行大于小于、等于不等于的比较,跳转到不同逻辑,有一些逻辑不涉及到改变v2的值,所以不会影响整个程序的流程,重点关注对v2进行赋值的代码。下面通过一整个循环大家就清楚了
判断v2是否等于4A2AC114,等于赋值FE0D772E给他
关键代码如上,就跟我们分析的一样关键是看v2的值的跳转,我们理清里边包含的逻辑,就是每一轮对v3进行加1,一共加到10,每一次循环都执行第一张图的处理,我们来分析这段代码的处理:
|
|
这里几个函数都是一些除法、求余的操作。经过几轮动态调试,事实上,这个for循环的前五行代码就是逆向取出一个10位整数的每一位。举个例子,输入为1234567890,每一轮得到的依次为0,9,8,7,6,5,4,3,2,1。
现在还剩下一个 my_pow,顾名思义这是个幂相关的函数。注意我们不需要去具体分析 my_pow,因为我们的输入只有0-9这10种可能,几轮测试过后,得到结论:my_pow返回输入数据的十次幂。
用C代码重现下这个for循环:
|
|
继续执行:
sub_7A965AA4,计算括号内的值,为负补之返回,不为负则直接返回
继续看主函数,需要返回负值,并且输入大于1000000000,则输出为6。由于逻辑太复杂,所以简单点的方法是直接修改返回值,可以看到这两个就是最终的判断条件,其他地方再没有任何有用的判断代码了。
所以关键在于我们要让之前的sub_7A965AA4返回负值。通过对sub_1AA4的汇编代码的分析,发现了溢出点:NEGS R1, R1。其中 NEGS
的作用是这样的:将目的操作数的所有数据位取反加1。当参数为0x80000000(负的2^31,最小的负数)时,所有数据位取反后为0x8fffffff,再加1后发生溢出,最后值为0x80000000。
解密代码:
|
|
输入为1422445956
,Flag为BCTF{wrhav3f4nwxo}
。