ollvm反混淆
一、前言
此篇文章只讲思路,具体代码实现见https://github.com/gal2xy/AngrDeobfuscator。
二、去混淆
2.1 去指令替换混淆
指令替换一般可以使用llvm
的pass
进行优化,使用llvm-dis
反汇编再使用opt
的-o3
等级优化。或者使用Miasm
框架进行匹配,然后优化处理即可。
2.2 去字符串混淆
没怎么了解字符串混淆这一块,就贴一下随风而行aa大佬的文章内容。
字符串加密的的常规解决方式:
(1)特征搜索
一般在so中可以直接搜索datadiv_decode
,一般很多编写解密函数进行操作是这个函数,针对这种情况,一般可以通过frida hook
就可以拿到解密后的值,然后进行patch
。
(2)init_array中解密
字符串解密操作在init_arrray
中进行,一般可以通过模拟执行init_array
,然后将解密后的字符串全部保存下来。
(3)jni_onload解密
在jni_onload
函数中进行解密操作,这时候就要进行inlinehook
拿到解密后寄存器的值,也可以进行hook
,也可以使用unicorn
进行操作。
2.3 去虚假控制流混淆
虚假控制流主要是通过不透明谓词控制代码流,ollvm中的不透明谓词与输入无关,因此可以通过符号执行来去除虚假块。具体思路为,使用angr执行整个程序,记录下可到达的基本块,其余基本块则是不可达基本块,可以认为使虚假块。接下来就需要对后继块中存在虚假块的基本块进行patch,将最后一条指令(jcc
指令)修改成jmp
指令。
2.4 去控制流平坦化混淆
ollvm中控制流平坦化混淆的效果如下图所示:
其代码的真实逻辑在:序言块、真实块(相关块)、retn块中。
整个去混淆思路如下:
区分基本块
这些基本块在CFG中具有很明显的特征,例如前继基本块为0的是序言块,后继基本块为0的是retn块,后继基本块为预处理器(预分发器)的为真实块,而预处理器的后继基本块为主分发器,而主分发器的前继基本块为序言块。因此,通过这些特征很容区分出这些基本块。
(不过如果是魔改混淆,则可能需要另找特征进行区分)
恢复相关块(包括序言块)之间的跳转关系
这个可以通过angr的符号执行来恢复。即从每个相关块(包括序言块)进行符号执行,当执行到其他相关块中时,则认为找到了当前相关块的后继相关块。当然,后继块数量可能 > 2,则就需要下断点,更改条件表达式变量的值,使其强制执行我们所需要的分支,,这样才能恢复得全面。
关于后继块的数量,可以通过块中有无
cmov
指令指令来判断。修补程序
把无用块都
nop
掉。根据得到的跳转关系,如果当前块只有一个后继块,则将当前块中的最后一条指令修改成jmp
指令跳转到该后继块,如果当前块存在两个后继块,则将当前块中的cmovxxx
指令修改成对应的jcc
指令,然后再将后面的指令修改成jmp
指令跳转到另一条分支。
参考:
相关博客参考:
利用符号执行去除控制流平坦化 - 博客 - 腾讯安全应急响应中心 (tencent.com)
[原创]Android APP漏洞之战(14)——Ollvm混淆与反混淆-Android安全-看雪-安全社区|安全招聘|kanxue.com
angr API参考:
https://docs.angr.io/en/latest/analyses/cfg.html
https://docs.angr.io/en/latest/api.html#angr.knowledge_plugins.cfg.CFGModel.nodes
https://docs.angr.io/en/latest/api.html#angr.knowledge_plugins.cfg.cfg_node.CFGNode
https://docs.angr.io/en/latest/api.html#angr.block.CapstoneInsn
am_graph库:
https://github.com/angr/angr-management/blob/master/angrmanagement/utils/graph.py