Compare commits

...

4 Commits

Author SHA1 Message Date
“18670363079” 1d189325cd 1.2
2 months ago
“18670363079” eabae9582c 1
2 months ago
“18670363079” 1004ce885a 12.16
2 months ago
“18670363079” 42a166966e 这下交对了吧
4 months ago

@ -113,6 +113,7 @@ static u8 count_class_lookup[256] = {
};
//对内存中的元组计数进行分类。如果edges_only标志被设置则只标记边界情况否则使用count_class_lookup数组对每个字节的计数进行分类
static void classify_counts(u8* mem) {
u32 i = MAP_SIZE;
@ -137,7 +138,7 @@ static void classify_counts(u8* mem) {
/* See if any bytes are set in the bitmap. */
//检查在追踪位图中是否有任何字节被设置。如果有则返回1表示有变化如果没有则返回0。
static inline u8 anything_set(void) {
u32* ptr = (u32*)trace_bits;
@ -151,7 +152,7 @@ static inline u8 anything_set(void) {
/* Get rid of shared memory and temp files (atexit handler). */
//用于在程序退出时清理共享内存和临时文件。它会尝试删除程序输入文件并调用shmctl来删除共享内存标识符
static void remove_shm(void) {
unlink(prog_in); /* Ignore errors */
@ -161,7 +162,7 @@ static void remove_shm(void) {
/* Configure shared memory. */
//配置共享内存。创建一个共享内存段并将其ID设置到环境变量中以便子进程可以使用。同时将trace_bits指向共享内存的起始地址
static void setup_shm(void) {
u8* shm_str;
@ -186,7 +187,7 @@ static void setup_shm(void) {
/* Read initial file. */
//读取要分析的初始文件并将其内容存储在in_data中。同时记录文件的长度并检查文件大小是否在允许的范围内
static void read_initial_file(void) {
struct stat st;
@ -213,7 +214,7 @@ static void read_initial_file(void) {
/* Write output file. */
//将内存中的数据写入到指定的文件路径。如果文件已存在,则先删除。然后创建新文件,并将数据写入
static s32 write_to_file(u8* path, u8* mem, u32 len) {
s32 ret;
@ -234,7 +235,7 @@ static s32 write_to_file(u8* path, u8* mem, u32 len) {
/* Handle timeout signal. */
//处理超时信号。设置child_timed_out标志并尝试杀死子进程
static void handle_timeout(int sig) {
child_timed_out = 1;
@ -245,7 +246,7 @@ static void handle_timeout(int sig) {
/* Execute target application. Returns exec checksum, or 0 if program
times out. */
//执行目标程序并返回执行的校验和或者如果程序超时则返回0。这个函数负责设置环境执行程序并分析退出条件
static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
static struct itimerval it;
@ -370,7 +371,7 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
#ifdef USE_COLOR
/* Helper function to display a human-readable character. */
//显示一个可读的字符。如果字符是控制字符或非打印字符,则显示其十六进制代码
static void show_char(u8 val) {
switch (val) {
@ -386,7 +387,7 @@ static void show_char(u8 val) {
/* Show the legend */
//显示图例,解释不同颜色和标记的含义
static void show_legend(void) {
SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block "
@ -403,7 +404,7 @@ static void show_legend(void) {
/* Interpret and report a pattern in the input file. */
//解释并报告输入文件中的模式。它分析文件中的每个字节,并根据其对程序执行路径的影响对其进行分类
static void dump_hex(u8* buf, u32 len, u8* b_data) {
u32 i;
@ -558,7 +559,7 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
/* Actually analyze! */
//实际执行分析。它通过改变输入文件中的字节并观察对执行路径的影响来推断文件格式的结构
static void analyze(char** argv) {
u32 i;
@ -652,7 +653,7 @@ static void analyze(char** argv) {
/* Handle Ctrl-C and the like. */
//处理停止信号如Ctrl-C。设置stop_soon标志并尝试杀死子进程
static void handle_stop_sig(int sig) {
stop_soon = 1;
@ -663,7 +664,7 @@ static void handle_stop_sig(int sig) {
/* Do basic preparations - persistent fds, filenames, etc. */
//进行基本的准备工作,包括设置持久的文件描述符、文件名等,并配置环境变量
static void set_up_environment(void) {
u8* x;
@ -733,7 +734,7 @@ static void set_up_environment(void) {
/* Setup signal handlers, duh. */
//设置信号处理程序,以便处理停止和超时信号
static void setup_signal_handlers(void) {
struct sigaction sa;
@ -760,7 +761,7 @@ static void setup_signal_handlers(void) {
/* Detect @@ in args. */
//检测@@在参数中的位置,并替换为实际的文件路径
static void detect_file_args(char** argv) {
u32 i = 0;
@ -802,7 +803,7 @@ static void detect_file_args(char** argv) {
/* Display usage hints. */
//显示使用提示
static void usage(u8* argv0) {
SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
@ -836,7 +837,7 @@ static void usage(u8* argv0) {
/* Find binary. */
//查找并验证二进制文件的路径
static void find_binary(u8* fname) {
u8* env_path = 0;
@ -889,7 +890,7 @@ static void find_binary(u8* fname) {
/* Fix up argv for QEMU. */
//为QEMU模式修复argv设置QEMU的参数
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
@ -952,7 +953,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
/* Main entry point */
//程序的主入口点。处理命令行参数,设置环境,执行分析,并输出结果
int main(int argc, char** argv) {
s32 opt;

@ -56,19 +56,19 @@
#include <sys/wait.h>
#include <sys/time.h>
static u8** as_params; /* Parameters passed to the real 'as' */
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' */
static u8* input_file; /* Originally specified input file */
static u8* modified_file; /* Instrumented file for the real 'as' */ //用于as的插有instrumentation的文件
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 u8 be_quiet, /* Quiet mode (no stderr output) */ //是否开启安静模式不输出到stderr
clang_mode, /* Running in clang mode? */ // 是否在clang模式下运行
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' */
static u32 inst_ratio = 100, /* Instrumentation probability (%) */ //Instrumentation概率%
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
@ -92,7 +92,7 @@ static u8 use_64bit = 0;
/* 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. */
//处理和修改传递给asGNU assembler的参数。这个函数会检查命令行参数并根据这些参数来设置全局变量如use_64bit和as_params
static void edit_params(int argc, char** argv) {
u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS");
@ -129,7 +129,7 @@ static void edit_params(int argc, char** argv) {
/* 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、AFL_AS等环境变量确定临时目录和汇编器路径
if (!tmp_dir) tmp_dir = getenv("TEMP");
if (!tmp_dir) tmp_dir = getenv("TMP");
if (!tmp_dir) tmp_dir = "/tmp";
@ -148,7 +148,9 @@ static void edit_params(int argc, char** argv) {
#ifdef __APPLE__
/* The Apple case is a bit different... */
//这段代码检查命令行参数中是否有 -arch并检查其后的参数。
//如果 -arch 后面是 x86_64则设置 use_64bit 为 1表示使用 64 位模式。
//如果 -arch 后面是 i386则输出错误信息并终止程序因为 32 位的 Apple 平台不被支持
if (!strcmp(argv[i], "-arch") && i + 1 < argc) {
if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1;
@ -159,12 +161,12 @@ static void edit_params(int argc, char** argv) {
/* Strip options that set the preference for a particular upstream
assembler in Xcode. */
//如果当前处于 clang 模式,并且命令行参数中有 -q 或 -Q则跳过这些参数不将它们传递给汇编器
if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q")))
continue;
#endif /* __APPLE__ */
//将当前处理的命令行参数 argv[i] 添加到 as_params 数组中as_par_cnt 用于记录已添加的参数数量
as_params[as_par_cnt++] = argv[i];
}
@ -173,7 +175,9 @@ static void edit_params(int argc, char** argv) {
/* When calling clang as the upstream assembler, append -c -x assembler
and hope for the best. */
//如果 use_clang_as 为真(即在 macOS 下使用 clang 作为汇编器)
//则向 as_params 数组中添加 -c、-x 和 assembler 参数
//以确保 clang 正确处理汇编文件
if (use_clang_as) {
as_params[as_par_cnt++] = "-c";
@ -183,7 +187,9 @@ static void edit_params(int argc, char** argv) {
}
#endif /* __APPLE__ */
//如果是 --version则设置 just_version 为 1并跳转到 wrap_things_up直接返回版本信息。
//如果输入文件以 - 开头但不是 --version则输出错误信息并终止程序。
//如果输入文件是单独的 -,则将 input_file 设置为 NULL表示从标准输入读取
input_file = argv[argc - 1];
if (input_file[0] == '-') {
@ -203,16 +209,17 @@ static void edit_params(int argc, char** argv) {
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. */
//检查输入文件是否位于临时目录(如 /tmp 或 /var/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;
}
//生成一个临时文件名,用于存储插桩后的汇编文件。文件名格式为 tmp_dir/.afl-PID-TIMESTAMP.s
modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),
(u32)time(NULL));
//将生成的临时文件名添加到 as_params 数组中,作为汇编器的输入文件,并将数组末尾设置为 NULL表示参数列表结束
wrap_things_up:
as_params[as_par_cnt++] = modified_file;
@ -223,9 +230,13 @@ wrap_things_up:
/* Process input file, generate modified_file. Insert instrumentation in all
the appropriate places. */
//处理输入文件并生成一个修改过的文件modified_file在其中插入 instrumentation仪器化代码。这个函数会读取原始的汇编文件根据配置插入instrumentation代码并将结果写入新的文件
static void add_instrumentation(void) {
//line[MAX_LINE]:用于存储从输入文件中读取的每一行。
//inf 和 outf分别表示输入文件和输出文件的指针。
//outfd输出文件的文件描述符。
//ins_lines记录插桩的行数。
static u8 line[MAX_LINE];
FILE* inf;
@ -241,14 +252,16 @@ static void add_instrumentation(void) {
u8* colon_pos;
#endif /* __APPLE__ */
//打开输入文件以供读取。
//如果 input_file 不为空,则打开该文件;否则从标准输入读取
if (input_file) {
inf = fopen(input_file, "r");
if (!inf) PFATAL("Unable to read '%s'", input_file);
} else inf = stdin;
//创建并打开输出文件以供写入。
//使用 open 创建文件,并使用 fdopen 将其转换为 FILE* 类型
outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600);
if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file);
@ -256,14 +269,15 @@ static void add_instrumentation(void) {
outf = fdopen(outfd, "w");
if (!outf) PFATAL("fdopen() failed");
//
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 段、需要插桩等),则插入插桩代码,并增加插桩行数
if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
instrument_next && line[0] == '\t' && isalpha(line[1])) {
@ -276,15 +290,17 @@ static void add_instrumentation(void) {
}
/* Output the actual line, call it a day in pass-thru mode. */
//将当前行写入输出文件。
fputs(line, outf);
//如果处于 pass_thru 模式,则跳过后续处理
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. */
//检测并处理 .text 段。
//如果当前行表示 .text 段,则设置 instr_ok 为 1表示可以插桩。
//如果当前行表示其他段(如 .bss 或 .data则设置 instr_ok 为 0表示跳过插桩
if (line[0] == '\t' && line[1] == '.') {
/* OpenBSD puts jump tables directly inline with the code, which is
@ -315,7 +331,8 @@ static void add_instrumentation(void) {
/* 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. */
//处理 .code 指令。
//如果检测到 .code32 或 .code64则根据当前模式设置 skip_csect跳过插桩
if (strstr(line, ".code")) {
if (strstr(line, ".code32")) skip_csect = use_64bit;
@ -325,12 +342,14 @@ static void add_instrumentation(void) {
/* Detect syntax changes, as could happen with hand-written assembly.
Skip Intel blocks, resume instrumentation when back to AT&T. */
//处理汇编语法变化。
//如果检测到 .intel_syntax则跳过插桩如果检测到 .att_syntax则恢复插桩
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. */
//处理 #APP 和 #NO_APP 块。
//如果检测到 #APP则跳过插桩如果检测到 #NO_APP则恢复插桩
if (line[0] == '#' || line[1] == '#') {
if (strstr(line, "#APP")) skip_app = 1;
@ -361,7 +380,8 @@ static void add_instrumentation(void) {
later on.
*/
//处理函数标签和条件分支。
//如果当前行是条件分支指令(如 jnz则插入插桩代码
if (skip_intel || skip_app || skip_csect || !instr_ok ||
line[0] == '#' || line[0] == ' ') continue;
@ -391,7 +411,8 @@ static void add_instrumentation(void) {
#ifdef __APPLE__
/* Apple: L<whatever><digit>: */
//处理标签。
//如果当前行是标签(如 .L0: 或 LBB0_0:),则根据需要设置 instrument_next表示后续需要插桩
if ((colon_pos = strstr(line, ":"))) {
if (line[0] == 'L' && isdigit(*(colon_pos - 1))) {
@ -450,10 +471,11 @@ static void add_instrumentation(void) {
}
}
//在所有插桩完成后,插入主插桩代码
if (ins_lines)
fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
//关闭文件并输出插桩结果。
//如果没有插桩目标,则输出警告;否则输出插桩的详细信息
if (input_file) fclose(inf);
fclose(outf);
@ -473,25 +495,30 @@ static void add_instrumentation(void) {
/* Main entry point */
//程序的主入口点。处理命令行参数设置随机种子调用edit_params来编辑参数
//根据环境变量AFL_INST_RATIO设置instrumentation概率
//然后调用add_instrumentation来添加instrumentation代码并最终执行as
int main(int argc, char** argv) {
s32 pid;
u32 rand_seed;
int status;
u8* inst_ratio_str = getenv("AFL_INST_RATIO");
s32 pid; //用于存储 fork 后的子进程 ID
u32 rand_seed; //用于存储随机种子
int status; //用于存储子进程的退出状态
u8* inst_ratio_str = getenv("AFL_INST_RATIO"); //从环境变量 AFL_INST_RATIO 中获取插桩比例
struct timeval tv;
struct timezone tz;
//检查是否处于 clang 模式。
//如果环境变量 CLANG_ENV_VAR 存在,则设置 clang_mode 为 1否则为 0
clang_mode = !!getenv(CLANG_ENV_VAR);
//检查是否在终端运行,并输出提示信息。
//如果标准错误输出是终端且未设置 AFL_QUIET则输出程序名称和版本信息否则设置 be_quiet 为 1表示静默模式
if (isatty(2) && !getenv("AFL_QUIET")) {
SAYF(cCYA "afl-as " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
} else be_quiet = 1;
//检查命令行参数是否足够。
//如果参数少于 2 个,则输出帮助信息并退出程序
if (argc < 2) {
SAYF("\n"
@ -506,22 +533,27 @@ int main(int argc, char** argv) {
exit(1);
}
//生成并设置随机种子。
//使用当前时间、微秒数和进程 ID 生成随机种子,并调用 srandom 设置随机数生成器
gettimeofday(&tv, &tz);
rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
srandom(rand_seed);
//调用 edit_params 函数处理命令行参数。
//该函数会解析命令行参数并设置相关变量(如 input_file、modified_file 等)
edit_params(argc, argv);
//从环境变量 AFL_INST_RATIO 中读取插桩比例。
//如果插桩比例无效(不在 0 到 100 之间),则输出错误信息并终止程序
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)");
}
//检查并设置环境变量 AS_LOOP_ENV_VAR。
//如果该环境变量已存在,则输出错误信息并终止程序(防止无限循环)。
//否则设置该环境变量为 "1"
if (getenv(AS_LOOP_ENV_VAR))
FATAL("Endless loop when calling 'as' (remove '.' from your PATH)");
@ -530,27 +562,34 @@ int main(int argc, char** argv) {
/* When compiling with ASAN, we don't have a particularly elegant way to skip
ASAN-specific branches. But we can probabilistically compensate for
that... */
//处理 ASAN 或 MSAN 模式。
//如果启用了 ASAN 或 MSAN则设置 sanitizer 为 1并将插桩比例除以 3
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
sanitizer = 1;
inst_ratio /= 3;
}
//调用 add_instrumentation 函数进行插桩。
//如果未设置 just_version则执行插桩操作
if (!just_version) add_instrumentation();
//创建子进程并执行 as 命令。
//使用 fork 创建子进程,并在子进程中调用 execvp 执行 as 命令。
//如果执行失败,则输出错误信息并终止程序
if (!(pid = fork())) {
execvp(as_params[0], (char**)as_params);
execvp(as_params[0], (char**)as_params);//传递给as的参数数组
FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]);
}
//等待子进程结束。
//如果 fork 失败,则输出错误信息并终止程序。
//使用 waitpid 等待子进程结束,并获取其退出状态
if (pid < 0) PFATAL("fork() failed");
if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
//删除临时文件。
//如果未设置 AFL_KEEP_ASSEMBLY则删除生成的临时文件
if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file);
//使用 WEXITSTATUS 获取子进程的退出状态,并将其作为程序的返回值
exit(WEXITSTATUS(status));
}

@ -111,7 +111,7 @@
oddball register it may touch.
*/
//32位系统的_trampoline_格式用于在函数调用前后保存和恢复寄存器状态并调用__afl_maybe_log函数
static const u8* trampoline_fmt_32 =
"\n"
@ -135,6 +135,7 @@ static const u8* trampoline_fmt_32 =
"/* --- END --- */\n"
"\n";
//64位系统的_trampoline_格式功能与32位版本类似
static const u8* trampoline_fmt_64 =
"\n"
@ -156,6 +157,7 @@ static const u8* trampoline_fmt_64 =
"/* --- END --- */\n"
"\n";
//这些是32位和64位系统的主要payload包含了AFL的instrumentation逻辑
static const u8* main_payload_32 =
"\n"
@ -167,7 +169,7 @@ static const u8* main_payload_32 =
".align 8\n"
"\n"
"__afl_maybe_log:\n"
"__afl_maybe_log:\n" //检查共享内存区域是否已经映射并调用__afl_store来记录分支信息
"\n"
" lahf\n"
" seto %al\n"
@ -178,7 +180,7 @@ static const u8* main_payload_32 =
" testl %edx, %edx\n"
" je __afl_setup\n"
"\n"
"__afl_store:\n"
"__afl_store:\n" //计算并存储指定代码位置的分支信息
"\n"
" /* Calculate and store hit for the code location specified in ecx. There\n"
" is a double-XOR way of doing this without tainting another register,\n"
@ -199,7 +201,7 @@ static const u8* main_payload_32 =
" incb (%edx, %edi, 1)\n"
#endif /* ^SKIP_COUNTS */
"\n"
"__afl_return:\n"
"__afl_return:\n" //处理返回前的标志寄存器
"\n"
" addb $127, %al\n"
" sahf\n"
@ -207,7 +209,7 @@ static const u8* main_payload_32 =
"\n"
".align 8\n"
"\n"
"__afl_setup:\n"
"__afl_setup:\n" //设置共享内存区域如果设置失败则跳转到__afl_setup_abort
"\n"
" /* Do not retry setup if we had previous failures. */\n"
"\n"
@ -249,7 +251,7 @@ static const u8* main_payload_32 =
" popl %ecx\n"
" popl %eax\n"
"\n"
"__afl_forkserver:\n"
"__afl_forkserver:\n" //进入fork服务器模式避免execve()调用的开销
"\n"
" /* Enter the fork server mode to avoid the overhead of execve() calls. */\n"
"\n"
@ -269,9 +271,9 @@ static const u8* main_payload_32 =
" addl $12, %esp\n"
"\n"
" cmpl $4, %eax\n"
" jne __afl_fork_resume\n"
" jne __afl_fork_resume\n" //在子进程中关闭文件描述符并继续执行
"\n"
"__afl_fork_wait_loop:\n"
"__afl_fork_wait_loop:\n" //等待父进程的指令如果读取失败则跳转到__afl_die
"\n"
" /* Wait for parent by reading from the pipe. Abort if read fails. */\n"
"\n"
@ -282,7 +284,7 @@ static const u8* main_payload_32 =
" addl $12, %esp\n"
"\n"
" cmpl $4, %eax\n"
" jne __afl_die\n"
" jne __afl_die\n" //终止当前进程
"\n"
" /* Once woken up, create a clone of our process. This is an excellent use\n"
" case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly\n"
@ -346,7 +348,7 @@ static const u8* main_payload_32 =
" xorl %eax, %eax\n"
" call _exit\n"
"\n"
"__afl_setup_abort:\n"
"__afl_setup_abort:\n" //记录设置失败避免重复调用shmget()/shmat()
"\n"
" /* Record setup failure so that we don't keep calling\n"
" shmget() / shmat() over and over again. */\n"
@ -356,7 +358,7 @@ static const u8* main_payload_32 =
" popl %eax\n"
" jmp __afl_return\n"
"\n"
".AFL_VARS:\n"
".AFL_VARS:\n" //定义了一系列全局变量包括共享内存区域指针、设置失败标志、前一个位置指针、fork的PID和临时变量
"\n"
" .comm __afl_area_ptr, 4, 32\n"
" .comm __afl_setup_failure, 1, 32\n"
@ -366,7 +368,7 @@ static const u8* main_payload_32 =
" .comm __afl_fork_pid, 4, 32\n"
" .comm __afl_temp, 4, 32\n"
"\n"
".AFL_SHM_ENV:\n"
".AFL_SHM_ENV:\n" //定义了共享内存环境变量的名称
" .asciz \"" SHM_ENV_VAR "\"\n"
"\n"
"/* --- END --- */\n"
@ -379,7 +381,7 @@ static const u8* main_payload_32 =
they are doing relocations differently from everybody else. We also need
to work around the crash issue with .lcomm and the fact that they don't
recognize .string. */
//由于这些平台在指令识别和库函数调用方面与其他平台不同,因此需要特定的代码来处理这些差异
#ifdef __APPLE__
# define CALL_L64(str) "call _" str "\n"
#else

@ -408,7 +408,7 @@ static void shuffle_ptrs(void** ptrs, u32 cnt) {
/* Build a list of processes bound to specific cores. Returns -1 if nothing
can be found. Assumes an upper bound of 4k CPUs. */
//绑定到空闲CPU核心。如果可能这个函数会将AFL的进程绑定到一个空闲的CPU核心上以减少上下文切换和调度延迟。
static void bind_to_free_cpu(void) {
DIR* d;
@ -722,7 +722,7 @@ static u8* DTD(u64 cur_ms, u64 event_ms) {
/* Mark deterministic checks as done for a particular queue entry. We use the
.state file to avoid repeating deterministic fuzzing when resuming aborted
scans. */
//mark_as_det_done 和 mark_as_variable共同用于标记测试用例的状态用于标记测试用例是否已经完成了确定性的测试或者是否表现出可变的行为。
static void mark_as_det_done(struct queue_entry* q) {
u8* fn = strrchr(q->fname, '/');
@ -838,7 +838,7 @@ static void add_to_queue(u8* fname, u32 len, u8 passed_det) {
/* Destroy the entire queue. */
//销毁队列的函数。它释放与测试用例队列相关的所有内存资源。
EXP_ST void destroy_queue(void) {
struct queue_entry *q = queue, *n;
@ -903,7 +903,7 @@ EXP_ST void read_bitmap(u8* fname) {
This function is called after every exec() on a fairly large buffer, so
it needs to be fast. We do this in 32-bit and 64-bit flavors. */
//检查执行路径是否带来了新的位图信息。这有助于AFL确定一个测试用例是否探索了新的代码路径。
static inline u8 has_new_bits(u8* virgin_map) {
#ifdef WORD_SIZE_64
@ -976,7 +976,7 @@ static inline u8 has_new_bits(u8* virgin_map) {
/* Count the number of bits set in the provided bitmap. Used for the status
screen several times every second, does not have to be fast. */
//计算位图中设置的位数,用于评估测试用例的覆盖率。
static u32 count_bits(u8* mem) {
u32* ptr = (u32*)mem;
@ -1011,7 +1011,7 @@ static u32 count_bits(u8* mem) {
/* Count the number of bytes set in the bitmap. Called fairly sporadically,
mostly to update the status screen or calibrate and examine confirmed
new paths. */
//计算位图中设置的字节数,用于评估测试用例的覆盖率。
static u32 count_bytes(u8* mem) {
u32* ptr = (u32*)mem;
@ -1077,7 +1077,7 @@ static const u8 simplify_lookup[256] = {
};
#ifdef WORD_SIZE_64
//简化跟踪信息,处理执行跟踪数据,以便更高效地存储和比较。
static void simplify_trace(u64* mem) {
u32 i = MAP_SIZE >> 3;
@ -1156,7 +1156,7 @@ static const u8 count_class_lookup8[256] = {
static u16 count_class_lookup16[65536];
//初始化计数分类表。这个函数用于设置一个查找表,该表用于快速分类和处理执行跟踪数据。
EXP_ST void init_count_class16(void) {
u32 b1, b2;
@ -1171,7 +1171,7 @@ EXP_ST void init_count_class16(void) {
#ifdef WORD_SIZE_64
//分类执行跟踪信息,处理执行跟踪数据,以便更高效地存储和比较。
static inline void classify_counts(u64* mem) {
u32 i = MAP_SIZE >> 3;
@ -1261,7 +1261,7 @@ static void minimize_bits(u8* dst, u8* src) {
The first step of the process is to maintain a list of top_rated[] entries
for every byte in the bitmap. We win that slot if there is no previous
contender, or if the contender has a more favorable speed x size factor. */
//更新位图分数的函数。它根据测试用例的执行时间和覆盖率来调整其在队列中的优先级。
static void update_bitmap_score(struct queue_entry* q) {
u32 i;
@ -1312,7 +1312,7 @@ static void update_bitmap_score(struct queue_entry* q) {
previously-unseen bytes (temp_v) and marks them as favored, at least
until the next run. The favored entries are given more air time during
all fuzzing steps. */
//筛选队列的函数。它通过评估队列中的测试用例,移除那些不再需要进一步测试的案例,以优化模糊测试的效率。
static void cull_queue(void) {
struct queue_entry* q;
@ -1367,7 +1367,7 @@ static void cull_queue(void) {
/* Configure shared memory and virgin_bits. This is called at startup. */
//设置共享内存。共享内存用于存储位图和其他状态信息,这些信息在目标程序的多个实例之间共享。
EXP_ST void setup_shm(void) {
u8* shm_str;
@ -1430,7 +1430,7 @@ static void setup_post(void) {
/* Read all testcases from the input directory, then queue them for testing.
Called at startup. */
//从输入目录中读取所有测试用例,并将它们排队以供测试。
static void read_testcases(void) {
struct dirent **nl;
@ -1687,7 +1687,7 @@ static void load_extras_file(u8* fname, u32* min_len, u32* max_len,
/* Read extras from the extras directory and sort them by size. */
//加载额外的测试用例,处理从外部源加载额外的测试用例。
static void load_extras(u8* dir) {
DIR* d;
@ -1905,7 +1905,7 @@ sort_a_extras:
/* Save automatically generated extras. */
//保存额外的测试用例,保存在模糊测试过程中自动生成的测试用例。
static void save_auto(void) {
u32 i;
@ -1933,7 +1933,7 @@ static void save_auto(void) {
/* Load automatically generated extras. */
// 用于加载自动生成的extras的函数。
static void load_auto(void) {
u32 i;
@ -1976,7 +1976,7 @@ static void load_auto(void) {
/* Destroy extras. */
// 用于销毁extras的函数。
static void destroy_extras(void) {
u32 i;
@ -2001,7 +2001,7 @@ static void destroy_extras(void) {
In essence, the instrumentation allows us to skip execve(), and just keep
cloning a stopped child. So, we just execute once, and then send commands
through a pipe. The other part of this logic is in afl-as.h. */
//初始化fork服务器的函数。在AFL中fork服务器是一种优化技术它允许AFL避免频繁地执行execve()来启动目标程序。相反,它通过克隆一个已经执行的目标程序进程来实现。
EXP_ST void init_forkserver(char** argv) {
static struct itimerval it;
@ -2286,7 +2286,7 @@ EXP_ST void init_forkserver(char** argv) {
/* Execute target application, monitoring for timeouts. Return status
information. The called program will update trace_bits[]. */
//运行目标程序并监控超时。它负责启动目标程序,等待其执行完成,并捕获任何超时或崩溃。
static u8 run_target(char** argv, u32 timeout) {
static struct itimerval it;
@ -2504,7 +2504,7 @@ static u8 run_target(char** argv, u32 timeout) {
/* Write modified data to file for testing. If out_file is set, the old file
is unlinked and a new one is created. Otherwise, out_fd is rewound and
truncated. */
//将修改后的数据写入测试用例文件。如果指定了输出文件,则会创建一个新文件;否则,会重置并截断现有的输出文件描述符。
static void write_to_testcase(void* mem, u32 len) {
s32 fd = out_fd;
@ -2561,13 +2561,13 @@ static void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) {
}
//显示模糊测试的统计信息,如执行速度、发现的路径数量、唯一崩溃等。
static void show_stats(void);
/* Calibrate a new test case. This is done when processing the input directory
to warn about flaky or otherwise problematic test cases early on; and when
new paths are discovered to detect variable behavior and so on. */
//校准测试用例的函数。它执行目标程序多次,以确定测试用例的执行时间和覆盖率,这有助于确定测试用例的优先级。
static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem,
u32 handicap, u8 from_queue) {
@ -2722,7 +2722,7 @@ abort_calibration:
/* Examine map coverage. Called once, for first test case. */
//检查位图覆盖率。如果位图的覆盖率不足,这个函数会提醒用户可能需要重新编译目标程序以获得更好的覆盖率。
static void check_map_coverage(void) {
u32 i;
@ -2739,7 +2739,7 @@ static void check_map_coverage(void) {
/* Perform dry run of all test cases to confirm that the app is working as
expected. This is done only for the initial inputs, and only once. */
//执行干运行。在正式开始模糊测试之前,这个函数用于验证目标程序的行为,确保它能够正确处理初始的测试用例集。
static void perform_dry_run(char** argv) {
struct queue_entry* q = queue;
@ -2971,7 +2971,7 @@ static void link_or_copy(u8* old_path, u8* new_path) {
}
// 用于删除用于即时会话恢复的临时目录的函数。
static void nuke_resume_dir(void);
/* Create hard links for input test cases in the output directory, choosing
@ -3417,7 +3417,7 @@ static void find_timeout(void) {
/* Update stats file for unattended monitoring. */
// 用于更新统计文件的函数,用于无人值守的监控。
static void write_stats_file(double bitmap_cvg, double stability, double eps) {
static double last_bcvg, last_stab, last_eps;
@ -3516,7 +3516,7 @@ static void write_stats_file(double bitmap_cvg, double stability, double eps) {
/* Update the plot file if there is a reason to. */
// 用于更新绘图文件的函数,如果有必要的话。
static void maybe_update_plot_file(double bitmap_cvg, double eps) {
static u32 prev_qp, prev_pf, prev_pnf, prev_ce, prev_md;
@ -3556,7 +3556,7 @@ static void maybe_update_plot_file(double bitmap_cvg, double eps) {
/* A helper function for maybe_delete_out_dir(), deleting all prefixed
files in a directory. */
// 用于删除所有以特定前缀开头的文件的辅助函数。
static u8 delete_files(u8* path, u8* prefix) {
DIR* d;
@ -3587,7 +3587,7 @@ static u8 delete_files(u8* path, u8* prefix) {
/* Get the number of runnable processes, with some simple smoothing. */
// 用于获取可运行进程数量的函数,带有一定的简单平滑处理。
static double get_runnable_processes(void) {
static double res;
@ -3680,7 +3680,7 @@ dir_cleanup_failed:
/* Delete fuzzer output directory if we recognize it as ours, if the fuzzer
is not currently running, and if the last run time isn't too great. */
// 用于删除输出目录的函数如果它被认为是我们的并且fuzzer没有在运行并且最后一次运行时间不是很长。
static void maybe_delete_out_dir(void) {
FILE* f;
@ -4646,7 +4646,7 @@ abort_trimming:
/* Write a modified test case, run program, process results. Handle
error conditions, returning 1 if it's time to bail out. This is
a helper function for fuzz_one(). */
//执行模糊测试的通用部分。它负责将变异后的测试用例写入文件,执行目标程序,并处理执行结果。
EXP_ST u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) {
u8 fault;
@ -5000,6 +5000,7 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) {
function is a tad too long... returns 0 if fuzzed successfully, 1 if
skipped or bailed out. */
// 执行一次模糊测试的函数。它接受一个测试用例,应用各种变异技术来生成新的测试用例,然后执行目标程序来检查新测试用例的效果。
static u8 fuzz_one(char** argv) {
s32 len, fd, temp_len, i, j;
@ -6691,7 +6692,7 @@ abandon_entry:
/* Grab interesting test cases from other fuzzers. */
//在分布式模糊测试中这个函数用于同步不同fuzzer实例的进度。
static void sync_fuzzers(char** argv) {
DIR* sd;
@ -6829,7 +6830,7 @@ static void sync_fuzzers(char** argv) {
/* Handle stop signal (Ctrl-C, etc). */
//处理停止信号如Ctrl+C的函数。它设置一个标志来告诉主循环停止执行。
static void handle_stop_sig(int sig) {
stop_soon = 1;
@ -6841,7 +6842,7 @@ static void handle_stop_sig(int sig) {
/* Handle skip request (SIGUSR1). */
// 用于处理用户请求跳过当前输入的信号SIGUSR1的函数。
static void handle_skipreq(int sig) {
skip_requested = 1;
@ -6849,7 +6850,7 @@ static void handle_skipreq(int sig) {
}
/* Handle timeout (SIGALRM). */
//处理超时信号的函数。它设置一个标志来指示目标程序已经超时。
static void handle_timeout(int sig) {
if (child_pid > 0) {
@ -6870,7 +6871,7 @@ static void handle_timeout(int sig) {
/* Do a PATH search and find target binary to see that it exists and
isn't a shell script - a common and painful mistake. We also check for
a valid ELF header and for evidence of AFL instrumentation. */
//检查目标二进制文件是否存在是否可执行以及是否具有AFL所需的 instrumentation instrumentation是AFL用来追踪执行路径的一种技术
EXP_ST void check_binary(u8* fname) {
u8* env_path = 0;
@ -7067,7 +7068,7 @@ static void fix_up_banner(u8* name) {
/* Check if we're on TTY. */
//检查程序是否运行在TTY上。这个函数用于确定输出是否应该是交互式的以及是否应该显示进度信息。
static void check_if_tty(void) {
struct winsize ws;
@ -7150,7 +7151,7 @@ static void usage(u8* argv0) {
/* Prepare output directories and fds. */
//准备输出目录和文件描述符。它确保所有必要的目录都存在,并设置了一些文件描述符,以便在模糊测试过程中使用。
EXP_ST void setup_dirs_fds(void) {
u8* tmp;
@ -7273,7 +7274,7 @@ EXP_ST void setup_dirs_fds(void) {
/* Setup the output file for fuzzed data, if not using -f. */
// 准备输出文件用于测试的函数。如果指定了输出文件,则会创建一个新文件;否则,会重置并截断现有的输出文件描述符。
EXP_ST void setup_stdio_file(void) {
u8* fn = alloc_printf("%s/.cur_input", out_dir);
@ -7356,7 +7357,7 @@ static void check_crash_handling(void) {
/* Check CPU governor. */
//检查CPU调速器设置的函数。确保CPU在高负载下不会降频从而影响测试的执行速度。
static void check_cpu_governor(void) {
FILE* f;
@ -7412,7 +7413,7 @@ static void check_cpu_governor(void) {
/* Count the number of logical CPU cores. */
//获取CPU核心数。这个函数用于确定系统中可用的CPU核心数以便AFL可以有效地分配工作负载。
static void get_core_count(void) {
u32 cur_runnable = 0;
@ -7541,14 +7542,14 @@ static void fix_up_sync(void) {
/* Handle screen resize (SIGWINCH). */
// 处理屏幕大小变化的信号处理函数。
static void handle_resize(int sig) {
clear_screen = 1;
}
/* Check ASAN options. */
// 检查ASAN选项的函数。
static void check_asan_opts(void) {
u8* x = getenv("ASAN_OPTIONS");
@ -7579,7 +7580,7 @@ static void check_asan_opts(void) {
/* Detect @@ in args. */
// 用于处理文件名中'@@'参数的函数。
EXP_ST void detect_file_args(char** argv) {
u32 i = 0;
@ -7628,7 +7629,7 @@ EXP_ST void detect_file_args(char** argv) {
/* Set up signal handlers. More complicated that needs to be, because libc on
Solaris doesn't resume interrupted reads(), sets SA_RESETHAND when you call
siginterrupt(), and does other unnecessary things. */
// 设置信号处理程序的函数。
EXP_ST void setup_signal_handlers(void) {
struct sigaction sa;
@ -7671,7 +7672,7 @@ EXP_ST void setup_signal_handlers(void) {
/* Rewrite argv for QEMU. */
// 用于重写argv以用于QEMU的函数。
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
@ -7774,7 +7775,7 @@ static void save_cmdline(u32 argc, char** argv) {
#ifndef AFL_LIB
/* Main entry point */
//程序的主入口点。它处理命令行参数,设置信号处理程序,初始化共享内存,读取测试用例,执行干运行以验证目标程序,然后进入主循环进行模糊测试。
int main(int argc, char** argv) {
s32 opt;
@ -8194,4 +8195,4 @@ stop_fuzzing:
}
#endif /* !AFL_LIB */
#endif /* !AFL_LIB */

@ -60,7 +60,7 @@ static u8 be_quiet, /* Quiet mode */
/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
from argv[0]. If that fails, abort. */
// 查找并设置汇编器as的路径这是AFL编译过程中需要的。
static void find_as(u8* argv0) {
u8 *afl_path = getenv("AFL_PATH");
@ -114,7 +114,7 @@ static void find_as(u8* argv0) {
/* Copy argv to cc_params, making the necessary edits. */
// 复制并编辑参数为调用实际的编译器如gcc或clang做准备
static void edit_params(u32 argc, char** argv) {
u8 fortify_set = 0, asan_set = 0;
@ -306,7 +306,7 @@ static void edit_params(u32 argc, char** argv) {
/* Main entry point */
// 程序的主入口点,处理命令行参数并调用实际的编译器。
int main(int argc, char** argv) {
if (isatty(2) && !getenv("AFL_QUIET")) {

Loading…
Cancel
Save