首页
社区
课程
招聘
[原创]使用Binary Ninja去除ollvm流程平坦混淆
发表于: 2019-12-8 17:49 21351

[原创]使用Binary Ninja去除ollvm流程平坦混淆

2019-12-8 17:49
21351

流程平坦化混淆是ollvm里面最难还原,最影响分析效率的一个混淆pass,最近花了点时间,终于把它搞掉了.
刚开始是打算使用IDA的微码进行反混淆,搜到了Rolf Rolles大佬的HexRaysDeob,
还有它的Python版本PyHexRaysDeob.
研究了下发现微码API确实是太难用了,而且很多分析好像都得自己做,所以我暂时放弃该方案.
随后转到了binja, 发现这篇文章Dissecting LLVM Obfuscator Part 1,貌似简单可行,修改了下,用无名侠大佬基于Unicorn 的ARM64 OLLVM反混淆帖子的样本跑,失败了.
看来还是得自己干.

下文中反混淆用的样本是无名侠的libvdog.so(vdog)和自己编译的另外一个小程序(cff).

反混淆主要分两步:分析和修复.

这里思路源于llvm-defobfuscator,熟悉它的话可以直接跳到后面修复内容.

流程平坦混淆代码结构:

反混淆需要的信息主要有:

这些信息通过binja还是很容易分析出来的.
vdog中的代码:

我们可以看到,最后一条指令是状态变量与0x76579ace进行比较,相等的情况下走0x70688(假分支),这个地址就是case 0x76579aced对应代码区域的起始地址.

对于这个测试, 0xdcf244e1对应的case 入口是0x71080(真分支)

用这个方式来获取case代码区域的起始地址是比较可靠的.
因为llvm在给switch生成代码时,据我观察,在无法使用跳转表的情况下,会转到使用二分查找实现,查找到最后一般都会有这么一条测试case编号的指令.

找到case出口也比较简单:包含对状态变量进行更新基本块的出口就是case出口.

实际上,现实代码中会存在有多个出口的情况,这个问题在修复的时候再讨论.

如果是条件跳转,我们还需要知道跳转目标与测试条件的对应关系.

case 0x91c5439 对应的条件可以通过查看入口边的类型得到

到这反混淆所需信息就完全获取到了.

以上我主要说明了思路, 省略了很多细节.

例如cff中下面这种case

这个基本块既是case入口,又是出口,还是分发器代码的一部分.

vdog JNI_OnLoad里面的一个case:

313处, 在分发器里面,状态变量(x9.w9)跟另外一个变量比较(x8_22.w8), 这时候x8_22.w8的值是多少?
上面两个问题我在代码里面都有处理,有兴趣可以参考代码.
即使没有处理也没有关系,我反混淆的修复是保守的,对于无法处理的情况,仍然会走原来逻辑,只是最终反混淆效果会差一点.

下面开始修复部分内容,修复难度在我看来要比分析难. 修复cff这个看起来简单的样本要比vdog难.

代码修复主要有两个难点:

修复时,在代码中寻找用来patch代码空洞还是比较困难的.
之前考虑使用IDA微码进行反混淆,这是最主要的原因.
llvm-deobfuscator是把混淆框架代码作为空洞使用.
而我反混淆目标是为了在IDA中, 使用他的反编译器进行静态分析, 不需要创建一个新的可执行的二进制, 所有我是用IDA创建一个新的代码段来存放patch代码.
这种处理方法能让我保留原来的分发器,有意的保留分发器, 可以在未识别到真实后继基本块或者case出入口的情况下,走原来分发器流程.
如果所有状态的后继都被我处理了, 分发器就变成不可达代码, IDA的反编译器会自动把它优化掉.

目前看到的反混淆文章好像都没有提到如何处理这个问题的.
如下函数:

有可能会被编译器优化成:

我解决的办法是通过代码拷贝,把foo2转换成foo1.具体做法是

问题2, 属于分析阶段的问题, 我现在还没有处理.
当前反混淆方案会在进入case 1之后, 由于state 2对应case 2的入口没有识别到, 控制流会重新进入分发器, 从分发器进入case 2.

现实代码中,还会出现下面的情况

case有多个出口情况,伪码:

为了处理这种代码, 我们在修复的时候, 需要找到case的所有出口,在每个出口处都进行patch.

综上,我们需要进行如下修复操作:

修复后的代码有如下两种布局.

下面是两个具体的修复实例.

cff样本的分发器位于0x00000768
分发器入口代码

这个块内的W0是函数返回值,不可以删除这条指令.
从函数入口到真实后继的修复代码:

可以看到,修复代码复制了分发器到case入口路径上的所有指令.

再来看下vdog中起始地址为0x000704dc的基本块

从函数入口到真实后继的修复代码:

0x000704dc基本块另外一个前驱

修复后:

当前实现拷贝了大量的垃圾指令,有时候函数过大会导致IDA无法反编译;同时也会增加IDA分析用时.修复vdog JNI_OnLoad大概用了21K字节代码,还原用时15分.

拷贝pc相关指令,如函数调用,全局变量引用,需要进行重定位修复.

vdog的JNI_OnLoad里面有6个分发器,不知道是改了ollvm还是inline进来的.

附件包含所有的代码和样本.
所有代码和样本也可从github下载: ollvm-breaker
另外, 反混淆脚本是在binja GUI-less模式下运行, 需要Binary Ninja商业版.

最后,放效果.

在IDA中打开cff或vdog后,运行对应的fix-xxx.py脚本反混淆.
我修复了vdog五个函数,JNI_OnLoad,crazy::GetPackageName,prevent_attach_one,attach_thread_scn,crazy::CheckDex.在IDA中运行修复脚本,重新分析分析程序后,可以查看这些函数的反混淆效果.

先看cff的原始代码

还原后

vdog的JNI_OnLoad还原后

 
 
 
 
 
 
 
 
 
 
 
 
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 11
支持
分享
最新回复 (21)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
哇塞,精华帖,终于占了个沙发啊。
2019-12-8 18:06
0
雪    币: 1705
活跃值: (676)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
二楼
2019-12-8 18:14
0
雪    币: 916
活跃值: (3434)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
4
羡慕又有IDA又有Binja的大佬。
最后于 2019-12-9 14:10 被葫芦娃编辑 ,原因:
2019-12-8 20:21
0
雪    币: 2335
活跃值: (1314)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
主要是得有钱,划重点IDA7.2或者Ninja商业版
2019-12-9 09:55
0
雪    币: 208
活跃值: (268)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
没得7.2啊
2019-12-9 11:43
0
雪    币: 3836
活跃值: (4218)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
7
SANCDAYE 没得7.2啊
修复是直接patch二进制, 没有使用IDA微码, IDA 7.0就可以
2019-12-9 12:46
0
雪    币: 2
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
mark
2019-12-9 20:03
0
雪    币: 1841
活跃值: (1265)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
9
占楼
2019-12-10 02:14
0
雪    币: 2
活跃值: (105)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
10
学习,那大佬有考虑用IDA的微码再实现下么,想知道难点在哪里,想重新实现下,个人目前还买不起binja 穷哭
2019-12-10 10:37
0
雪    币: 285
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11

萌新求问 用ida64 7.0加载cff-arm64-v8a.elf后执行fix-cff-arm64-v8a.py后 代码就变成这样了

void sub_4000000()
{
  JUMPOUT(&loc_7D0);
}

是哪里做错了么

2019-12-10 14:26
0
雪    币: 3836
活跃值: (4218)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
12
mmmmman 学习,那大佬有考虑用IDA的微码再实现下么,想知道难点在哪里,想重新实现下,个人目前还买不起binja 穷哭
微码的话你可以去研究Rolf Rolles大佬的HexRaysDeob.binja个人版也是可以的,只是写脚本的时候没有GUI-less调试来得方便.
2019-12-10 19:58
1
雪    币: 3836
活跃值: (4218)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
13
pancts 萌新求问 用ida64 7.0加载cff-arm64-v8a.elf后执行fix-cff-arm64-v8a.py后 代码就变成这样了 ``` void sub_4000000() { J ...

没有错.你在函数sub_4000000上按u取消函数定义,再按c把他重新转成代码,在IDA状态栏,也就是可以输入python代码下面那栏右键,选重新分析.
图片描述

2019-12-10 20:06
1
雪    币: 2
活跃值: (105)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
14
个人版也要一千元左右,,还是滚去学HexRaysDeob。。
2019-12-10 21:16
0
雪    币: 1705
活跃值: (676)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
老板哪里有c和c++的文法,就是能将程序解析成语法树的,看了这么多,都没找到。还是说现在根本就没有啊
2019-12-11 04:14
0
雪    币: 422
活跃值: (2729)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
ollvm
2019-12-26 13:02
0
雪    币: 35
活跃值: (88)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
弱冠甕卿还仓 老板哪里有c和c++的文法,就是能将程序解析成语法树的,看了这么多,都没找到。还是说现在根本就没有啊
antlr4提供了C和C++14的g4,配合antlr4可以直接解析得到ast
2019-12-26 15:02
0
雪    币: 5235
活跃值: (3260)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
19
看了半天没看明白心情很失落,看到评论发现自己没有IDA7.2或者Ninja商业版,更难受了。。。。
2020-3-6 17:04
0
雪    币: 83
活跃值: (1087)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
20
感谢分享呵呵
2020-3-6 17:14
0
雪    币: 2536
活跃值: (4368)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
21
楼主,ida和ninja是如何联合使用的,为什么ida跑的脚本能够到ninja去
2023-7-11 11:36
0
雪    币: 2787
活跃值: (30801)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
感谢分享
2023-7-11 13:41
1
游客
登录 | 注册 方可回帖
返回
//