From 87123a176bbc5a91e3aa2e2d7c035299bf0e63a7 Mon Sep 17 00:00:00 2001 From: X1A <2417339154@qq.com> Date: Wed, 8 Jan 2025 21:17:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E9=87=8A=20afl-clang-fast.c=20afl-tmi?= =?UTF-8?q?n.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/afl-tmin.c | 931 ++++++++++++++++----------------- src/llvm_mode/afl-clang-fast.c | 593 +++++++++++++++------ 2 files changed, 877 insertions(+), 647 deletions(-) diff --git a/src/afl-tmin.c b/src/afl-tmin.c index fd7d5b5..9e642b1 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -105,23 +105,22 @@ static const u8 count_class_lookup[256] = { }; /* 根据分类查找表对位图中的计数值进行分类 */ - static void classify_counts(u8* mem) { - u32 i = MAP_SIZE; + u32 i = MAP_SIZE; // 初始化计数器,MAP_SIZE 是位图的大小 - if (edges_only) { + if (edges_only) { // 如果只处理边缘情况 - while (i--) { - if (*mem) *mem = 1; - mem++; + while (i--) { // 遍历位图中的每个字节 + if (*mem) *mem = 1; // 如果当前字节不为0,则将其设置为1 + mem++; // 移动到下一个字节 } - } else { + } else { // 如果不是只处理边缘情况 - while (i--) { - *mem = count_class_lookup[*mem]; - mem++; + while (i--) { // 遍历位图中的每个字节 + *mem = count_class_lookup[*mem]; // 使用查找表对当前字节进行分类 + mem++; // 移动到下一个字节 } } @@ -129,467 +128,462 @@ static void classify_counts(u8* mem) { } /* 如果设置了掩码,则对分类后的位图应用掩码 */ - static void apply_mask(u32* mem, u32* mask) { - u32 i = (MAP_SIZE >> 2); + u32 i = (MAP_SIZE >> 2); // 初始化计数器,MAP_SIZE 是位图的大小,右移2位表示以32位为单位处理 - if (!mask) return; + if (!mask) return; // 如果没有掩码,直接返回 - while (i--) { + while (i--) { // 遍历位图中的每个32位块 - *mem &= ~*mask; - mem++; - mask++; + *mem &= ~*mask; // 对当前32位块应用掩码的反码 + mem++; // 移动到下一个32位块 + mask++; // 移动到下一个掩码块 } } /* 检查位图中是否有任何字节被设置 */ - static inline u8 anything_set(void) { - u32* ptr = (u32*)trace_bits; - u32 i = (MAP_SIZE >> 2); + u32* ptr = (u32*)trace_bits; // 将位图指针转换为32位指针 + u32 i = (MAP_SIZE >> 2); // 初始化计数器,MAP_SIZE 是位图的大小,右移2位表示以32位为单位处理 - while (i--) if (*(ptr++)) return 1; + while (i--) if (*(ptr++)) return 1; // 遍历位图中的每个32位块,如果有任何一个块不为0,则返回1 - return 0; + return 0; // 如果所有块都为0,则返回0 } /* 清理共享内存和临时文件(atexit处理程序) */ - static void remove_shm(void) { - if (prog_in) unlink(prog_in); /* Ignore errors */ - shmctl(shm_id, IPC_RMID, NULL); + if (prog_in) unlink(prog_in); /* 忽略错误,删除临时文件 */ + shmctl(shm_id, IPC_RMID, NULL); // 删除共享内存段 } /* 配置共享内存 */ - static void setup_shm(void) { u8* shm_str; - shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); + 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"); // 如果附加失败,输出错误信息并退出 } /* 读取初始文件 */ - static void read_initial_file(void) { - struct stat st; - s32 fd = open(in_file, O_RDONLY); + struct stat st; // 用于存储文件状态信息 + s32 fd = open(in_file, O_RDONLY); // 以只读模式打开输入文件 - if (fd < 0) PFATAL("Unable to open '%s'", in_file); + if (fd < 0) PFATAL("Unable to open '%s'", in_file); // 如果文件打开失败,输出错误信息并退出 - if (fstat(fd, &st) || !st.st_size) - FATAL("Zero-sized input file."); + if (fstat(fd, &st) || !st.st_size) // 获取文件状态信息,并检查文件大小是否为0 + FATAL("Zero-sized input file."); // 如果文件大小为0,输出错误信息并退出 - if (st.st_size >= TMIN_MAX_FILE) - FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024); + if (st.st_size >= TMIN_MAX_FILE) // 检查文件大小是否超过最大允许大小 + FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024); // 如果文件过大,输出错误信息并退出 - in_len = st.st_size; - in_data = ck_alloc_nozero(in_len); + in_len = st.st_size; // 记录文件大小 + in_data = ck_alloc_nozero(in_len); // 分配与文件大小相同的内存空间 - ck_read(fd, in_data, in_len, in_file); + ck_read(fd, in_data, in_len, in_file); // 将文件内容读取到分配的内存中 - close(fd); + close(fd); // 关闭文件描述符 - OKF("Read %u byte%s from '%s'.", in_len, in_len == 1 ? "" : "s", in_file); + OKF("Read %u byte%s from '%s'.", in_len, in_len == 1 ? "" : "s", in_file); // 输出成功读取文件的信息 } /* 将数据写入文件 */ - static s32 write_to_file(u8* path, u8* mem, u32 len) { s32 ret; - unlink(path); /* Ignore errors */ + unlink(path); /* 忽略错误,删除目标文件(如果存在) */ - ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); + ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); // 以读写模式创建新文件,权限为0600(仅所有者可读写) - if (ret < 0) PFATAL("Unable to create '%s'", path); + if (ret < 0) PFATAL("Unable to create '%s'", path); // 如果文件创建失败,输出错误信息并退出 - ck_write(ret, mem, len, path); + ck_write(ret, mem, len, path); // 将内存中的数据写入文件 - lseek(ret, 0, SEEK_SET); + lseek(ret, 0, SEEK_SET); // 将文件指针重置到文件开头 - return ret; + return ret; // 返回文件描述符 } /* 处理超时信号 */ - static void handle_timeout(int sig) { - child_timed_out = 1; - if (child_pid > 0) kill(child_pid, SIGKILL); + child_timed_out = 1; // 设置超时标志 + if (child_pid > 0) kill(child_pid, SIGKILL); // 如果子进程存在,发送SIGKILL信号终止子进程 } -/* 执行目标应用程序。如果更改无效返回0,否则返回1 */ +/* 执行目标应用程序。如果更改无效返回0,否则返回1 */ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) { - static struct itimerval it; - int status = 0; + static struct itimerval it; // 用于设置定时器的结构体 + int status = 0; // 用于存储子进程的退出状态 - s32 prog_in_fd; - u32 cksum; + s32 prog_in_fd; // 文件描述符,用于存储输入文件 + u32 cksum; // 校验和(未使用) - memset(trace_bits, 0, MAP_SIZE); - MEM_BARRIER(); + memset(trace_bits, 0, MAP_SIZE); // 清空位图,用于存储执行路径信息 + MEM_BARRIER(); // 内存屏障,确保内存操作顺序 - prog_in_fd = write_to_file(prog_in, mem, len); + prog_in_fd = write_to_file(prog_in, mem, len); // 将输入数据写入临时文件 - 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 (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 || - dup2(dev_null_fd, 1) < 0 || - dup2(dev_null_fd, 2) < 0) { + // 重定向标准输入、输出和错误流 + if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 || // 重定向标准输入 + dup2(dev_null_fd, 1) < 0 || // 重定向标准输出到/dev/null + dup2(dev_null_fd, 2) < 0) { // 重定向标准错误到/dev/null - *(u32*)trace_bits = EXEC_FAIL_SIG; - PFATAL("dup2() failed"); + *(u32*)trace_bits = EXEC_FAIL_SIG; // 如果重定向失败,设置执行失败标志 + PFATAL("dup2() failed"); // 输出错误信息并退出 } - close(dev_null_fd); - close(prog_in_fd); + close(dev_null_fd); // 关闭/dev/null文件描述符 + close(prog_in_fd); // 关闭输入文件描述符 - setsid(); + setsid(); // 创建新的会话,使子进程脱离终端控制 - 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; // 将内存限制转换为字节 #ifdef RLIMIT_AS - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + setrlimit(RLIMIT_AS, &r); /* 忽略错误,设置地址空间限制 */ #else - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ + setrlimit(RLIMIT_DATA, &r); /* 忽略错误,设置数据段限制 */ #endif /* ^RLIMIT_AS */ } - r.rlim_max = r.rlim_cur = 0; - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + r.rlim_max = r.rlim_cur = 0; // 设置核心转储文件大小为0,禁止生成核心转储 + setrlimit(RLIMIT_CORE, &r); /* 忽略错误,设置核心转储限制 */ - execv(target_path, argv); + execv(target_path, argv); // 执行目标程序 - *(u32*)trace_bits = EXEC_FAIL_SIG; - exit(0); + *(u32*)trace_bits = EXEC_FAIL_SIG; // 如果execv失败,设置执行失败标志 + exit(0); // 退出子进程 } - close(prog_in_fd); + close(prog_in_fd); // 父进程关闭输入文件描述符 /* 配置超时,等待子进程,取消超时 */ - 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; + child_pid = 0; // 重置子进程ID + it.it_value.tv_sec = 0; // 重置定时器的秒数 + it.it_value.tv_usec = 0; // 重置定时器的微秒数 - setitimer(ITIMER_REAL, &it, NULL); + setitimer(ITIMER_REAL, &it, NULL); // 取消定时器 - MEM_BARRIER(); + MEM_BARRIER(); // 内存屏障,确保内存操作顺序 /* 清理位图,分析退出条件等 */ - if (*(u32*)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); - apply_mask((u32*)trace_bits, (u32*)mask_bitmap); - total_execs++; + classify_counts(trace_bits); // 对位图中的计数值进行分类 + apply_mask((u32*)trace_bits, (u32*)mask_bitmap); // 对位图应用掩码 + total_execs++; // 增加总执行次数计数器 - if (stop_soon) { + if (stop_soon) { // 如果用户请求停止 - SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST); - close(write_to_file(out_file, in_data, in_len)); - exit(1); + SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST); // 输出用户中止信息 + close(write_to_file(out_file, in_data, in_len)); // 将当前输入数据写入输出文件 + exit(1); // 退出程序 } /* 始终丢弃超时的输入 */ - if (child_timed_out) { + if (child_timed_out) { // 如果子进程超时 - missed_hangs++; - return 0; + missed_hangs++; // 增加超时计数器 + return 0; // 返回0,表示输入无效 } + /* 根据当前模式处理崩溃的输入 */ - if (WIFSIGNALED(status) || - (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) || - (WIFEXITED(status) && WEXITSTATUS(status) && exit_crash)) { + if (WIFSIGNALED(status) || // 如果子进程因信号终止 + (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) || // 或者子进程退出且退出状态为MSAN错误 + (WIFEXITED(status) && WEXITSTATUS(status) && exit_crash)) { // 或者子进程退出且退出状态非0且启用了崩溃模式 - if (first_run) crash_mode = 1; + if (first_run) crash_mode = 1; // 如果是第一次运行,设置崩溃模式 - if (crash_mode) { + if (crash_mode) { // 如果处于崩溃模式 - if (!exact_mode) return 1; + if (!exact_mode) return 1; // 如果不是精确模式,返回1(表示输入有效) - } else { + } else { // 如果不是崩溃模式 - missed_crashes++; - return 0; + missed_crashes++; // 增加崩溃计数器 + return 0; // 返回0(表示输入无效) } - } else + } else // 如果子进程正常退出且没有崩溃 /* 根据当前模式处理非崩溃的输入 */ - if (crash_mode) { + if (crash_mode) { // 如果处于崩溃模式 - missed_paths++; - return 0; + missed_paths++; // 增加路径计数器 + return 0; // 返回0(表示输入无效) } - cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算位图的校验和 - if (first_run) orig_cksum = cksum; + if (first_run) orig_cksum = cksum; // 如果是第一次运行,记录原始校验和 - if (orig_cksum == cksum) return 1; + if (orig_cksum == cksum) return 1; // 如果当前校验和与原始校验和相同,返回1(表示输入有效) - missed_paths++; - return 0; + missed_paths++; // 增加路径计数器 + return 0; // 返回0(表示输入无效) } /* 找到大于或等于val的第一个2的幂 */ - static u32 next_p2(u32 val) { - u32 ret = 1; - while (val > ret) ret <<= 1; - return ret; + u32 ret = 1; // 初始化结果为1 + while (val > ret) ret <<= 1; // 不断左移,直到结果大于或等于val + return ret; // 返回结果 } /* 实际地进行最小化 */ - static void minimize(char** argv) { - static u32 alpha_map[256]; + static u32 alpha_map[256]; // 用于存储字符频率的数组 - u8* tmp_buf = ck_alloc_nozero(in_len); - u32 orig_len = in_len, stage_o_len; + u8* tmp_buf = ck_alloc_nozero(in_len); // 分配与输入数据大小相同的临时缓冲区 + u32 orig_len = in_len, stage_o_len; // 记录原始输入长度和当前阶段的长度 - u32 del_len, set_len, del_pos, set_pos, i, alpha_size, cur_pass = 0; - u32 syms_removed, alpha_del0 = 0, alpha_del1, alpha_del2, alpha_d_total = 0; - u8 changed_any, prev_del; + u32 del_len, set_len, del_pos, set_pos, i, alpha_size, cur_pass = 0; // 各种临时变量 + u32 syms_removed, alpha_del0 = 0, alpha_del1, alpha_del2, alpha_d_total = 0; // 用于统计删除的字符数量 + u8 changed_any, prev_del; // 标志变量,用于记录是否有变化 /*********************** * BLOCK NORMALIZATION * ***********************/ - set_len = next_p2(in_len / TMIN_SET_STEPS); - set_pos = 0; + set_len = next_p2(in_len / TMIN_SET_STEPS); // 计算块大小 + set_pos = 0; // 初始化块起始位置 - if (set_len < TMIN_SET_MIN_SIZE) set_len = TMIN_SET_MIN_SIZE; + if (set_len < TMIN_SET_MIN_SIZE) set_len = TMIN_SET_MIN_SIZE; // 如果块大小小于最小允许值,设置为最小允许值 - ACTF(cBRI "Stage #0: " cRST "One-time block normalization..."); + ACTF(cBRI "Stage #0: " cRST "One-time block normalization..."); // 输出阶段信息 - while (set_pos < in_len) { + while (set_pos < in_len) { // 遍历输入数据 - u8 res; - u32 use_len = MIN(set_len, in_len - set_pos); + u8 res; // 用于存储运行目标程序的结果 + u32 use_len = MIN(set_len, in_len - set_pos); // 计算当前块的实际长度 - for (i = 0; i < use_len; i++) + for (i = 0; i < use_len; i++) // 检查当前块是否全为'0' if (in_data[set_pos + i] != '0') break; - if (i != use_len) { + if (i != use_len) { // 如果当前块不全为'0' - memcpy(tmp_buf, in_data, in_len); - memset(tmp_buf + set_pos, '0', use_len); + memcpy(tmp_buf, in_data, in_len); // 复制输入数据到临时缓冲区 + memset(tmp_buf + set_pos, '0', use_len); // 将当前块替换为'0' - res = run_target(argv, tmp_buf, in_len, 0); + res = run_target(argv, tmp_buf, in_len, 0); // 运行目标程序 - if (res) { + if (res) { // 如果运行结果有效 - memset(in_data + set_pos, '0', use_len); - changed_any = 1; - alpha_del0 += use_len; + memset(in_data + set_pos, '0', use_len); // 将输入数据中的当前块替换为'0' + changed_any = 1; // 设置变化标志 + alpha_del0 += use_len; // 增加删除的字符数量 } } - set_pos += set_len; + set_pos += set_len; // 移动到下一个块 } - alpha_d_total += alpha_del0; + alpha_d_total += alpha_del0; // 更新总删除字符数量 OKF("Block normalization complete, %u byte%s replaced.", alpha_del0, - alpha_del0 == 1 ? "" : "s"); + alpha_del0 == 1 ? "" : "s"); // 输出块归一化完成信息 next_pass: - ACTF(cYEL "--- " cBRI "Pass #%u " cYEL "---", ++cur_pass); - changed_any = 0; + ACTF(cYEL "--- " cBRI "Pass #%u " cYEL "---", ++cur_pass); // 输出当前阶段信息 + changed_any = 0; // 重置变化标志 + /****************** * BLOCK DELETION * ******************/ - del_len = next_p2(in_len / TRIM_START_STEPS); - stage_o_len = in_len; + del_len = next_p2(in_len / TRIM_START_STEPS); // 计算初始删除块大小 + stage_o_len = in_len; // 记录当前阶段的输入长度 - ACTF(cBRI "Stage #1: " cRST "Removing blocks of data..."); + ACTF(cBRI "Stage #1: " cRST "Removing blocks of data..."); // 输出阶段信息 next_del_blksize: - if (!del_len) del_len = 1; - del_pos = 0; - prev_del = 1; + if (!del_len) del_len = 1; // 如果删除块大小为0,设置为1 + del_pos = 0; // 初始化删除块的起始位置 + prev_del = 1; // 初始化前一个块是否被删除的标志 SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST, - del_len, in_len); + del_len, in_len); // 输出当前删除块大小和剩余输入长度 - while (del_pos < in_len) { + while (del_pos < in_len) { // 遍历输入数据 - u8 res; - s32 tail_len; + u8 res; // 用于存储运行目标程序的结果 + s32 tail_len; // 用于存储删除块后的尾部长度 - tail_len = in_len - del_pos - del_len; - if (tail_len < 0) tail_len = 0; + tail_len = in_len - del_pos - del_len; // 计算删除块后的尾部长度 + if (tail_len < 0) tail_len = 0; // 如果尾部长度为负,设置为0 /* 如果我们已经处理了一个完整的块(最初,prev_del == 1), 并且没有删除前一个块,且我们不在缓冲区的末尾(tail_len > 0), 并且当前块与前一个块相同... 则跳过此步骤作为无操作 */ if (!prev_del && tail_len && !memcmp(in_data + del_pos - del_len, - in_data + del_pos, del_len)) { + in_data + del_pos, del_len)) { // 如果当前块与前一个块相同 - del_pos += del_len; + del_pos += del_len; // 跳过当前块 continue; } - prev_del = 0; + prev_del = 0; // 重置前一个块是否被删除的标志 /* 头部 */ - memcpy(tmp_buf, in_data, del_pos); + memcpy(tmp_buf, in_data, del_pos); // 复制删除块前的数据到临时缓冲区 /* 尾部 */ - memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len); + memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len); // 复制删除块后的数据到临时缓冲区 - res = run_target(argv, tmp_buf, del_pos + tail_len, 0); + res = run_target(argv, tmp_buf, del_pos + tail_len, 0); // 运行目标程序 - if (res) { + if (res) { // 如果运行结果有效 - memcpy(in_data, tmp_buf, del_pos + tail_len); - prev_del = 1; - in_len = del_pos + tail_len; + memcpy(in_data, tmp_buf, del_pos + tail_len); // 将临时缓冲区的内容复制回输入数据 + prev_del = 1; // 设置前一个块被删除的标志 + in_len = del_pos + tail_len; // 更新输入长度 - changed_any = 1; + changed_any = 1; // 设置变化标志 - } else del_pos += del_len; + } else del_pos += del_len; // 如果运行结果无效,跳过当前块 } - if (del_len > 1 && in_len >= 1) { + if (del_len > 1 && in_len >= 1) { // 如果删除块大小大于1且输入长度大于等于1 - del_len /= 2; - goto next_del_blksize; + del_len /= 2; // 将删除块大小减半 + goto next_del_blksize; // 继续处理下一个块大小 } - OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len); + OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len); // 输出块删除完成信息 + + if (!in_len && changed_any) // 如果输入长度为0且发生了变化 + WARNF(cLRD "Down to zero bytes - check the command line and mem limit!" cRST); // 输出警告信息 - if (!in_len && changed_any) - WARNF(cLRD "Down to zero bytes - check the command line and mem limit!" cRST); + if (cur_pass > 1 && !changed_any) goto finalize_all; // 如果当前阶段大于1且没有变化,跳转到最终处理 - if (cur_pass > 1 && !changed_any) goto finalize_all; /************************* * ALPHABET MINIMIZATION * *************************/ - alpha_size = 0; - alpha_del1 = 0; - syms_removed = 0; + alpha_size = 0; // 初始化字符种类数量 + alpha_del1 = 0; // 初始化字符删除数量 + syms_removed = 0; // 初始化符号删除数量 - memset(alpha_map, 0, 256 * sizeof(u32)); + memset(alpha_map, 0, 256 * sizeof(u32)); // 初始化字符频率映射表 - for (i = 0; i < in_len; i++) { - if (!alpha_map[in_data[i]]) alpha_size++; - alpha_map[in_data[i]]++; + for (i = 0; i < in_len; i++) { // 遍历输入数据 + if (!alpha_map[in_data[i]]) alpha_size++; // 如果当前字符未记录过,增加字符种类数量 + alpha_map[in_data[i]]++; // 增加当前字符的频率 } - ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...", + ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...", // 输出阶段信息 alpha_size, alpha_size == 1 ? "" : "s"); - for (i = 0; i < 256; i++) { + for (i = 0; i < 256; i++) { // 遍历所有可能的字符(0-255) - u32 r; - u8 res; + u32 r; // 用于遍历输入数据的索引 + u8 res; // 用于存储运行目标程序的结果 - if (i == '0' || !alpha_map[i]) continue; + if (i == '0' || !alpha_map[i]) continue; // 如果当前字符是'0'或未出现在输入数据中,跳过 - memcpy(tmp_buf, in_data, in_len); + memcpy(tmp_buf, in_data, in_len); // 复制输入数据到临时缓冲区 - for (r = 0; r < in_len; r++) - if (tmp_buf[r] == i) tmp_buf[r] = '0'; + for (r = 0; r < in_len; r++) // 遍历输入数据 + if (tmp_buf[r] == i) tmp_buf[r] = '0'; // 将当前字符替换为'0' - res = run_target(argv, tmp_buf, in_len, 0); + res = run_target(argv, tmp_buf, in_len, 0); // 运行目标程序 - if (res) { + if (res) { // 如果运行结果有效 - memcpy(in_data, tmp_buf, in_len); - syms_removed++; - alpha_del1 += alpha_map[i]; - changed_any = 1; + memcpy(in_data, tmp_buf, in_len); // 将临时缓冲区的内容复制回输入数据 + syms_removed++; // 增加符号删除数量 + alpha_del1 += alpha_map[i]; // 增加字符删除数量 + changed_any = 1; // 设置变化标志 } } - alpha_d_total += alpha_del1; + alpha_d_total += alpha_del1; // 更新总字符删除数量 - OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.", + OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.", // 输出符号最小化完成信息 syms_removed, syms_removed == 1 ? "" : "s", alpha_del1, alpha_del1 == 1 ? "" : "s"); @@ -597,41 +591,41 @@ next_del_blksize: * CHARACTER MINIMIZATION * **************************/ - alpha_del2 = 0; + alpha_del2 = 0; // 初始化字符删除数量 - ACTF(cBRI "Stage #3: " cRST "Character minimization..."); + ACTF(cBRI "Stage #3: " cRST "Character minimization..."); // 输出阶段信息 - memcpy(tmp_buf, in_data, in_len); + memcpy(tmp_buf, in_data, in_len); // 复制输入数据到临时缓冲区 - for (i = 0; i < in_len; i++) { + for (i = 0; i < in_len; i++) { // 遍历输入数据 - u8 res, orig = tmp_buf[i]; + u8 res, orig = tmp_buf[i]; // 用于存储运行目标程序的结果和原始字符 - if (orig == '0') continue; - tmp_buf[i] = '0'; + if (orig == '0') continue; // 如果当前字符是'0',跳过 + tmp_buf[i] = '0'; // 将当前字符替换为'0' - res = run_target(argv, tmp_buf, in_len, 0); + res = run_target(argv, tmp_buf, in_len, 0); // 运行目标程序 - if (res) { + if (res) { // 如果运行结果有效 - in_data[i] = '0'; - alpha_del2++; - changed_any = 1; + in_data[i] = '0'; // 将输入数据中的当前字符替换为'0' + alpha_del2++; // 增加字符删除数量 + changed_any = 1; // 设置变化标志 - } else tmp_buf[i] = orig; + } else tmp_buf[i] = orig; // 如果运行结果无效,恢复原始字符 } - alpha_d_total += alpha_del2; + alpha_d_total += alpha_del2; // 更新总字符删除数量 - OKF("Character minimization done, %u byte%s replaced.", + OKF("Character minimization done, %u byte%s replaced.", // 输出字符最小化完成信息 alpha_del2, alpha_del2 == 1 ? "" : "s"); - if (changed_any) goto next_pass; + if (changed_any) goto next_pass; // 如果有变化,跳转到下一个阶段 finalize_all: - SAYF("\n" + SAYF("\n" // 输出最终结果 cGRA " File size reduced by : " cRST "%0.02f%% (to %u byte%s)\n" cGRA " Characters simplified : " cRST "%0.02f%%\n" cGRA " Number of execs done : " cRST "%u\n" @@ -641,155 +635,140 @@ finalize_all: total_execs, missed_paths, missed_crashes, missed_hangs ? cLRD : "", missed_hangs); - if (total_execs > 50 && missed_hangs * 10 > total_execs) - WARNF(cLRD "Frequent timeouts - results may be skewed." cRST); + if (total_execs > 50 && missed_hangs * 10 > total_execs) // 如果超时次数过多 + WARNF(cLRD "Frequent timeouts - results may be skewed." cRST); // 输出警告信息 } /* 处理Ctrl-C等信号 */ - static void handle_stop_sig(int sig) { - stop_soon = 1; + stop_soon = 1; // 设置停止标志 - if (child_pid > 0) kill(child_pid, SIGKILL); + if (child_pid > 0) kill(child_pid, SIGKILL); // 如果子进程存在,发送SIGKILL信号终止子进程 } /* 进行基本的准备工作 - 持久性文件描述符,文件名等 */ - static void set_up_environment(void) { - u8* x; + u8* x; // 临时变量 - dev_null_fd = open("/dev/null", O_RDWR); - if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); + dev_null_fd = open("/dev/null", O_RDWR); // 打开/dev/null文件 + if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); // 如果打开失败,输出错误信息并退出 - if (!prog_in) { + if (!prog_in) { // 如果临时文件路径未设置 - u8* use_dir = "."; + u8* use_dir = "."; // 默认使用当前目录 - if (access(use_dir, R_OK | W_OK | X_OK)) { + if (access(use_dir, R_OK | W_OK | X_OK)) { // 检查当前目录是否可读写 - use_dir = getenv("TMPDIR"); - if (!use_dir) use_dir = "/tmp"; + use_dir = getenv("TMPDIR"); // 尝试获取环境变量TMPDIR + if (!use_dir) use_dir = "/tmp"; // 如果未设置,使用/tmp目录 } - prog_in = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid()); + prog_in = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid()); // 生成临时文件路径 } - /* 设置合理的默认值... */ - - x = getenv("ASAN_OPTIONS"); - - if (x) { - if (!strstr(x, "abort_on_error=1")) - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + /* 设置合理的默认值... */ + x = getenv("ASAN_OPTIONS"); // 获取环境变量ASAN_OPTIONS的值 - if (!strstr(x, "symbolize=0")) - FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + if (x) { // 如果ASAN_OPTIONS被设置 + if (!strstr(x, "abort_on_error=1")) // 检查是否包含abort_on_error=1 + FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); // 如果没有设置,输出错误信息并退出 + if (!strstr(x, "symbolize=0")) // 检查是否包含symbolize=0 + FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); // 如果没有设置,输出错误信息并退出 } - x = getenv("MSAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) - FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(MSAN_ERROR) " - please fix!"); + x = getenv("MSAN_OPTIONS"); // 获取环境变量MSAN_OPTIONS的值 - if (!strstr(x, "symbolize=0")) - FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + if (x) { // 如果MSAN_OPTIONS被设置 + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) // 检查是否包含正确的exit_code + FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(MSAN_ERROR) " - please fix!"); // 如果没有设置,输出错误信息并退出 + if (!strstr(x, "symbolize=0")) // 检查是否包含symbolize=0 + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); // 如果没有设置,输出错误信息并退出 } - setenv("ASAN_OPTIONS", "abort_on_error=1:" - "detect_leaks=0:" - "symbolize=0:" - "allocator_may_return_null=1", 0); - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "symbolize=0:" - "abort_on_error=1:" - "allocator_may_return_null=1:" - "msan_track_origins=0", 0); - - if (getenv("AFL_PRELOAD")) { - setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); - setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + // 设置ASAN和MSAN的默认选项 + setenv("ASAN_OPTIONS", "abort_on_error=1:" // 设置ASAN选项:遇到错误时终止程序 + "detect_leaks=0:" // 禁用内存泄漏检测 + "symbolize=0:" // 禁止符号化堆栈跟踪 + "allocator_may_return_null=1", 0); // 允许分配器返回NULL + + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" // 设置MSAN选项:指定退出代码 + "symbolize=0:" // 禁止符号化堆栈跟踪 + "abort_on_error=1:" // 遇到错误时终止程序 + "allocator_may_return_null=1:" // 允许分配器返回NULL + "msan_track_origins=0", 0); // 禁用原始数据跟踪 + + if (getenv("AFL_PRELOAD")) { // 如果设置了AFL_PRELOAD环境变量 + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); // 设置LD_PRELOAD环境变量用于动态链接 + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); // 对于macOS,设置DYLD_INSERT_LIBRARIES用于动态链接 } } /* 设置信号处理程序 */ - static void setup_signal_handlers(void) { - struct sigaction sa; + struct sigaction sa; // 定义信号操作结构体 - sa.sa_handler = NULL; - sa.sa_flags = SA_RESTART; - sa.sa_sigaction = NULL; + sa.sa_handler = NULL; // 初始化处理程序为NULL + sa.sa_flags = SA_RESTART; // 设置标志以重新启动被信号中断的系统调用 + sa.sa_sigaction = NULL; // 使用默认的信号处理程序 - sigemptyset(&sa.sa_mask); + sigemptyset(&sa.sa_mask); // 初始化信号屏蔽字 /* 各种方式表示“停止”。 */ - - sa.sa_handler = handle_stop_sig; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = handle_stop_sig; // 将处理程序设置为自定义的停止信号处理器 + sigaction(SIGHUP, &sa, NULL); // 注册SIGHUP信号 + sigaction(SIGINT, &sa, NULL); // 注册SIGINT信号 + sigaction(SIGTERM, &sa, NULL); // 注册SIGTERM信号 /* 执行超时通知。 */ - - sa.sa_handler = handle_timeout; - sigaction(SIGALRM, &sa, NULL); + sa.sa_handler = handle_timeout; // 将处理程序设置为自定义的超时信号处理器 + sigaction(SIGALRM, &sa, NULL); // 注册SIGALRM信号 } /* 检测参数中的@@ */ - static void detect_file_args(char** argv) { - u32 i = 0; - u8* cwd = getcwd(NULL, 0); - - if (!cwd) PFATAL("getcwd() failed"); + u32 i = 0; // 初始化参数索引 + u8* cwd = getcwd(NULL, 0); // 获取当前工作目录 - while (argv[i]) { + if (!cwd) PFATAL("getcwd() failed"); // 如果获取当前工作目录失败,输出错误信息并退出 - u8* aa_loc = strstr(argv[i], "@@"); + while (argv[i]) { // 遍历参数列表 + u8* aa_loc = strstr(argv[i], "@@"); // 查找参数中是否存在@@ - if (aa_loc) { - - u8 *aa_subst, *n_arg; + if (aa_loc) { // 如果找到@@ + u8 *aa_subst, *n_arg; // 临时变量 /* 确保总是使用全路径。 */ - - if (prog_in[0] == '/') aa_subst = prog_in; - else aa_subst = alloc_printf("%s/%s", cwd, prog_in); + if (prog_in[0] == '/') aa_subst = prog_in; // 如果prog_in是绝对路径 + else aa_subst = alloc_printf("%s/%s", cwd, prog_in); // 否则生成完整路径 /* 构造替换的argv值 */ + *aa_loc = 0; // 将@@进行分割 + n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); // 生成替换后的参数 + argv[i] = n_arg; // 更新参数列表 + *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 = '@'; - - if (prog_in[0] != '/') ck_free(aa_subst); + if (prog_in[0] != '/') ck_free(aa_subst); // 释放临时变量内存(如果需要) } - i++; + i++; // 继续处理下一个参数 } - free(cwd); /* not tracked */ - -} + free(cwd); /* not tracked */ // 释放当前工作目录字符串,不跟踪内存 /* 显示使用提示 */ @@ -830,236 +809,224 @@ static void usage(u8* argv0) { static void find_binary(u8* fname) { - u8* env_path = 0; - struct stat st; + u8* env_path = 0; // 用于存储环境变量PATH的值 + struct stat st; // 用于获取文件状态信息 + // 如果文件名包含斜杠或环境变量PATH未设置 if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { - target_path = ck_strdup(fname); + target_path = ck_strdup(fname); // 直接将文件名复制到目标路径 - 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); + // 检查目标路径是否为一个可执行文件 + if (stat(target_path, &st) || !S_ISREG(st.st_mode) || // 如果文件不存在或不是常规文件 + !(st.st_mode & 0111) || st.st_size < 4) // 如果文件不可执行或文件大小小于4字节 + FATAL("Program '%s' not found or not executable", fname); // 输出错误信息并退出 - } else { + } else { // 如果文件名不包含斜杠且环境变量PATH已设置 - while (env_path) { + while (env_path) { // 遍历环境变量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); // 没有找到分隔符,复制剩余的路径 - } else 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; + // 检查构建的目标路径是否存在且可执行 + if (!stat(target_path, &st) && S_ISREG(st.st_mode) && // 如果目标路径存在且是常规文件 + (st.st_mode & 0111) && st.st_size >= 4) break; // 以及可执行且文件大小大于等于4字节 - 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); + // 如果没有找到可执行的程序 + if (!target_path) FATAL("Program '%s' not found or not executable", fname); // 输出错误信息并退出 } } /* 为QEMU修复argv */ - static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { - char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); - u8 *tmp, *cp, *rsl, *own_copy; + char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); // 分配内存以存储新的argv,包括额外的参数 + u8 *tmp, *cp, *rsl, *own_copy; // 临时变量 - /* 为QEMU稳定性问题的解决提供了一个临时的解决方案。 */ - - setenv("QEMU_LOG", "nochain", 1); + // 为QEMU稳定性问题的解决提供了一个临时的解决方案。 + setenv("QEMU_LOG", "nochain", 1); // 禁用链式日志 + // 将原始argv参数复制到新的argv中 memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); - /* 现在我们需要实际地找到qemu来运行argv[0] */ - - new_argv[2] = target_path; - new_argv[1] = "--"; + // 现在我们需要实际地找到qemu来运行argv[0] + new_argv[2] = target_path; // 设置新argv的第二个元素为目标路径 + new_argv[1] = "--"; // 将" -- "添加到argv中 + // 尝试从环境变量AFL_PATH获取QEMU的路径 tmp = getenv("AFL_PATH"); - if (tmp) { + if (tmp) { // 如果AFL_PATH环境变量被设置 + cp = alloc_printf("%s/afl-qemu-trace", tmp); // 构造QEMU的路径 - cp = alloc_printf("%s/afl-qemu-trace", tmp); + if (access(cp, X_OK)) // 检查该路径是否可执行 + FATAL("Unable to find '%s'", tmp); // 如果不可执行,输出错误信息并退出 - if (access(cp, X_OK)) - FATAL("Unable to find '%s'", tmp); - - target_path = new_argv[0] = cp; - return new_argv; + target_path = new_argv[0] = cp; // 将目标路径设置为新argv的第一个元素 + return new_argv; // 返回新的argv } - own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); - - if (rsl) { - - *rsl = 0; + // 处理own_loc来查找qemu + own_copy = ck_strdup(own_loc); // 复制own_loc字符串 + rsl = strrchr(own_copy, '/'); // 获取最后一个斜杠的位置 - cp = alloc_printf("%s/afl-qemu-trace", own_copy); - ck_free(own_copy); + if (rsl) { // 如果找到斜杠 + *rsl = 0; // 将其替换为字符串结束符 - if (!access(cp, X_OK)) { - - target_path = new_argv[0] = cp; - return new_argv; + cp = alloc_printf("%s/afl-qemu-trace", own_copy); // 构造QEMU路径 + ck_free(own_copy); // 释放复制的字符串内存 + if (!access(cp, X_OK)) { // 如果路径可执行 + target_path = new_argv[0] = cp; // 设置目标路径 + return new_argv; // 返回新的argv } - } else ck_free(own_copy); - - if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { - - target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; - return new_argv; + } else ck_free(own_copy); // 如果未找到斜杠,释放复制的字符串内存 + // 检查QEMU的默认路径 + if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { // 检查是否能够访问默认路径 + target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; // 设置目标路径 + return new_argv; // 返回新的argv } + // 如果找不到QEMU的路径,输出错误信息并退出 FATAL("Unable to find 'afl-qemu-trace'."); } + /* 从文件读取掩码位图。这是为-B选项服务的 */ static void read_bitmap(u8* fname) { - s32 fd = open(fname, O_RDONLY); + s32 fd = open(fname, O_RDONLY); // 以只读模式打开位图文件 - if (fd < 0) PFATAL("Unable to open '%s'", fname); + if (fd < 0) PFATAL("Unable to open '%s'", fname); // 如果打开失败,输出错误信息并退出 - ck_read(fd, mask_bitmap, MAP_SIZE, fname); + ck_read(fd, mask_bitmap, MAP_SIZE, fname); // 读取位图数据到mask_bitmap数组中,确保读取的字节数与MAP_SIZE相等 - close(fd); + close(fd); // 关闭文件描述符 } /* 主入口点 */ - int main(int argc, char** argv) { - s32 opt; - u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0; - char** use_argv; + s32 opt; // 用于存储命令行选项的变量 + u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0; // 初始化选项标志 + char** use_argv; // 用于存储可变参数的数组 - doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; + doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; // 检查文档路径是否存在,如果不存在,则使用默认文档路径 - SAYF(cCYA "afl-tmin " cBRI VERSION cRST " by \n"); - - while ((opt = getopt(argc,argv,"+i:o:f:m:t:B:xeQV")) > 0) + SAYF(cCYA "afl-tmin " cBRI VERSION cRST " by \n"); // 输出程序的版本信息 + while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeQV")) > 0) // 解析命令行选项 switch (opt) { - - case 'i': - - if (in_file) FATAL("Multiple -i options not supported"); - in_file = optarg; + case 'i': // 输入文件选项 + if (in_file) FATAL("Multiple -i options not supported"); // 如果已经设置了输入文件,输出错误信息 + in_file = optarg; // 保存输入文件路径 break; - case 'o': - - if (out_file) FATAL("Multiple -o options not supported"); - out_file = optarg; + case 'o': // 输出文件选项 + if (out_file) FATAL("Multiple -o options not supported"); // 如果已经设置了输出文件,输出错误信息 + out_file = optarg; // 保存输出文件路径 break; - case 'f': - - if (prog_in) FATAL("Multiple -f options not supported"); - use_stdin = 0; - prog_in = optarg; + case 'f': // 处理程序输入选项 + if (prog_in) FATAL("Multiple -f options not supported"); // 如果已经设置了程序输入,输出错误信息 + use_stdin = 0; // 设置不使用标准输入 + prog_in = optarg; // 保存程序输入路径 break; - case 'e': - - if (edges_only) FATAL("Multiple -e options not supported"); - edges_only = 1; + case 'e': // 仅处理边缘选项 + if (edges_only) FATAL("Multiple -e options not supported"); // 如果已经启用边缘处理,输出错误信息 + edges_only = 1; // 启用边缘处理 break; - case 'x': - - if (exit_crash) FATAL("Multiple -x options not supported"); - exit_crash = 1; + case 'x': // 退出崩溃选项 + if (exit_crash) FATAL("Multiple -x options not supported"); // 如果已经启用崩溃退出,输出错误信息 + exit_crash = 1; // 启用崩溃退出 break; - case 'm': { + case 'm': { // 内存限制选项 - u8 suffix = 'M'; + u8 suffix = 'M'; // 默认单位是MB - if (mem_limit_given) FATAL("Multiple -m options not supported"); - mem_limit_given = 1; + if (mem_limit_given) FATAL("Multiple -m options not supported"); // 如果已经设置了内存限制,输出错误信息 + mem_limit_given = 1; // 设置内存限制标志 - if (!strcmp(optarg, "none")) { - - mem_limit = 0; + if (!strcmp(optarg, "none")) { // 如果选项值为"none" + mem_limit = 0; // 不设置内存限制 break; - } - if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -m"); + 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; - case 'G': mem_limit *= 1024; break; - case 'k': mem_limit /= 1024; break; - case 'M': break; - - default: FATAL("Unsupported suffix or bad syntax for -m"); - + case 'T': mem_limit *= 1024 * 1024; break; // TB转为MB + case 'G': mem_limit *= 1024; break; // GB转为MB + case 'k': mem_limit /= 1024; break; // kB转为MB + case 'M': break; // 如果是MB,不需要更改 + default: FATAL("Unsupported suffix or bad syntax for -m"); // 如果单位不支持,输出错误信息 } - if (mem_limit < 5) FATAL("Dangerously low value of -m"); + // 检查内存限制值的合法范围 + if (mem_limit < 5) FATAL("Dangerously low value of -m"); // 如果小于5MB,输出错误信息 + // 检查32位系统的内存限制 if (sizeof(rlim_t) == 4 && mem_limit > 2000) - FATAL("Value of -m out of range on 32-bit systems"); + 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; + case 't': // 超时选项 + if (timeout_given) FATAL("Multiple -t options not supported"); // 如果已经设置了超时,输出错误信息 + timeout_given = 1; // 设置超时标志 - exec_tmout = atoi(optarg); + exec_tmout = atoi(optarg); // 将选项值转换为整数并设置超时 - if (exec_tmout < 10 || optarg[0] == '-') - FATAL("Dangerously low value of -t"); + if (exec_tmout < 10 || optarg[0] == '-') // 检查超时值是否合法 + FATAL("Dangerously low value of -t"); // 如果小于10,输出错误信息 break; - case 'Q': - - if (qemu_mode) FATAL("Multiple -Q options not supported"); - if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; + case 'Q': // QEMU模式选项 + if (qemu_mode) FATAL("Multiple -Q options not supported"); // 如果已经启用QEMU模式,输出错误信息 + if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; // 如果未设置内存限制,则默认使用QEMU内存限制 - qemu_mode = 1; + qemu_mode = 1; // 启用QEMU模式 break; - case 'B': /* load bitmap */ - + case 'B': { /* load bitmap */ /* 这是一个秘密的未公开选项!如果有一个基线“无聊”的输入文件和另一个“有趣”的文件你想最小化, 这个选项可能是有用的。 @@ -1067,78 +1034,74 @@ int main(int argc, char** argv) { 然后通过-B加载到afl-tmin中。最小化器将只保留有趣输入文件中独特的边, 但忽略来自原始映射的所有内容。 - 如果这个选项被证明是有用的,可能会被扩展并正式记录。 */ + 如果这个选项被证明是有用,可能会被扩展并正式记录。 */ + + if (mask_bitmap) FATAL("Multiple -B options not supported"); // 如果已经设置了掩码位图,输出错误信息 + mask_bitmap = ck_alloc(MAP_SIZE); // 分配掩码位图内存 + read_bitmap(optarg); // 读取位图文件 - if (mask_bitmap) FATAL("Multiple -B options not supported"); - mask_bitmap = ck_alloc(MAP_SIZE); - read_bitmap(optarg); break; + } case 'V': /* Show version number */ - /* 版本号已经打印过了,直接退出 */ - exit(0); + exit(0); // 退出程序 default: - - usage(argv[0]); + usage(argv[0]); // 如果遇到未知选项,显示用法信息 } - if (optind == argc || !in_file || !out_file) usage(argv[0]); +} - setup_shm(); - setup_signal_handlers(); + if (optind == argc || !in_file || !out_file) usage(argv[0]); // 检查命令行参数,如果缺少输入文件或输出文件,则显示用法信息并退出 - set_up_environment(); + setup_shm(); // 设置共享内存,用于进程间通信 + setup_signal_handlers(); // 设置信号处理程序,以处理如Ctrl-C等中断信号 - find_binary(argv[optind]); - detect_file_args(argv + optind); + set_up_environment(); // 进行环境准备,设置临时文件描述符等 - if (qemu_mode) - use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); - else - use_argv = argv + optind; + find_binary(argv[optind]); // 查找并验证要执行的目标程序的路径 + detect_file_args(argv + optind); // 检测命令行参数中的“@@”并进行替换 - exact_mode = !!getenv("AFL_TMIN_EXACT"); + if (qemu_mode) // 如果处于QEMU模式 + use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); // 获取QEMU模式下的参数 + else + use_argv = argv + optind; // 在非QEMU模式下使用原始参数 - SAYF("\n"); + exact_mode = !!getenv("AFL_TMIN_EXACT"); // 检查环境变量以确定是否启用精确模式 - read_initial_file(); + SAYF("\n"); // 输出换行 + + read_initial_file(); // 读取初始输入文件内容 - ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", + ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", // 输出干运行信息 mem_limit, exec_tmout, edges_only ? ", edges only" : ""); - run_target(use_argv, in_data, in_len, 1); - - if (child_timed_out) - FATAL("Target binary times out (adjusting -t may help)."); + run_target(use_argv, in_data, in_len, 1); // 执行目标程序,将初始数据传递给它 - if (!crash_mode) { + if (child_timed_out) // 检查是否子进程超时 + FATAL("Target binary times out (adjusting -t may help)."); // 如果超时,输出错误信息并退出 + if (!crash_mode) { // 如果不是崩溃模式 OKF("Program terminates normally, minimizing in " - cCYA "instrumented" cRST " mode."); - - if (!anything_set()) FATAL("No instrumentation detected."); - - } else { - + cCYA "instrumented" cRST " mode."); // 输出程序正常结束信息,进入仪器模式 + if (!anything_set()) FATAL("No instrumentation detected."); // 检查是否有仪器信息,如果没有则退出 + } else { // 如果处于崩溃模式 OKF("Program exits with a signal, minimizing in " cMGN "%scrash" cRST - " mode.", exact_mode ? "EXACT " : ""); - + " mode.", exact_mode ? "EXACT " : ""); // 输出程序因信号退出的信息 } - minimize(use_argv); + minimize(use_argv); // 进入最小化阶段,执行最小化算法 - ACTF("Writing output to '%s'...", out_file); + ACTF("Writing output to '%s'...", out_file); // 输出正在写入输出文件的信息 - unlink(prog_in); - prog_in = NULL; + unlink(prog_in); // 删除临时输入文件 + prog_in = NULL; // 重置临时输入文件路径 - close(write_to_file(out_file, in_data, in_len)); + close(write_to_file(out_file, in_data, in_len)); // 将最小化后的数据写入输出文件并关闭文件描述符 - OKF("We're done here. Have a nice day!\n"); + OKF("We're done here. Have a nice day!\n"); // 输出完成信息 - exit(0); - -} \ No newline at end of file + exit(0); // 正常退出程序 +} diff --git a/src/llvm_mode/afl-clang-fast.c b/src/llvm_mode/afl-clang-fast.c index 709fb2b..84eef01 100644 --- a/src/llvm_mode/afl-clang-fast.c +++ b/src/llvm_mode/afl-clang-fast.c @@ -28,209 +28,470 @@ of flags, and then calls the real compiler. */ -#define AFL_MAIN // 定义宏AFL_MAIN +#define AFL_MAIN -#include "../config.h" // 引入配置文件 -#include "../types.h" // 引入类型定义文件 -#include "../debug.h" // 引入调试相关文件 -#include "../alloc-inl.h" // 引入内存分配相关的文件 +#include "../config.h" +#include "../types.h" +#include "../debug.h" +#include "../alloc-inl.h" -#include // 引入标准输入输出库 -#include // 引入UNIX标准库 -#include // 引入标准库 -#include // 引入字符串处理库 +#include +#include +#include +#include -static u8* obj_path; /* 运行时库的路径 */ -static u8** cc_params; /* 传递给真实编译器的参数 */ -static u32 cc_par_cnt = 1; /* 参数计数,初始包含argv0 */ +static u8* obj_path; /* Path to runtime libraries */ +static u8** cc_params; /* Parameters passed to the real CC */ +static u32 cc_par_cnt = 1; /* Param count, including argv0 */ -/* 尝试查找运行时库。如果失败,则中止。 */ + +/* + 尝试查找运行时库。如果失败,则中止程序。 +*/ static void find_obj(u8* argv0) { - u8 *afl_path = getenv("AFL_PATH"); // 获取环境变量AFL_PATH + u8 *afl_path = getenv("AFL_PATH"); // 获取环境变量 AFL_PATH 的值 + u8 *slash, *tmp; // 定义用于存储路径分隔符和临时路径的变量 + + if (afl_path) { // 如果 AFL_PATH 环境变量存在 + tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path); // 构造运行时库的路径 + + if (!access(tmp, R_OK)) { // 检查路径是否可读 + obj_path = afl_path; // 如果可读,设置 obj_path 为 AFL_PATH + ck_free(tmp); // 释放临时路径的内存 + return; // 返回,结束函数 + } + + ck_free(tmp); // 如果路径不可读,释放临时路径的内存 + } + + slash = strrchr(argv0, '/'); // 查找 argv0 中最后一个斜杠的位置 + + if (slash) { // 如果找到斜杠 + u8 *dir; // 定义用于存储目录路径的变量 + + *slash = 0; // 将斜杠位置置为字符串结束符,以便提取目录路径 + dir = ck_strdup(argv0); // 复制 argv0 的目录路径 + *slash = '/'; // 恢复斜杠 + + tmp = alloc_printf("%s/afl-llvm-rt.o", dir); // 构造运行时库的路径 + + if (!access(tmp, R_OK)) { // 检查路径是否可读 + obj_path = dir; // 如果可读,设置 obj_path 为当前目录 + ck_free(tmp); // 释放临时路径的内存 + return; // 返回,结束函数 + } + + ck_free(tmp); // 如果路径不可读,释放临时路径的内存 + ck_free(dir); // 释放目录路径的内存 + } + + // 检查默认的 AFL_PATH 目录下是否存在 'afl-llvm-rt.o' 文件 + if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) { + obj_path = AFL_PATH; // 如果存在,设置 obj_path 为 AFL_PATH + return; // 返回,结束函数 + } + + // 如果以上方法都未找到运行时库,抛出致命错误 + FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH"); +} + +/* + 复制 argv 到 cc_params,并进行必要的编辑。 +*/ +static void edit_params(u32 argc, char** argv) { + + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; // 初始化标志变量 + u8 *name; // 定义用于存储程序名的变量 + + // 为 cc_params 分配足够的内存,以容纳传入的参数加上额外的空间 + cc_params = ck_alloc((argc + 128) * sizeof(u8*)); + + // 提取程序名 + name = strrchr(argv[0], '/'); // 查找 argv0 中最后一个斜杠的位置 + if (!name) name = argv[0]; else name++; // 如果没有斜杠,使用完整的 argv[0] + + // 根据程序名确定使用的编译器 + if (!strcmp(name, "afl-clang-fast++")) { // 如果程序名是 afl-clang-fast++ + u8* alt_cxx = getenv("AFL_CXX"); // 获取环境变量 AFL_CXX 的值 + cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++"; // 如果存在则使用其值,否则默认使用 clang++ + } else { // 如果程序名不是 afl-clang-fast++ + u8* alt_cc = getenv("AFL_CC"); // 获取环境变量 AFL_CC 的值 + cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; // 如果存在则使用其值,否则默认使用 clang + } + +#ifdef USE_TRACE_PC // 如果定义了 USE_TRACE_PC + // 添加用于 sanitization 的覆盖率参数 + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // 启用 trace-pc-guard 插桩 +#ifndef __ANDROID__ // 如果不是 Android 平台 + cc_params[cc_par_cnt++] = "-mllvm"; // 添加 LLVM 相关参数 + cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; // 设置目标覆盖率的阈值为 0 +#endif +#else // 如果没有定义 USE_TRACE_PC + // 添加 LLVM 插件的加载参数 + cc_params[cc_par_cnt++] = "-Xclang"; // 添加 Clang 参数 + cc_params[cc_par_cnt++] = "-load"; // 指定加载插件 + cc_params[cc_par_cnt++] = "-Xclang"; // 添加 Clang 参数 + +cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path); +// 追加 AFL 关键依赖项 afl-llvm-pass.so 到 cc_params,用于插桩分析 +#endif /* ^USE_TRACE_PC */ + +cc_params[cc_par_cnt++] = "-Qunused-arguments"; +// 添加一个参数,告诉编译器忽略未使用的命令行参数 + +while (--argc) { // 循环处理剩余的命令行参数 + u8* cur = *(++argv); // 获取当前参数 + + if (!strcmp(cur, "-m32")) bit_mode = 32; // 如果参数是 -m32,设置 bit_mode 为 32 + if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; // 针对特定架构的设置 + if (!strcmp(cur, "-m64")) bit_mode = 64; // 如果参数是 -m64,设置 bit_mode 为 64 + + if (!strcmp(cur, "-x")) x_set = 1; // 如果参数为 -x,设置 x_set 为 1,表示启用此选项 + + // 检查是否使用地址或内存的安全检测 + if (!strcmp(cur, "-fsanitize=address") || + !strcmp(cur, "-fsanitize=memory")) asan_set = 1; + + // 检查是否启用了 FORTIFY_SOURCE + if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; + + // 如果链接器选项是 -Wl,-z,defs 或 -Wl,--no-undefined,跳过该参数 + if (!strcmp(cur, "-Wl,-z,defs") || + !strcmp(cur, "-Wl,--no-undefined")) continue; + + cc_params[cc_par_cnt++] = cur; // 将当前参数添加到 cc_params +} + +// 如果环境变量 AFL_HARDEN 存在 +if (getenv("AFL_HARDEN")) { + cc_params[cc_par_cnt++] = "-fstack-protector-all"; // 启用所有堆栈保护 + + // 如果未启用 FORTIFY_SOURCE,添加定义以启用其功能 + if (!fortify_set) + cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2"; +} + +// 如果尚未启用 AddressSanitizer +if (!asan_set) { + // 检查是否设置了使用 AddressSanitizer 的环境变量 + if (getenv("AFL_USE_ASAN")) { + // 检查 MSAN 和 ASAN 互斥 + if (getenv("AFL_USE_MSAN")) + FATAL("ASAN and MSAN are mutually exclusive"); + + // 检查 AFL_HARDEN 是否与 ASAN 互斥 + if (getenv("AFL_HARDEN")) + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); + + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 取消定义 FORTIFY_SOURCE + cc_params[cc_par_cnt++] = "-fsanitize=address"; // 启用 AddressSanitizer + } + // 检查是否设置了使用 MemorySanitizer 的环境变量 + else if (getenv("AFL_USE_MSAN")) { + // 检查 ASAN 和 MSAN 互斥 + if (getenv("AFL_USE_ASAN")) + FATAL("ASAN and MSAN are mutually exclusive"); + + // 检查 AFL_HARDEN 是否与 MSAN 互斥 + if (getenv("AFL_HARDEN")) + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); + + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 取消定义 FORTIFY_SOURCE + cc_params[cc_par_cnt++] = "-fsanitize=memory"; // 启用 MemorySanitizer + } +} + +// 如果定义了 USE_TRACE_PC +#ifdef USE_TRACE_PC +// 检查 AFL_INST_RATIO 环境变量是否可用 +if (getenv("AFL_INST_RATIO")) + FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'."); +#endif /* USE_TRACE_PC */ + +// 如果未设置 AFL_DONT_OPTIMIZE,则启用优化选项 +if (!getenv("AFL_DONT_OPTIMIZE")) { + cc_params[cc_par_cnt++] = "-g"; // 添加调试信息 + cc_params[cc_par_cnt++] = "-O3"; // 启用高优化级别 + cc_params[cc_par_cnt++] = "-funroll-loops"; // 启用循环展开 +} + +// 如果设置了 AFL_NO_BUILTIN,则禁用特定的内置函数 +if (getenv("AFL_NO_BUILTIN")) { + cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; // 禁用 strcmp 的内置实现 + cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; // 禁用 strncmp 的内置实现 + cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; // 禁用 strcasecmp 的内置实现 + cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; // 禁用 strncasecmp 的内置实现 + cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; // 禁用 memcmp 的内置实现 +} + +// 添加 AFL 控制宏,确保手动控制和编译器的定义 +cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; // 指示支持手动控制 +cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; // 编译器标识 +cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; // 表示为不安全的生产模式 + + + /* + 当用户尝试通过在程序末尾添加单行来使用持久化或延迟的 forkserver 模式时, + 我们想可靠地将签名注入二进制文件 (以便被 afl-fuzz 拿到) + 并想调用运行时 .o 文件中的函数。由于以下三个原因,这非常复杂: + + 1) 我们需要说服编译器不要优化掉签名。 + 这是利用 __attribute__((used)) 实现的。 + + 2) 我们需要说服链接器,在调用 -Wl,--gc-sections 时, + 不要做同样的事情。 这是通过强制赋值给 'volatile' 指针来实现的。 + + 3) 我们需要在全局命名空间中声明 __afl_persistent_loop(), + 但在类的方法中做到这一点是困难的 - :: 和 extern "C" + 是禁止的,且 __attribute__((alias(...))) 无效。因此使用了 __asm__ 别名技巧。 + + */ + +// 定义一个宏,用于处理 AFL 循环的持久化 +cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)=" + // 开始一个代码块,其中静态的、易失的字符指针用于存放持久化的信号 + "({ static volatile char *_B __attribute__((used)); " + " _B = (char*)\"" PERSIST_SIG "\"; "; // 将持久化信号字符串赋值给指针 _B + +#ifdef __APPLE__ // 根据系统平台选择合适的函数名称 + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " // Apple 平台下的持久化函数 +#else + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " // 其他平台下的持久化函数 +#endif /* ^__APPLE__ */ + "_L(_A); })"; // 调用持久化函数,并结束代码块 + +// 定义一个宏,用于初始化 AFL +cc_params[cc_par_cnt++] = "-D__AFL_INIT()=" + "do { static volatile char *_A __attribute__((used)); " + " _A = (char*)\"" DEFER_SIG "\"; "; // 将延迟信号字符串赋值给指针 _A + +#ifdef __APPLE__ // 根据系统平台选择合适的初始化函数名称 + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"___afl_manual_init\"); " // Apple 平台下的初始化函数 +#else + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"__afl_manual_init\"); " // 其他平台下的初始化函数 +#endif /* ^__APPLE__ */ + "_I(); } while (0)"; // 调用初始化函数,并结束循环结构 + +// 如果 x_set 被设置,添加参数来指示关闭类型检查 +if (x_set) { + cc_params[cc_par_cnt++] = "-x"; // 添加-x参数 + cc_params[cc_par_cnt++] = "none"; // 指示后续没有特定类型 +} + +// 如果不是在 Android 平台下 +#ifndef __ANDROID__ + switch (bit_mode) { // 根据位数选择合适的运行时库 + + case 0: // 如果未设置位模式 + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); // 使用默认的 afl-llvm-rt.o 路径 + break; + + case 32: // 如果设置了 32 位模式 + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); // 使用 32 位运行时库路径 + + // 检查路径是否可读,如果不可读则抛出错误 + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); // 抛出错误信息 + + break; + + case 64: // 如果设置了 64 位模式 + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); // 使用 64 位运行时库路径 + +/* 查找运行时库的路径。如果找不到,则中止程序。 */ +static void find_obj(u8* argv0) { + + // 获取环境变量 AFL_PATH + u8 *afl_path = getenv("AFL_PATH"); u8 *slash, *tmp; - if (afl_path) { // 如果设置了AFL_PATH + // 如果找到 AFL_PATH + if (afl_path) { - tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path); // 格式化路径 + // 生成 afl-llvm-rt.o 的完整路径 + tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path); - if (!access(tmp, R_OK)) { // 检查文件是否可读 - obj_path = afl_path; // 设置运行时库路径 - ck_free(tmp); // 释放临时字符串 - return; + // 检查文件是否可读 + if (!access(tmp, R_OK)) { + obj_path = afl_path; // 设置对象路径为 AFL_PATH + ck_free(tmp); // 释放临时路径内存 + return; // 找到文件,结束函数 } - ck_free(tmp); // 释放临时字符串 + ck_free(tmp); // 释放临时路径内存 } - slash = strrchr(argv0, '/'); // 查找路径中的最后一个斜杠 + // 查找 argv0 中最后一个 '/' 的位置 + slash = strrchr(argv0, '/'); - if (slash) { // 如果找到斜杠 + // 如果找到 '/' + if (slash) { u8 *dir; - *slash = 0; // 将斜杠替换为结束符 - dir = ck_strdup(argv0); // 复制路径 - *slash = '/'; // 恢复斜杠 + *slash = 0; // 将 '/' 替换为结束符,以获取目录 + dir = ck_strdup(argv0); // 复制目录名 + *slash = '/'; // 恢复原来的 '/' 字符 - tmp = alloc_printf("%s/afl-llvm-rt.o", dir); // 格式化路径 + // 生成 afl-llvm-rt.o 的完整路径 + tmp = alloc_printf("%s/afl-llvm-rt.o", dir); - if (!access(tmp, R_OK)) { // 检查文件是否可读 - obj_path = dir; // 设置运行时库路径 - ck_free(tmp); // 释放临时字符串 - return; + // 检查文件是否可读 + if (!access(tmp, R_OK)) { + obj_path = dir; // 设置对象路径为找到的目录 + ck_free(tmp); // 释放临时路径内存 + return; // 找到文件,结束函数 } - ck_free(tmp); // 释放临时字符串 - ck_free(dir); // 释放目录字符串 + ck_free(tmp); // 释放临时路径内存 + ck_free(dir); // 释放目录名内存 } - if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) { // 检查默认路径 - obj_path = AFL_PATH; // 设置运行时库路径 - return; + // 检查默认路径下的 afl-llvm-rt.o 是否可读 + if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) { + obj_path = AFL_PATH; // 设置对象路径为默认的 AFL_PATH + return; // 找到文件,结束函数 } - FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH"); // 查找失败时提示错误 - + // 如果都找不到,则抛出致命错误 + FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH"); } -/* 复制argv到cc_params,并进行必要的编辑。 */ + +/* 复制 argv 到 cc_params,并进行必要的编辑。 */ static void edit_params(u32 argc, char** argv) { - u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; // 初始化标志变量 - u8 *name; // 当前文件名 + // 初始化标志变量 + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; + u8 *name; - cc_params = ck_alloc((argc + 128) * sizeof(u8*)); // 分配参数数组内存 + // 分配空间以存储参数 + cc_params = ck_alloc((argc + 128) * sizeof(u8*)); - name = strrchr(argv[0], '/'); // 获取程序名 - if (!name) name = argv[0]; else name++; // 如果没有斜杠,则使用完整路径 + // 获取执行的程序名称 + name = strrchr(argv[0], '/'); + if (!name) name = argv[0]; else name++; - // 根据程序名设置编译器 + // 根据程序名称选择合适的编译器 if (!strcmp(name, "afl-clang-fast++")) { - u8* alt_cxx = getenv("AFL_CXX"); // 获取环境变量AFL_CXX - cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++"; // 设置C++编译器 + u8* alt_cxx = getenv("AFL_CXX"); // 获取环境变量 AFL_CXX + cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++"; // 设置 C++ 编译器 } else { - u8* alt_cc = getenv("AFL_CC"); // 获取环境变量AFL_CC - cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; // 设置C编译器 + u8* alt_cc = getenv("AFL_CC"); // 获取环境变量 AFL_CC + cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; // 设置 C 编译器 } - /* 有两种编译afl-clang-fast的方式。传统模式下,我们使用afl-llvm-pass.so注入插桩。 - 在实验性的'trace-pc-guard'模式下,我们使用原生的LLVM插桩回调。后者是最近添加的。 */ - #ifdef USE_TRACE_PC - cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // 添加Trace PC Guard标志 + // 启用跟踪 PC 的覆盖率 + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; #ifndef __ANDROID__ - cc_params[cc_par_cnt++] = "-mllvm"; // 添加LLVM的命令行标志 - cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; // 设置阈值为0 + cc_params[cc_par_cnt++] = "-mllvm"; // LLVM 选项 + cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; // 设定阈值 #endif #else - cc_params[cc_par_cnt++] = "-Xclang"; // 添加编译器的命令行标志 - cc_params[cc_par_cnt++] = "-load"; // 加载指定共享库 - cc_params[cc_par_cnt++] = "-Xclang"; // 添加编译器的命令行标志 - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path); // 添加动态库路径 + // 加载 afl-llvm-pass.so 插件 + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path); #endif /* ^USE_TRACE_PC */ - cc_params[cc_par_cnt++] = "-Qunused-arguments"; // 添加未使用参数警告 + cc_params[cc_par_cnt++] = "-Qunused-arguments"; // 忽略未使用的参数 - // 处理命令行参数 + // 循环处理输入参数 while (--argc) { - u8* cur = *(++argv); // 当前参数 + u8* cur = *(++argv); // 获取当前参数 - if (!strcmp(cur, "-m32")) bit_mode = 32; // 检查32位模式 - if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; // ARM平台 - if (!strcmp(cur, "-m64")) bit_mode = 64; // 检查64位模式 + // 检查位模式 + if (!strcmp(cur, "-m32")) bit_mode = 32; + if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; + if (!strcmp(cur, "-m64")) bit_mode = 64; - if (!strcmp(cur, "-x")) x_set = 1; // 检查-x标志 + // 检查其他编译选项 + if (!strcmp(cur, "-x")) x_set = 1; - // 检查Address Sanitizer + // 检查是否启用地址/内存的 sanitization if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) asan_set = 1; - if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; // 检查FORTIFY_SOURCE + // 检查 FORTIFY_SOURCE 是否启用 + if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; + // 跳过某些链接器选项 if (!strcmp(cur, "-Wl,-z,defs") || - !strcmp(cur, "-Wl,--no-undefined")) continue; // 跳过链接器选项 + !strcmp(cur, "-Wl,--no-undefined")) continue; - cc_params[cc_par_cnt++] = cur; // 添加当前参数到编译参数数组 + // 将当前参数添加到 cc_params + cc_params[cc_par_cnt++] = cur; } - // 检查环境变量AFL_HARDEN + // 如果启用了硬化选项 if (getenv("AFL_HARDEN")) { cc_params[cc_par_cnt++] = "-fstack-protector-all"; // 启用栈保护 - if (!fortify_set) // 如果没有FORTIFY_SOURCE,则添加相关标志 + // 如果没有启用 FORTIFY_SOURCE 则添加对应宏定义 + if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2"; - } - // 检查Address Sanitizer和Memory Sanitizer的互斥 + // 检查地址或内存 sanitization 的设置 if (!asan_set) { - if (getenv("AFL_USE_ASAN")) { // 如果启用Address Sanitizer - + if (getenv("AFL_USE_ASAN")) { if (getenv("AFL_USE_MSAN")) - FATAL("ASAN和MSAN是互斥的"); // 互斥检查 - + FATAL("ASAN and MSAN are mutually exclusive"); // 互斥检查 if (getenv("AFL_HARDEN")) - FATAL("ASAN和AFL_HARDEN是互斥的"); // 互斥检查 - - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 添加标志 - cc_params[cc_par_cnt++] = "-fsanitize=address"; // 启用Address Sanitizer - - } else if (getenv("AFL_USE_MSAN")) { // 如果启用Memory Sanitizer - + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); // 互斥检查 + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 禁用 FORTIFY_SOURCE + cc_params[cc_par_cnt++] = "-fsanitize=address"; // 启用地址条件检测 + } else if (getenv("AFL_USE_MSAN")) { if (getenv("AFL_USE_ASAN")) - FATAL("ASAN和MSAN是互斥的"); // 互斥检查 - + FATAL("ASAN and MSAN are mutually exclusive"); // 互斥检查 if (getenv("AFL_HARDEN")) - FATAL("MSAN和AFL_HARDEN是互斥的"); // 互斥检查 - - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 添加标志 - cc_params[cc_par_cnt++] = "-fsanitize=memory"; // 启用Memory Sanitizer - + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); // 互斥检查 + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 禁用 FORTIFY_SOURCE + cc_params[cc_par_cnt++] = "-fsanitize=memory"; // 启用内存条件检测 } - } #ifdef USE_TRACE_PC - + // 检查 AFL_INST_RATIO 环境变量的设置 if (getenv("AFL_INST_RATIO")) - FATAL("AFL_INST_RATIO在'trace-pc'模式下不可用"); // 提示用户 - -#endif /* USE_TRACE_PC */ - - if (!getenv("AFL_DONT_OPTIMIZE")) { // 如果没有设置不优化选项 - cc_params[cc_par_cnt++] = "-g"; // 添加调试信息 - cc_params[cc_par_cnt++] = "-O3"; // 添加优化等级 - cc_params[cc_par_cnt++] = "-funroll-loops"; // 启用循环展开 - } + FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'."); - if (getenv("AFL_NO_BUILTIN")) { // 检查是否使用内建函数 - cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; // 禁用strcmp内建 - cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; // 禁用strncmp内建 - cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; // 禁用strcasecmp内建 - cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; // 禁用strncasecmp内建 - cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; // 禁用memcmp内建 - } - cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; // 添加手动控制标志 - cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; // 添加编译器标志 - cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; // 添加不安全模式标志 +#endif /* USE_TRACE_PC */ - /* 当用户尝试使用持久化或延迟fork服务器模式时, - 我们需要在二进制文件中注入一个签名,并调用运行时.o文件中的函数。 - 这有三个原因: +// 检查是否不优化 +if (!getenv("AFL_DONT_OPTIMIZE")) { + cc_params[cc_par_cnt++] = "-g"; // 启用调试信息 + cc_params[cc_par_cnt++] = "-O3"; // 启用最高级别的优化 + cc_params[cc_par_cnt++] = "-funroll-loops"; // 循环展开以提高性能 +} - 1) 我们需要说服编译器不要优化掉签名。使用__attribute__((used))实现。 - 2) 我们需要说服链接器在使用-Wl,--gc-sections时不要做同样的事情。 - 通过强制赋值给'volatile'指针实现。 - 3) 我们需要在全局命名空间中声明__afl_persistent_loop(),但在类的方法中这样做很困难。 - 使用__asm__别名技巧来解决。 +// 检查是否禁用内置函数的使用 +if (getenv("AFL_NO_BUILTIN")) { + // 禁用特定的内置字符串比较和内存比较函数 + cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; +} - */ +// 添加 AFL 相关的宏定义 +cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; // 手动控制 +cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; // 标记为 AFL 编译器 +cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; // 不适合生产的模糊构建模式 - cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)=" +// 添加 AFL 循环的实现 +cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)=" "({ static volatile char *_B __attribute__((used)); " " _B = (char*)\"" PERSIST_SIG "\"; " #ifdef __APPLE__ @@ -240,9 +501,10 @@ static void edit_params(u32 argc, char** argv) { "__attribute__((visibility(\"default\"))) " "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " #endif /* ^__APPLE__ */ - "_L(_A); })"; + "_L(_A); })"; // 定义 AFL 循环的宏 - cc_params[cc_par_cnt++] = "-D__AFL_INIT()=" +// 添加 AFL 初始化函数的实现 +cc_params[cc_par_cnt++] = "-D__AFL_INIT()=" "do { static volatile char *_A __attribute__((used)); " " _A = (char*)\"" DEFER_SIG "\"; " #ifdef __APPLE__ @@ -252,57 +514,59 @@ static void edit_params(u32 argc, char** argv) { "__attribute__((visibility(\"default\"))) " "void _I(void) __asm__(\"__afl_manual_init\"); " #endif /* ^__APPLE__ */ - "_I(); } while (0)"; + "_I(); } while (0)"; // 定义 AFL 初始化的宏 - if (x_set) { // 检查是否有-x标志 - cc_params[cc_par_cnt++] = "-x"; // 添加-x标志 - cc_params[cc_par_cnt++] = "none"; // 设置参数 - } +// 如果启用 -x 选项 +if (x_set) { + cc_params[cc_par_cnt++] = "-x"; // 添加 -x 选项 + cc_params[cc_par_cnt++] = "none"; // 设置为 none,表示不对输入进行特定语言解析 +} #ifndef __ANDROID__ - switch (bit_mode) { // 根据位模式选择运行时库 - +// 根据位模式选择不同的运行时库文件 +switch (bit_mode) { case 0: - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); // 添加默认路径 - break; + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); // 默认情况下使用通用的运行时文件 + break; case 32: - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); // 32位库路径 - - if (access(cc_params[cc_par_cnt - 1], R_OK)) // 检查权限 - FATAL("-m32不被你的编译器支持"); // 提示不支持 - - break; + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); // 使用 32 位版本的运行时文件 + // 检查此文件是否可读 + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); // 如果不可读则报错 + break; case 64: - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); // 64位库路径 - - if (access(cc_params[cc_par_cnt - 1], R_OK)) // 检查权限 - FATAL("-m64不被你的编译器支持"); // 提示不支持 - - break; + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); // 使用 64 位版本的运行时文件 + // 检查此文件是否可读 + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); // 如果不可读则报错 + break; +} - } -#endif +// 结束参数数组,以空指针结尾 +cc_params[cc_par_cnt] = NULL; - cc_params[cc_par_cnt] = NULL; // 结束参数数组 - -} /* 主入口点 */ int main(int argc, char** argv) { - if (isatty(2) && !getenv("AFL_QUIET")) { // 检查终端和环境变量 + // 检查标准错误是否连接到终端且 AFL_QUIET 环境变量未设置 + if (isatty(2) && !getenv("AFL_QUIET")) { #ifdef USE_TRACE_PC - SAYF(cCYA "afl-clang-fast [tpcg] " cBRI VERSION cRST " by \n"); // 输出版本信息 + // 如果启用了 USE_TRACE_PC,打印版本信息 + SAYF(cCYA "afl-clang-fast [tpcg] " cBRI VERSION cRST " by \n"); #else - SAYF(cCYA "afl-clang-fast " cBRI VERSION cRST " by \n"); // 输出版本信息 + // 否则,仅打印版本信息 + SAYF(cCYA "afl-clang-fast " cBRI VERSION cRST " by \n"); #endif /* ^USE_TRACE_PC */ } - if (argc < 2) { // 检查参数数量 + // 如果参数数量小于 2,打印帮助信息并退出 + if (argc < 2) { + SAYF("\n" "This is a helper application for afl-fuzz. It serves as a drop-in replacement\n" "for clang, letting you recompile third-party code with the required runtime\n" @@ -316,22 +580,25 @@ int main(int argc, char** argv) { "You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. Setting\n" "AFL_HARDEN enables hardening optimizations in the compiled code.\n\n", - BIN_PATH, BIN_PATH); // 输出用法说明 + BIN_PATH, BIN_PATH); - exit(1); // 退出程序 + exit(1); // 退出程序,返回错误码 1 } #ifndef __ANDROID__ - find_obj(argv[0]); // 查找运行时库 + // 在非 Android 平台下调用 find_obj 函数查找运行时库 + find_obj(argv[0]); #endif - edit_params(argc, argv); // 编辑参数 - - execvp(cc_params[0], (char**)cc_params); // 执行真实编译器 + // 调用 edit_params 函数处理命令行参数并设置编译参数 + edit_params(argc, argv); - FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); // 执行失败提示 + // 使用 execvp 执行指定的编译器命令 + execvp(cc_params[0], (char**)cc_params); - return 0; // 返回0表示成功 + // 如果 execvp 失败,抛出致命错误并打印相关信息 + FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); + return 0; // 正常结束程序 }