感谢无名侠大佬提供的思路
感谢《利用符号执行去除控制流平坦化》提供的思路
代码公开了,各种拼凑,比较龊,轻喷。希望能够有所帮助
在无名侠大佬 发表了《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下并不是所有的真实块都是引用主分发器,一些真实块居然引用了次分发器?
第二次的思路: 其实第一次思路并没有出现错误的识别,而是出现了漏识别,那么我们可以在第一次的思路上进行优化: 从主分发器开始遍历接下来引用的块,记录每一次遍历到的块,如果发现这个块在记录列表里,那么他上次遍历到的块是真实块。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-3-8 23:16
被FraMeQ编辑
,原因: