首页
论坛
课程
招聘
雪    币: 41
活跃值: 活跃值 (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝

[原创]Unicorn反ollvm控制流平坦化之bb

2020-2-24 14:02 3884

[原创]Unicorn反ollvm控制流平坦化之bb

2020-2-24 14:02
3884

                                                  ollvm控制流混淆学习、逆向过程


    感谢无名大佬提供的思路,使用了下unicorn,编写idapython脚本去掉控制流混淆,unicorn一个字,好使,推荐大家可以用用,

                        第一部分ollvm编译,实际测试下控制流混淆的效果

    1、https://github.com/obfuscator-llvm/obfuscator下载最新的ollvm分支编译,再build/bin下生成clang。

                 1)、 在llvm目录下创建build文件。

                 2)、 在build目录下行 cmake  -DCMAKE_BUILD_TYPE=Release-DLLVM_INCLUDE_TESTS=OFF ../

                 3)、 make -j4

                 4)、 build目录下会生成clang的可执行文件bin、lib目录

    2、将Lib\Transforms\Obfuscation\Flattening.cpp移植出来单独编译,方便测试,否则修改了源文件还得放到整个ollvm源码中编译,费时不方便测试。编译生成            libflatten.so

    


    3、编写一个简单的llvm-test.cpp,只有一个测试函数。

    


    4、使用clang分别进行编译,一个加载控制流混淆pass,一个不加载,在ida中进行对比下看看效果

    


    未加控制流混淆pass:

    除了入口块,还剩余4个真实块

    


    添加控制流混淆pass:

        可以看到添加了好多用来寻找真实块的无用块,统一从0xDB0块出发,找到真实块,最后统一从块0xDC0回到块0xDB0,跳转到入口块的指令都是有很明显的

    特征的,后边会提到

    


        以上是llvm生成的最终的机器指令控制流程图

        llvm 通过Function.viewCFG()提供生成的中间IR指令的控制流图

        在控制流混淆Pass的runOnFunction函数的前后调用f.viewCFG(),可以清晰的看到

        混淆前后中间IR指令控制流图对比


        对比下IR控制流图,

        混淆前:

    

        

        混淆后:

       


                第二部分简单看看ollvm控制流平坦源码流程:

    看下源码, https://github.com/obfuscator-llvm/obfuscator Lib\Transforms\Obfuscation\Flattening.cpp中的flatten函数

    1、  第一个块              :添加 AllocaInst、StoreInst、BranchInst指令,alloca分配switchVar变量空间,store填充switchVar为随机值,bl跳转到loopEntry块

    2、  loopEntry块          :添加 LoadInst、SwitchInst指令,load获取switchVar变量地址,switch根据获取的switchVar值,查表跳转

    3、  loopEnd块            :添加 BranchInst指令,跳转到loopEntry块

    4、  真实块                  :如果是无条件分支指令,去掉终止指令br,添加StoreInst、BranchInst指令,根据真实块后继对应的switchNum用store更新    

                                            switchVar值,跳转到loopEnd块如果是条件分支指令,去掉终止指令br,添加SelectInst、StoreInst、BranchInst指令,根据SelectInst                                              指令选择的真实块后继对应的switchNum用store更新switchVar值,跳转到loopEnd块

    5、 switchDefault块    :添加 BranchInst指令,跳转到loopEnd块

    引用网上的一张图

    


    总结控制流混淆的原理:

        搭建一个switch代码框架,将真实块塞到框架case中,根据switchVar变量的值,通过switch指令寻找到真实的代码块,跟前边开启了混淆编译,中间生成

    IR 流程就能匹配起来了。

        控制流混淆生成的中间IR控制流图和源码是完全能对应起来的,都是通过switch指令,随机值串联起来的,但是和生成的汇编控制流图还不太一样,反汇编生      成的控制流图是通过多个块中的比较指令,最终找到真实块


                        第三部分bb平坦化反混淆

    Hook app输出日志函数pBA425510043BB3BE41A7E30AA69623BE

    反混淆之前控制流图:

    


    F5伪代码:外层一个大的while循环,内部通过switch case的方式寻找到真实块

    

    


    反混淆之后控制流图:

    F5伪代码:

    可以看到是劫持了libcutils.so、liblog.so中的符号:__android_log_write、 __android_log_buf_write

    

    



    字符串解密函数:

    反混淆之前控制流图:

    

    F5伪代码:

    


    反混淆之后

    F5伪代码

    


                               第四部分通过Unicorn去掉bb控制流混淆

    1、  找出函数中的分支分发指令TBB/TBH的地址,switch表的个数通过Capstone反汇编引擎,通过简单的助记符匹配即可找到,这一组指令的地址,

           并获取其中的操作数

    

    第一个cmp的操作数,一般为函数的pop指令所在块的入口号

    第二个cmp的操作数加一,即为switch表的个数

    

    

    

     

    2、  从tbb/tbh指令开始,已知switch表的地址,表的个数n,计算出tbb指令跳转的case地址,即为真实块的入口地址,找到switchVar和块地址映射关系,tbb指令             表的每一项大小位1字节,tbh每一项大小位2字节

    

    



    3、  从tbb/tbh指令开始,模拟执行所有可能的路径,在路径退出时获取R3的值,,再对应前边找到的switch表的映射关系,就能知道当前路径所要跳转的下一 

           个真实块的地址,以及需要patch的地址

           a)  、执行到pop指令,栈平衡,函数退出指令

           b)  、又回到分支分发块

    


    4、  根据找到的对应关系利用keystone引擎patch,即可实现对控制流平坦化去混淆

    总结:脚本还不完善,实际操作过程中各种情况还是很多的,有很多细节需要手动修复,需要注意的问题,比如:

              .  寻找switch指令特征可能是cmp/bhi/tbb。

              .  一个case链中还存在switch表。

              .  给swtichVar变量赋值,不是直接给定立即数,可能是上个真实块中的某个寄存器,再进行逻辑运算生成的。

              .  一个真实块会根据其中的某些结果,跳转到不同的真实块,通过ITE/MOVELT/MOVEGE给R3赋值。

         等等还是有好多细节,可能无法完全自动化,需要根据各个函数的特征,进行匹配,但控制流平坦化去混淆的思路是一样的。

    参考

    1. https://bbs.pediy.com/thread-252321.htm

    2. https://security.tencent.com/index.php/blog/msg/112

    3. https://github.com/obfuscator-llvm/obfuscator.git

    4. https://rpis.ec/blog/dissection-llvm-obfuscator-p1/



HWS计划·2020安全精英夏令营来了!我们在华为松山湖欧洲小镇等你

最新回复 (7)
雪    币: 4768
活跃值: 活跃值 (2500)
能力值: (RANK:65 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-2-24 14:38
2
0
感谢分享!
雪    币: 1446
活跃值: 活跃值 (22)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Yougar 活跃值 2020-2-24 16:42
3
0
也在学无名侠师傅的思路,楼主加油!
雪    币: 94
活跃值: 活跃值 (43)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
wangzehua 活跃值 2020-2-24 17:56
4
0
可以,很6
雪    币: 3811
活跃值: 活跃值 (149)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
neilwu 活跃值 2020-2-24 21:57
5
0
666
雪    币: 202
活跃值: 活跃值 (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Jecelyin 活跃值 2020-2-25 08:38
6
0
牛b啊
雪    币: 164
活跃值: 活跃值 (73)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
GeneBlue 活跃值 2 2020-2-25 14:59
7
0
看这示例函数名感觉像是 libdexhelper.so 
雪    币: 285
活跃值: 活跃值 (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huuio 活跃值 2020-3-27 11:34
8
0
学习
游客
登录 | 注册 方可回帖
返回