ollvm bcf fla 学习记录并实战某博

样例: libbxxdxprotect.so

前后对比图

绕过前 F5 长这个样子, 有一部分bcf(bugus control flow 虚假控制流) 的痕迹.

img_1.png

这些变量决定着分支的走向.

点击进入看看变量 dword_C0118 (dword_C0120 同理)
img_1.png

可以看到是引用了bss段上的数据. 我们知道bss段上存放的是未初始化的全局变量和未初始化的局部静态变量. 未初始化的全局变量和未初始化的局部静态变量默认值是0.

按X 看看交叉引用 有没有哪些地方修改过这个变量.

img_1.png

可以看到有 1588 处引用. 但都是读取操作, 并没有 str 等赋值操作.

但由于bss段是可读可写的, 所以ida 并没有帮我们优化掉这个判断, 导致我们F5有 if 存在. 如果我们将这个地址设置为只读. ida 会帮我们进行优化.

要怎么修改呢?

快捷键 SHIFT + F7 查看segment
img_1.png

右键选择 Edit segment, 不要勾选 write
img_1.png

这样整个bss段的属性就被修改了. 但是bss段有有多变量, 会影响到其他变量. 所以并不推荐这样做, 好的做法就是新增一个段. 然后将 dword_C0118, dword_C0120 这两个地址放在新增的段中, 然后设置新增的段的属性为只读. 我们来实操一下
img_1.png

给这个新增的段命名, 并填写start_addr和 end_addr, 经过个人踩坑, 这个区间是 [) 前闭后开的, 也就是start_addr 可以取到 , end_addr 取不到.
img_1.png

新增 my_segment 段后再次 SHIFT + F7 看看所有的段
img_1.png

可以看到我们新增的段 my_segment.

然后鼠标右键 Edit segment, 编辑我们新加的段. my_segment, 只勾选只读.

修改完成后我们直接F5, 看看效果如何?

发现失败了. what?????????????
为什么, 我们用idc patch dword_C0118、dword_C0120 试试看

1
2
idc.patch_dword(0xC0118, 0)
idc.patch_dword(0xC0120, 0)

这里插一嘴. ida 还有两个设置段边界的api, 此处只为记录, 具体如下:

1
2
3
4
# 第一个参数是你要修改的段的地址, 段中的任意一个地址即可, 第二个参数是段边界的start_addr,  第二个参数是段边界的 end_addr, 注意是开区间, 也就是[start_add, end_addr), end_addr是取不到的. 第四个参数目前还不清楚
idc.set_segment_bounds(0xC0148, 0xC0118, 0xC0149, idc.SEGMOD_SILENT)
# 设置段的属性. 第一个参数是某个段其中的任意一个地址, 第二个参数固定, 第三个参数是 4, 2, 1 分别对应 r, w, e
idc.set_segm_attr(0xC0148, idc.SEGATTR_PERM, 4)

再次F5 看看
img_1.png

可以看到,大功告成 这些分支已经被去除了. 静态分析更易读了.

再加深一下概念, 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 这个地址就是你要去混淆的函数的地址.

来看看前后对比图

混淆的代码如下
img_1.png

去混淆后
img_1.png

可以看到效果杠杠的.

我们试试 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 打开看看
img_1.png
可以看到部分分支没有去掉, 我们用老办法, 新增segment 设置只读, 并且 patch dword = 0, 操作后我们再来看看效果

img_1.png
可以看到有一丢丢效果 但不多. 并没有去除掉 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, 还原效果如下
还原前

img_1.png

还原后
img_1.png

感兴趣的可以试试 点击下载

支持arm32, arm64, 但本人用deflat 去某微b fla报错了, 应该是函数中穿插一些其他的函数调用导致的.

接下来试试ida的处理ollvm的插件 obpo-plugin. 看看效果如何.
github地址

装好后, 发现标记分发快后, 直接提示如下
img_1.png

应该是证书导致的, 目前无法使用. 暂时放弃.

试试看另外一个插件 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图
img_1.png

ctrl + shift + D 唤出界面如下:
img_1.png

点击start 等待一会. 然后再次F5 看看效果

img_1.png

尼玛, 一点效果都没有, 好吧 扔回收站了.

等等 别放弃, 貌似刚刚只是选择的 JumpFixer, 也就是对跳转的修复. 这让我想起了某音的花枝零, 或许可以用这个插件修复一下. 后面再进行测试.
我们这里选择 unflatting, while 这么多循环一看就是控制流平坦化
img_1.png

然后点击 start 再次试试F5

img_1.png

双击6666666

我们用 D810 处理一下 某风控 libbxxdxprotect.so 看看.
处理时间会稍稍有点久. 用了大概几分钟, 我们来看看效果

img_1.png

可以看到还是有一定效果的. D810在面对某成熟风控sdk也依然能发挥一定辅助分析的作用. 从反编译的c语言伪代码来看, 起码bcf处理的很好. deflat 也有一定的效果
我想说 D810 yyds

实战

我们再试试另外一个社交类app某博的so文件, 随便找个函数

还原前
img_1.png

还原后
img_1.png

再一次想说 D810 yyyyyyyyyyyyyyyds

总结一下, 目前试了这么多处理 ollvm 的插件, 貌似deflat 和 d810 这两个是比较有效的. 而对于bcf, 可以使用deflat开源项目中的bogus脚本, 也可以手动 patch + 新增segment + 设置只读的方式来去混淆.
后续遇到ollvm 先 deflat 再d810, 否则就手动分析, 再或者通过unicorn unidbg 或者 frida-stalker 动态调试来确定执行流