AFL源码分析之afl-gcc.c

AFL 源码初探,分析的是 afl-gcc.c

初号机师傅博客里写道:afl-gcc 是 gcc 的一个封装(wrapper),能够实现对于一些关键节点进行插桩,从而记录一些程序执行路径之类的信息,方便对程序的一些运行情况进行反馈。

分析完整个源码后感觉,afl-gcc 更多的是做了一些检查,并且对参数(路径)进行了整理,并没有执行关键步骤,比如插桩(只是个人猜测)

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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/*
Copyright 2013 Google LLC All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
american fuzzy lop - wrapper for GCC and clang
----------------------------------------------

Written and maintained by Michal Zalewski <lcamtuf@google.com>

This program is a drop-in replacement for GCC or clang. The most common way
of using it is to pass the path to afl-gcc or afl-clang via CC when invoking
./configure.

(Of course, use CXX and point it to afl-g++ / afl-clang++ for C++ code.)

The wrapper needs to know the path to afl-as (renamed to 'as'). The default
is /usr/local/lib/afl/. A convenient way to specify alternative directories
would be to set AFL_PATH.

If AFL_HARDEN is set, the wrapper will compile the target app with various
hardening options that may help detect memory management issues more
reliably. You can also specify AFL_USE_ASAN to enable ASAN.

If you want to call a non-default compiler as a next step of the chain,
specify its location via AFL_CC or AFL_CXX.

*/

#define AFL_MAIN

#include "config.h"
#include "types.h"
#include "debug.h"
#include "alloc-inl.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

static u8* as_path; /* Path to the AFL 'as' wrapper */
static u8** cc_params; /* Parameters passed to the real CC */
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
static u8 be_quiet, /* Quiet mode */
clang_mode; /* Invoked as afl-clang*? */


/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
from argv[0]. If that fails, abort. */
//find_as函数通过argv[0](也就是当前文件的路径)寻找对应的汇编器as(linux中常用的一款汇编器,负责把汇编代码翻译成二进制)
static void find_as(u8* argv0) {

u8 *afl_path = getenv("AFL_PATH");//获取环境中 AFL_PATH 变量
u8 *slash, *tmp;
//如果 AFL_PATH 变量获取成功
if (afl_path) {

tmp = alloc_printf("%s/as", afl_path);//利用alloc_printf函数动态分配一段空间来存储对应路径

if (!access(tmp, X_OK)) { //检查路径是否能访问
as_path = afl_path;//如果可以就将afl_path路径赋给as_path
ck_free(tmp);//释放掉alloc_printf函数分配的内存
return;
}

ck_free(tmp);//路径不能访问就直接释放内存

}
//如果 AFL_PATH 变量获取失败
slash = strrchr(argv0, '/'); //匹配argv[0],提取当前路径dir
//说的通俗点就是会检查路径(如%HOME/AFL/afl-gcc)中是否存在'/',如果存在就取最后'/'前面的字符串作为dir
if (slash) {//如果获取到当前路径的dir

u8 *dir;

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

tmp = alloc_printf("%s/afl-as", dir);//alloc_printf为dir开辟空间存放路径

if (!access(tmp, X_OK)) {//检测路径{dir}/afl是否可访问
as_path = dir;//如果可以就将dir路径赋给as_path
ck_free(tmp);//释放alloc_printf函数分配的内存
return;
}

ck_free(tmp);//路径不能访问就直接释放内存
ck_free(dir);

}
//如果前两种都没有成功则直接找as
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");

}


/* Copy argv to cc_params, making the necessary edits. */
//该函数主要是用来设置 CC 参数
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分配空间,cc_params是保存编译参数的数组
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
//获取最右边(后面)第一次出现'/'的位置并赋值给name
//strchr是获取最左边
name = strrchr(argv[0], '/');
if (!name) name = argv[0]; else name++;
//如果是afl-clang开头
if (!strncmp(name, "afl-clang", 9)) {
//将clang_mode赋值为1,之后判断是 afl-clang++ 还是 afl-clang
clang_mode = 1;

setenv(CLANG_ENV_VAR, "1", 1);
//如果是afl-clang++
if (!strcmp(name, "afl-clang++")) {
//获取环境变量 AFL_CXX
u8* alt_cxx = getenv("AFL_CXX");
//获取到了就赋值,没获取到就直接赋字符串'clang++'
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
} else {//如果不是afl-clang++,即 afl-clang
//获取环境变量 AFL_CC
u8* alt_cc = getenv("AFL_CC");
//获取到了就赋值,没获取到就直接赋字符串'clang'
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. */
//如果不是afl_clang开头,并且是Apple平台,就会进入这个分支
#ifdef __APPLE__
//是否是afl-g++,是则获取变量并赋值
if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX");
//是否是afl-gcj,获取变量并赋值
else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ");
//如果前两种都不满足,则直接获取AFL_CC变量给cc_params[0]
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//不是Apple平台
//判断是不是afl-g++,是就赋值,同141行,就不多说了
if (!strcmp(name, "afl-g++")) {
u8* alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
} else if (!strcmp(name, "afl-gcj")) {
u8* alt_cc = getenv("AFL_GCJ");
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
} else {
u8* alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
}

#endif /* __APPLE__ */

}
//进入while循环,遍历参数
while (--argc) {
u8* cur = *(++argv);//扫描argv[],将参数放入cc_params
//如果扫描到了-B
//-B 用于设置编译器的搜索路径
if (!strncmp(cur, "-B", 2)) {
//跳过,因为find_as已经处理过了
if (!be_quiet) WARNF("-B is already set, overriding");

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

}
//扫描到 -integrated-as跳过
if (!strcmp(cur, "-integrated-as")) continue;
//扫到-pipe跳过
if (!strcmp(cur, "-pipe")) continue;

#if defined(__FreeBSD__) && defined(__x86_64__)//判断如果是FreeBSD系统或者64位系统
if (!strcmp(cur, "-m32")) m32_set = 1;//当参数是-m32时,设置参数
#endif
//扫描到-fsanitize=address 或 -fsanitize=memory,设置参数,并且会告诉gcc要检查内存访问错误
if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
//扫描到FORTIFY_SOURCE,设置参数
//FORIFY_SOURE在使用各种字符串和内存操作功能时执行一些轻量级检查,以检查缓冲区溢出错误
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
//将参数传入cc_params
cc_params[cc_par_cnt++] = cur;

}
//跳出循环后,给cc_params加上-B和as_path
cc_params[cc_par_cnt++] = "-B";
cc_params[cc_par_cnt++] = as_path;
//检查clang_mode的值,之前检查是不是clang有设置过
if (clang_mode)
//如果时clang就在cc_params后面再加上-no-integrated-as
cc_params[cc_par_cnt++] = "-no-integrated-as";
//如果环境变量中有AFL_HARDEN
if (getenv("AFL_HARDEN")) {
//就在cc_params后面再加上-fstack-protector-all
cc_params[cc_par_cnt++] = "-fstack-protector-all";
//如果没有设置fortify_set,则在cc_params后面再加上-D_FORTIFY_SOURCE=2
if (!fortify_set)
cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";

}
//如果通过 -fsanitize= 设置了asan_set(判断是否检查内存)
if (asan_set) {

/* Pass this on to afl-as to adjust map density. */
//设置环境变量 AFL_USE_ASAN = 1
setenv("AFL_USE_ASAN", "1", 1);
//如果设置了AFL_USE_ASAN
} else if (getenv("AFL_USE_ASAN")) {
//检测AFL_USE_MSAN,设置了就报错,因为两者互斥
if (getenv("AFL_USE_MSAN"))
FATAL("ASAN and MSAN are mutually exclusive");
//检测AFL_HARDEN,设置了就报错
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";
//如果设置了AFL_USE_MSAN
} else if (getenv("AFL_USE_MSAN")) {
//检测AFL_USE_ASAN,设置了就报错,因为互斥
if (getenv("AFL_USE_ASAN"))
FATAL("ASAN and MSAN are mutually exclusive");
//检测AFL_HARDEN,设置了就报错
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";


}
//接下来对于优化选项进行判断
//如果没有设置AFL_DONT_OPTIMIZE(也就是允许优化)
if (!getenv("AFL_DONT_OPTIMIZE")) {
//如果是FreeBSD系统或者64位系统,进入分支
#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)//如果没有设置clang模式或者没有设置-m32参数则进入分支
cc_params[cc_par_cnt++] = "-g";//cc_params中追加“-g”参数

#else//如果不是上述两种系统

cc_params[cc_par_cnt++] = "-g";//cc_params中追加“-g”参数

#endif
//追加一系列参数
cc_params[cc_par_cnt++] = "-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";

}
//如果设置了AFL_NO_BUILTIN(不允许优化)
if (getenv("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";

}
//最后补上NULL标志结束
cc_params[cc_par_cnt] = NULL;

}


/* Main entry point */

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) {

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]);
//参数处理,将确定好的参数放入cc_params[] 数组
edit_params(argc, argv);
//执行afl-gcc
execvp(cc_params[0], (char**)cc_params);
//报错
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);

return 0;

}

参考博客:

https://bbs.pediy.com/thread-265936.htm#msg_header_h2_1

https://blog.csdn.net/qq_41202237/article/details/121268909

https://blog.csdn.net/sls_xsl/article/details/114379402