反逆向分析

First Post:

Last Update:

Word Count:
1.4k

Read Time:
6 min

反逆向分析

Ollvm

0x01 Ollvm介绍

OLLVM(Obfuscator-LLVM)是瑞士西北应用科技大学安全实验室于2010年6月份发起的一个项目,该项目旨在提供一套开源的针对LLVM的代码混淆工具,以增加逆向工程的难度, 只不过Ollvm仅更新到llvm的4.0,2017年开始就没再更新。

0x02 Ollvm混淆介绍

Ollvm混淆主要分成三种模式,这三种模式主要是流程平坦化,指令替换,以及控制流伪造。

流程平坦化 :这个模式主要通过将if-else语句替换成do-while语句,然后通过switch语句来对流程的控制,这样就能模糊基本块之间的前后关系。

指令替换 :这个模式主要通过使用更复杂的指令序列来替换一些标准的二元运算符,从而增加逆向的难度。

控制流伪造 :这个模式主要是会在一个简单的运算中外包好几层if-else的判断,从而增加逆向的难度。

对Ollvm有更深入的理解,可参阅论文《Obfuscating C++ programs via control flow flattening》

0x03 Ollvm编译

env: arch-linux

使用apt源安装g++和cmake,从github获取ollvm的源码,并进行编译。

Ollvm 6.0: https://github.com/yazhiwang/ollvm-tll.git (编译成功!)

原版src: https://github.com/obfuscator-llvm/obfuscator/tree/llvm-4.0 (编译失败!)

ollvm-til编译

1
2
3
4
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ../ollvm-tll-master
make -j8

官方编译

1
2
3
4
cd 
mkdir build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF ../
make -j8

0x4 命令介绍

控制流扁平化

这个模式主要是把一些if-else语句,嵌套成do-while语句

-mllvm -fla:激活控制流扁平化

-mllvm -split:激活基本块分割。在一起使用时改善展平。

-mllvm -split_num=3:如果激活了传递,则在每个基本块上应用3次。默认值:1

指令替换

这个模式主要用功能上等效但更复杂的指令序列替换标准二元运算符(+ , – , & , | 和 ^)

-mllvm -sub:激活指令替换

-mllvm -sub_loop=3:如果激活了传递,则在函数上应用3次。默认值:1

虚假控制流程

这个模式主要嵌套几层判断逻辑,一个简单的运算都会在外面包几层if-else,所以这个模式加上编译速度会慢很多因为要做几层假的逻辑包裹真正有用的代码。

-mllvm -bcf:激活虚假控制流程
-mllvm -bcf_loop=3:如果激活了传递,则在函数上应用3次。默认值:1
-mllvm -bcf_prob=40:如果激活了传递,基本块将以40%的概率进行模糊处理。默认值:30

其他

-mllvm -sobf 开启字符串混淆

-mllvm -seed=0xdeadbeaf 指定随机数种子生成器 bcf可以配合下面参数使用

例子

对a.c进行混淆

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
void exec() {
printf("OOOKKKK!");
}
int main(void) {
int a = 0;
a ++;
printf("Hello World");
if(a >= 1) {
exec();
}
return 0;
}
1
~/tools/ollvm/bin/clang a.c -mllvm -fla  -mllvm -sobf  -mllvm -bcf  -o a

ida64打开F5

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
int __cdecl main(int argc, const char **argv, const char **envp)
{
signed int v3; // eax
__int64 *v4; // rcx
int v5; // eax
signed int v6; // edx
signed int v7; // eax
signed int v8; // eax
signed int v9; // eax
__int64 *v11; // rcx
int v12; // eax
__int64 v13; // [rsp+0h] [rbp-50h]
int v14; // [rsp+8h] [rbp-48h]
int v15; // [rsp+Ch] [rbp-44h]
__int64 *v16; // [rsp+10h] [rbp-40h]
int v17; // [rsp+18h] [rbp-38h]
int v18; // [rsp+1Ch] [rbp-34h]
int v19; // [rsp+20h] [rbp-30h]
int v20; // [rsp+24h] [rbp-2Ch]
int v21; // [rsp+28h] [rbp-28h]
int v22; // [rsp+2Ch] [rbp-24h]
int v23; // [rsp+30h] [rbp-20h]
int v24; // [rsp+34h] [rbp-1Ch]
int v25; // [rsp+38h] [rbp-18h]
int v26; // [rsp+3Ch] [rbp-14h]
int v27; // [rsp+40h] [rbp-10h]
bool v28; // [rsp+45h] [rbp-Bh]
bool v29; // [rsp+46h] [rbp-Ah]
bool v30; // [rsp+47h] [rbp-9h]

v28 = (((_BYTE)x_2 - 1) * (_BYTE)x_2 & 1) == 0;
v29 = y_3 < 10;
v27 = 1567572003;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v26 = v27;
v25 = v27 + 1130483098;
if ( v27 != -1130483098 )
break;
v9 = -1065940243;
if ( y_3 < 10 || (((_BYTE)x_2 - 1) * (_BYTE)x_2 & 1) == 0 )
v9 = -714366873;
v27 = v9;
}
v24 = v26 + 1065940243;
if ( v26 != -1065940243 )
break;
v27 = -1130483098;
}
v23 = v26 + 714366873;
if ( v26 == -714366873 )
break;
v22 = v26 + 511865542;
if ( v26 == -511865542 )
{
v8 = -1065940243;
if ( y_3 < 10 || (((_BYTE)x_2 - 1) * (_BYTE)x_2 & 1) == 0 )
v8 = -1130483098;
v27 = v8;
}
else
{
v21 = v26 + 329192939;
if ( v26 == -329192939 )
{
v11 = &v13 - 2;
*((_DWORD *)&v13 - 4) = 0;
*(_DWORD *)v11 = 0;
*(_DWORD *)v11 = *((_DWORD *)&v13 - 4) + 1;
v12 = printf(&byte_404030);
v27 = 191033970;
v14 = v12;
}
else
{
v20 = v26 - 191033970;
if ( v26 == 191033970 )
{
v4 = &v13 - 2;
*((_DWORD *)&v13 - 4) = 0;
*(_DWORD *)v4 = 0;
*(_DWORD *)v4 = *((_DWORD *)&v13 - 4) + 1;
v16 = &v13 - 2;
v5 = printf(&byte_404030);
v6 = -329192939;
v30 = *(_DWORD *)v16 >= 1;
if ( y_3 < 10 || (((_BYTE)x_2 - 1) * (_BYTE)x_2 & 1) == 0 )
v6 = 1347608926;
v27 = v6;
v15 = v5;
}
else
{
v19 = v26 - 1347608926;
if ( v26 == 1347608926 )
{
v7 = -511865542;
if ( v30 )
v7 = 1700479422;
v27 = v7;
}
else
{
v18 = v26 - 1567572003;
if ( v26 == 1567572003 )
{
v3 = -329192939;
if ( v29 || v28 )
v3 = 191033970;
v27 = v3;
}
else
{
v17 = v26 - 1700479422;
if ( v26 == 1700479422 )
{
exec();
v27 = -511865542;
}
}
}
}
}
}
}
return 0;
}

以上成功实现代码混淆

参考

Obfuscating C++ programs via control flow flattening

去除符号表

查看符号表

readelf -s test

采用strip工具对符号表进行去除

.symtab因为是在调试和链接时有用的,所以,可以从生产的二进制执行文件中移除。可以使用strip。strip是可以移除符号以及节的工具。

  1. 移除.symtab 符号表 以及 .strtab 符号字符串表
    直接运行

    • strip --remove-section=.symtab file_in
    • strip --remove-section=.strtab file_in
  2. 想要让可执行文件没有.dynsym动态链接表,.dynstr动态链接字符表。
    gcc编译时添加参数,从而使用静态编译。gcc -static 或者 gcc -nostdlib

    • gcc -nostdlib :不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。
    • gcc -static:在支持动态链接的系统上,阻止连接共享库。该选项在其它系统上 无效。
打赏点小钱
支付宝 | Alipay
微信 | WeChat