AFL源码分析(一)

本文最后更新于:2023年10月6日 晚上

AFL源码分析(一)

笔者最近学了点fuzz,做了官方提供的示例后,对fuzz的理解还是一知半解的,所以特意阅读了AFL源码,以加深对fuzz的理解。由于源码比较长,笔者分成了几篇文章。

文章参考了众多大佬的文章,还做了不少引用。所以,这篇文章仅作为笔者阅读源码的记录。

afl-gcc.c

main

流程图如下:

image-20231004003133350

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
int main(int argc, char** argv) {

if (isatty(2) && !getenv("AFL_QUIET")) { // 获取环境变量的值

SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");

} else be_quiet = 1;

if (argc < 2) { // 判断参数个数,若个数小于2,打印错误消息并退出

SAYF("\n"
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
"for gcc or clang, letting you recompile third-party code with the required\n"
"runtime instrumentation. A common use pattern would be one of the following:\n\n"

" CC=%s/afl-gcc ./configure\n"
" CXX=%s/afl-g++ ./configure\n\n"

"You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n"
"Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
BIN_PATH, BIN_PATH);

exit(1);

}

find_as(argv[0]); // 获取as的路径

edit_params(argc, argv); // 复制argv到 cc_params[] 数组,并做必要的处理

execvp(cc_params[0], (char**)cc_params); // 执行cc_params[0]

FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);

return 0;

}

为了方便查看cc_params的值,我们追加如下代码:

1
2
3
4
for(int i = 0; i < sizeof(cc_params); ++i)
{
printf("\targ%d: %s\n", i, cc_params[i]);
}

当我们执行afl-gcc /home/p2lst/Documents/fuzz/test1/myafl.c -o /home/p2lst/Documents/fuzz/test1/myafl时,输出如下:

1
2
3
4
5
6
7
8
arg0: gcc
arg1: /home/p2lst/Documents/fuzz/test1/myafl.c
arg2: -o
arg3: /home/p2lst/Documents/fuzz/test1/myafl
arg4: -B
arg5: /home/p2lst/tools/afl-2.57b
arg6: -g
arg7: -O3

可以看到,最终执行的是gcc。

由此可见,afl-gcc仅仅是对gcc进行再封装。

find_as

尝试在AFL_PATH或从argv[0]中找到汇编器as。如果失败,中止。

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
static void find_as(u8* argv0) {

u8 *afl_path = getenv("AFL_PATH"); // 获取环境变量AFL_PATH作为汇编器as的目录
u8 *slash, *tmp;

if (afl_path) { // 判断afl_path是否为空

tmp = alloc_printf("%s/as", afl_path);

if (!access(tmp, X_OK)) { // 判断该路径是否存在汇编器as,若存在,设置as_path的值,并返回
as_path = afl_path;
ck_free(tmp);
return;
}

ck_free(tmp);

}

slash = strrchr(argv0, '/'); // 从argv0中提取as路径,取最后一个'/'前的路径作为dir

if (slash) {

u8 *dir;

*slash = 0;
dir = ck_strdup(argv0);
*slash = '/';

tmp = alloc_printf("%s/afl-as", dir);

if (!access(tmp, X_OK)) { // 判断dir/afl-as是否存在
as_path = dir;
ck_free(tmp);
return;
}

ck_free(tmp);
ck_free(dir);

}

if (!access(AFL_PATH "/as", X_OK)) {
as_path = AFL_PATH;
return;
}

FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH"); // 打印报错信息

}

edit_params

复制argv到 cc_params[] 数组,并做必要的处理。

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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
static void edit_params(u32 argc, char** argv) {

u8 fortify_set = 0, asan_set = 0;
u8 *name;

#if defined(__FreeBSD__) && defined(__x86_64__)
u8 m32_set = 0;
#endif

cc_params = ck_alloc((argc + 128) * sizeof(u8*)); // 为cc_params分配足够的空间

name = strrchr(argv[0], '/'); // 获取最后一个"/"后的编译器名称
if (!name) name = argv[0]; else name++;

if (!strncmp(name, "afl-clang", 9)) { // 判断是否为clang模式

clang_mode = 1; // 设置clang_mode为1

setenv(CLANG_ENV_VAR, "1", 1);

if (!strcmp(name, "afl-clang++")) { // 判断是否为afl-clang++,如果是,获取环境变量AFL_CXX,
// 不为空的话设置为cc_params[0],否则设置为clang++
u8* alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
} else {
u8* alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
}

} else {

/* With GCJ and Eclipse installed, you can actually compile Java! The
instrumentation will work (amazingly). Alas, unhandled exceptions do
not call abort(), so afl-fuzz would need to be modified to equate
non-zero exit codes with crash conditions when working with Java
binaries. Meh. */

#ifdef __APPLE__ // 单独处理apple平台

if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX");
else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ");
else cc_params[0] = getenv("AFL_CC");

if (!cc_params[0]) {

SAYF("\n" cLRD "[-] " cRST
"On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
" 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
" set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n");

FATAL("AFL_CC or AFL_CXX required on MacOS X");

}

#else

if (!strcmp(name, "afl-g++")) { // 判断是否为afl-g++
u8* alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
} else if (!strcmp(name, "afl-gcj")) { // 判断是否为afl-gcj
u8* alt_cc = getenv("AFL_GCJ");
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
} else { // 若都不是,则默认为afl-gcc
u8* alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
}

#endif /* __APPLE__ */

}

while (--argc) { // 循环处理其它参数
u8* cur = *(++argv); // 当前参数的值

if (!strncmp(cur, "-B", 2)) { // 若cur为-B,判断be_quiet是否存在

if (!be_quiet) WARNF("-B is already set, overriding");

if (!cur[2] && argc > 1) { argc--; argv++; }
continue;

}

if (!strcmp(cur, "-integrated-as")) continue; // 若cur为-integrated-as,跳过本次循环

if (!strcmp(cur, "-pipe")) continue; // 若cur为-pipe,跳过本次循环

#if defined(__FreeBSD__) && defined(__x86_64__)
if (!strcmp(cur, "-m32")) m32_set = 1; // 若cur为-m32,则为32位架构。默认为x64
#endif

if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1; // 判断是否开启asan保护

if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; // 判断是否开启fortify保护

cc_params[cc_par_cnt++] = cur;

}

cc_params[cc_par_cnt++] = "-B";
cc_params[cc_par_cnt++] = as_path; // 默认添加-B选项,并指定参数为汇编器as路径

if (clang_mode)
cc_params[cc_par_cnt++] = "-no-integrated-as"; // 追加参数-no-integrated-as

if (getenv("AFL_HARDEN")) { // 若设置环境变量AFL_HARDEN,则追加下述参数

cc_params[cc_par_cnt++] = "-fstack-protector-all";

if (!fortify_set)
cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";

}

if (asan_set) { // 若asan_set为1,则设置环境变量AFL_USE_ASAN=1

/* Pass this on to afl-as to adjust map density. */

setenv("AFL_USE_ASAN", "1", 1);

} else if (getenv("AFL_USE_ASAN")) { // 获取环境变量AFL_USE_ASAN的值

if (getenv("AFL_USE_MSAN"))
FATAL("ASAN and MSAN are mutually exclusive"); // 互斥

if (getenv("AFL_HARDEN"))
FATAL("ASAN and AFL_HARDEN are mutually exclusive"); // 互斥

cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
cc_params[cc_par_cnt++] = "-fsanitize=address";

} else if (getenv("AFL_USE_MSAN")) { // 获取环境变量AFL_USE_MSAN的值

if (getenv("AFL_USE_ASAN"))
FATAL("ASAN and MSAN are mutually exclusive"); // 互斥

if (getenv("AFL_HARDEN"))
FATAL("MSAN and AFL_HARDEN are mutually exclusive"); // 互斥

cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
cc_params[cc_par_cnt++] = "-fsanitize=memory";


}

if (!getenv("AFL_DONT_OPTIMIZE")) { // 获取环境变量AFL_DONT_OPTIMIZE的值

#if defined(__FreeBSD__) && defined(__x86_64__)

/* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself
works OK. This has nothing to do with us, but let's avoid triggering
that bug. */

if (!clang_mode || !m32_set)
cc_params[cc_par_cnt++] = "-g";

#else

cc_params[cc_par_cnt++] = "-g";

#endif

cc_params[cc_par_cnt++] = "-O3"; // 默认开启-O3优化
cc_params[cc_par_cnt++] = "-funroll-loops";

/* Two indicators that you're building for fuzzing; one of them is
AFL-specific, the other is shared with libfuzzer. */

cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";

}

if (getenv("AFL_NO_BUILTIN")) { // 获取环境变量AFL_NO_BUILTIN的值,若为真,则追加以下参数

cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";

}

cc_params[cc_par_cnt] = NULL; // cc_params最后追加NULL,表示参数数组结束

}

参考链接

https://eternalsakura13.com/2020/08/23/afl/

https://hollk.blog.csdn.net/category_11470526.html

https://paper.seebug.org/1732/

https://www.z1r0.top/2023/03/23/AFL-fuzz%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/


AFL源码分析(一)
http://example.com/2023/10/04/AFL源码分析一/
作者
l1s00t
发布于
2023年10月4日
更新于
2023年10月6日
许可协议