-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathafl-as.c
581 lines (412 loc) · 23.7 KB
/
afl-as.c
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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
/*
american fuzzy lop - wrapper for GNU as
---------------------------------------
Written and maintained by Michal Zalewski <[email protected]>
Copyright 2013, 2014, 2015 Google Inc. 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
The sole purpose of this wrapper is to preprocess assembly files generated
by GCC / clang and inject the instrumentation bits included from afl-as.h. It
is automatically invoked by the toolchain when compiling programs using
afl-gcc / afl-clang.
Note that it's an explicit non-goal to instrument hand-written assembly,
be it in separate .s files or in __asm__ blocks. The only aspiration this
utility has right now is to be able to skip them gracefully and allow the
compilation process to continue.
That said, see experimental/clang_asm_normalize/ for a solution that may
allow clang users to make things work even with hand-crafted assembly. Just
note that there is no equivalent for GCC.
*/
//as程序是AFL对clang assembler的包装,主要在链接阶段对目标程序“插桩”,“插桩”的汇编代码在afl-as.h中
#define AFL_MAIN
#include "config.h"
#include "types.h"
#include "debug.h"
#include "alloc-inl.h"
#include "afl-as.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
static u8** as_params; /* Parameters passed to the real 'as' 传递给as的参数*/
static u8* input_file; /* Originally specified input file 输入文件*/
static u8* modified_file; /* Instrumented file for the real 'as' as进行插桩处理的文件*/
static u8 be_quiet, /* Quiet mode (no stderr output) 静默模式,没有标准输出*/
clang_mode, /* Running in clang mode? */
pass_thru, /* Just pass data through? 只通过数据*/
just_version, /* Just show version? 只显示版本*/
sanitizer; /* Using ASAN / MSAN 是否使用ASAN/MSAN*/
static u32 inst_ratio = 100, /* Instrumentation probability (%) 插桩覆盖率*/
as_par_cnt = 1; /* Number of params to 'as' 传递给as的参数数量初始值*/
/* If we don't find --32 or --64 in the command line, default to
instrumentation for whichever mode we were compiled with. This is not
perfect, but should do the trick for almost all use cases. */
#ifdef __x86_64__
static u8 use_64bit = 1;
#else
static u8 use_64bit = 0;
#ifdef __APPLE__
# error "Sorry, 32-bit Apple platforms are not supported."
#endif /* __APPLE__ */
#endif /* ^__x86_64__ */
/* Examine and modify parameters to pass to 'as'. Note that the file name
is always the last parameter passed by GCC, so we exploit this property
to keep the code simple. */
//检查并修改参数以传递给as。请注意,文件名始终是GCC传递的最后一个参数,因此我们利用这个特性使代码保持简单。
//主要是设置变量as_params的值,以及use_64bit/modified_file的值。
static void edit_params(int argc, char** argv) {
u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS"); //读取AFL_AS环境变量,如果存在就设置为afl_as的值
u32 i;
#ifdef __APPLE__
u8 use_clang_as = 0;
/* On MacOS X, the Xcode cctool 'as' driver is a bit stale and does not work
with the code generated by newer versions of clang that are hand-built
by the user. See the thread here: http://goo.gl/HBWDtn.
To work around this, when using clang and running without AFL_AS
specified, we will actually call 'clang -c' instead of 'as -q' to
compile the assembly file.
The tools aren't cmdline-compatible, but at least for now, we can
seemingly get away with this by making only very minor tweaks. Thanks
to Nico Weber for the idea. */
//因为apple的一些原因,所以如果我们定义了__APPLE__宏,且当前是在clang_mode且没有设置AFL_AS环境变量,就设置use_clang_as为1,并设置afl_as为AFL_CC/AFL_CXX/clang中的一种
//对于 __APPLE_ 宏, 如果当前在 clang_mode 且没有设置 AFL_AS 环境变量,会设置 use_clang_mode = 1,并设置 afl-as 为 AFL_CC/AFL_CXX/clang中的一种
if (clang_mode && !afl_as) {
use_clang_as = 1;
afl_as = getenv("AFL_CC");
if (!afl_as) afl_as = getenv("AFL_CXX");
if (!afl_as) afl_as = "clang";
}
#endif /* __APPLE__ */
/* Although this is not documented, GCC also uses TEMP and TMP when TMPDIR
is not set. We need to check these non-standard variables to properly
handle the pass_thru logic later on. */
//依次检查是否存在TMPDIR/TEMP/TMP环境变量,如果存在就设置,如果都不存在就设置tmp_dir为”/tmp”
if (!tmp_dir) tmp_dir = getenv("TEMP");
if (!tmp_dir) tmp_dir = getenv("TMP");
if (!tmp_dir) tmp_dir = "/tmp";
//首先为as_params分配空间,大小为(argc+32)*8
as_params = ck_alloc((argc + 32) * sizeof(u8*));
//如果afl_as不为空,就设置as_params[0]为afl_as,否则设置为as
as_params[0] = afl_as ? afl_as : (u8*)"as";
as_params[argc] = 0;
//遍历从argv[1]开始,到argv[argc-1](也就是最后一个参数)之前的argv参数
for (i = 1; i < argc - 1; i++) {
if (!strcmp(argv[i], "--64")) use_64bit = 1;
else if (!strcmp(argv[i], "--32")) use_64bit = 0;
#ifdef __APPLE__
/* The Apple case is a bit different... */
//如果是apple,则如果存在-arch x86_64,设置use_64bit为1,并跳过-q和-Q选项
if (!strcmp(argv[i], "-arch") && i + 1 < argc) {
if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1;
else if (!strcmp(argv[i + 1], "i386"))
FATAL("Sorry, 32-bit Apple platforms are not supported.");
}
/* Strip options that set the preference for a particular upstream
assembler in Xcode. */
if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q")))
continue;
#endif /* __APPLE__ */
//设置as_params的值为argv对应的参数值
as_params[as_par_cnt++] = argv[i];
}
//开始设置其他的as_params参数
#ifdef __APPLE__
/* When calling clang as the upstream assembler, append -c -x assembler
and hope for the best. */
//如果use_clang_as为1,则设置-c -x assembler选项
if (use_clang_as) {
as_params[as_par_cnt++] = "-c";
as_params[as_par_cnt++] = "-x";
as_params[as_par_cnt++] = "assembler";
}
#endif /* __APPLE__ */
//读取argv[argc - 1]的值,赋给input_file的值,也就是传递的最后一个参数的值作为input_file
input_file = argv[argc - 1];
if (input_file[0] == '-') {
if (!strcmp(input_file + 1, "-version")) { //这里就只是做version的查询
just_version = 1;
modified_file = input_file;
goto wrap_things_up;
}
if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)");
else input_file = NULL;
} else {
/* Check if this looks like a standard invocation as a part of an attempt
to compile a program, rather than using gcc on an ad-hoc .s file in
a format we may not understand. This works around an issue compiling
NSS. */
//比较input_file和tmp_dir、/var/tmp/、/tmp/的前strlen(tmp_dir)、9、5个字节是否相同,如果不相同,就设置pass_thru为1
if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
strncmp(input_file, "/var/tmp/", 9) &&
strncmp(input_file, "/tmp/", 5)) pass_thru = 1;
}
//设置modified_file的值为alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),(u32) time(NULL));,简单的说就是tmp_dir/.afl-pid-time.s这样的字符串
modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),
(u32)time(NULL));
wrap_things_up:
as_params[as_par_cnt++] = modified_file;
as_params[as_par_cnt] = NULL;
}
/* Process input file, generate modified_file. Insert instrumentation in all
the appropriate places. */
//处理输入文件,生成modified_file,将instrumentation插入所有适当的位置
static void add_instrumentation(void) {
static u8 line[MAX_LINE];
FILE* inf;
FILE* outf;
s32 outfd;
u32 ins_lines = 0;
u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0,
skip_intel = 0, skip_app = 0, instrument_next = 0;
#ifdef __APPLE__
u8* colon_pos;
#endif /* __APPLE__ */
//如果input_file不为空,则尝试打开这个文件,如果打开失败就抛出异常,如果为空,则读取标准输入,最终获取FILE* 指针inf
if (input_file) {
inf = fopen(input_file, "r");
if (!inf) PFATAL("Unable to read '%s'", input_file);
} else inf = stdin;
//打开modified_file对应的临时文件,并获取其句柄outfd,再根据句柄通过fdopen函数拿到FILE*指针outf
outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600);
if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file);
//验证该文件是否可写,不可写返回异常
outf = fdopen(outfd, "w");
if (!outf) PFATAL("fdopen() failed");
//通过fgets从inf中逐行读取内容保存到line数组里,每行最多读取的字节数是MAX_LINE(8192),这个值包括’\0’,所以
//实际读取的有内容的字节数是MAX_LINE-1个字节。从line数组里将读取的内容写入到outf对应的文件里,,然后进入到真正的插桩逻辑。这里需要注意的是,插桩只向 .text 段插入
while (fgets(line, MAX_LINE, inf)) {
/* In some cases, we want to defer writing the instrumentation trampoline
until after all the labels, macros, comments, etc. If we're in this
mode, and if the line starts with a tab followed by a character, dump
the trampoline now. */
//接下来是有趣的部分。只在.text部分进行插桩,这部分涉及到多平台以及优化后的汇编文件格式
//首先跳过标签、宏、注释
//判断instrument_next和instr_ok是否都为1,以及line是否以\t开始,且line[1]是否是字母
//变量 instr_ok 本质上是一个flag,用于表示是否位于.text段。变量设置为1,表示位于 .text 中,如果不为1,则表示不在。于是,如果instr_ok 为1,就会在分支处执行插桩逻辑,否则就不插桩
if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
instrument_next && line[0] == '\t' && isalpha(line[1])) {
//如果都满足,则设置instrument_next = 0,并向outf中写入trampoline_fmt,并将插桩计数器ins_lines加一
//这其实是因为我们想要插入instrumentation trampoline到所有的标签,宏,注释之后
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
R(MAP_SIZE)); //这里对 instr_ok, instrument_next 变量进行了检验是否为1,而且进一步校验是否位于 .text 段中,且设置了 defered mode 进行插桩,则就进行插桩操作,写入 trampoline_fmt_64/32
instrument_next = 0;
ins_lines++;
}
/* Output the actual line, call it a day in pass-thru mode. */
fputs(line, outf);
if (pass_thru) continue;
/* All right, this is where the actual fun begins. For one, we only want to
instrument the .text section. So, let's keep track of that in processed
files - and let's set instr_ok accordingly. */
//如果line的值为\t.[text\n|section\t.text|section\t__TEXT,__text|section __TEXT,__text]...其中之一,则设置instr_ok为1,然后跳转到while循环首部,去读取下一行的数据到line数组里
//首先判断读入的行是否以‘\t’ 开头,本质上是在匹配.s文件中声明的段,然后判断line[1]是否为.
if (line[0] == '\t' && line[1] == '.') {
/* OpenBSD puts jump tables directly inline with the code, which is
a bit annoying. They use a specific format of p2align directives
around them, so we use that as a signal. */
if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
isdigit(line[10]) && line[11] == '\n') skip_next_label = 1; //'\t'开头,且line[1]=='.',检查是否为 p2align 指令,如果是,则设置 skip_next_label = 1
if (!strncmp(line + 2, "text\n", 5) ||
!strncmp(line + 2, "section\t.text", 13) ||
!strncmp(line + 2, "section\t__TEXT,__text", 21) ||
!strncmp(line + 2, "section __TEXT,__text", 21)) {
instr_ok = 1; //尝试匹配 "text\n" "section\t.text" "section\t__TEXT,__text" "section __TEXT,__text" 其中任意一个,匹配成功, 设置 instr_ok = 1, 表示位于 .text 段中,continue 跳出,进行下一次遍历
continue;
}
//如果不是上面的几种情况,且line的值为\t.[section\t|section |bss\n|data\n]...,则设置instr_ok为0,并跳转到while循环首部,去读取下一行的数据到line数组里
//尝试匹配"section\t" "section " "bss\n" "data\n" 其中任意一个,匹配成功,设置 instr_ok = 0,表位于其他段中,continue 跳出,进行下一次遍历
if (!strncmp(line + 2, "section\t", 8) ||
!strncmp(line + 2, "section ", 8) ||
!strncmp(line + 2, "bss\n", 4) ||
!strncmp(line + 2, "data\n", 5)) {
instr_ok = 0;
continue;
}
}
/* Detect off-flavor assembly (rare, happens in gdb). When this is
encountered, we set skip_csect until the opposite directive is
seen, and we do not instrument. */
//通过几个 if 判断,来设置一些标志信息,包括 off-flavor assembly,Intel/AT&T的块处理方式、ad-hoc __asm__块的处理方式等
if (strstr(line, ".code")) {
if (strstr(line, ".code32")) skip_csect = use_64bit;
if (strstr(line, ".code64")) skip_csect = !use_64bit;
}
/* Detect syntax changes, as could happen with hand-written assembly.
Skip Intel blocks, resume instrumentation when back to AT&T. */
if (strstr(line, ".intel_syntax")) skip_intel = 1;
if (strstr(line, ".att_syntax")) skip_intel = 0;
/* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */
if (line[0] == '#' || line[1] == '#') {
if (strstr(line, "#APP")) skip_app = 1;
if (strstr(line, "#NO_APP")) skip_app = 0;
}
/*
AFL在插桩时重点关注的内容包括:^main, ^.L0, ^.LBB0_0, ^\tjnz foo (_main函数, gcc和clang下的分支标记,条件跳转分支标记),这些内容通常标志了程序的流程变化,因此AFL会重点在这些位置进行插桩:
对于形如\tj[^m].格式的指令,即条件跳转指令,且R(100)产生的随机数小于插桩密度inst_ratio,直接使用fprintf将trampoline_fmt_64(插桩部分的指令)写入 outf 指向的文件,写入大小为小于 MAP_SIZE的随
机数——R(MAP_SIZE),然后插桩计数ins_lines加一,continue 跳出,进行下一次遍历
*/
/* If we're in the right mood for instrumenting, check for function
names or conditional labels. This is a bit messy, but in essence,
we want to catch:
^main: - function entry point (always instrumented)
^.L0: - GCC branch label
^.LBB0_0: - clang branch label (but only in clang mode)
^\tjnz foo - conditional branches
...but not:
^# BB#0: - clang comments
^ # BB#0: - ditto
^.Ltmp0: - clang non-branch labels
^.LC0 - GCC non-branch labels
^.LBB0_0: - ditto (when in GCC mode)
^\tjmp foo - non-conditional jumps
Additionally, clang and GCC on MacOS X follow a different convention
with no leading dots on labels, hence the weird maze of #ifdefs
later on.
*/
if (skip_intel || skip_app || skip_csect || !instr_ok ||
line[0] == '#' || line[0] == ' ') continue;
/* Conditional branch instruction (jnz, etc). We append the instrumentation
right after the branch (to instrument the not-taken path) and at the
branch destination label (handled later on). */
//插桩^\tjnz foo条件跳转指令
if (line[0] == '\t') {
if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) {
//如果line的值为\tj[!m]...,且R(100) < inst_ratio,R(100)会返回一个100以内的随机数,inst_ratio是我们之前设置的插桩密度,默认为100,如果设置了asan之类的就会默认设置成30左右
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
R(MAP_SIZE)); //根据use_64bit来判断向outfd里写入trampoline_fmt_64还是trampoline_fmt_32
//这里的R(x)实际上是用来区分每个桩的,也就是是一个标识
ins_lines++;
}
continue;
}
/* Label of some sort. This may be a branch destination, but we need to
tread carefully and account for several different formatting
conventions. */
//对于label的相关评估,有一些label可能是一些分支的目的地,需要自己的评判
#ifdef __APPLE__
/* Apple: L<whatever><digit>: */
if ((colon_pos = strstr(line, ":"))) {
if (line[0] == 'L' && isdigit(*(colon_pos - 1))) {
#else
/* Everybody else: .L<whatever>: */
//首先检查该行中是否存在:,然后检查是否以.开始
if (strstr(line, ":")) {
if (line[0] == '.') {
#endif /* __APPLE__ */
/* .L0: or LBB0_0: style jump destination */
#ifdef __APPLE__
/* Apple: L<num> / LBB<num> */
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
&& R(100) < inst_ratio) {
#else
/* Apple: .L<num> / .LBB<num> */
//如果以.开始,则代表想要插桩^.L0:或者^.LBB0_0:这样的branch label,即style jump destination
//然后检查line[2]是否为数字 或者 如果是在clang_mode下,比较从line[1]开始的三个字节是否为LBB. 前述所得结果和R(100) < inst_ratio)相与
if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
&& R(100) < inst_ratio) {
#endif /* __APPLE__ */
/* An optimization is possible here by adding the code only if the
label is mentioned in the code in contexts other than call / jmp.
That said, this complicates the code by requiring two-pass
processing (messy with stdin), and results in a speed gain
typically under 10%, because compilers are generally pretty good
about not generating spurious intra-function jumps.
We use deferred output chiefly to avoid disrupting
.Lfunc_begin0-style exception handling calculations (a problem on
MacOS X). */
if (!skip_next_label) instrument_next = 1; else skip_next_label = 0; //如果结果为真,则设置instrument_next = 1
}
} else { //否则代表这是一个function,插桩^func:function entry point
/* Function label (always instrumented, deferred mode). */
instrument_next = 1; //直接设置instrument_next = 1(defer mode)
}
}
}
//如果插桩计数器ins_lines不为0,就在完全拷贝input_file之后,依据架构,像outf中写入main_payload_64或者main_payload_32,然后关闭这两个文件
if (ins_lines)
fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
if (input_file) fclose(inf);
fclose(outf);
if (!be_quiet) {
if (!ins_lines) WARNF("No instrumentation targets found%s.",
pass_thru ? " (pass-thru mode)" : "");
else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).",
ins_lines, use_64bit ? "64" : "32",
getenv("AFL_HARDEN") ? "hardened" :
(sanitizer ? "ASAN/MSAN" : "non-hardened"),
inst_ratio);
}
}
//从add_instrumentation可以看出,插桩是通过汇编的前导命令来判断这是否是一个分支或者函数,然后插入instrumentation trampoline
/* Main entry point */
int main(int argc, char** argv) {
s32 pid;
u32 rand_seed;
int status;
u8* inst_ratio_str = getenv("AFL_INST_RATIO"); //该环境变量主要控制检测每个分支的概率,取值为0到100%,设置为0时则只检测函数入口的跳转,而不会检测函数分支的跳转
struct timeval tv;
struct timezone tz;
clang_mode = !!getenv(CLANG_ENV_VAR);
if (isatty(2) && !getenv("AFL_QUIET")) {
SAYF(cCYA "afl-as " cBRI VERSION cRST " by <[email protected]>\n");
} else be_quiet = 1;
if (argc < 2) {
SAYF("\n"
"This is a helper application for afl-fuzz. It is a wrapper around GNU 'as',\n"
"executed by the toolchain whenever using afl-gcc or afl-clang. You probably\n"
"don't want to run this program directly.\n\n"
"Rarely, when dealing with extremely complex projects, it may be advisable to\n"
"set AFL_INST_RATIO to a value less than 100 in order to reduce the odds of\n"
"instrumenting every discovered branch.\n\n");
exit(1);
}
//通过 gettimeofday(&tv,&tz);获取时区和时间,然后设置 srandom() 的随机种子 rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid()
gettimeofday(&tv, &tz);
rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
srandom(rand_seed); //设置srandom的随机种子
//调用 edit_params(argc, argv) 函数进行参数处理
edit_params(argc, argv);
//检测 inst_ratio_str 的值是否合法范围内,并设置环境变量 AFL_LOOP_ENV_VAR
if (inst_ratio_str) {
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100)
FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)");
}
if (getenv(AS_LOOP_ENV_VAR))
FATAL("Endless loop when calling 'as' (remove '.' from your PATH)");
//设置环境变量AS_LOOP_ENV_VAR的值为1
setenv(AS_LOOP_ENV_VAR, "1", 1);
/* When compiling with ASAN, we don't have a particularly elegant way to skip
ASAN-specific branches. But we can probabilistically compensate for
that... */
//读取环境变量AFL_USE_ASAN和AFL_USE_MSAN的值,如果其中有一个为1,则设置sanitizer为1,且将inst_ratio除3
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
sanitizer = 1;
inst_ratio /= 3; //这是因为AFL无法在插桩的时候识别出ASAN specific branches,所以会插入很多无意义的桩,为了降低这种概率,粗暴的将整个插桩的概率都除以3
}
if (!just_version) add_instrumentation(); //调用 add_instrumentation() 函数,这是实际的插桩函数
/* 实际执行的参数
printf("---------------------------");
for(int i = 0; i < sizeof(as_params); i++) {
printf("as_params[%d]:%s\n", i, as_params[i]);
}
*/
//fork出一个子进程,让子进程来执行execvp(as_params[0], (char **) as_params);
if (!(pid = fork())) {
//execvp执行的时候,会用as_params[0]来完全替换掉当前进程空间中的程序,如果不通过子进程来执行实际的as,那么后续就无法在执行完实际的as之后,还能unlink掉modified_file
execvp(as_params[0], (char**)as_params);
FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]);
}
if (pid < 0) PFATAL("fork() failed");
//等待子进程结束
if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
//读取环境变量AFL_KEEP_ASSEMBLY的值,如果没有设置这个环境变量,就unlink掉modified_file(已插桩的文件)。设置该环境变量主要是为了防止 afl-as 删掉插桩后的汇编文件,设置为1则会保留插桩后的汇编文件
if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file);
exit(WEXITSTATUS(status));
}