AFL源码分析之afl-as.c

上一篇分析 afl-gcc.c 中有说到,afl-gcc.c 好像并没有执行插桩的操作,只是进行了一些检查,对参数进行了一些整理。

看了一些博客后发现果不其然,插桩发生在 afl-as.c

重要代码

首先要知道插桩是什么,插桩说得通俗一点就是在代码里面插代码,再具体一点就是在程序编译期间插入汇编代码(伪代码),记录覆盖率。

而编译程序就是通过 afl-gcc/afl-clang 等工具完成,这个过程会对程序进行插桩。参考网上师傅写的博客,讲一下 afl-as.c 是怎么工作的,以 afl-gcc 为例,添加一些输出使得在调用 afl-gcc.c 的 execvp()之前打印全部命令行参数,看看 afl-gcc 究竟执行了啥:

1
gcc /tmp/hello.c -B /root/src/afl-2.52b -g -O3 -funroll-loops -D__AFL_COMPILER=1 -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1

可以看到,afl-gcc最终调用的是 gcc,同时定义了一些宏,设置了一些参数。最关键的是-B /root/src/afl-2.52b-B

选项用于设置编译器的搜索路径,这里相当于设置成了/root/src/afl-2.52b(这里设置的是AFL_PATH,也就是AFL的目录)

在 “源代码”->“汇编代码”->“二进制” 过程中,将汇编代码编译成二进制的工具——汇编器,Linux中常用的汇编器是as。在编译完成AFL后,当前目录下会出现一个as文件,并作为符号链接指向afl-as。而之前又通过-B参数设置了搜索路径,那么afl-as便会作为汇编器,执行汇编操作, afl-as 就会在这个时候插桩。

afl-as.c 中插桩代码如下:

1
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, R(MAP_SIZE));//插桩

通过 fprintf() 将代码添加到汇编文件的相应位置,分析 32 位的情况:

trampoline_fmt_32的具体内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static const u8* trampoline_fmt_32 =

"\n"
"/* --- AFL TRAMPOLINE (32-BIT) --- */\n"
"\n"
".align 4\n"
"\n"
"leal -16(%%esp), %%esp\n"
"movl %%edi, 0(%%esp)\n"
"movl %%edx, 4(%%esp)\n"
"movl %%ecx, 8(%%esp)\n"
"movl %%eax, 12(%%esp)\n"
"movl $0x%08x, %%ecx\n"
"call __afl_maybe_log\n"
"movl 12(%%esp), %%eax\n"
"movl 8(%%esp), %%ecx\n"
"movl 4(%%esp), %%edx\n"
"movl 0(%%esp), %%edi\n"
"leal 16(%%esp), %%esp\n"
"\n"
"/* --- END --- */\n"
"\n";

这一段代码主要操作是:

  • 保存edi等寄存器
  • ecx 的值设置为 fprintf() 所要打印的内容
  • 调用__afl_maybe_log
  • 恢复寄存器

R(MAP_SIZE)对应的就是ecx,根据定义,宏MAP_SIZE为 64k,R(x)的定义是(random() % (x)),所以R(MAP_SIZE)为0 到 MAP_SIZE 之间的一个随机数。

因此,在处理到某个分支,需要插入桩代码时,afl-as会生成一个随机数,作为运行时保存在ecx中的值。而这个随机数,便是用于标识这个代码块的key。

全部代码

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
/*
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 GNU as
---------------------------------------

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

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.

*/

#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' */

static u8* input_file; /* Originally specified input file */
static u8* modified_file; /* Instrumented file for the real '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 */

static u32 inst_ratio = 100, /* Instrumentation probability (%) */
as_par_cnt = 1; /* Number of params to '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 WORD_SIZE_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 /* ^WORD_SIZE_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. */

static void edit_params(int argc, char** argv) {
//获取环境变量
u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("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.m

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. */
//如果是clang模式并且没有获取到afl_as环境变量
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. */

if (!tmp_dir) tmp_dir = getenv("TEMP");
if (!tmp_dir) tmp_dir = getenv("TMP");
if (!tmp_dir) tmp_dir = "/tmp";//获取环境变量并赋值
//为as_params开辟空间
as_params = ck_alloc((argc + 32) * sizeof(u8*));
//判断afl_as是否已经获取到“AFL_AS”环境变量,获取了就赋值,没获取就赋值字符串”as“
as_params[0] = afl_as ? afl_as : (u8*)"as";
//设置最后一个参数为0
as_params[argc] = 0;
//遍历参数
for (i = 1; i < argc - 1; i++) {

if (!strcmp(argv[i], "--64")) use_64bit = 1;//如果遍历到"--64",设置参数
else if (!strcmp(argv[i], "--32")) use_64bit = 0;//反之也设置参数

#ifdef __APPLE__//如果是苹果平台

/* The Apple case is a bit different... */
//如果遍历到-arch
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. */
//如果是clang模式且没匹配到”-q“参数,跳出循环
if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q")))
continue;

#endif /* __APPLE__ */
//#ifdef结束标志
as_params[as_par_cnt++] = argv[i];

}

#ifdef __APPLE__

/* When calling clang as the upstream assembler, append -c -x assembler
and hope for the best. */

if (use_clang_as) {
//如果是clang模式,追加一系列参数
as_params[as_par_cnt++] = "-c";
as_params[as_par_cnt++] = "-x";
as_params[as_par_cnt++] = "assembler";

}

#endif /* __APPLE__ */
//把最后一个参数的值赋给input_file
input_file = argv[argc - 1];
//如果input_file首字母是“-”
if (input_file[0] == '-') {
//如果是-version,设置对应参数
if (!strcmp(input_file + 1, "-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首字母不是“-”
/*比对input_file的前strlen(tmp_dir)、9、5个字节是否与tmp_dir、"/var/tmp/"、"/tmp/"是否相同,
如果都不相同则设置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为类似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,插桩
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__ */
//如果存在输入文件名称
if (input_file) {
//获取input_file,把fd赋值给inf
inf = fopen(input_file, "r");
//找不到报错
if (!inf) PFATAL("Unable to read '%s'", input_file);

} else inf = stdin;//不存在输入文件名则赋值标准输入
//以写的形式打开modified_file,如果文件存在就直接打开,不存在就创一个
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");

/*循环读取inf指向的文件逐行读取并保存到line数组,每行最多MAX_LINE(8192)个字节,包含'\0'
注意:每读一行进入一次循环*/
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. */

//做了一系列判断,instrument_next和instr_ok是否都为1,以及line是否以\t开始,且line[1]是否是字母
/*instr_ok代表的是一个flag,当它被设为1时,代表在.text段,当它为1时,就会在分支处进行插桩*/
if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
instrument_next && line[0] == '\t' && isalpha(line[1])) {
//向outf中写入trampoline_fmt64/32,长度小于MAP_SIZE(插桩)
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
R(MAP_SIZE));

instrument_next = 0;
ins_lines++;//插桩计数器++

}

/* Output the actual line, call it a day in pass-thru mode. */

//将line里的内容放入outf
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."
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. */

//检查到line里有p2align指令且line[10]是数字,赋值
if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
isdigit(line[10]) && line[11] == '\n') skip_next_label = 1;

/*检测到line里有\t.[text\n|section\t.text|section\t__TEXT,__text|section __TEXT,__text]...
其中一个,则将instr_ok设为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;
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 (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;//判断是否为intel汇编语法
if (strstr(line, ".att_syntax")) skip_intel = 0;//判断是否为att汇编语法

/* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */
//ad-hoc __asm__ 块是否跳过
if (line[0] == '#' || line[1] == '#') {

if (strstr(line, "#APP")) skip_app = 1;
if (strstr(line, "#NO_APP")) skip_app = 0;

}

/* 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). */

//检查line是否以'\t'开头
if (line[0] == '\t') {
/*如果line形如\tj[!m]...,且R(100) < inst_ratio,进入分支
其中R(100)会返回一个100以内的随机数,inst_ratio是之前设置的插桩密度,默认100*/
if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) {
//向outf中写入trampoline_fmt64/32,长度小于MAP_SIZE(插桩)
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
R(MAP_SIZE));

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. */

#ifdef __APPLE__//如果是苹果平台

/* Apple: L<whatever><digit>: */

if ((colon_pos = strstr(line, ":"))) {//判断line中是否有“:”

if (line[0] == 'L' && isdigit(*(colon_pos - 1))) {

#else//如果不是苹果,#else与#ifdef对立

/* Everybody else: .L<whatever>: */
//检查line中是否存在':'
if (strstr(line, ":")) {
//检查line开头是否是'.'
if (line[0] == '.') {

#endif /* __APPLE__ */

/* .L0: or LBB0_0: style jump destination */

#ifdef __APPLE__//如果是苹果

/* Apple: L<num> / LBB<num> */
//检查line[1]是否是数字,或者在clang模式下line[0]三个字节是否为"LBB"且随机数小于插桩密度
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
&& R(100) < inst_ratio) {

#else//如果不是苹果

/* Apple: .L<num> / .LBB<num> */

//检查line[2]是否为数字,或者在clang模式下line[1]三个字节是否为"LBB"且随机数小于插桩密度
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;

}

} else {

/* Function label (always instrumented, deferred mode). */

instrument_next = 1;

}

}

}

if (ins_lines)//如果插桩计数器不为0
//向outf写入main_payload_64或32
fputs(use_64bit ? main_payload_64 : main_payload_32, outf);

if (input_file) fclose(inf);
fclose(outf);
//如果使用的不是静默模式
if (!be_quiet) {
//如果插桩计数器为0,报错
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);

}

}


/* 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 <lcamtuf@google.com>\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);

rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();//设置srandom的随机种子

srandom(rand_seed);

edit_params(argc, argv);

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)");

setenv(AS_LOOP_ENV_VAR, "1", 1);//设置环境变量AS_LOOP_ENV_VAR为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... */
//判断两者的值是否有1的情况
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
sanitizer = 1;
inst_ratio /= 3;
}
/*如果不是只查阅version,就进入add_instrumentation函数,该函数主要处理输入文件,
生成modified_file,将桩插入释放的位置*/
if (!just_version) add_instrumentation();

if (!(pid = fork())) {//调用fork函数创建一个子进程

execvp(as_params[0], (char**)as_params);//子进程执行该指令
/*这是因为我们执行execvp执行的时候,as_params[0]会完全替换掉当前进程中的程序,
如果不通过子程序执行实际的as,后续就无法在执行完实际的as之后,还能unlink掉modified_file*/
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));

}

参考博客:

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

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

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