-
-
[原创]吾爱破解2016安全挑战赛cm7 Android CrackMe 分析详解
-
发表于:
2016-4-4 22:07
8842
-
[原创]吾爱破解2016安全挑战赛cm7 Android CrackMe 分析详解
CM带OLLVM混淆,搞过android的同学们估计都知道这是一个什么玩意儿。一般来说,假如逆向需要的时间是100%,然后ida的F5插件可以降到20%,那么这个OLLVM就是可以把这20%的可能性给扼杀掉,顺便把时间提升到 10000%的神器。
举个例子,正常函数的F5代码是这样的
OLLVM混淆后的代码是这样的
后面还有好长好长的一段,妈呀画面太美我不敢看
在这里我想先解释一下这个OLLVM到底是一个什么样的东西,不然看着就只知道是一堆if-else判断,那么后面的分析就不用看了。
看代码说话
这是正常的求和代码,当然,我们可以脑洞一开,把它弄得复杂一点。
怎么样,看懂了吗?看懂了的同学可以直接看下一part了。实际上,这个就实现的功能上来说是和上面的一样。简化一下可以变为这个样子。
可以看到,函数执行的流程不再是由上到下的顺序流了,而是根据pc的值,动态得选择if块中的语句执行,同时把下一个if块的地址赋予pc指针。这个就是OLLVM大概的思路,当然,我这个是大大简化了的。对此,不得不说,城里人真会玩啊!!!
咳咳,真实的语句被打乱,隐藏起来。通常,一个if块中只包含一句甚至是半句的指令,而逆向分析人员则不得不花费大量时间去整理程序执行的流程,提取真实的代码。这就是OLLVM的困难所在,也是其设计巧妙之处。
而这个CM,也就利用了这个技术,把80%的逆向者阻挡了在门外。
好了,既然知道了这么难,哪就散了吧,做不来的?对吧。如果这就放弃了,那么待得日后出了真正的VM,那么就是不是完全不可能搞定了?咳咳,在这里放弃还是有点早了,鼓起信心迎难而上吧。
先看看CM的大致验证流程,首先是调用验证的地方0x000125F4
非常明显的校验,进去看看,可以知道主要有4个校验的地方。
直接上F5代码
首先是对密码进行base64解密放到v360N(8字节)处,然后对v360N进行rsa变换,接着是一个tea变换,得到(“360N”,CheckCode)结构的解码代码,最后对用户名进行hash,得到值与CheckCode比较,正确的话就返回成功。大致的流程是这个样子,这一部分的分析还是挺简单的, 然而要如何正确地分析这几个函数的时候才是困难所在,毕竟这几个变换都不是标准的算法。
为了方便分析,先使用一组key
Name:360
Key:=6@LdGUI1qEN
①变形base64算法
第一个变换是变形的base64变换。这有非常明显的特征,在进行密码测试时,解密长度变化为4变3,8变6,12变9。然而这又不是标准base64算法,毕竟第一个字符为’=’号。这也好办,只要在内存中dump出转换表,结合key算一下组合方法,就完事了。然而这才是难点所在,算法的代码是这样的。
里面调用到 0x0000C6EC 这个地址进行真实的转换
神马鬼,这么多的跳转,都跟踪到头晕了。其实这里就是真实的OLLVM,此时R0就是上面代码中的PC指针,然后这里就是一堆if - else 语句的跳转。
这里则是算出下一步函数地址的地方。上一点的地方则是这一节的真实指令。
因为这个函数体比较大,而且还有循环的指令,所以一步步跟踪起来代码可能会有上十万行。接下来就是耐心的比拼了,记住我们的目标,就是dump转换table,很简单,所以大可以一步一步地进行跟踪。当然,这并不是做好的方法。总体地看一下,OLLVM中,真实的代码是隐藏在if 的块里面,所以我们只需要从中dump出真实的指令出来就可以了。
这方面的工作有不少人已经做过了,参考一下,可以写一个脚本来进行提取,脚本效果大致如下:
类似于ida的跟踪功能,只是跳过了无用的跳转指令,顺便打印寄存器的变化。虽然代码依旧丑的很,不过可阅读性已经提高了不少了。以时间来说的话,就是把10000%的时间缩短到300%了。对于这个cm来说,这样就差不多了,好吧,上吧。
因为base64里面有循环取出加密数据,取置换表,然后写解密数据的操作,所以我们优先找到取加密数据的位置,以及写解密数据的位置,那么在这之间的便是解密操作了。直接搜索dump出来的trace文件,查找传递进去的R0(未解密密码地址)的值。这里就是搜索4D7C8,得到:
得到取pass数据的地址为DCD4,并且可以看到每次循环都是使用4个字节,从这个位置跟踪下去可以发现
根据输入在内存中取表置换,对base64熟悉的同学应该知道这就是取表运算的地方,直接把内存中的表dump(0xBEA85108)出来,然后根据合法key的转换可以发现这个base64的组合和标准的base64是反序的。结果算法如下:
这个算法和PYG上的随机base64算法十分相似,估计就是用同样的工具生成的。然后标准12字节变8字节,填充字节为1个,那么填充的字符自然就是最后的N了。
②标准rsa64算法
Rsa算法部分最为简单,里面对base64解密的结果通过str2hex转换为字符串后,直接推送到解密函数处
函数内没有任何混淆,直接分析即可。
内存中的大数数据。
以及将其转换为大数的函数。跟进去可以发现相关提示
恩,是openssl的大数库。
中间大数的各种加减乘除运算后得到模数n和指数e,然后对v360N进行rsa变换
公钥解密算法,好在这里只是一个64bit的RSA(长一点就无解了),可以用工具来分解。
③tea变形算法
接着是一个变形tea算法的解密算法。
先对name进行hash,取得加密表,然后利用加密表对上一步得到的8字节密码进行tea解密,得到(“360N”,CheckCode)
这里变形tea算法的求逆这里我们可以取巧,首先通过用户名计算出0x20的hash值,我们可以在加密算法中直接使用。这样就省去了逆向这个HASH算法。其次验证第二部分的数值第一个DWORD是固定值‘360N’,第二个dword是用用户名计算的一个hash值,这里我们也可以直接使用,同样地省去一步的逆向。估计这里是设计者由于比赛时间原因,故意留下的一些破绽。
好,正式上吧。
首先函数内会再次初始化一个寻址表addr,然后调用真正的解密函数。
这里输入的R0,R1,R2数据如下
R0,待解密的key, unsigned v[2]
R1, 地址table -> addrtable, unsigned char addr[0x20]
R2,nameHashTable, unsigned k[8]
记录下来这几个地址以及数据,对后面的分析会大有帮助的。
里面全是OLLVM混淆,直接跑脚本进行分析吧。
直接搜索R0,R1,R2的地址,获取它的运算流程。
初始化
交换v[0]与v[1]的值,然后后面就是一个长0x20的大循环
可以看到一次循环如下:
循环开始,i=0,下一次经过的时候则会变为1
取R0 = k[addr[0x1F-i]],得到中间值k1,用于后面的计算,
I=0时,得到值DF6450CB,直接搜索这个值。
K2 = chgR0(k1 + v[1]),chgR0这个函数后面再分析,现在搜索中间key,k2的值9EF4E7CC,其实就在下面一段。
更新v[0],v[1]然后又喜闻乐见地开始下一个循环。
好了,这个函数算是完了,翻译后c代码如下:
那么就只剩最后一个unsigned chgR0(unsigned )函数了。
依旧是跑脚本,此时注意将脚本的PC指针从R0切为R7
这里以R0=17CD18FB为示例,跑脚本
搜索17CD18FB,查找处理的地址
首先是将输入的k每个字节取出来,放到内存BE92E294 --> uk中
变成这个样子,因为当前SP为:BE92E1E8,而BE92E294-SP= AC,所以下面搜索AC,查找引用地方。
取出uk数据,在根据置换表4C0D6取出相应的数据,保存在B6中。
置换表如上,是一个[8][0x10]的置换表,相信大家都已经猜到是怎么转换的吧。
所有数据转换完成以后,重新组合为unsigned F993DE9C
最后还有移位,
两者求或,完事,完整的c代码如下:
于是,把整个算法逆一下,得到其加密算法如下:
④最后对用户名求hash,与上一步解密得到的CheckHash进行对比。这里没有逆下去,因为根据前面弄出来的算法,已经足够去求出一个有效key了。
(⊙v⊙)嗯,由于时间关系,对于这个cm的分析也只能到此为止了,没能写出keygen,算是有点可惜。只能下次努力了。相信大家都会有个感觉,一开始总觉得这个cm很复杂,又是OLLVM,又是修改算法之类的。但是分析下来,就会发现这其实还是做得中规中矩的,并没有特别难,特别复杂的地方。而又不会特别简单以至于不需要分析就能弄懂其算法。当然,其中一部分的强度都是由OLLVM所提供的,这对于有心想要搞OLLVM的同学来说,相信还是能够从中学到不少东西的。
最后放出2组key
①F8LEFT
3Mbf#wOwS3%N
②CURRWIN
$IrLqsfZ$<XN
上面分析中用到的脚本可以在我的GitHub上下载得到:
https://github.com/F8LEFT/DecLLVM
排版有问题的可以直接下载doc文档
分析.doc
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)