首页
社区
课程
招聘
[分享]ollvm反混淆学习
发表于: 2021-9-22 00:10 37274

[分享]ollvm反混淆学习

2021-9-22 00:10
37274

看了@无名侠大佬发的一篇关于使用unicorn模拟执行还原ollvm的贴子受到了很大的启发, 自己也基于这个思路做了些样本学习,下面来探讨以下。

ollvm原理

Ollvm大致可分为 bcf(虚假块), fla(控制流展开), sub(指令膨胀), Split(基本块分割)
bcf:
克隆一个真实块,并随机替换其中的一些指令,然后用一个永远为真的条件建立一个分支。克隆后的块是不会被执行的。
Fla:
将所有的真实块使用一个switch case结构包裹起来,每个真实块执行完毕后都会重新赋值switch var,对于有分支的块会使用select指令,并跳转到switch起始代码块(分发器)上,根据switch var来执行下一个真实块。
Sub:
指令膨胀,将一条运算指令,替换为多条等价的运算指令。
Split:
利用随机数产生分割点,将一个基本块分割为两个,并使用绝对跳转连接起来。
关于ollvm具体的实现,可参考源码。

还原思路:

网上有很多还原ollvm的脚本,但是只能还原特征很明显的ollvm,或者说只是debug版的ollvm。在debug版中ollvm的特征非常明显,一个分发器,和引用了这个分发器的真实块。但经过编译器优化后,分发器可能会变成多个,基本块会合并造成虚假块也可能会和真实块合并,等等。
现实情况是,你基本上碰不到简单的ollvm,所以那些东西个人感觉意义不是很大,还是需要靠自己。
谈下还原思路
Bcf:
Bcf块是执行不到的块,所以说当使用unicorn 跑过一遍函数后,其中没有执行到的块肯定有包括bcf块,我们只需要将它挑出来标记下就好。
但函数中可能存在分支,只跑一遍函数是无法覆盖到所有分支的,所以要想办法找到函数的所有分支。一开始采用的是无名侠大佬的方法,当碰到csel指令时人工干预让其覆盖所有分支,但整个函数经常陷入死循环,分析过后发现虚假块的跳转也有可能使用csel指令。后来想到了在二进制漏洞挖掘中的思路fuzz(模糊测试),即变异函数的参数传递给函数,来覆盖更多的分支。这样做也不能说能够找到函数的所有分支。影响一个函数的分支执行大概有三种情况,
参数,全局变量,内部函数调用的返回值。后两种情况的话留意下模糊执行的trace应该能找到些蛛丝马迹,可能会比较麻烦。

 

Fla
这个环节会产生控制流块,我们只需要将这些块挑出来标记,找出所有的真实块,并通过模拟执行还原真实块之间的关系就好。
控制流块的剔除采用了无名侠大佬对基本块签名的方法。

 

Sub:
指令膨胀的还原,使用llvm的pass优化效果还可以,但目前一些ir翻译工具对arm64的支持不怎么样。
Split:
基本块分割更多是用来增加bcf和fla效果的。
总结整体思路:
1 利用模拟执行和fuzz技术,找出bcf块并剔除。
2 使用基本块签名剔除控制流块。
3 将剩余的块标记为真实块,并使用模拟执行找出对应关系。
4 根据对应关系,重构cfg。

实战:

自己编译的一个样本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void HexDump(char *buf,int len,int addr)
__attribute((__annotate__(("split"))))
__attribute((__annotate__(("fla"))))
__attribute((__annotate__(("bcf"))))
{
    int i,j,k;
    char binstr[80];
 
    for (i=0;i<len;i++) {
        if (0==(i%16)) {
            sprintf(binstr,"%08x -",i+addr);
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
        } else if (15==(i%16)) {
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
            sprintf(binstr,"%s  ",binstr);
            for (j=i-15;j<=i;j++) {
                sprintf(binstr,"%s%c",binstr,('!'='~')?buf[j]:'.');
            }
            printf("%s\n",binstr);
        } else {
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
        }
    }
    if (0!=(i%16)) {
        k=16-(i%16);
        for (j=0;j++) {
            sprintf(binstr,"%s   ",binstr);
        }
        sprintf(binstr,"%s  ",binstr);
        k=16-k;
        for (j=i-k;j++) {
            sprintf(binstr,"%s%c",binstr,('!'='~')?buf[j]:'.');
        }
        printf("%s\n",binstr);
    }
}

[注意]看雪招聘,专注安全领域的专业人才平台!

收藏
免费 18
支持
分享
最新回复 (10)
雪    币: 6
活跃值: (1242)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持
2021-9-23 09:00
0
雪    币: 142
活跃值: (10282)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
3
好文章,咋没人顶....
2021-9-23 09:03
1
雪    币: 154
活跃值: (1225)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
哈哈
2021-9-23 10:02
1
雪    币: 18
活跃值: (1026)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
大佬康康代码
2021-9-23 19:06
0
雪    币: 208
活跃值: (1903)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
好文章!
2021-9-24 14:25
0
雪    币: 134
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
大佬啊
2021-9-24 15:52
0
雪    币: 45
活跃值: (234)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
mark
2021-12-19 10:07
0
雪    币: 1931
活跃值: (2223)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
支持一下
2021-12-19 19:55
0
雪    币: 7
活跃值: (496)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10

全部下断点,动态trace几遍

最后于 2022-6-7 20:09 被霸业编辑 ,原因:
2022-6-7 20:08
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
fuzz执行 后为啥不能去除FLA呢? block 签名是如何去除fla呢?这个原理没搞明白
2024-10-1 15:56
0
游客
登录 | 注册 方可回帖
返回