首页
社区
课程
招聘
[原创]ARM64 OLLVM反混淆(续)
发表于: 2019-8-1 19:23 13789

[原创]ARM64 OLLVM反混淆(续)

2019-8-1 19:23
13789

感谢无名侠大佬提供的思路

感谢《利用符号执行去除控制流平坦化》提供的思路

代码公开了,各种拼凑,比较龊,轻喷。希望能够有所帮助

在无名侠大佬 发表了《ARM64 OLLVM反混淆》之后,尝试跑了一下附件的脚本,发现有很大缺陷。的确这篇文章只是提供了大体思路,后续优化的还需要很多。

仔细研究了现有资料,分享分享自己的心得,抛砖引玉。


先简单的总结下反ollvm混淆的思路

1. 获取真实块、序言、retn块和无用块(也就是区别出真实块和垃圾块),通过静态分析提取真实块内在的某些规则

2. 确定真实块、序言和retn块的前后关系,利用符号执行或者Unicorn 模拟执行,记录一个真实块到下一个真实块的关联
3. 修复真实块之间的关联
2. 确定真实块、序言和retn块的前后关系,利用符号执行或者Unicorn 模拟执行,记录一个真实块到下一个真实块的关联

实际在反ollvm遇到最大的难题是 如何得到所有的真实块

遇到的一些问题:
1. 现有的文章ollvm混淆的cfg图和实际遇到的差别很大?





    可以看到这两个生成的cfg图是完全不一样的,深究原因发现是因为在编译Debug时编译器并没有进行优化,而在编译Release的时候编译器优化指令后 "预分发器" 被优化掉了,生成的cfg就不同了,那么采用 《利用符号执行去除控制流平坦化》文章中寻找真实块是不可行的。

2. 采用《ARM64 OLLVM反混淆》文章中的方式寻找到的真实块存在无用块?
    文章中采用寻找真实块的方式是: 如果某个代码块的引用大于1,就可能是分发器,引用该分发器的代码块就可能为真实块。
那么思路其实是先找分发器再找真实块。那么这样就存在一个问题:如果子分发器后面再跟随子分发器,那么子分发器也被识别为了真实块?代码中不得已加入了特征码将这些子分发器移除。仔细研究最后发现这种方式还会漏掉某些真实块。那么这种方式其实也不是特别靠谱

那么还有没有其他方式去寻找真实块?
其实在研究真实块的逻辑关系时,debug版本给了很多可以参考的位置

第一次的思路: 《利用符号执行去除控制流平坦化》文章中寻找真实块是通过预分发器,那么既然预分发器已经被优化掉了,引用主分发器的是否就是真实块?
那么这个思路就是:先找到主分发器,在查看谁引用了主分发器。被引用次数最多的块一定就是主分发器

结果显而是失败的,原因在于在release下并不是所有的真实块都是引用主分发器,一些真实块居然引用了次分发器?

第二次的思路: 其实第一次思路并没有出现错误的识别,而是出现了漏识别,那么我们可以在第一次的思路上进行优化: 从主分发器开始遍历接下来引用的块,记录每一次遍历到的块,如果发现这个块在记录列表里,那么他上次遍历到的块是真实块。

类似二叉树,从上往下找,发现最后的节点的下一个节点是之前遍历到的,那么这个节点就是真实块。
这个思路能够将引用次分发器的真实块寻找到
get_relevant_nodes(supergraph, main_dispatcher_node, [])

def get_relevant_nodes(supergraph, node, founded_node):
    global relevant_nodes
    branch_nodes = list(supergraph.successors(node))

    founded_node.append(node)      
    for i in branch_nodes:
        if i not in founded_node:
            get_relevant_nodes(supergraph, i, founded_node)

大部分的真实块已经被识别到了,但是还是存在没有找到真实块,原因在于被release优化的部分存在一个逻辑关系,真实块的前继块有可能还是真实块,举一个例子



这三个都是真实块,原因在于release将两个puts优化为了一个puts。

第三次的思路:这一次只需要找到真实块前面的真实块,规则是真实块的前继块的后继如果只有一个,那么则也是真实块。有点绕,看代码
def get_relevant_nodes(supergraph, node, founded_node):
    global relevant_nodes
    branch_nodes = list(supergraph.successors(node))

    if len(branch_nodes) == 1 and branch_nodes[0] in founded_node:
        if node in relevant_nodes:
            for i in supergraph.predecessors(node):
                relevant_nodes.append(i)
        else:
            relevant_nodes.append(node)
    else:
        founded_node.append(node)      
        for i in branch_nodes:
            if i not in founded_node:
                get_relevant_nodes(supergraph, i, founded_node)

通过这三次优化 已经能够将使用 -fla 参数 被ollvm混淆的的真实块识别出来了,并且已经修复(这个是拿真实的样本测试的)

仍未解决的点:
1. 在使用 -bcf (虚假控制流) 参数之后 有一些块是死循环,而本来一个真实块被死循环打乱成为了两个真实块,例如


这个时候真实块1没有被识别出来,需要更进一步的优化。

这个没太深入研究了,可以直接将真实块1 手动的加入到真实块列表中

2. 在修复真实块的条件跳转逻辑时,有时候条件完全是相反的,这个后续还需要优化

参考



    可以看到这两个生成的cfg图是完全不一样的,深究原因发现是因为在编译Debug时编译器并没有进行优化,而在编译Release的时候编译器优化指令后 "预分发器" 被优化掉了,生成的cfg就不同了,那么采用 《利用符号执行去除控制流平坦化》文章中寻找真实块是不可行的。

2. 采用《ARM64 OLLVM反混淆》文章中的方式寻找到的真实块存在无用块?
    文章中采用寻找真实块的方式是: 如果某个代码块的引用大于1,就可能是分发器,引用该分发器的代码块就可能为真实块。
那么思路其实是先找分发器再找真实块。那么这样就存在一个问题:如果子分发器后面再跟随子分发器,那么子分发器也被识别为了真实块?代码中不得已加入了特征码将这些子分发器移除。仔细研究最后发现这种方式还会漏掉某些真实块。那么这种方式其实也不是特别靠谱

那么还有没有其他方式去寻找真实块?
其实在研究真实块的逻辑关系时,debug版本给了很多可以参考的位置

第一次的思路: 《利用符号执行去除控制流平坦化》文章中寻找真实块是通过预分发器,那么既然预分发器已经被优化掉了,引用主分发器的是否就是真实块?
那么这个思路就是:先找到主分发器,在查看谁引用了主分发器。被引用次数最多的块一定就是主分发器

结果显而是失败的,原因在于在release下并不是所有的真实块都是引用主分发器,一些真实块居然引用了次分发器?

第二次的思路: 其实第一次思路并没有出现错误的识别,而是出现了漏识别,那么我们可以在第一次的思路上进行优化: 从主分发器开始遍历接下来引用的块,记录每一次遍历到的块,如果发现这个块在记录列表里,那么他上次遍历到的块是真实块。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-3-8 23:16 被FraMeQ编辑 ,原因:
收藏
免费 10
支持
分享
打赏 + 2.00雪花
打赏次数 1 雪花 + 2.00
 
赞赏  junkboy   +2.00 2019/08/01 感谢分享~
最新回复 (17)
雪    币: 1553
活跃值: (492)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
2
2019-8-2 11:40
0
雪    币: 175
活跃值: (201)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
先顶一个, 
2019-8-2 15:23
0
雪    币: 8427
活跃值: (5021)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
4
大佬牛逼!无名侠牛逼!
2019-8-2 19:39
0
雪    币: 1597
活跃值: (719)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
那种死循环块正常不会进入的,模拟执行的时候应该也不影响。
2019-8-4 13:11
0
雪    币: 6911
活跃值: (9059)
能力值: ( LV17,RANK:797 )
在线值:
发帖
回帖
粉丝
6
xia0 那种死循环块正常不会进入的,模拟执行的时候应该也不影响。
会受到影响,但是死循环很好检测,剔除就行
2019-8-4 16:20
0
雪    币: 1597
活跃值: (719)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
无名侠 会受到影响,但是死循环很好检测,剔除就行
没注意看,以为是ret块中的。这种特征明显的,剔除就行或者执行的时候判断下不进行真值处理应该也行。
2019-8-4 16:41
0
雪    币: 297
活跃值: (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
牛逼啊
2019-8-5 01:39
0
雪    币: 2335
活跃值: (1319)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
9
无名侠 会受到影响,但是死循环很好检测,剔除就行
主要是死循环把一个真实块分割成了两个真实块
2019-8-5 09:33
0
雪    币: 6911
活跃值: (9059)
能力值: ( LV17,RANK:797 )
在线值:
发帖
回帖
粉丝
10
FraMeQ 主要是死循环把一个真实块分割成了两个真实块
哦哦,可以改进一下算法,希望得到样本
2019-8-5 09:46
0
雪    币: 2335
活跃值: (1319)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
11
这个问题你那个样本就存在,我测试arm64的时候使用的是你的样本,最后截图就是vdog样本

最后于 2019-8-5 10:31 被FraMeQ编辑 ,原因:
2019-8-5 10:31
0
雪    币: 5482
活跃值: (3272)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
12
支持 学习下
2019-8-5 15:21
0
雪    币: 22
活跃值: (94)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
mark
2019-8-5 16:24
0
雪    币: 63
活跃值: (324)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
mark
2019-8-6 10:26
0
雪    币: 48
活跃值: (3414)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
支持一下
2019-8-27 20:52
0
雪    币: 5235
活跃值: (3260)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
16
膜拜大佬 正在入门ollvm 学习了
2020-3-18 15:49
0
雪    币: 159
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
如果真实块里本来就有if语句怎么办呢?
2021-2-27 00:01
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
大佬有这几个测试用例的源码吗- -求分享求交流
2021-3-18 16:01
0
游客
登录 | 注册 方可回帖
返回
//