本帖最后由 currwin 于 2016-4-5 13:01 编辑
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的同学来说,相信还是能够从中学到不少东西的。 PS: 更新一下,第三个算法经同学们提醒,确认是gost算法的变形,有兴趣的可以从网上查找一下相关资料。 这里是对key求法弄个变形 最后放出2组key ①F8LEFT
3Mbf#wOwS3%N
②CURRWIN
$IrLqsfZ$<XN 上面分析中用到的脚本可以在我的GitHub上下载得到: https://github.com/F8LEFT/DecLLVM
|