本文主要针对x86架构下Obfuscator-LLVM的控制流平坦化

OLLVM

llvm是一个底层虚拟机,OLLVMObfuscator-LLVM)是瑞士西北应用科技大学安全实验室于20106月份发起的一个项目,这个项目的目标是提供一个LLVM编译套件的开源分支,能够通过代码混淆和防篡改,增加对逆向工程的难度,提供更高的软件安全性。目前,OLLVM已经支持LLVM-4.0.1版本;所使用的编译器是clang

llvm保护的大概方法是:程序主要使用一个主分发器来控制程序基本块的执行流程进而模糊每个模块之间的联系。

控制流平坦化

控制流平坦化(control flow flattening)的基本思想主要是通过一个主分发器来控制程序基本块的执行流程,例如下图是正常的执行流程

添加控制流平坦化

1
build/bin/clang check_passwd.c -o check_passwd_flat -mllvm -fla

经过控制流平坦化后的执行流程就如下图

这样可以模糊基本块之间的前后关系,增加程序分析的难度,同时这个流程也很像VM的执行流程

平坦化程序特征

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
__int64 __fastcall check(__int64 a1)
{
size_t v1; // rax@13
signed int v2; // ecx@13
char v3; // ST04_1@16
signed int v4; // eax@18
signed int v5; // eax@21
signed int v6; // eax@24
signed int v7; // eax@27
signed int v9; // [sp+44h] [bp-1Ch]@1
int v10; // [sp+48h] [bp-18h]@1
signed int v11; // [sp+5Ch] [bp-4h]@0

srand(0x64u);
v10 = 0;
v9 = 706310565;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( v9 == -2109444161 )
{
v7 = 1322041670;
if ( *(_BYTE *)(a1 + 3) == 100 )
v7 = 867560817;
v9 = v7;
}
if ( v9 != -2069803162 )
break;
++v10;
v9 = 706310565;
}
if ( v9 != 306556692 )
break;
v4 = 1322041670;
if ( *(_BYTE *)a1 == 97 )
v4 = 564228819;
v9 = v4;
}
if ( v9 != 341947172 )
break;
v6 = 1322041670;
if ( *(_BYTE *)(a1 + 2) == 99 )
v6 = -2109444161;
v9 = v6;
}
if ( v9 != 564228819 )
break;
v5 = 1322041670;
if ( *(_BYTE *)(a1 + 1) == 98 )
v5 = 341947172;
v9 = v5;
}
if ( v9 != 682671478 )
break;
v3 = *(_BYTE *)(a1 + v10);
*(_BYTE *)(a1 + v10) = rand() % 5 + v3 - 97 + 97;
v9 = -2069803162;
}
if ( v9 != 706310565 )
break;
v1 = strlen((const char *)a1);
v2 = 306556692;
if ( v10 < v1 )
v2 = 682671478;
v9 = v2;
}
if ( v9 != 867560817 )
break;
v11 = 4;
v9 = 1000770411;
}
if ( v9 == 1000770411 )
break;
if ( v9 == 1322041670 )
{
v11 = 0;
v9 = 1000770411;
}
}
return (unsigned int)v11;
}
  1. 函数的开始地址为序言的地址
  2. 序言的后继为主分发器
  3. 后继为主分发器的块为预处理器
  4. 后继为预处理器的块为真实块
  5. 无后继的块为retn
  6. 剩下的为无用块

去平坦化方法

D810去平坦化脚本使用方法

Edit->plugins-D-810或快捷键CTRL+shift+D启动D810

1:选中你需要的反混淆规则,我是反ollvm所以选ollvm

2:start点击后右边会变成绿色loaded

3:回到需要反混淆的函数,F5大法好

此处Decompiling需要等一会

deflat 去平坦化脚本使用方法

基于SnowGirlsdeflat,利用angr框架实现去除控制流平坦化 https://security.tencent.com/index.php/blog/msg/112

1
2
python + deflat.py + 文件名 + 起始地址(基本就是main函数的地址)
#python deflat.py hardCpp 0x4007E0

关键之处在于寻找程序的address

注意到“函数的开始地址为序言的地址”,因此我们需要在序言中找函数起始位置

期间可能会出现大量类似警告:

没事,只要最后是这样就好:

得到一个已成功去平坦化的recovered新文件,用这个文件继续分析

参考链接

https://pinguw.github.io/2024/03/26/Reverse/LLVM/#/

https://www.52pojie.cn/thread-1872852-1-1.html

https://www.52pojie.cn/thread-1601573-1-1.html

https://security.tencent.com/index.php/blog/msg/112