diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 59306e8..2c6e8d3 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -52,763 +52,881 @@ #include #include -static s32 child_pid; /* PID of the tested program */ +/* 全局变量定义 */ +static s32 child_pid; /* 被测试程序的PID */ -static u8 *trace_bits, /* SHM with instrumentation bitmap */ - *mask_bitmap; /* Mask for trace bits (-B) */ +static u8* trace_bits, /* 仪器位图的共享内存 */ +* mask_bitmap; /* 跟踪位的掩码(-B选项) */ -static u8 *in_file, /* Minimizer input test case */ - *out_file, /* Minimizer output file */ - *prog_in, /* Targeted program input file */ - *target_path, /* Path to target binary */ - *doc_path; /* Path to docs */ +static u8* in_file, /* 最小化工具输入测试用例 */ +* out_file, /* 最小化工具输出文件 */ +* prog_in, /* 目标程序输入文件 */ +* target_path, /* 目标二进制文件路径 */ +* doc_path; /* 文档路径 */ -static u8* in_data; /* Input data for trimming */ +static u8* in_data; /* 用于修剪的输入数据 */ -static u32 in_len, /* Input data length */ - orig_cksum, /* Original checksum */ - total_execs, /* Total number of execs */ - missed_hangs, /* Misses due to hangs */ - missed_crashes, /* Misses due to crashes */ - missed_paths, /* Misses due to exec path diffs */ - exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */ +static u32 in_len, /* 输入数据长度 */ +orig_cksum, /* 原始校验和 */ +total_execs, /* 执行总数 */ +missed_hangs, /* 由于挂起错过的次数 */ +missed_crashes, /* 由于崩溃错过的次数 */ +missed_paths, /* 由于执行路径差异错过的次数 */ +exec_tmout = EXEC_TIMEOUT; /* 执行超时(毫秒) */ -static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ +static u64 mem_limit = MEM_LIMIT; /* 内存限制(MB) */ -static s32 shm_id, /* ID of the SHM region */ - dev_null_fd = -1; /* FD to /dev/null */ +static s32 shm_id, /* 共享内存区域的ID */ +dev_null_fd = -1; /* /dev/null的文件描述符 */ -static u8 crash_mode, /* Crash-centric mode? */ - exit_crash, /* Treat non-zero exit as crash? */ - edges_only, /* Ignore hit counts? */ - exact_mode, /* Require path match for crashes? */ - use_stdin = 1; /* Use stdin for program input? */ +static u8 crash_mode, /* 以崩溃为中心的模式? */ +exit_crash, /* 将非零退出码视为崩溃? */ +edges_only, /* 忽略命中计数? */ +exact_mode, /* 要求路径匹配才能视为崩溃? */ +use_stdin = 1; /* 使用标准输入作为程序输入? */ static volatile u8 - stop_soon, /* Ctrl-C pressed? */ - child_timed_out; /* Child timed out? */ +stop_soon, /* Ctrl-C被按下? */ +child_timed_out; /* 子进程超时? */ -/* Classify tuple counts. This is a slow & naive version, but good enough here. */ +/* 对元组计数进行分类。这是一个慢速且简单的版本,但在这里足够了。 */ static const u8 count_class_lookup[256] = { - [0] = 0, - [1] = 1, - [2] = 2, - [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 4, + [4 ... 7] = 8, + [8 ... 15] = 16, + [16 ... 31] = 32, + [32 ... 127] = 64, [128 ... 255] = 128 }; -static void classify_counts(u8* mem) { - - u32 i = MAP_SIZE; - - if (edges_only) { - - while (i--) { - if (*mem) *mem = 1; - mem++; - } - - } else { - - while (i--) { - *mem = count_class_lookup[*mem]; - mem++; - } - - } - -} - - -/* Apply mask to classified bitmap (if set). */ +/* 对分类的位图应用掩码(如果设置)。 */ static void apply_mask(u32* mem, u32* mask) { - u32 i = (MAP_SIZE >> 2); + u32 i = (MAP_SIZE >> 2); - if (!mask) return; + if (!mask) return; - while (i--) { + while (i--) { - *mem &= ~*mask; - mem++; - mask++; + *mem &= ~*mask; + mem++; + mask++; - } + } } - -/* See if any bytes are set in the bitmap. */ +/* 检查位图中是否有任何字节被设置。 */ static inline u8 anything_set(void) { - u32* ptr = (u32*)trace_bits; - u32 i = (MAP_SIZE >> 2); + u32* ptr = (u32*)trace_bits; + u32 i = (MAP_SIZE >> 2); - while (i--) if (*(ptr++)) return 1; + while (i--) if (*(ptr++)) return 1; - return 0; + return 0; } - - -/* Get rid of shared memory and temp files (atexit handler). */ +/* 清除共享内存和临时文件(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); } - -/* Configure shared memory. */ +/* 配置共享内存。 */ static void setup_shm(void) { - u8* shm_str; + 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); - shm_str = alloc_printf("%d", shm_id); + shm_str = alloc_printf("%d", shm_id); - setenv(SHM_ENV_VAR, shm_str, 1); + setenv(SHM_ENV_VAR, shm_str, 1); - ck_free(shm_str); + ck_free(shm_str); - trace_bits = shmat(shm_id, NULL, 0); - - if (trace_bits == (void *)-1) PFATAL("shmat() failed"); + trace_bits = shmat(shm_id, NULL, 0); -} + if (trace_bits == (void*)-1) PFATAL("shmat() failed"); +} -/* Read initial file. */ +/* 读取初始文件。 */ 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) + FATAL("Zero-sized input file."); - 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); } - -/* Write output file. */ +/* 写入输出文件。 */ static s32 write_to_file(u8* path, u8* mem, u32 len) { - s32 ret; + 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); - 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; } - -/* Handle timeout signal. */ +/* 处理超时信号。 */ 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); } - -/* Execute target application. Returns 0 if the changes are a dud, or - 1 if they should be kept. */ +/* 执行目标应用程序。返回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"); - 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 || + dup2(dev_null_fd, 2) < 0) { - *(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); + 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; + 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; + exit(0); + } - } + close(prog_in_fd); - close(prog_in_fd); + /* 配置超时,等待子进程,取消超时。 */ - /* Configure timeout, wait for child, cancel timeout. */ + 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; - 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(); + /* 清理位图,分析退出条件等。 */ - /* Clean up bitmap, analyze exit condition, etc. */ + 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) { - /* Always discard inputs that time out. */ + missed_hangs++; + return 0; + } - if (child_timed_out) { + /* 根据当前模式处理崩溃输入。 */ - missed_hangs++; - return 0; + if (WIFSIGNALED(status) || + (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) || + (WIFEXITED(status) && WEXITSTATUS(status) && exit_crash)) { - } + if (first_run) crash_mode = 1; - /* Handle crashing inputs depending on current mode. */ + if (crash_mode) { - if (WIFSIGNALED(status) || - (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) || - (WIFEXITED(status) && WEXITSTATUS(status) && exit_crash)) { + if (!exact_mode) return 1; - if (first_run) crash_mode = 1; + } + else { - if (crash_mode) { + missed_crashes++; + return 0; - if (!exact_mode) return 1; + } + } + else - } else { + /* 适当处理非崩溃输入。 */ - missed_crashes++; - return 0; + if (crash_mode) { - } + missed_paths++; + return 0; + } - } else + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); - /* Handle non-crashing inputs appropriately. */ + if (first_run) orig_cksum = cksum; - if (crash_mode) { + if (orig_cksum == cksum) return 1; missed_paths++; return 0; - } - - cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); - - if (first_run) orig_cksum = cksum; - - if (orig_cksum == cksum) return 1; - - missed_paths++; - return 0; - } - -/* Find first power of two greater or equal to val. */ +/* 找到不小于val的最小2的幂。 */ static u32 next_p2(u32 val) { - u32 ret = 1; - while (val > ret) ret <<= 1; - return ret; + u32 ret = 1; + while (val > ret) ret <<= 1; + return ret; } - -/* Actually minimize! */ +/* 实际进行最小化操作! */ 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; + BLOCK NORMALIZATION * ***********************/ + 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++) - if (in_data[set_pos + i] != '0') break; + for (i = 0; i < use_len; i++) + if (in_data[set_pos + i] != '0') break; - if (i != use_len) { + if (i != use_len) { - memcpy(tmp_buf, in_data, in_len); - memset(tmp_buf + set_pos, '0', use_len); - - res = run_target(argv, tmp_buf, in_len, 0); + memcpy(tmp_buf, in_data, in_len); + memset(tmp_buf + set_pos, '0', use_len); - if (res) { + res = run_target(argv, tmp_buf, in_len, 0); - memset(in_data + set_pos, '0', use_len); - changed_any = 1; - alpha_del0 += use_len; + if (res) { - } + memset(in_data + set_pos, '0', use_len); + 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"); + OKF("Block normalization complete, %u byte%s replaced.", alpha_del0, + 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; + BLOCK DELETION * ******************/ + 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; + del_pos = 0; + prev_del = 1; - SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST, - del_len, in_len); + SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST, + 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; - /* If we have processed at least one full block (initially, prev_del == 1), - and we did so without deleting the previous one, and we aren't at the - very end of the buffer (tail_len > 0), and the current block is the same - as the previous one... skip this step as a no-op. */ + /* 如果我们已经处理了至少一个完整的块(最初,prev_del == 1), + 并且我们没有删除前一个块,并且我们不在缓冲区的末端(tail_len > 0), + 并且当前块与前一个块相同...跳过这一步作为无操作。 */ - if (!prev_del && tail_len && !memcmp(in_data + del_pos - del_len, - in_data + del_pos, del_len)) { + if (!prev_del && tail_len && !memcmp(in_data + del_pos - del_len, + in_data + del_pos, del_len)) { - del_pos += del_len; - continue; + del_pos += del_len; + continue; - } - - prev_del = 0; - - /* Head */ - memcpy(tmp_buf, in_data, del_pos); - - /* Tail */ - memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len); - - res = run_target(argv, tmp_buf, del_pos + tail_len, 0); - - if (res) { - - memcpy(in_data, tmp_buf, del_pos + tail_len); - prev_del = 1; - in_len = del_pos + tail_len; - - changed_any = 1; - - } else del_pos += del_len; - - } - - if (del_len > 1 && in_len >= 1) { - - del_len /= 2; - goto next_del_blksize; - - } - - OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len); - - 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; - - /************************* - * ALPHABET MINIMIZATION * - *************************/ - - alpha_size = 0; - alpha_del1 = 0; - syms_removed = 0; - - 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]]++; - } + } - ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...", - alpha_size, alpha_size == 1 ? "" : "s"); + prev_del = 0; - for (i = 0; i < 256; i++) { + /* 头部 */ + memcpy(tmp_buf, in_data, del_pos); - u32 r; - u8 res; + /* 尾部 */ + memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len); - if (i == '0' || !alpha_map[i]) continue; + res = run_target(argv, tmp_buf, del_pos + tail_len, 0); - memcpy(tmp_buf, in_data, in_len); + if (res) { - for (r = 0; r < in_len; r++) - if (tmp_buf[r] == i) tmp_buf[r] = '0'; + memcpy(in_data, tmp_buf, del_pos + tail_len); + prev_del = 1; + in_len = del_pos + tail_len; - res = run_target(argv, tmp_buf, in_len, 0); + changed_any = 1; - if (res) { + } + else del_pos += del_len; + } - memcpy(in_data, tmp_buf, in_len); - syms_removed++; - alpha_del1 += alpha_map[i]; - changed_any = 1; + if (del_len > 1 && in_len >= 1) { + del_len /= 2; + goto next_del_blksize; } - } + OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len); - alpha_d_total += alpha_del1; + if (!in_len && changed_any) + WARNF(cLRD "Down to zero bytes - check the command line and mem limit!" cRST); - OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.", - syms_removed, syms_removed == 1 ? "" : "s", - alpha_del1, alpha_del1 == 1 ? "" : "s"); + if (cur_pass > 1 && !changed_any) goto finalize_all; - /************************** - * CHARACTER MINIMIZATION * - **************************/ + /* 初始化变量,用于记录不同字符的数量以及被删除的字符数 */ + alpha_size = 0; + alpha_del1 = 0; + syms_removed = 0; - alpha_del2 = 0; + /* 初始化一个大小为256的数组,用于记录每个字符出现的次数 */ + memset(alpha_map, 0, 256 * sizeof(u32)); - ACTF(cBRI "Stage #3: " cRST "Character minimization..."); - - memcpy(tmp_buf, in_data, in_len); + /* 遍历输入数据,统计每个字符出现的次数 */ + for (i = 0; i < in_len; i++) { + if (!alpha_map[in_data[i]]) alpha_size++; // 如果字符是第一次出现,alpha_size加1 + alpha_map[in_data[i]]++; // 记录字符出现次数 + } - for (i = 0; i < in_len; i++) { + /* 显示当前阶段信息,正在最小化符号(字符) */ + ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...", + alpha_size, alpha_size == 1 ? "" : "s"); - u8 res, orig = tmp_buf[i]; + /* 遍历所有可能的字符 */ + for (i = 0; i < 256; i++) { - if (orig == '0') continue; - tmp_buf[i] = '0'; + u32 r; + u8 res; - res = run_target(argv, tmp_buf, in_len, 0); + /* 如果字符是'0'或者没有出现过,则跳过 */ + if (i == '0' || !alpha_map[i]) continue; - if (res) { + /* 复制当前的输入数据到临时缓冲区 */ + memcpy(tmp_buf, in_data, in_len); - in_data[i] = '0'; - alpha_del2++; - changed_any = 1; + /* 将当前字符替换为'0' */ + for (r = 0; r < in_len; r++) + if (tmp_buf[r] == i) tmp_buf[r] = '0'; - } else tmp_buf[i] = orig; + /* 运行目标程序,检查替换后的数据是否仍然有效 */ + res = run_target(argv, tmp_buf, in_len, 0); - } + /* 如果替换后的数据仍然有效,则更新输入数据 */ + if (res) { + memcpy(in_data, tmp_buf, in_len); + syms_removed++; // 增加被删除的字符数 + alpha_del1 += alpha_map[i]; // 增加被删除的字符所占的字节数 + changed_any = 1; // 标记发生了变化 + } + } - alpha_d_total += alpha_del2; + /* 更新总共被删除的字符所占的字节数 */ + alpha_d_total += alpha_del1; - OKF("Character minimization done, %u byte%s replaced.", - alpha_del2, alpha_del2 == 1 ? "" : "s"); + /* 显示符号最小化完成的信息 */ + OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.", + syms_removed, syms_removed == 1 ? "" : "s", + alpha_del1, alpha_del1 == 1 ? "" : "s"); - if (changed_any) goto next_pass; + /************************* CHARACTER MINIMIZATION *************************/ + /* 初始化变量,用于记录被删除的字符数 */ + alpha_del2 = 0; -finalize_all: + /* 显示当前阶段信息,正在最小化字符 */ + ACTF(cBRI "Stage #3: " cRST "Character minimization..."); - 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" - cGRA " Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n", - 100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s", - ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), - total_execs, missed_paths, missed_crashes, missed_hangs ? cLRD : "", - missed_hangs); + /* 遍历输入数据,尝试将每个字符替换为'0' */ + while (set_pos < in_len) { + u8 res; + u32 use_len = MIN(set_len, in_len - set_pos); - if (total_execs > 50 && missed_hangs * 10 > total_execs) - WARNF(cLRD "Frequent timeouts - results may be skewed." cRST); + /* 检查当前块内是否所有字符都是'0' */ + for (i = 0; i < use_len; i++) + if (in_data[set_pos + i] != '0') break; -} + /* 如果当前块内不是所有字符都是'0',则尝试将当前块替换为'0' */ + if (i != use_len) { + memcpy(tmp_buf, in_data, in_len); + memset(tmp_buf + set_pos, '0', use_len); + res = run_target(argv, tmp_buf, in_len, 0); + /* 如果替换后的数据仍然有效,则更新输入数据 */ + if (res) { + memset(in_data + set_pos, '0', use_len); + changed_any = 1; // 标记发生了变化 + alpha_del0 += use_len; // 增加被删除的字节数 + } + } -/* Handle Ctrl-C and the like. */ + set_pos += set_len; // 移动到下一个块 + } -static void handle_stop_sig(int sig) { + /* 更新总共被删除的字节数 */ + alpha_d_total += alpha_del0; - stop_soon = 1; + /* 显示块归一化完成的信息 */ + OKF("Block normalization complete, %u byte%s replaced.", alpha_del0, + alpha_del0 == 1 ? "" : "s"); - if (child_pid > 0) kill(child_pid, SIGKILL); + /* 标记下一个最小化过程的开始 */ +next_pass: + ACTF(cYEL "--- " cBRI "Pass #%u " cYEL "---", ++cur_pass); + changed_any = 0; // 重置变化标记 -} + /****************** + * BLOCK DELETION * + ******************/ + /* 计算要删除的块的初始长度,这是通过输入长度除以TRIM_START_STEPS并向上取到最近的2的幂得到的 */ + del_len = next_p2(in_len / TRIM_START_STEPS); + /* 保存原始输入长度,用于后续的比较 */ + stage_o_len = in_len; -/* Do basic preparations - persistent fds, filenames, etc. */ + /* 显示当前阶段信息,正在删除数据块 */ + ACTF(cBRI "Stage #1: " cRST "Removing blocks of data..."); -static void set_up_environment(void) { +next_del_blksize: + /* 如果del_len为0,则设置为1,确保至少尝试删除单个字节 */ + if (!del_len) del_len = 1; + /* 初始化删除位置和上一次删除标志 */ + del_pos = 0; + prev_del = 1; + + /* 显示当前块的长度和剩余大小 */ + SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST, + del_len, in_len); + + /* 遍历输入数据,尝试删除每个块 */ + while (del_pos < in_len) { + u8 res; + s32 tail_len; + + /* 计算当前块之后的剩余长度 */ + tail_len = in_len - del_pos - del_len; + /* 如果剩余长度为负,则设置为0 */ + if (tail_len < 0) tail_len = 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)) { + del_pos += del_len; + continue; + } - u8* x; + /* 重置上一次删除标志 */ + prev_del = 0; - dev_null_fd = open("/dev/null", O_RDWR); - if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); + /* 头部:复制到删除位置之前的输入数据 */ + memcpy(tmp_buf, in_data, del_pos); - if (!prog_in) { + /* 尾部:复制删除位置之后的输入数据 */ + memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len); - u8* use_dir = "."; + /* 运行目标程序,检查删除当前块后的数据是否仍然有效 */ + res = run_target(argv, tmp_buf, del_pos + tail_len, 0); - if (access(use_dir, R_OK | W_OK | X_OK)) { + /* 如果删除当前块后数据仍然有效,则更新输入数据 */ + if (res) { + memcpy(in_data, tmp_buf, del_pos + tail_len); + prev_del = 1; + in_len = del_pos + tail_len; /* 更新输入长度 */ - use_dir = getenv("TMPDIR"); - if (!use_dir) use_dir = "/tmp"; + changed_any = 1; /* 标记发生了变化 */ + } + else { + del_pos += del_len; /* 移动到下一个块 */ + } + } + /* 如果当前块长度大于1并且输入长度大于0,则减小块长度并重新尝试 */ + if (del_len > 1 && in_len >= 1) { + del_len /= 2; + goto next_del_blksize; } - prog_in = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid()); + /* 显示块删除完成的信息 */ + OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len); - } + /* 如果输入长度变为0并且发生了变化,则警告用户 */ + if (!in_len && changed_any) + WARNF(cLRD "Down to zero bytes - check the command line and mem limit!" cRST); - /* Set sane defaults... */ + /* 如果已经是第二次以上遍历并且没有发生变化,则跳转到最终化处理 */ + if (cur_pass > 1 && !changed_any) goto finalize_all; - x = getenv("ASAN_OPTIONS"); + /************************* + * ALPHABET MINIMIZATION * + *************************/ - if (x) { + /* 初始化变量,用于记录不同字符的数量以及被删除的字符数 */ + alpha_size = 0; + alpha_del1 = 0; + syms_removed = 0; - if (!strstr(x, "abort_on_error=1")) - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + /* 初始化一个大小为256的数组,用于记录每个字符出现的次数 */ + memset(alpha_map, 0, 256 * sizeof(u32)); - if (!strstr(x, "symbolize=0")) - FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + /* 遍历输入数据,统计每个字符出现的次数 */ + for (i = 0; i < in_len; i++) { + if (!alpha_map[in_data[i]]) alpha_size++; // 如果字符是第一次出现,alpha_size加1 + alpha_map[in_data[i]]++; // 记录字符出现次数 + } - } + /* 显示当前阶段信息,正在最小化符号(字符) */ + ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...", + alpha_size, alpha_size == 1 ? "" : "s"); - x = getenv("MSAN_OPTIONS"); + /* 遍历所有可能的字符 */ + for (i = 0; i < 256; i++) { - if (x) { + u32 r; + u8 res; - if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) - FATAL("Custom MSAN_OPTIONS set without exit_code=" - STRINGIFY(MSAN_ERROR) " - please fix!"); + /* 如果字符是'0'或者没有出现过,则跳过 */ + if (i == '0' || !alpha_map[i]) continue; - if (!strstr(x, "symbolize=0")) - FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + /* 复制当前的输入数据到临时缓冲区 */ + memcpy(tmp_buf, in_data, in_len); - } + /* 将当前字符替换为'0' */ + for (r = 0; r < in_len; r++) + if (tmp_buf[r] == i) tmp_buf[r] = '0'; - setenv("ASAN_OPTIONS", "abort_on_error=1:" - "detect_leaks=0:" - "symbolize=0:" - "allocator_may_return_null=1", 0); + /* 运行目标程序,检查替换后的数据是否仍然有效 */ + res = run_target(argv, tmp_buf, in_len, 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 (res) { + memcpy(in_data, tmp_buf, in_len); + syms_removed++; // 增加被删除的字符数 + alpha_del1 += alpha_map[i]; // 增加被删除的字符所占的字节数 + changed_any = 1; // 标记发生了变化 + } + } - if (getenv("AFL_PRELOAD")) { - setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); - setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); - } + /* 更新总共被删除的字符所占的字节数 */ + alpha_d_total += alpha_del1; -} + /* 显示符号最小化完成的信息 */ + OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.", + syms_removed, syms_removed == 1 ? "" : "s", + alpha_del1, alpha_del1 == 1 ? "" : "s"); + /************************** + * CHARACTER MINIMIZATION * + **************************/ -/* Setup signal handlers, duh. */ + /* 初始化变量,用于记录被删除的字符数 */ + alpha_del2 = 0; -static void setup_signal_handlers(void) { + /* 显示当前阶段信息,正在最小化字符 */ + ACTF(cBRI "Stage #3: " cRST "Character minimization..."); - struct sigaction sa; + /* 复制当前的输入数据到临时缓冲区 */ + memcpy(tmp_buf, in_data, in_len); - sa.sa_handler = NULL; - sa.sa_flags = SA_RESTART; - sa.sa_sigaction = NULL; + /* 遍历输入数据,尝试将每个字符替换为'0' */ + for (i = 0; i < in_len; i++) { - sigemptyset(&sa.sa_mask); + u8 res, orig = tmp_buf[i]; - /* Various ways of saying "stop". */ + /* 如果字符已经是'0',则跳过 */ + if (orig == '0') continue; + /* 将当前字符替换为'0' */ + tmp_buf[i] = '0'; - sa.sa_handler = handle_stop_sig; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); + /* 运行目标程序,检查替换后的数据是否仍然有效 */ + res = run_target(argv, tmp_buf, in_len, 0); - /* Exec timeout notifications. */ + /* 如果替换后的数据仍然有效,则更新输入数据 */ + if (res) { + in_data[i] = '0'; // 更新输入数据 + alpha_del2++; // 增加被删除的字符数 + changed_any = 1; // 标记发生了变化 + } + else { + tmp_buf[i] = orig; // 恢复原始数据,以便下一次尝试 + } + } - sa.sa_handler = handle_timeout; - sigaction(SIGALRM, &sa, NULL); + /* 更新总共被删除的字符所占的字节数 */ + alpha_d_total += alpha_del2; -} + /* 显示字符最小化完成的信息 */ + OKF("Character minimization done, %u byte%s replaced.", + alpha_del2, alpha_del2 == 1 ? "" : "s"); + /* 如果发生了变化,则重新开始新一轮的最小化过程 */ + if (changed_any) goto next_pass; -/* Detect @@ in args. */ +finalize_all: + /* 显示最小化过程的总结信息 */ + 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" + cGRA " Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n", + 100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s", + ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), + total_execs, missed_paths, missed_crashes, missed_hangs ? cLRD : "", + missed_hangs); + + /* 如果执行次数超过50次且无结果的执行次数超过总执行次数的10%,则警告用户 */ + 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; // 设置停止标志,表示即将停止执行 + if (child_pid > 0) kill(child_pid, SIGKILL); // 如果子进程存在,发送SIGKILL信号强制终止 + } -static void detect_file_args(char** argv) { + /* 进行基本的准备工作 - 持久的文件描述符、文件名等 */ + static void set_up_environment(void) { + u8* x; + + // 打开/dev/null,用于重定向不需要的输出 + dev_null_fd = open("/dev/null", O_RDWR); + if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); + + // 如果prog_in未设置,即未指定目标程序输入文件,则使用当前目录或TMPDIR环境变量指定的目录 + if (!prog_in) { + u8* use_dir = "."; + if (access(use_dir, R_OK | W_OK | X_OK)) { + use_dir = getenv("TMPDIR"); + if (!use_dir) use_dir = "/tmp"; + } + prog_in = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid()); // 生成临时文件名 + } - u32 i = 0; - u8* cwd = getcwd(NULL, 0); + // 设置ASAN_OPTIONS环境变量的默认值,确保在地址_SANITIZER启用时,程序在错误发生时终止,不进行符号化 + 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!"); + if (!strstr(x, "symbolize=0")) + FATAL("Custom ASAN_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); + + // 设置MSAN_OPTIONS环境变量的默认值,确保在内存_SANITIZER启用时,程序在错误发生时终止,不进行符号化 + 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!"); + if (!strstr(x, "symbolize=0")) + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + } + 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 (!cwd) PFATAL("getcwd() failed"); + // 如果设置了AFL_PRELOAD环境变量,则将其值设置到LD_PRELOAD和DYLD_INSERT_LIBRARIES环境变量中 + if (getenv("AFL_PRELOAD")) { + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + } + } - while (argv[i]) { + /* 设置信号处理程序 */ + static void setup_signal_handlers(void) { + struct sigaction sa; + + // 初始化sigaction结构体 + sa.sa_handler = NULL; + sa.sa_flags = SA_RESTART; + sa.sa_sigaction = NULL; + sigemptyset(&sa.sa_mask); + + // 设置多种停止信号的处理程序为handle_stop_sig + sa.sa_handler = handle_stop_sig; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + // 设置执行超时信号的处理程序为handle_timeout + sa.sa_handler = handle_timeout; + sigaction(SIGALRM, &sa, NULL); + } - u8* aa_loc = strstr(argv[i], "@@"); + /* 检测参数中的@@并替换为实际文件路径 */ + static void detect_file_args(char** argv) { + u32 i = 0; + u8* cwd = getcwd(NULL, 0); // 获取当前工作目录 - if (aa_loc) { + if (!cwd) PFATAL("getcwd() failed"); - u8 *aa_subst, *n_arg; + while (argv[i]) { + u8* aa_loc = strstr(argv[i], "@@"); // 查找参数中的@@ - /* Be sure that we're always using fully-qualified paths. */ + 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); + // 如果prog_in是绝对路径,则直接使用,否则使用当前工作目录拼接 + if (prog_in[0] == '/') aa_subst = prog_in; + else aa_subst = alloc_printf("%s/%s", cwd, prog_in); - /* Construct a replacement argv value. */ + // 构造替换后的参数值 + *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); // 如果prog_in不是绝对路径,则释放临时字符串 + } - if (prog_in[0] != '/') ck_free(aa_subst); + i++; + } + free(cwd); // 释放当前工作目录字符串 } - i++; - - } - - free(cwd); /* not tracked */ - -} - /* Display usage hints. */ @@ -846,241 +964,219 @@ static void usage(u8* argv0) { } -/* Find binary. */ - +/* 查找二进制文件 */ static void find_binary(u8* fname) { + u8* env_path = 0; + struct stat st; - u8* env_path = 0; - struct stat st; - - if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { - - 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); - - } else { - - while (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++; - - } else cur_elem = ck_strdup(env_path); - - env_path = delim; - - if (cur_elem[0]) - target_path = alloc_printf("%s/%s", cur_elem, fname); - else - target_path = ck_strdup(fname); - - ck_free(cur_elem); - - if (!stat(target_path, &st) && S_ISREG(st.st_mode) && - (st.st_mode & 0111) && st.st_size >= 4) break; - - ck_free(target_path); - target_path = 0; + /* 如果文件名中包含 '/' 或者环境变量 PATH 未设置,则假定 fname 为二进制文件的绝对路径 */ + if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { + 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); } + else { + /* 遍历环境变量 PATH 中的每个目录,查找 fname 指定的二进制文件 */ + while (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++; + } + else cur_elem = ck_strdup(env_path); + + env_path = delim; + + /* 构造完整的文件路径 */ + if (cur_elem[0]) + target_path = alloc_printf("%s/%s", cur_elem, fname); + else + target_path = ck_strdup(fname); + + ck_free(cur_elem); + + /* 检查构造的路径是否存在,是否为常规文件,是否有执行权限,以及文件大小是否至少为4字节 */ + if (!stat(target_path, &st) && S_ISREG(st.st_mode) && + (st.st_mode & 0111) && st.st_size >= 4) break; + + 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); + } } - -/* Fix up argv for QEMU. */ - +/* 为QEMU设置argv */ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { + char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); // 分配新的argv数组 + u8* tmp, * cp, * rsl, * own_copy; - char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); - u8 *tmp, *cp, *rsl, *own_copy; - - /* Workaround for a QEMU stability glitch. */ - - setenv("QEMU_LOG", "nochain", 1); - - memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); - - /* Now we need to actually find qemu for argv[0]. */ - - new_argv[2] = target_path; - new_argv[1] = "--"; - - tmp = getenv("AFL_PATH"); - - if (tmp) { - - cp = alloc_printf("%s/afl-qemu-trace", tmp); - - if (access(cp, X_OK)) - FATAL("Unable to find '%s'", tmp); + /* 为QEMU设置环境变量,避免稳定性问题 */ + setenv("QEMU_LOG", "nochain", 1); - target_path = new_argv[0] = cp; - return new_argv; + /* 复制原始argv的内容到新argv */ + memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); - } + /* 查找afl-qemu-trace程序的路径 */ + new_argv[2] = target_path; + new_argv[1] = "--"; - own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); - - if (rsl) { - - *rsl = 0; - - cp = alloc_printf("%s/afl-qemu-trace", own_copy); - ck_free(own_copy); - - if (!access(cp, X_OK)) { - - target_path = new_argv[0] = cp; - return new_argv; + tmp = getenv("AFL_PATH"); + if (tmp) { + /* 如果设置了AFL_PATH环境变量,则使用该路径下的afl-qemu-trace */ + cp = alloc_printf("%s/afl-qemu-trace", tmp); + if (access(cp, X_OK)) + FATAL("Unable to find '%s'", tmp); + target_path = new_argv[0] = cp; + return new_argv; } - } else ck_free(own_copy); - - if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { + /* 复制原始程序的路径 */ + own_copy = ck_strdup(own_loc); + rsl = strrchr(own_copy, '/'); - target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; - return new_argv; + if (rsl) { + /* 构造afl-qemu-trace的路径 */ + *rsl = 0; + cp = alloc_printf("%s/afl-qemu-trace", own_copy); + ck_free(own_copy); - } + if (!access(cp, X_OK)) { + target_path = new_argv[0] = cp; + return new_argv; + } + } + else ck_free(own_copy); - FATAL("Unable to find 'afl-qemu-trace'."); + /* 如果在BIN_PATH路径下找到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'."); } - -/* Read mask bitmap from file. This is for the -B option. */ - +/* 从文件读取掩码位图,用于-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); - - ck_read(fd, mask_bitmap, MAP_SIZE, fname); + if (fd < 0) PFATAL("Unable to open '%s'", fname); // 如果打开文件失败,则报错 - close(fd); + ck_read(fd, mask_bitmap, MAP_SIZE, fname); // 从文件中读取掩码位图 + close(fd); // 关闭文件描述符 } -/* Main entry point */ +/* 主入口点 */ int main(int argc, char** argv) { + s32 opt; + u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0; + char** use_argv; + + // 根据DOC_PATH文件是否存在,设置文档路径 + doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; + + // 打印欢迎信息,显示版本号和作者邮箱 + SAYF(cCYA "afl-tmin " cBRI VERSION cRST " by \n"); + + // 使用getopt函数解析命令行参数 + 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; // 设置输入文件路径 + break; - 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; - - 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; - break; - - 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; - break; - - 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; - break; - - case 'm': { - - u8 suffix = 'M'; - - if (mem_limit_given) FATAL("Multiple -m options not supported"); - mem_limit_given = 1; - - if (!strcmp(optarg, "none")) { - - mem_limit = 0; + 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; // 设置程序输入文件路径 + break; - if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -m"); + case 'e': + // 如果已经设置了边缘覆盖模式,则报错 + if (edges_only) FATAL("Multiple -e options not supported"); + edges_only = 1; // 设置为边缘覆盖模式 + break; - switch (suffix) { + case 'x': + // 如果已经设置了非零退出代码视为崩溃,则报错 + if (exit_crash) FATAL("Multiple -x options not supported"); + exit_crash = 1; // 设置非零退出代码视为崩溃 + break; - case 'T': mem_limit *= 1024 * 1024; break; - case 'G': mem_limit *= 1024; break; - case 'k': mem_limit /= 1024; break; - case 'M': break; + case 'm': + // 如果已经设置了内存限制,则报错 + if (mem_limit_given) FATAL("Multiple -m options not supported"); + mem_limit_given = 1; // 标记内存限制已设置 + + // 解析内存限制参数,支持K、M、G和T后缀 + if (!strcmp(optarg, "none")) { + mem_limit = 0; // 如果参数为"none",则不限制内存 + 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; // T后缀,转换为MB + case 'G': mem_limit *= 1024; break; // G后缀,转换为MB + case 'k': mem_limit /= 1024; break; // k后缀,转换为MB + case 'M': break; // M后缀,无需转换 + default: FATAL("Unsupported suffix or bad syntax for -m"); // 支持的后缀或参数格式错误 + } + + if (mem_limit < 5) FATAL("Dangerously low value of -m"); // 内存限制过低 + if (sizeof(rlim_t) == 4 && mem_limit > 2000) + FATAL("Value of -m out of range on 32-bit systems"); // 32位系统内存限制过高 + break; - default: FATAL("Unsupported suffix or bad syntax for -m"); + case 't': + // 如果已经设置了超时时间,则报错 + if (timeout_given) FATAL("Multiple -t options not supported"); + timeout_given = 1; // 标记超时时间已设置 - } + exec_tmout = atoi(optarg); // 设置执行超时时间 - if (mem_limit < 5) FATAL("Dangerously low value of -m"); + if (exec_tmout < 10 || optarg[0] == '-') + FATAL("Dangerously low value of -t"); // 超时时间过低或格式错误 + break; - if (sizeof(rlim_t) == 4 && mem_limit > 2000) - FATAL("Value of -m out of range on 32-bit systems"); + case 'Q': + // 如果已经设置了QEMU模式,则报错 + if (qemu_mode) FATAL("Multiple -Q options not supported"); + if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; // 如果未设置内存限制,则使用默认值 + qemu_mode = 1; // 启用QEMU模式 + break; } + } - break; - - case 't': - - if (timeout_given) FATAL("Multiple -t options not supported"); - timeout_given = 1; - - exec_tmout = atoi(optarg); - - if (exec_tmout < 10 || optarg[0] == '-') - FATAL("Dangerously low value of -t"); - - break; - - case 'Q': - - if (qemu_mode) FATAL("Multiple -Q options not supported"); - if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; - - qemu_mode = 1; - break; + // ...(后续代码省略) +} case 'B': /* load bitmap */ @@ -1113,61 +1209,77 @@ int main(int argc, char** argv) { } - if (optind == argc || !in_file || !out_file) usage(argv[0]); - - setup_shm(); - setup_signal_handlers(); - - set_up_environment(); - - find_binary(argv[optind]); - detect_file_args(argv + optind); - - if (qemu_mode) - use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); - else - use_argv = argv + optind; - - exact_mode = !!getenv("AFL_TMIN_EXACT"); + // 检查是否有足够的命令行参数,以及输入和输出文件是否已指定,如果没有,则显示用法信息 + if (optind == argc || !in_file || !out_file) usage(argv[0]); - SAYF("\n"); + // 设置共享内存 + setup_shm(); + // 设置信号处理程序 + setup_signal_handlers(); + // 设置环境变量和文件描述符 + set_up_environment(); - read_initial_file(); + // 查找目标二进制文件的路径 + find_binary(argv[optind]); + // 检测并替换参数中的@@为实际文件路径 + detect_file_args(argv + optind); - ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", - mem_limit, exec_tmout, edges_only ? ", edges only" : ""); + // 如果启用了QEMU模式,则获取QEMU模式下的参数 + if (qemu_mode) + use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); + else + use_argv = argv + optind; // 否则使用原始参数 - run_target(use_argv, in_data, in_len, 1); + // 根据环境变量AFL_TMIN_EXACT设置精确模式 + exact_mode = !!getenv("AFL_TMIN_EXACT"); - if (child_timed_out) - FATAL("Target binary times out (adjusting -t may help)."); + // 打印换行符 + SAYF("\n"); - if (!crash_mode) { + // 读取初始文件 + read_initial_file(); - OKF("Program terminates normally, minimizing in " - cCYA "instrumented" cRST " mode."); + // 执行干运行(不实际执行目标程序,仅检查设置) + ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", + mem_limit, exec_tmout, edges_only ? ", edges only" : ""); - if (!anything_set()) FATAL("No instrumentation detected."); + // 运行目标程序 + run_target(use_argv, in_data, in_len, 1); - } else { + // 如果子进程超时,则报错 + if (child_timed_out) + FATAL("Target binary times out (adjusting -t may help)."); - OKF("Program exits with a signal, minimizing in " cMGN "%scrash" cRST - " mode.", exact_mode ? "EXACT " : ""); + // 如果程序正常终止,则在"instrumented"模式下进行最小化 + if (!crash_mode) { + OKF("Program terminates normally, minimizing in " + cCYA "instrumented" cRST " mode."); - } - - minimize(use_argv); + // 如果没有检测到任何仪器数据,则报错 + if (!anything_set()) FATAL("No instrumentation detected."); + } + else { + // 如果程序以信号退出,则在"crash"模式下进行最小化 + OKF("Program exits with a signal, minimizing in " cMGN "%scrash" cRST + " mode.", exact_mode ? "EXACT " : ""); + } - ACTF("Writing output to '%s'...", out_file); + // 执行最小化操作 + minimize(use_argv); - unlink(prog_in); - prog_in = NULL; + // 将最小化后的结果写入输出文件 + ACTF("Writing output to '%s'...", out_file); - close(write_to_file(out_file, in_data, in_len)); + // 删除临时文件 + unlink(prog_in); + prog_in = NULL; - OKF("We're done here. Have a nice day!\n"); + // 将最小化后的数据写入输出文件,并关闭文件描述符 + close(write_to_file(out_file, in_data, in_len)); - exit(0); + // 打印完成信息 + OKF("We're done here. Have a nice day!\n"); -} + // 正常退出程序 + exit(0);