From 8f017a1f4cdb90084ec8df71a79d416b2c28abee Mon Sep 17 00:00:00 2001 From: em0 <1834530227@qq.com> Date: Wed, 8 Jan 2025 21:49:05 +0800 Subject: [PATCH] aaa --- src/afl-showmap.c | 592 ++++++++++++++++------------------------------ 1 file changed, 206 insertions(+), 386 deletions(-) diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 0bc41db..f9b667f 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -111,132 +111,132 @@ static const u8 count_class_binary[256] = { }; +/* 对计数进行分类,根据 edges_only 标志决定是否忽略命中计数 */ static void classify_counts(u8* mem, const u8* map) { u32 i = MAP_SIZE; if (edges_only) { + // 如果 edges_only 为真,只保留是否命中的信息(1 或 0) while (i--) { - if (*mem) *mem = 1; - mem++; + if (*mem) *mem = 1; // 如果 mem 中的值不为 0,则将其设置为 1 + mem++; // 移动到下一个字节 } } else { + // 如果 edges_only 为假,使用 map 中的值来更新 mem 中的值 while (i--) { - *mem = map[*mem]; - mem++; + *mem = map[*mem]; // 使用 map 中的值替换 mem 中的值 + mem++; // 移动到下一个字节 } } } - -/* Get rid of shared memory (atexit handler). */ - +/* 清理共享内存(atexit 处理程序) */ static void remove_shm(void) { - shmctl(shm_id, IPC_RMID, NULL); + shmctl(shm_id, IPC_RMID, NULL); // 删除共享内存段 } - -/* Configure shared memory. */ - +/* 配置共享内存 */ static void setup_shm(void) { u8* shm_str; + // 创建一个新的共享内存段 shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); - if (shm_id < 0) PFATAL("shmget() failed"); + if (shm_id < 0) PFATAL("shmget() failed"); // 如果创建失败,输出错误信息并退出 - atexit(remove_shm); + atexit(remove_shm); // 注册 atexit 处理程序,确保程序退出时删除共享内存 - shm_str = alloc_printf("%d", shm_id); + shm_str = alloc_printf("%d", shm_id); // 将共享内存 ID 转换为字符串 - setenv(SHM_ENV_VAR, shm_str, 1); + setenv(SHM_ENV_VAR, shm_str, 1); // 将共享内存 ID 设置到环境变量中 - ck_free(shm_str); + ck_free(shm_str); // 释放分配的内存 - trace_bits = shmat(shm_id, NULL, 0); + trace_bits = shmat(shm_id, NULL, 0); // 将共享内存附加到当前进程的地址空间 - if (trace_bits == (void *)-1) PFATAL("shmat() failed"); + if (trace_bits == (void *)-1) PFATAL("shmat() failed"); // 如果附加失败,输出错误信息并退出 } -/* Write results. */ - +/* 写入结果 */ static u32 write_results(void) { s32 fd; u32 i, ret = 0; + // 获取环境变量 AFL_CMIN_CRASHES_ONLY 和 AFL_CMIN_ALLOW_ANY 的值 u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"), caa = !!getenv("AFL_CMIN_ALLOW_ANY"); + // 根据 out_file 的值决定如何打开输出文件 if (!strncmp(out_file, "/dev/", 5)) { - fd = open(out_file, O_WRONLY, 0600); - if (fd < 0) PFATAL("Unable to open '%s'", out_file); + fd = open(out_file, O_WRONLY, 0600); // 打开设备文件 + if (fd < 0) PFATAL("Unable to open '%s'", out_file); // 如果打开失败,输出错误信息并退出 } else if (!strcmp(out_file, "-")) { - fd = dup(1); - if (fd < 0) PFATAL("Unable to open stdout"); + fd = dup(1); // 如果输出文件是 "-",则复制标准输出文件描述符 + if (fd < 0) PFATAL("Unable to open stdout"); // 如果复制失败,输出错误信息并退出 } else { - unlink(out_file); /* Ignore errors */ - fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", out_file); + unlink(out_file); /* Ignore errors */ // 删除已存在的文件(忽略错误) + fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建新文件 + if (fd < 0) PFATAL("Unable to create '%s'", out_file); // 如果创建失败,输出错误信息并退出 } - if (binary_mode) { + // 如果处于二进制模式,直接写入 trace_bits 的内容 for (i = 0; i < MAP_SIZE; i++) - if (trace_bits[i]) ret++; + if (trace_bits[i]) ret++; // 统计非零的字节数 - ck_write(fd, trace_bits, MAP_SIZE, out_file); - close(fd); + ck_write(fd, trace_bits, MAP_SIZE, out_file); // 将 trace_bits 写入文件 + close(fd); // 关闭文件描述符 } else { - FILE* f = fdopen(fd, "w"); + FILE* f = fdopen(fd, "w"); // 将文件描述符转换为 FILE 指针 - if (!f) PFATAL("fdopen() failed"); + if (!f) PFATAL("fdopen() failed"); // 如果转换失败,输出错误信息并退出 for (i = 0; i < MAP_SIZE; i++) { - if (!trace_bits[i]) continue; - ret++; + if (!trace_bits[i]) continue; // 如果 trace_bits[i] 为 0,跳过 + ret++; // 统计非零的字节数 if (cmin_mode) { - if (child_timed_out) break; - if (!caa && child_crashed != cco) break; + // 如果处于 cmin_mode,根据条件决定是否写入 + if (child_timed_out) break; // 如果子进程超时,停止写入 + if (!caa && child_crashed != cco) break; // 如果不允许任意崩溃且崩溃状态不匹配,停止写入 - fprintf(f, "%u%u\n", trace_bits[i], i); + fprintf(f, "%u%u\n", trace_bits[i], i); // 写入 trace_bits[i] 和索引 i - } else fprintf(f, "%06u:%u\n", i, trace_bits[i]); + } else fprintf(f, "%06u:%u\n", i, trace_bits[i]); // 否则,写入索引 i 和 trace_bits[i] } - fclose(f); + fclose(f); // 关闭文件 } - return ret; + return ret; // 返回写入的非零字节数 } - -/* Handle timeout signal. */ - +/* 处理超时信号 */ static void handle_timeout(int sig) { child_timed_out = 1; @@ -244,124 +244,118 @@ static void handle_timeout(int sig) { } - -/* Execute target application. */ - +/* 执行目标程序 */ static void run_target(char** argv) { - static struct itimerval it; - int status = 0; + static struct itimerval it; // 定义一个定时器结构体 + int status = 0; // 用于存储子进程的状态 if (!quiet_mode) - SAYF("-- Program output begins --\n" cRST); + SAYF("-- Program output begins --\n" cRST); // 如果不是静默模式,输出程序开始信息 - MEM_BARRIER(); + MEM_BARRIER(); // 内存屏障,确保内存操作顺序 - child_pid = fork(); + child_pid = fork(); // 创建子进程 - if (child_pid < 0) PFATAL("fork() failed"); + if (child_pid < 0) PFATAL("fork() failed"); // 如果fork失败,输出错误信息并退出 - if (!child_pid) { + if (!child_pid) { // 如果是子进程 - struct rlimit r; + struct rlimit r; // 定义一个资源限制结构体 - if (quiet_mode) { + if (quiet_mode) { // 如果是静默模式 - s32 fd = open("/dev/null", O_RDWR); + s32 fd = open("/dev/null", O_RDWR); // 打开/dev/null以忽略输出 - if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) { - *(u32*)trace_bits = EXEC_FAIL_SIG; - PFATAL("Descriptor initialization failed"); + if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) { // 将标准输出和标准错误重定向到/dev/null + *(u32*)trace_bits = EXEC_FAIL_SIG; // 如果失败,设置trace_bits为EXEC_FAIL_SIG + PFATAL("Descriptor initialization failed"); // 输出错误信息并退出 } - close(fd); + close(fd); // 关闭/dev/null文件描述符 } - if (mem_limit) { + if (mem_limit) { // 如果设置了内存限制 - r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; + r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; // 设置内存限制为指定的MB数 #ifdef RLIMIT_AS - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + setrlimit(RLIMIT_AS, &r); /* Ignore errors */ // 设置地址空间大小限制,忽略错误 #else - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ + setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ // 设置数据段大小限制,忽略错误 #endif /* ^RLIMIT_AS */ } - if (!keep_cores) r.rlim_max = r.rlim_cur = 0; - else r.rlim_max = r.rlim_cur = RLIM_INFINITY; + if (!keep_cores) r.rlim_max = r.rlim_cur = 0; // 如果不需要核心转储文件,设置核心转储大小为0 + else r.rlim_max = r.rlim_cur = RLIM_INFINITY; // 否则设置为无限大小 - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ // 设置核心转储大小限制,忽略错误 - if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); + if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); // 设置环境变量以立即绑定动态链接库 - setsid(); + setsid(); // 创建一个新的会话,使子进程成为会话首进程,并与控制台分离 - execv(target_path, argv); + execv(target_path, argv); // 执行目标程序 - *(u32*)trace_bits = EXEC_FAIL_SIG; - exit(0); + *(u32*)trace_bits = EXEC_FAIL_SIG; // 如果execv失败,设置trace_bits为EXEC_FAIL_SIG + exit(0); // 退出子进程 } - /* Configure timeout, wait for child, cancel timeout. */ - - if (exec_tmout) { + /* 配置超时,等待子进程,取消超时 */ + if (exec_tmout) { // 如果设置了执行超时时间 - child_timed_out = 0; - it.it_value.tv_sec = (exec_tmout / 1000); - it.it_value.tv_usec = (exec_tmout % 1000) * 1000; + child_timed_out = 0; // 初始化子进程超时标志为未超时 + it.it_value.tv_sec = (exec_tmout / 1000); // 设置定时器的秒数部分 + it.it_value.tv_usec = (exec_tmout % 1000) * 1000; // 设置定时器的微秒数部分 } - setitimer(ITIMER_REAL, &it, NULL); + setitimer(ITIMER_REAL, &it, NULL); // 设置定时器 - if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); + if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); // 等待子进程结束,如果失败则输出错误信息并退出 - child_pid = 0; - it.it_value.tv_sec = 0; - it.it_value.tv_usec = 0; - setitimer(ITIMER_REAL, &it, NULL); + child_pid = 0; // 重置子进程ID + it.it_value.tv_sec = 0; // 重置定时器的秒数部分 + it.it_value.tv_usec = 0; // 重置定时器的微秒数部分 + setitimer(ITIMER_REAL, &it, NULL); // 取消定时器 - MEM_BARRIER(); + MEM_BARRIER(); // 内存屏障,确保内存操作顺序 - /* Clean up bitmap, analyze exit condition, etc. */ + /* 清理位图,分析退出条件等 */ + if (*(u32*)trace_bits == EXEC_FAIL_SIG) // 如果trace_bits等于EXEC_FAIL_SIG,表示程序执行失败 + FATAL("Unable to execute '%s'", argv[0]); // 输出错误信息并退出 - if (*(u32*)trace_bits == EXEC_FAIL_SIG) - FATAL("Unable to execute '%s'", argv[0]); + classify_counts(trace_bits, binary_mode ? // 根据二进制模式选择分类方法 + count_class_binary : count_class_human); // 分类trace_bits中的覆盖率信息 - classify_counts(trace_bits, binary_mode ? - count_class_binary : count_class_human); + if (!quiet_mode) // 如果不是静默模式 + SAYF(cRST "-- Program output ends --\n"); // 输出程序结束信息 - if (!quiet_mode) - SAYF(cRST "-- Program output ends --\n"); - - if (!child_timed_out && !stop_soon && WIFSIGNALED(status)) - child_crashed = 1; + if (!child_timed_out && !stop_soon && WIFSIGNALED(status)) // 如果子进程没有超时、没有被用户中断且以信号方式结束 + child_crashed = 1; // 设置子进程崩溃标志为真 - if (!quiet_mode) { + if (!quiet_mode) { // 如果不是静默模式 if (child_timed_out) - SAYF(cLRD "\n+++ Program timed off +++\n" cRST); + SAYF(cLRD "\n+++ Program timed off +++\n" cRST); // 如果子进程超时,输出超时信息 else if (stop_soon) - SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST); + SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST); // 如果用户中断,输出中断信息 else if (child_crashed) - SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status)); + SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status)); // 如果子进程崩溃,输出崩溃信号信息 } - } -/* Handle Ctrl-C and the like. */ - +/* 处理 Ctrl-C 等信号 */ static void handle_stop_sig(int sig) { stop_soon = 1; @@ -370,9 +364,7 @@ static void handle_stop_sig(int sig) { } - -/* Do basic preparations - persistent fds, filenames, etc. */ - +/* 进行基本准备 - 持久化文件描述符、文件名等 */ static void set_up_environment(void) { setenv("ASAN_OPTIONS", "abort_on_error=1:" @@ -393,9 +385,7 @@ static void set_up_environment(void) { } - -/* Setup signal handlers, duh. */ - +/* 设置信号处理程序 */ static void setup_signal_handlers(void) { struct sigaction sa; @@ -406,382 +396,212 @@ static void setup_signal_handlers(void) { sigemptyset(&sa.sa_mask); - /* Various ways of saying "stop". */ - + /* 各种停止信号的处理 */ sa.sa_handler = handle_stop_sig; sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); - /* Exec timeout notifications. */ - + /* 执行超时通知的处理 */ sa.sa_handler = handle_timeout; sigaction(SIGALRM, &sa, NULL); } - -/* Detect @@ in args. */ - +/* 检测参数中的 @@ */ static void detect_file_args(char** argv) { - u32 i = 0; - u8* cwd = getcwd(NULL, 0); + u32 i = 0; // 初始化索引变量 i 用于遍历 argv 数组 + u8* cwd = getcwd(NULL, 0); // 获取当前工作目录的路径 - if (!cwd) PFATAL("getcwd() failed"); + if (!cwd) PFATAL("getcwd() failed"); // 如果获取当前工作目录失败,则输出错误信息并终止程序 - while (argv[i]) { + while (argv[i]) { // 遍历命令行参数数组,直到遇到 NULL 结束符 - u8* aa_loc = strstr(argv[i], "@@"); + u8* aa_loc = strstr(argv[i], "@@"); // 查找当前参数中是否包含 "@@" 字符串 - if (aa_loc) { + if (aa_loc) { // 如果找到了 "@@" 字符串 u8 *aa_subst, *n_arg; - if (!at_file) FATAL("@@ syntax is not supported by this tool."); - - /* Be sure that we're always using fully-qualified paths. */ + if (!at_file) FATAL("@@ syntax is not supported by this tool."); // 如果 at_file 为空,则 "@@" 语法不被支持,输出错误信息并终止程序 - if (at_file[0] == '/') aa_subst = at_file; - else aa_subst = alloc_printf("%s/%s", cwd, at_file); + /* 确保始终使用完全限定的路径 */ + if (at_file[0] == '/') aa_subst = at_file; // 如果 at_file 是绝对路径,则直接使用 + else aa_subst = alloc_printf("%s/%s", cwd, at_file); // 如果 at_file 是相对路径,则将其转换为绝对路径 - /* Construct a replacement argv value. */ + /* 构造替换的 argv 值 */ + *aa_loc = 0; // 在 "@@" 出现的位置临时截断字符串 + n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); // 将 aa_subst 替换到 "@@" 的位置,形成新的命令行参数 + argv[i] = n_arg; // 更新 argv 数组中的当前参数为替换后的值 + *aa_loc = '@'; // 恢复原字符串的 "@@" 部分,以便在后续处理中保持一致 - *aa_loc = 0; - n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); - argv[i] = n_arg; - *aa_loc = '@'; - - // 如果 at_file 不是绝对路径,则释放 aa_subst 内存 - if (at_file[0] != '/') ck_free(aa_subst); + if (at_file[0] != '/') ck_free(aa_subst); // 如果 at_file 是相对路径,则释放通过 alloc_printf 分配的内存空间 } - i++; // 进入下一个参数 + i++; // 移动到下一个命令行参数 } - free(cwd); // 释放当前工作目录内存,但不进行追踪 + free(cwd); // 释放通过 getcwd 分配的内存空间 } -// 显示工具的横幅信息 +/* 显示工具的横幅信息 */ static void show_banner(void) { - // 输出工具名称以及作者信息 - SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by \n"); + SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by \n"); // 输出工具的名称、版本号及作者信息,使用彩色控制码美化输出 } -// 显示用法提示信息 +/* 显示用法提示信息 */ static void usage(u8* argv0) { - show_banner(); // 显示横幅信息 - - // 输出工具的用法信息和参数说明 - SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" + show_banner(); // 首先显示横幅信息 - "Required parameters:\n\n" + SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" // 输出基本用法示例 - " -o file - file to write the trace data to\n\n" // 输出文件参数 + "必需的参数:\n\n" - // 输出工具的用法信息和参数说明 - "Execution control settings:\n\n" + " -o file - 将跟踪数据写入的文件\n\n" // 解释 -o 选项的作用,即指定输出跟踪数据的文件 - " -t msec - timeout for each run (none)\n" // 设置每次执行的超时时间,单位为毫秒 - " -m megs - memory limit for child process (%u MB)\n" // 设置子进程的内存限制,单位为MB + "执行控制设置:\n\n" - // 其他参数和设置选项的说明 - "Other settings:\n\n" + " -t msec - 每次运行的超时时间 (无限制)\n" // 解释 -t 选项的作用,即设置每个测试用例的超时时间 + " -m megs - 子进程的内存限制 (%u MB)\n" // 解释 -m 选项的作用,即设置子进程的内存限制,默认值为 MEM_LIMIT - " -q - sink program's output and don't show messages\n" // 隐藏程序输出,不显示信息 - " -e - show edge coverage only, ignore hit counts\n" // 仅显示边缘覆盖,忽略命中计数 - " -c - allow core dumps\n" // 允许生成核心转储 - " -V - show version number and exit\n\n" // 显示版本号并退出 + "其他设置:\n\n" - // 说明该工具的功能,并指向额外的帮助信息 - "This tool displays raw tuple data captured by AFL instrumentation.\n" - "For additional help, consult %s/README.\n\n" cRST, + " -q - 静默程序的输出,不显示消息\n" // 解释 -q 选项的作用,即抑制程序的输出 + " -e - 仅显示边缘覆盖率,忽略命中次数\n" // 解释 -e 选项的作用,即只显示边缘覆盖率而不关心具体命中次数 + " -c - 允许生成核心转储文件\n" // 解释 -c 选项的作用,即允许程序在崩溃时生成核心转储文件 + " -V - 显示版本号并退出\n\n" // 解释 -V 选项的作用,即显示工具的版本号后退出程序 - argv0, MEM_LIMIT, doc_path); // 输出用法信息,包括程序名、内存限制和文档路径 + "此工具显示由 AFL 仪器化捕获的原始元组数据。\n" // 说明此工具的功能 + "更多信息,请参考 %s/README。\n\n" cRST, // 提供更多帮助信息的文件路径,并使用彩色控制码重置输出格式 - exit(1); // 退出程序,返回错误状态 + argv0, MEM_LIMIT, doc_path); + exit(1); // 输出完帮助信息后,退出程序,返回状态码 1 } -// 查找可执行二进制文件的函数 static void find_binary(u8* fname) { - u8* env_path = 0; // 环境变量路径 - struct stat st; // 文件状态信息 + // 定义环境变量路径和文件状态结构体 + u8* env_path = 0; + struct stat st; - // 如果文件名包含 '/' 或者环境变量 PATH 不存在,直接使用提供的路径 + // 如果文件名中包含 '/', 或者环境变量 "PATH" 不存在 if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { - target_path = ck_strdup(fname); // 复制目标路径 + // 直接将文件名复制为目标路径 + target_path = ck_strdup(fname); - // 检查文件的状态,确认其存在且可执行 + // 检查文件是否存在、是否是普通文件、是否可执行、且大小是否至少为4字节 if (stat(target_path, &st) || !S_ISREG(st.st_mode) || !(st.st_mode & 0111) || st.st_size < 4) - FATAL("Program '%s' not found or not executable", fname); // 如果不可执行,则报错 + // 如果上述任一条件不满足,输出错误信息并终止程序 + FATAL("Program '%s' not found or not executable", fname); } else { - // 逐个遍历 PATH 中的目录,并查找目标程序 + // 环境变量 "PATH" 存在,逐个检查其中的路径 while (env_path) { - u8 *cur_elem, *delim = strchr(env_path, ':'); // 分割当前路径元素 + u8 *cur_elem, *delim = strchr(env_path, ':'); - // 如果找到分隔符,则处理当前路径元素 + // 检查当前路径元素是否以 ':' 结尾 if (delim) { - cur_elem = ck_alloc(delim - env_path + 1); // 分配内存 - memcpy(cur_elem, env_path, delim - env_path); // 复制当前路径 - delim++; // 指向下一个路径元素 + // 分配内存并复制当前路径元素 + cur_elem = ck_alloc(delim - env_path + 1); + memcpy(cur_elem, env_path, delim - env_path); + delim++; } else - cur_elem = ck_strdup(env_path); // 没有分隔符,直接复制 + // 如果没有 ':',则直接复制整个环境变量路径 + cur_elem = ck_strdup(env_path); - env_path = delim; // 更新环境路径 + // 更新环境变量路径指针到下一个元素 + env_path = delim; - // 构造目标路径 + // 如果当前路径元素不为空,将文件名添加到该路径后面形成完整路径 if (cur_elem[0]) - target_path = alloc_printf("%s/%s", cur_elem, fname); // 拼接路径 + target_path = alloc_printf("%s/%s", cur_elem, fname); else - target_path = ck_strdup(fname); // 若当前路径为空则直接使用文件名 + // 如果当前路径元素为空,则直接使用文件名作为目标路径 + target_path = ck_strdup(fname); - ck_free(cur_elem); // 释放当前路径元素的内存 + // 释放当前路径元素的空间 + ck_free(cur_elem); - // 检查拼接后的目标路径是否有效 + // 检查形成的路径是否指向一个存在且可执行的文件 if (!stat(target_path, &st) && S_ISREG(st.st_mode) && - (st.st_mode & 0111) && st.st_size >= 4) break; // 找到可执行文件,退出查找 + (st.st_mode & 0111) && st.st_size >= 4) break; - ck_free(target_path); // 释放无效目标路径内存 - target_path = 0; // 重新初始化目标路径 + // 如果当前路径无效,释放目标路径的空间并重置目标路径指针 + ck_free(target_path); + target_path = 0; } - // 如果没有找到目标程序,则报错 + // 如果遍历完所有路径后仍未找到目标程序,输出错误信息并终止程序 if (!target_path) FATAL("Program '%s' not found or not executable", fname); } } - - -// 修复针对 QEMU 的 argv 参数 +/* 修复针对 QEMU 的 argv 参数 */ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { - char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); // 分配新的参数数组内存 + // 分配足够的空间存储新的 argv 数组,包括额外的四个参数 + char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); u8 *tmp, *cp, *rsl, *own_copy; - // 为了处理 QEMU 的稳定性问题,禁止链式调用 + // 设置 QEMU 日志环境变量为 "nochain" setenv("QEMU_LOG", "nochain", 1); - // 将原始参数复制到新的参数数组中 + // 将原始 argv 数组中的参数从第二个开始复制到新的 argv 数组中 memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); - new_argv[2] = target_path; // 新参数的第三个元素为目标路径 - new_argv[1] = "--"; // 添加分隔符 " -- " 以告诉 QEMU 参数的结束 + // 将目标程序路径设置为新的 argv 数组的第三个参数 + new_argv[2] = target_path; + // 设置 "--" 作为新的 argv 数组的第二个参数 + new_argv[1] = "--"; - // 查找 afl-qemu-trace 的路径,使用 AFL_PATH 环境变量 + // 检查环境变量 "AFL_PATH" 是否存在 tmp = getenv("AFL_PATH"); if (tmp) { - cp = alloc_printf("%s/afl-qemu-trace", tmp); // 拼接 AFL_PATH + // 如果存在,构建 afl-qemu-trace 工具的完整路径 + cp = alloc_printf("%s/afl-qemu-trace", tmp); - // 如果没有找到,则报错 + // 检查 afl-qemu-trace 工具是否存在且可执行 if (access(cp, X_OK)) + // 如果工具不存在或不可执行,输出错误信息并终止程序 FATAL("Unable to find '%s'", tmp); - target_path = new_argv[0] = cp; // 将找到的路径赋值给目标路径 - return new_argv; // 返回新的参数数组 + // 更新目标路径和新的 argv 数组的第一个参数为 afl-qemu-trace 工具的路径 + target_path = new_argv[0] = cp; + return new_argv; } - // 复制当前程序的路径以查找 afl-qemu-trace + // 如果环境变量 "AFL_PATH" 不存在,复制当前程序的位置 own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); // 找到最后一个 '/' 的位置 + // 查找最后一个 '/' 的位置 + rsl = strrchr(own_copy, '/'); if (rsl) { - *rsl = 0; // 将最后一个 '/' 替换为终止符 + // 将最后一个 '/' 替换为0,截断字符串以获得目录路径 + *rsl = 0; - // 拼接 afl-qemu-trace 路径 + // 构建 afl-qemu-trace 工具的完整路径 cp = alloc_printf("%s/afl-qemu-trace", own_copy); - ck_free(own_copy); // 释放复制的路径内存 + // 释放复制的程序位置的空间 + ck_free(own_copy); - // 检查拼接后的路径是否有效 + // 检查 afl-qemu-trace 工具是否存在且可执行 if (!access(cp, X_OK)) { - target_path = new_argv[0] = cp; // 更新目标路径 - return new_argv; // 返回新的参数数组 + // 如果工具存在且可执行,更新目标路径和新的 argv 数组的第一个参数 + target_path = new_argv[0] = cp; + return new_argv; } } else - ck_free(own_copy); // 如果没有找到 '/',则释放内存 + // 如果没有找到 '/',直接释放复制的程序位置的空间 + ck_free(own_copy); - // 在默认的 BIN_PATH 查找 afl-qemu-trace + // 如果上述方法都未能找到 afl-qemu-trace 工具,检查预定义的二进制路径 if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { - target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; // 设置目标路径 - return new_argv; // 返回新的参数数组 - } - - // 如果以上步骤都未能找到 afl-qemu-trace,则报错 - FATAL("Unable to find 'afl-qemu-trace'."); -} - - - -/* - Main entry point -*/ -int main(int argc, char** argv) { - - s32 opt; // 选项变量 - u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0; // 标志变量 - u32 tcnt; // 计数变量,用于记录捕获的元组数量 - char** use_argv; // 用于存储最终的参数数组 - - // 检查文档路径是否存在,用于后续帮助信息 - doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; - - // 处理命令行参数 - while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQbcV")) > 0) - switch (opt) { - case 'o': - // 处理输出文件参数 - if (out_file) FATAL("Multiple -o options not supported"); // 防止重复设置 - out_file = optarg; // 保存输出文件路径 - break; - - case 'm': { - u8 suffix = 'M'; // 默认单位为MB - - // 检查是否已经设置了内存限制 - if (mem_limit_given) FATAL("Multiple -m options not supported"); - mem_limit_given = 1; - - // 如果输入为 "none",则不设置内存限制 - if (!strcmp(optarg, "none")) { - mem_limit = 0; // 设置内存限制为0 - break; - } - - // 解析输入的内存限制 - if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -m"); - - // 根据单位调整内存限制 - switch (suffix) { - case 'T': mem_limit *= 1024 * 1024; break; // TB - case 'G': mem_limit *= 1024; break; // GB - case 'k': mem_limit /= 1024; break; // kB - case 'M': break; // MB - default: FATAL("Unsupported suffix or bad syntax for -m"); // 报错 - } - - // 设置的内存值不能低于阈值5 - if (mem_limit < 5) FATAL("Dangerously low value of -m"); - - // 在32位系统中,内存限制不能超过2000MB - if (sizeof(rlim_t) == 4 && mem_limit > 2000) - FATAL("Value of -m out of range on 32-bit systems"); - } - break; - - case 't': - // 处理超时时间参数 - if (timeout_given) FATAL("Multiple -t options not supported"); - timeout_given = 1; - - // 如果不为 "none",则将输入解析为超时时间 - if (strcmp(optarg, "none")) { - exec_tmout = atoi(optarg); // 将时间转换为整数 - - // 超时时间设置不能低于20毫秒 - if (exec_tmout < 20 || optarg[0] == '-') - FATAL("Dangerously low value of -t"); - } - break; - - case 'e': - // 处理边缘覆盖参数 - if (edges_only) FATAL("Multiple -e options not supported"); // 防止重复设置 - edges_only = 1; // 设置为只显示边缘覆盖 - break; - - case 'q': - // 处理安静模式参数 - if (quiet_mode) FATAL("Multiple -q options not supported"); // 防止重复设置 - quiet_mode = 1; // 启用安静模式 - break; - - case 'Z': - // 处理 afl-cmin 特定功能参数 - cmin_mode = 1; - quiet_mode = 1; // 同时启用安静模式 - break; - - case 'A': - // 处理替代文件参数 - at_file = optarg; // 保存替代文件名 - break; - - case 'Q': - // 处理 QEMU 特定功能参数 - if (qemu_mode) FATAL("Multiple -Q options not supported"); // 防止重复设置 - if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; // 若未设置内存限制,则使用 QEMU 的默认值 - - qemu_mode = 1; // 启用 QEMU 模式 - break; - - case 'b': - // 处理原始二进制模式参数 - binary_mode = 1; // 设置为原始二进制模式 - break; - - case 'c': - // 处理核心转储允许参数 - if (keep_cores) FATAL("Multiple -c options not supported"); // 防止重复设置 - keep_cores = 1; // 允许核心转储 - break; - - case 'V': - // 处理版本显示参数 - show_banner(); // 显示工具的横幅信息 - exit(0); // 退出程序 - default: - // 如果遇到未知参数则显示用法提示 - usage(argv[0]); - } - - // 检查命令行参数的完整性,如果缺少必要参数则显示用法信息 - if (optind == argc || !out_file) usage(argv[0]); - - // 设置共享内存和信号处理 - setup_shm(); - setup_signal_handlers(); - - // 设置环境变量 - set_up_environment(); - - // 查找用户提供的二进制文件路径 - find_binary(argv[optind]); - - // 如果不是安静模式,则显示正在执行的程序信息 - if (!quiet_mode) { - show_banner(); // 显示横幅信息 - ACTF("Executing '%s'...\n", target_path); // 显示正在执行的程序路径 + // 如果工具存在且可执行,更新目标路径和新的 argv 数组的第一个参数 + target_path = new_argv[0] = alloc_printf("%s/afl-qemu-trace", BIN_PATH); + return new_argv; } - - // 检测需要替换的文件参数 - detect_file_args(argv + optind); - - // 根据是否使用 QEMU 来准备参数数组 - if (qemu_mode) - use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); - else - use_argv = argv + optind; // 如果不是 QEMU,使用原始参数数组 - - // 执行目标程序 - run_target(use_argv); - - // 写入执行结果 - tcnt = write_results(); - - // 如果不是安静模式,则输出结果提示信息 - if (!quiet_mode) { - if (!tcnt) FATAL("No instrumentation detected" cRST); // 如果没有捕获到元组数据,则报错 - OKF("Captured %u tuples in '%s'." cRST, tcnt, out_file); // 输出捕获到的元组数量 - } - - // 退出程序,返回状态值,状态值根据是否崩溃和超时进行设置 - exit(child_crashed * 2 + child_timed_out); -} - -