2014 NAGA&PIOWIND APP应用攻防竞赛 Crackme04
Java层比较简单

查看so,发现加密,依旧dump,IDA调试时未发现有反调试,不过有那么一瞬间看到了inotify,没具体看
auto fp, dex_addr, end_addr;
fp = fopen("E:\\libcrackme.so", "wb");
for(dex_addr = 0xA357D000; dex_addr < 0xA35DE000; dex_addr++)
fputc(Byte(dex_addr), fp);修复dump后的so文件头,使用IDA打开,关键的依旧是这个函数

但是我们跟入后,发现壳好像没有脱干净(后来发现其实不是没脱干净)

再次动态调试脱壳,这次我们找到校验函数,单步跟下去看看具体是什么情况
我们需要先找到校验函数的地址,使用给dvmUseJNIBridge函数下断点的方法

我们使用调试模式启动应用,IDA挂上去,找到libdvm.so的dvmUseJNIBridge函数,下断点
然后把IDA跑起来,在应用界面输入账号密码,点击登录,就可以发现断在这里了,我们注意观察参数,第二个参数就是我们的crackme函数

跟过去,可以看到确实是校验函数

找到我们看到是跳转地址的地方

双击过去

再次双击过去,发现是关键加解密点了,此时我们记录一下这个地址

再次dump这个so文件
动静结合,接下来看能力了

入口的数据初始化,然后调用_Unwind_GetCFAB,这和前几题是类似的

跟入,开始做了一些参数的存储操作,然后存储了_Unwind_GetCFAB函数的指针到栈中
一开始并没有看出来,所以使用了动态调试来确定

接下来的操作是为了调用tdog_decrypt而做参数的计算
看名字就可以猜到这个函数很重要
双击跟入,发现其调用了一个XorArray()函数
在XorArray函数里有一个PolyXorKey函数,用于生成秘钥,这个函数在后续的娜迦壳里面是一个比较重要的特征,后续的类抽取技术里就有用到这个函数进行秘钥的计算
我一直觉得这里加了junk code,前面有些指令反复做同样的操作时我就感觉出来了,但是加的junk code并不是很多,比如这该函数的第一个函数块后面的几句
开始进入循环
两个基址获取字节数据,进行异或操作,异或后的数据,存在_Unwind_GetCFAB + 0x14 + i指向的字节
这里是在计算一个四字节的数据

我们在内存中跟随,可以看到这四个字节的数据已经修改成了83 93 00 23,不清楚的同学可以在异或的地方下个断点循环调试看看
接着是调用PolyXorKey,参数是神秘变量自身
先使用异或操作对神秘变量进行修改
进入大循环,整个大循环就是循环计算神秘变量的四个字节,但是内部又有很多的循环计算
取字节,这里的var_C会在后面自加一
内部的循环
接下来的循环计算可以还原出C代码,但是具体是什么数学算法之类的就不是很清楚了,可能只是个计算,这个函数最终的功能目测应该是计算一个四字节的数据作为返回值
直接在最后面下个断点跑完这个函数,可以看到返回值是0x80FF1E18

这是整个大循环

回到上一层函数,这个值应该是固定的,暂时没有看到有其它参数对这个计算过程造成了影响
一边分析一边写的,估计有些地方会分析错
这个函数整个大循环是0xF0次,也就是240次,我们来验证一下PolyXorKey函数是否每次都是生成一样的数据
开始变成两个常数的交替出现,难道是动态调试出问题了
这里非常绕,跟了好几次都没有找到关键的地方,后来半猜半想,根据调用operator new[]()的函数往回找,找到了和前几题一样的函数,虽然这里算法不一样,但是对于用户名和注册码的存储还是一样的

接下来是校验的地方,单步走一遍先,找到关键的地方,可以看到这里调用了四个函数

但是在静态时这位置我是手动找的,这个费劲,有的函数没有识别出来,红色的

其实还有非常多的函数未识别出来,不过并不是很重要
由于前面没有完整的跟过来,所以这里的一些偏移需要根据动态调试确定指向的数据是什么
那么0x34偏移指向的就是用户名

并且有长度的限制,用户名长度应该在[8, 24]之间
偏移0x38指向的是注册码,注册码长度需要在[12, 100]之间
第二个函数比较长

获取用户名
存储一下中间变量
进入一个0x08 * 0x100次的循环,循环获取用户名的前八位数据
获取一个关键偏移
这个偏移在这里的作用是重定位一个Table,通过和用户名相同的偏移来进行数据获取,然后两者异或
大概就是
最后进行次数的判断
每个字节一共是0x100次,动态调试把整个表dump出来
补充一点,这个表其实不是动态生成的,静态分析时就可以dump出来

因为异或的计算比较有意思,整个表循环异或一遍其实可以等效于异或一个值,这个值我们可以通过计算来确定,输入为0x00,看输出是什么即可
在IDA里将这个表保存为文件,使用WinHex打开,拷贝存为C Source
写个程序跑一下
可以看到整个异或表的异或效果和单独异或0x93的效果是一样的

计算完后会判断计算后的数据是否为0
如果是0,则会改为0x99
每个字节计算完成存储到栈中,一共八次
最终我们可以看到生成的8字节数据

在上图的位置下个断点,数据区跟随R3,可以看到完整的生成过程
第三个函数,就一个小循环,应该比较简单

后来分析下来是我错了,它不简单,参数之类的预处理
调用了一个函数,这个函数可复杂了
参数是计算后的8字节数据,里面有五个函数的调用,继续一个个跟

开始做参数的存储,重定位了一个Table
这个Table在动态调试的过程中是有值的

但是在静态分析的时候是空的,这里有一个0x30的偏移

我看到了后面一层又一层的,而且明显的跟其它数据分开了
我发现不对劲,而且这么多计算我都看不懂,于是开启猜测模式
这里应该是某加密,前面那个8字节应该是秘钥,然后后面的函数一个个看,看看有没有什么Table,现代加密算法一般都有各种Table去做计算
运气不错,发现了DES加密算法的S盒

它是八个二维数组,规格就是8 * 4 * 16
可以自行对比一下,当然也可以靠其它Table的特征
当然AES也有S盒,但是这两者的S盒是有很多区别的,比如AES的S盒如下
首先是规模不一样,其实是数量不一样,输入输出的值也都不一样
那么这里可以确定是DES加密算法,但是它是加密还是解密就需要再考量一下了
先放着,我们接着看代码,在初始化完秘钥后,开始给两个数组进行初始化操作
顺带把注册码分为16字节每组,每组进行循环解密
解密后的数据存储到s2,结构体偏移0x3C
最后第四个函数就是解密后的注册码和用户名进行对比,红色表示异常分支,蓝色表示正常循环,最后由两个灰色的代码块结束循环

我们来计算一组有效的KEY
不过好像出了点问题,哪里不对的样子

因为在分析的时候我注意到了取了用户名前8位进行计算秘钥,而且后续使用了十六位进行分组解密
所以这里单纯的使用了一个八字节字符串当做用户名进行输入
再次打个断点进行调试,看看解密后的数据是个啥
首先获取注册码

然后两组计算完后,得到解密后的数据

那这个就很尴尬了,怎么会多出八位

发现用Java的加解密库计算出来的数据并不正确,其实可能是校验的过程改了
正常情况下解密出来的数据应该是这样的

既然这样,那我就不客气了,去网上找DES的C代码实现
随意找了个代码,看到了S盒,想起刚才也是S盒,会不会S盒动了手脚,于是对比了一波S盒
首先把正常DES算法的S盒准备好
然后将动态调试时的S盒dump出来

跟前面拷贝出xor_table一样,使用保存为文件,然后WinHex转为C Source
然后跟上面正常的S盒进行循环对比,找到不同的地方

找了个C实现的DES算法代码,发现结果不对
想了想,如果S盒有问题,那么其它几个Table和盒子可能也有问题,于是开始对比了一波,最后发现PC2_Table有问题

再一次的计算,发现注册码计算还是有问题,当时场面一度很尴尬
突然,我想起了一件事,秘钥开始的时候经过了一次神奇的异或
赶紧的赶紧的,继续改代码,东平西凑,瞎改瞎改

就先这样吧,眼泪掉下来,以后再找个时间分析一下这个样本的保护技术
最后,如果是第一次接触这种动静结合分析的同学,要时刻注意指令集的切换,中间有大量的指令集切换,看指令的地址即可,通常都是三步走,断在调用处,先别跟过去,此时跟过去会断不下来的,直接效果就是和F9一样,这一点应该有体会吧,比如使用的是BL R3,在这一句下个断点,先断下来,直接在反汇编窗口跟随R3,就可以看到要执行的代码了,但是如果指令集识别有问题,需要先ALT + G,选择Thumb模式,然后按一下C转为代码模式,再按P识别函数
Last updated