样例: libbxxdxprotect.so
前后对比图
绕过前 F5 长这个样子, 有一部分bcf(bugus control flow 虚假控制流) 的痕迹.
这些变量决定着分支的走向.
点击进入看看变量 dword_C0118 (dword_C0120 同理)
可以看到是引用了bss段上的数据. 我们知道bss段上存放的是未初始化的全局变量和未初始化的局部静态变量. 未初始化的全局变量和未初始化的局部静态变量默认值是0.
按X 看看交叉引用 有没有哪些地方修改过这个变量.
可以看到有 1588 处引用. 但都是读取操作, 并没有 str 等赋值操作.
但由于bss段是可读可写的, 所以ida 并没有帮我们优化掉这个判断, 导致我们F5有 if 存在. 如果我们将这个地址设置为只读. ida 会帮我们进行优化.
要怎么修改呢?
快捷键 SHIFT + F7 查看segment
右键选择 Edit segment, 不要勾选 write
这样整个bss段的属性就被修改了. 但是bss段有有多变量, 会影响到其他变量. 所以并不推荐这样做, 好的做法就是新增一个段. 然后将 dword_C0118, dword_C0120 这两个地址放在新增的段中, 然后设置新增的段的属性为只读. 我们来实操一下
给这个新增的段命名, 并填写start_addr和 end_addr, 经过个人踩坑, 这个区间是 [) 前闭后开的, 也就是start_addr 可以取到 , end_addr 取不到.
新增 my_segment 段后再次 SHIFT + F7 看看所有的段
可以看到我们新增的段 my_segment.
然后鼠标右键 Edit segment, 编辑我们新加的段. my_segment, 只勾选只读.
修改完成后我们直接F5, 看看效果如何?
发现失败了. what?????????????
为什么, 我们用idc patch dword_C0118、dword_C0120 试试看
1 | idc.patch_dword(0xC0118, 0) |
这里插一嘴. ida 还有两个设置段边界的api, 此处只为记录, 具体如下:
1 | 第一个参数是你要修改的段的地址, 段中的任意一个地址即可, 第二个参数是段边界的start_addr, 第二个参数是段边界的 end_addr, 注意是开区间, 也就是[start_add, end_addr), end_addr是取不到的. 第四个参数目前还不清楚 |
再次F5 看看
可以看到,大功告成 这些分支已经被去除了. 静态分析更易读了.
再加深一下概念, if else 中的判断条件就是所谓的 不透明谓词.
再来个练习, 这是一个32位的so
样本so target_arm_bogus_arm32.so
对于这个样本, 亲测可以通过 设置内存只读+patch_dword 来去除if else 混淆
但这里 我们不使用这种方式, 我们使用一个开源的 debogus的库来实现
代码地址 仓库地址
使用方式很简单.
1 | cd bogus_control_flow && python3 debogus.py -f samples/bin/target_arm_bogus --addr 0x8610 |
0x8610 这个地址就是你要去混淆的函数的地址.
来看看前后对比图
混淆的代码如下
去混淆后
可以看到效果杠杠的.
我们试试 debogus, 对libbxxxxprotect.so 去虚假控制流
1 | python3 debogus.py -f samples/bin/libbaiduprotect.so --addr 0x88060 |
报错, 跑不通, 原因是 target_function.transition_graph 中的 target_function 是NoneType类型.
试试deflat, 对libbxxxxprotect.so 去控制流平坦化
1 | python3 deflat.py -f samples/bin/libbaiduprotect.so --addr 0x88060 |
然后用ida64 打开看看
可以看到部分分支没有去掉, 我们用老办法, 新增segment 设置只读, 并且 patch dword = 0, 操作后我们再来看看效果
可以看到有一丢丢效果 但不多. 并没有去除掉 swith case。
我们再来试试另一个大佬写的 deobf https://github.com/maiyao1988/deobf
试了上面两个 so文件 都报错 AssertionError: block CodeBlock(0x00088060, 0x00000000) size <=0
调试了一下是 cfg.create_cfg 有问题, 应该是没有解析出 cfg
仔细看了文档 貌似还需要trace文件. 故pass
总结 deflat 还是很好用的
代码地址 仓库地址
亲测使用 deflat还原这个so, 还原效果如下
还原前
还原后
感兴趣的可以试试 点击下载
支持arm32, arm64, 但本人用deflat 去某微b fla报错了, 应该是函数中穿插一些其他的函数调用导致的.
接下来试试ida的处理ollvm的插件 obpo-plugin. 看看效果如何.
github地址
装好后, 发现标记分发快后, 直接提示如下
应该是证书导致的, 目前无法使用. 暂时放弃.
试试看另外一个插件 https://gitlab.com/eshard/d810
安装使用过程中可能会遇到 pyqt5的问题, 解决方案参考
https://blog.csdn.net/qq_49283465/article/details/127991431
https://blog.csdn.net/no1xium/article/details/126764041
按照上面的解决完后, 进入python解释器看看是否能否导入PyQt5, 发现是可以的, 但是ida 却报错 from PyQt5 import QtCore, QtWidgets, QtGui 找不到模块
猜测是ida 没有加载 python的模块, 看到ida的其他输出如下
WARNING: Python 3 is not configured (Python3TargetDLL value is not set).
意思是 python 3 没有配置. 解决方案 打开 IDA 的安装路径,在根目录下有一个 idapyswitch.exe 运行一下, 选择你的python版本.
然后可以正常使用了
来看看处理前的 cfg图
ctrl + shift + D 唤出界面如下:
点击start 等待一会. 然后再次F5 看看效果
尼玛, 一点效果都没有, 好吧 扔回收站了.
等等 别放弃, 貌似刚刚只是选择的 JumpFixer, 也就是对跳转的修复. 这让我想起了某音的花枝零, 或许可以用这个插件修复一下. 后面再进行测试.
我们这里选择 unflatting, while 这么多循环一看就是控制流平坦化
然后点击 start 再次试试F5
双击6666666
我们用 D810 处理一下 某风控 libbxxdxprotect.so 看看.
处理时间会稍稍有点久. 用了大概几分钟, 我们来看看效果
可以看到还是有一定效果的. D810在面对某成熟风控sdk也依然能发挥一定辅助分析的作用. 从反编译的c语言伪代码来看, 起码bcf处理的很好. deflat 也有一定的效果
我想说 D810 yyds
实战
我们再试试另外一个社交类app某博的so文件, 随便找个函数
还原前
还原后
再一次想说 D810 yyyyyyyyyyyyyyyds
总结一下, 目前试了这么多处理 ollvm 的插件, 貌似deflat 和 d810 这两个是比较有效的. 而对于bcf, 可以使用deflat开源项目中的bogus脚本, 也可以手动 patch + 新增segment + 设置只读的方式来去混淆.
后续遇到ollvm 先 deflat 再d810, 否则就手动分析, 再或者通过unicorn unidbg 或者 frida-stalker 动态调试来确定执行流