diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index 471970e..9f2d256 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -4,6 +4,12 @@ "*.wpy": "vue", "*.wxml": "html", "*.wxss": "css", - "string.h": "c" + "string.h": "c", + "cstdlib": "c", + "typeinfo": "c", + "stdlib.h": "c", + "alloc-inl.h": "c", + "debug.h": "c", + "android-ashmem.h": "c" } } \ No newline at end of file diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index da5175c..7625d8a 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -6570,288 +6570,282 @@ havoc_stage: splices them together at some offset, then relies on the havoc code to mutate that blob. */ -retry_splicing: +retry_splicing: // 重试拼接的标签 + // 检查是否能使用拼接,并且拼接循环次数小于预设的拼接次数, + // 同时队列中的路径数大于1且当前路径长度大于1 if (use_splicing && splice_cycle++ < SPLICE_CYCLES && queued_paths > 1 && queue_cur->len > 1) { - struct queue_entry* target; - u32 tid, split_at; - u8* new_buf; - s32 f_diff, l_diff; + struct queue_entry* target; // 目标队列条目 + u32 tid, split_at; // 目标 ID 和分割位置 + u8* new_buf; // 新缓冲区 + s32 f_diff, l_diff; // 第一个和最后一个不同字节的位置 - /* First of all, if we've modified in_buf for havoc, let's clean that - up... */ + /* 首先,如果我们在进行 havoc 变异时修改了 in_buf,则需要清理它... */ - if (in_buf != orig_in) { - ck_free(in_buf); - in_buf = orig_in; - len = queue_cur->len; + if (in_buf != orig_in) { // 如果 in_buf 不是原始输入 + ck_free(in_buf); // 释放 in_buf + in_buf = orig_in; // 恢复为原始输入 + len = queue_cur->len; // 更新当前长度 } - /* Pick a random queue entry and seek to it. Don't splice with yourself. */ + /* 随机选择一个队列条目,并寻址它。不能与自己拼接。 */ - do { tid = UR(queued_paths); } while (tid == current_entry); + do { tid = UR(queued_paths); } while (tid == current_entry); // 随机选择一个不同的条目 - splicing_with = tid; - target = queue; + splicing_with = tid; // 设置拼接目标 ID + target = queue; // 从队列的起始条目开始 + // 根据 tid 找到目标条目 while (tid >= 100) { target = target->next_100; tid -= 100; } while (tid--) target = target->next; - /* Make sure that the target has a reasonable length. */ + /* 确保目标条目有一个合理的长度。 */ while (target && (target->len < 2 || target == queue_cur)) { - target = target->next; - splicing_with++; + target = target->next; // 如果目标长度不合理或与当前条目相同,则继续寻找下一个 + splicing_with++; // 增加拼接计数 } - if (!target) goto retry_splicing; + if (!target) goto retry_splicing; // 如果没有找到合适的目标,则重试 - /* Read the testcase into a new buffer. */ + /* 将测试用例读入一个新的缓冲区。 */ - fd = open(target->fname, O_RDONLY); + fd = open(target->fname, O_RDONLY); // 打开目标文件 - if (fd < 0) PFATAL("Unable to open '%s'", target->fname); + if (fd < 0) PFATAL("Unable to open '%s'", target->fname); // 如果打开失败则报错 - new_buf = ck_alloc_nozero(target->len); + new_buf = ck_alloc_nozero(target->len); // 分配新缓冲区 - ck_read(fd, new_buf, target->len, target->fname); + ck_read(fd, new_buf, target->len, target->fname); // 从文件中读取数据到新的缓冲区 - close(fd); + close(fd); // 关闭文件描述符 - /* Find a suitable splicing location, somewhere between the first and - the last differing byte. Bail out if the difference is just a single - byte or so. */ + /* 找到一个合适的拼接位置,在第一个和最后一个不同字节之间。 + 如果差异只有一个字节或很小,则退出。 */ - locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); // 找到不同字节的位置 + // 检查差异是否有效 if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { - ck_free(new_buf); - goto retry_splicing; + ck_free(new_buf); // 释放新的缓冲区 + goto retry_splicing; // 重试拼接 } - /* Split somewhere between the first and last differing byte. */ + /* 在第一个和最后一个不同字节之间分割。 */ - split_at = f_diff + UR(l_diff - f_diff); + split_at = f_diff + UR(l_diff - f_diff); // 随机选择分割位置 - /* Do the thing. */ + /* 进行拼接。 */ - len = target->len; - memcpy(new_buf, in_buf, split_at); - in_buf = new_buf; + len = target->len; // 更新长度为目标条目长度 + memcpy(new_buf, in_buf, split_at); // 复制前半部分 + in_buf = new_buf; // 更新 in_buf 为新缓冲区 - ck_free(out_buf); - out_buf = ck_alloc_nozero(len); - memcpy(out_buf, in_buf, len); + ck_free(out_buf); // 释放输出缓冲区 + out_buf = ck_alloc_nozero(len); // 分配新的输出缓冲区 + memcpy(out_buf, in_buf, len); // 复制内容到输出缓冲区 - goto havoc_stage; + goto havoc_stage; // 跳转到 havoc 阶段 } -#endif /* !IGNORE_FINDS */ +#endif /* !IGNORE_FINDS */ // 结束条件编译 - ret_val = 0; + ret_val = 0; // 返回值初始化为0 -abandon_entry: +abandon_entry: // 放弃当前条目的标签 - splicing_with = -1; + splicing_with = -1; // 重置拼接目标 ID - /* Update pending_not_fuzzed count if we made it through the calibration - cycle and have not seen this entry before. */ + /* 如果我们通过了校准周期并且之前未见过此条目,则更新未模糊计数。 */ if (!stop_soon && !queue_cur->cal_failed && !queue_cur->was_fuzzed) { - queue_cur->was_fuzzed = 1; - pending_not_fuzzed--; - if (queue_cur->favored) pending_favored--; + queue_cur->was_fuzzed = 1; // 标记为已模糊 + pending_not_fuzzed--; // 减少未模糊计数 + if (queue_cur->favored) pending_favored--; // 如果是受青睐的条目,则减少相应计数 } - munmap(orig_in, queue_cur->len); - - if (in_buf != orig_in) ck_free(in_buf); - ck_free(out_buf); - ck_free(eff_map); + munmap(orig_in, queue_cur->len); // 解除映射原始输入 - return ret_val; + if (in_buf != orig_in) ck_free(in_buf); // 如果 in_buf 不是原始输入,则释放 + ck_free(out_buf); // 释放输出缓冲区 + ck_free(eff_map); // 释放有效映射 -#undef FLIP_BIT + return ret_val; // 返回结果 -} /* Grab interesting test cases from other fuzzers. */ static void sync_fuzzers(char** argv) { + // 定义同步模糊测试器的静态函数,接受命令行参数 - DIR* sd; - struct dirent* sd_ent; - u32 sync_cnt = 0; + DIR* sd; // 定义目录指针,用于同步目录 + struct dirent* sd_ent; // 定义目录项结构,用于读取目录条目 + u32 sync_cnt = 0; // 初始化同步计数器 - sd = opendir(sync_dir); - if (!sd) PFATAL("Unable to open '%s'", sync_dir); + sd = opendir(sync_dir); // 打开同步目录 + if (!sd) PFATAL("Unable to open '%s'", sync_dir); // 如果打开失败,输出错误信息并终止程序 - stage_max = stage_cur = 0; - cur_depth = 0; + stage_max = stage_cur = 0; // 初始化阶段最大值和当前值 + cur_depth = 0; // 初始化当前深度 - /* Look at the entries created for every other fuzzer in the sync directory. */ + /* 查看为每个其他模糊测试器在同步目录创建的条目。 */ - while ((sd_ent = readdir(sd))) { + while ((sd_ent = readdir(sd))) { // 读取同步目录中的每个条目 - static u8 stage_tmp[128]; + static u8 stage_tmp[128]; // 用于临时存储阶段名称 - DIR* qd; - struct dirent* qd_ent; - u8 *qd_path, *qd_synced_path; - u32 min_accept = 0, next_min_accept; + DIR* qd; // 定义另一个目录指针 + struct dirent* qd_ent; // 定义另一个目录项结构 + u8 *qd_path, *qd_synced_path; // 定义队列目录和同步路径 + u32 min_accept = 0, next_min_accept; // 定义接受的最小值和下一个最小接受值 - s32 id_fd; + s32 id_fd; // 定义文件描述符变量 - /* Skip dot files and our own output directory. */ + /* 跳过点文件和我们自己的输出目录。 */ - if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue; + if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue; // 跳过点文件和自身同步ID - /* Skip anything that doesn't have a queue/ subdirectory. */ + /* 跳过没有 queue/ 子目录的条目。 */ - qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name); + qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name); // 为队列目录分配路径 - if (!(qd = opendir(qd_path))) { - ck_free(qd_path); - continue; - } + if (!(qd = opendir(qd_path))) { // 打开队列目录 + ck_free(qd_path); // 释放路径内存 + continue; // 跳过当前条目 + } - /* Retrieve the ID of the last seen test case. */ + /* 获取最后一个已见测试用例的ID。 */ - qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name); + qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name); // 为同步文件分配路径 - id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600); + id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600); // 打开或创建同步文件 - if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path); + if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path); // 如果打开失败,输出错误信息并终止程序 - if (read(id_fd, &min_accept, sizeof(u32)) > 0) - lseek(id_fd, 0, SEEK_SET); + if (read(id_fd, &min_accept, sizeof(u32)) > 0) + lseek(id_fd, 0, SEEK_SET); // 如果读取成功,设置文件指针到开头 - next_min_accept = min_accept; + next_min_accept = min_accept; // 将下一个最小接受值设为当前的最小接受值 - /* Show stats */ + /* 显示统计信息 */ - sprintf(stage_tmp, "sync %u", ++sync_cnt); - stage_name = stage_tmp; - stage_cur = 0; - stage_max = 0; + sprintf(stage_tmp, "sync %u", ++sync_cnt); // 格式化当前同步阶段名称 + stage_name = stage_tmp; // 设置阶段名称 + stage_cur = 0; // 当前阶段计数器设为0 + stage_max = 0; // 当前阶段最大值设为0 - /* For every file queued by this fuzzer, parse ID and see if we have looked at - it before; exec a test case if not. */ + /* 对于这个模糊测试器排队的每个文件,解析ID并查看我们是否看过它; + 如果没有,则执行测试用例。 */ - while ((qd_ent = readdir(qd))) { + while ((qd_ent = readdir(qd))) { // 读取队列目录中的每个条目 - u8* path; - s32 fd; - struct stat st; + u8* path; // 定义路径变量 + s32 fd; // 定义文件描述符 + struct stat st; // 定义文件状态结构 - if (qd_ent->d_name[0] == '.' || - sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || - syncing_case < min_accept) continue; + if (qd_ent->d_name[0] == '.' || // 跳过点文件 + sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || // 解析文件名中的ID + syncing_case < min_accept) continue; // 如果ID小于最小接受值,则跳过 - /* OK, sounds like a new one. Let's give it a try. */ + /* 好的,听起来像是一个新的文件。我们来试试。 */ - if (syncing_case >= next_min_accept) - next_min_accept = syncing_case + 1; + if (syncing_case >= next_min_accept) // 检查是否是下一个要接受的测试用例 + next_min_accept = syncing_case + 1; // 更新下一个最小接受值 - path = alloc_printf("%s/%s", qd_path, qd_ent->d_name); + path = alloc_printf("%s/%s", qd_path, qd_ent->d_name); // 为当前文件分配路径 - /* Allow this to fail in case the other fuzzer is resuming or so... */ + /* 在其他模糊测试器恢复时允许此失败... */ - fd = open(path, O_RDONLY); + fd = open(path, O_RDONLY); // 以只读方式打开文件 - if (fd < 0) { - ck_free(path); - continue; - } + if (fd < 0) { // 如果打开失败 + ck_free(path); // 释放路径内存 + continue; // 跳过当前条目 + } - if (fstat(fd, &st)) PFATAL("fstat() failed"); + if (fstat(fd, &st)) PFATAL("fstat() failed"); // 获取文件状态,如果失败则终止程序 - /* Ignore zero-sized or oversized files. */ + /* 忽略零大小或超大文件。 */ - if (st.st_size && st.st_size <= MAX_FILE) { + if (st.st_size && st.st_size <= MAX_FILE) { // 检查文件大小 - u8 fault; - u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + u8 fault; // 定义故障变量 + u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); // 将文件映射到内存 - if (mem == MAP_FAILED) PFATAL("Unable to mmap '%s'", path); + if (mem == MAP_FAILED) PFATAL("Unable to mmap '%s'", path); // 如果映射失败,输出错误信息并终止程序 - /* See what happens. We rely on save_if_interesting() to catch major - errors and save the test case. */ + /* 看看会发生什么。我们依赖于 save_if_interesting() 来捕获主要 + 错误并保存测试用例。 */ - write_to_testcase(mem, st.st_size); + write_to_testcase(mem, st.st_size); // 写入测试用例 - fault = run_target(argv, exec_tmout); + fault = run_target(argv, exec_tmout); // 运行目标程序并获取故障信息 - if (stop_soon) return; + if (stop_soon) return; // 如果需要停止,返回 - syncing_party = sd_ent->d_name; - queued_imported += save_if_interesting(argv, mem, st.st_size, fault); - syncing_party = 0; + syncing_party = sd_ent->d_name; // 记录当前同步方 + queued_imported += save_if_interesting(argv, mem, st.st_size, fault); // 保存有趣的测试用例 + syncing_party = 0; // 重置当前同步方 - munmap(mem, st.st_size); + munmap(mem, st.st_size); // 解除内存映射 - if (!(stage_cur++ % stats_update_freq)) show_stats(); + if (!(stage_cur++ % stats_update_freq)) show_stats(); // 更新统计信息 - } + } - ck_free(path); - close(fd); + ck_free(path); // 释放路径内存 + close(fd); // 关闭文件描述符 - } + } - ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); + ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); // 将下一个最小接受值写入同步文件 - close(id_fd); - closedir(qd); - ck_free(qd_path); - ck_free(qd_synced_path); - - } + close(id_fd); // 关闭文件描述符 + closedir(qd); // 关闭队列目录 + ck_free(qd_path); // 释放队列路径内存 + ck_free(qd_synced_path); // 释放同步路径内存 + + } - closedir(sd); + closedir(sd); // 关闭同步目录 } - -/* Handle stop signal (Ctrl-C, etc). */ +/* 处理停止信号 (Ctrl-C 等)。 */ static void handle_stop_sig(int sig) { + stop_soon = 1; // 设置停止标志 - stop_soon = 1; - - if (child_pid > 0) kill(child_pid, SIGKILL); - if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); + if (child_pid > 0) kill(child_pid, SIGKILL); // 如果子进程存在,强制杀死子进程 + if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); // 如果子服务器存在,强制杀死子服务器 } - -/* Handle skip request (SIGUSR1). */ +/* 处理跳过请求 (SIGUSR1)。 */ static void handle_skipreq(int sig) { - - skip_requested = 1; + skip_requested = 1; // 设置跳过请求标志 } -/* Handle timeout (SIGALRM). */ +/* 处理超时 (SIGALRM)。 */ static void handle_timeout(int sig) { + if (child_pid > 0) { // 如果子进程存在 - if (child_pid > 0) { + child_timed_out = 1; // 设置计时器超时标志 + kill(child_pid, SIGKILL); // 强制杀死子进程 - child_timed_out = 1; - kill(child_pid, SIGKILL); + } else if (child_pid == -1 && forksrv_pid > 0) { // 如果没有子进程且子服务器存在 - } else if (child_pid == -1 && forksrv_pid > 0) { + child_timed_out = 1; // 设置计时器超时标志 + kill(forksrv_pid, SIGKILL); // 强制杀死子服务器 - child_timed_out = 1; - kill(forksrv_pid, SIGKILL); - - } + } } @@ -6861,310 +6855,341 @@ static void handle_timeout(int sig) { a valid ELF header and for evidence of AFL instrumentation. */ EXP_ST void check_binary(u8* fname) { + + u8* env_path = 0; // 用于存储环境变量 PATH 的路径 + struct stat st; // 用于获取文件状态的信息 - u8* env_path = 0; - struct stat st; - - s32 fd; - u8* f_data; - u32 f_len = 0; + s32 fd; // 文件描述符 + u8* f_data; // 用于存储映射的文件数据 + u32 f_len = 0; // 文件长度初始化为 0 - ACTF("Validating target binary..."); + ACTF("Validating target binary..."); // 输出正在验证目标二进制文件的消息 - if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { + // 如果文件名中包含 '/' 或者 PATH 环境变量不存在 + 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) || (f_len = st.st_size) < 4) - FATAL("Program '%s' not found or not executable", fname); + target_path = ck_strdup(fname); // 复制文件名到目标路径 + if (stat(target_path, &st) || !S_ISREG(st.st_mode) || // 检查文件是否存在且为常规文件 + !(st.st_mode & 0111) || (f_len = st.st_size) < 4) // 检查是否可执行且文件大小大于等于 4 + FATAL("Program '%s' not found or not executable", fname); // 如果不符合要求,报错 - } else { + } else { - while (env_path) { + // 如果 PATH 环境变量存在,则遍历每个路径 + while (env_path) { - u8 *cur_elem, *delim = strchr(env_path, ':'); + u8 *cur_elem, *delim = strchr(env_path, ':'); // 获取当前路径元素和分隔符 - if (delim) { + 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); - else - target_path = ck_strdup(fname); + // 如果路径元素不为空 + if (cur_elem[0]) + target_path = alloc_printf("%s/%s", cur_elem, fname); // 组合成完整路径 + else + 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) && (f_len = st.st_size) >= 4) break; + // 检查当前目标路径是否有效 + if (!stat(target_path, &st) && S_ISREG(st.st_mode) && + (st.st_mode & 0111) && (f_len = st.st_size) >= 4) break; // 找到有效的路径则跳出循环 - ck_free(target_path); - target_path = 0; + ck_free(target_path); // 如果未找到有效路径,则释放内存 + target_path = 0; // 重置目标路径 - } + } - if (!target_path) FATAL("Program '%s' not found or not executable", fname); + // 如果没有找到有效路径,则报错 + if (!target_path) FATAL("Program '%s' not found or not executable", fname); - } + } - if (getenv("AFL_SKIP_BIN_CHECK")) return; + // 如果环境变量 AFL_SKIP_BIN_CHECK 存在,则跳过二进制检查 + if (getenv("AFL_SKIP_BIN_CHECK")) return; - /* Check for blatant user errors. */ + /* 检查用户的明显错误 */ - if ((!strncmp(target_path, "/tmp/", 5) && !strchr(target_path + 5, '/')) || - (!strncmp(target_path, "/var/tmp/", 9) && !strchr(target_path + 9, '/'))) - FATAL("Please don't keep binaries in /tmp or /var/tmp"); + // 检查是否将二进制文件放在 /tmp 或 /var/tmp 目录下 + if ((!strncmp(target_path, "/tmp/", 5) && !strchr(target_path + 5, '/')) || + (!strncmp(target_path, "/var/tmp/", 9) && !strchr(target_path + 9, '/'))) + FATAL("Please don't keep binaries in /tmp or /var/tmp"); // 报错提示 - fd = open(target_path, O_RDONLY); + fd = open(target_path, O_RDONLY); // 打开目标文件 - if (fd < 0) PFATAL("Unable to open '%s'", target_path); + if (fd < 0) PFATAL("Unable to open '%s'", target_path); // 如果打开失败,报错 - f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); + f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); // 将文件数据映射到内存中 - if (f_data == MAP_FAILED) PFATAL("Unable to mmap file '%s'", target_path); + if (f_data == MAP_FAILED) PFATAL("Unable to mmap file '%s'", target_path); // 如果映射失败,报错 - close(fd); + close(fd); // 关闭文件描述符 - if (f_data[0] == '#' && f_data[1] == '!') { + // 检查是否为脚本文件 + if (f_data[0] == '#' && f_data[1] == '!') { - SAYF("\n" cLRD "[-] " cRST - "Oops, the target binary looks like a shell script. Some build systems will\n" - " sometimes generate shell stubs for dynamically linked programs; try static\n" - " library mode (./configure --disable-shared) if that's the case.\n\n" + SAYF("\n" cLRD "[-] " cRST + "Oops, the target binary looks like a shell script. Some build systems will\n" + " sometimes generate shell stubs for dynamically linked programs; try static\n" + " library mode (./configure --disable-shared) if that's the case.\n\n" - " Another possible cause is that you are actually trying to use a shell\n" - " wrapper around the fuzzed component. Invoking shell can slow down the\n" - " fuzzing process by a factor of 20x or more; it's best to write the wrapper\n" - " in a compiled language instead.\n"); + " Another possible cause is that you are actually trying to use a shell\n" + " wrapper around the fuzzed component. Invoking shell can slow down the\n" + " fuzzing process by a factor of 20x or more; it's best to write the wrapper\n" + " in a compiled language instead.\n"); - FATAL("Program '%s' is a shell script", target_path); + FATAL("Program '%s' is a shell script", target_path); // 报错,表示目标是一个 shell 脚本 - } + } #ifndef __APPLE__ - if (f_data[0] != 0x7f || memcmp(f_data + 1, "ELF", 3)) - FATAL("Program '%s' is not an ELF binary", target_path); + // 检查是否为 ELF 文件 + if (f_data[0] != 0x7f || memcmp(f_data + 1, "ELF", 3)) + FATAL("Program '%s' is not an ELF binary", target_path); // 报错,表示不是 ELF 文件 #else - if (f_data[0] != 0xCF || f_data[1] != 0xFA || f_data[2] != 0xED) - FATAL("Program '%s' is not a 64-bit Mach-O binary", target_path); + // 检查是否为 64 位 Mach-O 文件 + if (f_data[0] != 0xCF || f_data[1] != 0xFA || f_data[2] != 0xED) + FATAL("Program '%s' is not a 64-bit Mach-O binary", target_path); // 报错,表示不是 Mach-O 文件 #endif /* ^!__APPLE__ */ - if (!qemu_mode && !dumb_mode && - !memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { + // 检查是否存在仪器化环境变量 + if (!qemu_mode && !dumb_mode && + !memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { - SAYF("\n" cLRD "[-] " cRST - "Looks like the target binary is not instrumented! The fuzzer depends on\n" - " compile-time instrumentation to isolate interesting test cases while\n" - " mutating the input data. For more information, and for tips on how to\n" - " instrument binaries, please see %s/README.\n\n" + SAYF("\n" cLRD "[-] " cRST + "Looks like the target binary is not instrumented! The fuzzer depends on\n" + " compile-time instrumentation to isolate interesting test cases while\n" + " mutating the input data. For more information, and for tips on how to\n" + " instrument binaries, please see %s/README.\n\n" - " When source code is not available, you may be able to leverage QEMU\n" - " mode support. Consult the README for tips on how to enable this.\n" + " When source code is not available, you may be able to leverage QEMU\n" + " mode support. Consult the README for tips on how to enable this.\n" - " (It is also possible to use afl-fuzz as a traditional, \"dumb\" fuzzer.\n" - " For that, you can use the -n option - but expect much worse results.)\n", - doc_path); + " (It is also possible to use afl-fuzz as a traditional, \"dumb\" fuzzer.\n" + " For that, you can use the -n option - but expect much worse results.)\n", + doc_path); - FATAL("No instrumentation detected"); + FATAL("No instrumentation detected"); // 报错,表示没有检测到仪器化 - } + } - if (qemu_mode && - memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { + // 检查在 QEMU 模式中是否发现仪器化 + if (qemu_mode && + memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { - SAYF("\n" cLRD "[-] " cRST - "This program appears to be instrumented with afl-gcc, but is being run in\n" - " QEMU mode (-Q). This is probably not what you want - this setup will be\n" - " slow and offer no practical benefits.\n"); + SAYF("\n" cLRD "[-] " cRST + "This program appears to be instrumented with afl-gcc, but is being run in\n" + " QEMU mode (-Q). This is probably not what you want - this setup will be\n" + " slow and offer no practical benefits.\n"); - FATAL("Instrumentation found in -Q mode"); + FATAL("Instrumentation found in -Q mode"); // 报错,表示在 QEMU 模式中发现了仪器化 - } - - if (memmem(f_data, f_len, "libasan.so", 10) || - memmem(f_data, f_len, "__msan_init", 11)) uses_asan = 1; + } - /* Detect persistent & deferred init signatures in the binary. */ + // 检查是否使用了 ASAN + if (memmem(f_data, f_len, "libasan.so", 10) || + memmem(f_data, f_len, "__msan_init", 11)) uses_asan = 1; - if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { + // 检测持久模式和延迟初始化的标志 + if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { - OKF(cPIN "Persistent mode binary detected."); - setenv(PERSIST_ENV_VAR, "1", 1); - persistent_mode = 1; + OKF(cPIN "Persistent mode binary detected."); // 输出发现持久模式的消息 + setenv(PERSIST_ENV_VAR, "1", 1); // 设置环境变量表示持久模式 + persistent_mode = 1; // 更新持久模式标志 - } else if (getenv("AFL_PERSISTENT")) { + } else if (getenv("AFL_PERSISTENT")) { - WARNF("AFL_PERSISTENT is no longer supported and may misbehave!"); + WARNF("AFL_PERSISTENT is no longer supported and may misbehave!"); // 发出警告 - } + } - if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { + if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { - OKF(cPIN "Deferred forkserver binary detected."); - setenv(DEFER_ENV_VAR, "1", 1); - deferred_mode = 1; + OKF(cPIN "Deferred forkserver binary detected."); // 输出发现延迟 forkserver 的消息 + setenv(DEFER_ENV_VAR, "1", 1); // 设置环境变量表示延迟模式 + deferred_mode = 1; // 更新延迟模式标志 - } else if (getenv("AFL_DEFER_FORKSRV")) { + } else if (getenv("AFL_DEFER_FORKSRV")) { - WARNF("AFL_DEFER_FORKSRV is no longer supported and may misbehave!"); + WARNF("AFL_DEFER_FORKSRV is no longer supported and may misbehave!"); // 发出警告 - } + } - if (munmap(f_data, f_len)) PFATAL("unmap() failed"); + if (munmap(f_data, f_len)) PFATAL("unmap() failed"); // 解映射内存,如果失败则报错 } + /* Trim and possibly create a banner for the run. */ static void fix_up_banner(u8* name) { - + // 如果 use_banner 未设置 if (!use_banner) { - + // 如果 sync_id 存在,则使用 sync_id 作为 use_banner if (sync_id) { - use_banner = sync_id; - } else { - + // 否则,从 name 中提取最后一个 '/' 后的部分作为 use_banner u8* trim = strrchr(name, '/'); if (!trim) use_banner = name; else use_banner = trim + 1; - } - } + // 如果 use_banner 的长度超过 40 个字符,则截断并添加 "..." 后缀 if (strlen(use_banner) > 40) { - - u8* tmp = ck_alloc(44); - sprintf(tmp, "%.40s...", use_banner); - use_banner = tmp; - + u8* tmp = ck_alloc(44); // 分配 44 字节的内存 + sprintf(tmp, "%.40s...", use_banner); // 格式化字符串,保留前 40 个字符并添加 "..." + use_banner = tmp; // 更新 use_banner 为截断后的字符串 } - } - -/* Check if we're on TTY. */ - +/* 检查是否在 TTY 上运行 */ static void check_if_tty(void) { + struct winsize ws; // 用于存储终端窗口大小信息的结构体 - struct winsize ws; - + // 如果设置了 AFL_NO_UI 环境变量,则禁用 UI if (getenv("AFL_NO_UI")) { OKF("Disabling the UI because AFL_NO_UI is set."); - not_on_tty = 1; + not_on_tty = 1; // 标记不在 TTY 上运行 return; } + // 尝试获取终端窗口大小 if (ioctl(1, TIOCGWINSZ, &ws)) { - + // 如果 errno 为 ENOTTY,表示不在 TTY 上运行 if (errno == ENOTTY) { OKF("Looks like we're not running on a tty, so I'll be a bit less verbose."); - not_on_tty = 1; + not_on_tty = 1; // 标记不在 TTY 上运行 } - return; } - } - -/* Check terminal dimensions after resize. */ - +/* 检查终端尺寸是否在调整后发生变化 */ static void check_term_size(void) { + struct winsize ws; // 用于存储终端窗口大小信息的结构体 - struct winsize ws; - - term_too_small = 0; + term_too_small = 0; // 初始化终端尺寸是否过小的标志 + // 尝试获取终端窗口大小 if (ioctl(1, TIOCGWINSZ, &ws)) return; + // 如果终端窗口的行数或列数为 0,则返回 if (ws.ws_row == 0 && ws.ws_col == 0) return; - if (ws.ws_row < 25 || ws.ws_col < 80) term_too_small = 1; + // 如果终端窗口的行数小于 25 或列数小于 80,则标记为终端尺寸过小 + if (ws.ws_row < 25 || ws.ws_col < 80) term_too_small = 1; } + /* Display usage hints. */ -static void usage(u8* argv0) { +static void usage(u8* argv0) { // 定义一个静态函数usage,接收一个指向u8类型的指针argv0(程序名) - SAYF("\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" + SAYF("\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" // 输出使用说明的开头,包括程序名和命令格式 - "Required parameters:\n\n" + "Required parameters:\n\n" // 指出必须的参数部分 - " -i dir - input directory with test cases\n" - " -o dir - output directory for fuzzer findings\n\n" + " -i dir - input directory with test cases\n" // 输入目录选项,指定测试用例的存放位置 + " -o dir - output directory for fuzzer findings\n\n" // 输出目录选项,指定模糊测试结果的存放位置 - "Execution control settings:\n\n" + "Execution control settings:\n\n" // 执行控制设置部分 - " -f file - location read by the fuzzed program (stdin)\n" - " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" - " -m megs - memory limit for child process (%u MB)\n" - " -Q - use binary-only instrumentation (QEMU mode)\n\n" - - "Fuzzing behavior settings:\n\n" + " -f file - location read by the fuzzed program (stdin)\n" // 输入文件选项,指定模糊程序读取数据的位置 + " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" // 超时选项,设置每次运行的超时时间 + " -m megs - memory limit for child process (%u MB)\n" // 内存限制选项,限制子进程的内存使用 + " -Q - use binary-only instrumentation (QEMU mode)\n\n" // 二进制模式选项,使用仅限于二进制的插桩模式(QEMU) + + "Fuzzing behavior settings:\n\n" // 模糊�������� + +```c +static void usage(u8* argv0) { // 定义一个静态函数usage,用于显示程序的用法信息,argv0是程序的名称 + + SAYF("\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" // 输出程序的用法格式,%s会被替换为argv0 + + "Required parameters:\n\n" // 输出“必需参数”标题 + + " -i dir - input directory with test cases\n" // 指定输入目录,包含测试用例 + " -o dir - output directory for fuzzer findings\n\n" // 指定输出目录,用于存放fuzzer的发现结果 + + "Execution control settings:\n\n" // 输出“执行控制设置”标题 + + " -f file - location read by the fuzzed program (stdin)\n" // 指定被fuzz程序读取的文件位置(默认为stdin) + " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" // 指定每次运行的超时时间(自动调整,范围50到%u毫秒) + " -m megs - memory limit for child process (%u MB)\n" // 指定子进程的内存限制(%u MB) + " -Q - use binary-only instrumentation (QEMU mode)\n\n" // 使用仅二进制插桩(QEMU模式) - " -d - quick & dirty mode (skips deterministic steps)\n" - " -n - fuzz without instrumentation (dumb mode)\n" - " -x dir - optional fuzzer dictionary (see README)\n\n" + "Fuzzing behavior settings:\n\n" // 输出“Fuzzing行为设置”标题 - "Other stuff:\n\n" + " -d - quick & dirty mode (skips deterministic steps)\n" // 启用快速模式(跳过确定性步骤) + " -n - fuzz without instrumentation (dumb mode)\n" // 不使用插桩进行fuzz(傻瓜模式) + " -x dir - optional fuzzer dictionary (see README)\n\n" // 指定可选的fuzzer字典目录(参见README) - " -T text - text banner to show on the screen\n" - " -M / -S id - distributed mode (see parallel_fuzzing.txt)\n" - " -C - crash exploration mode (the peruvian rabbit thing)\n" - " -V - show version number and exit\n\n" - " -b cpu_id - bind the fuzzing process to the specified CPU core\n\n" + "Other stuff:\n\n" // 输出“其他内容”标题 - "For additional tips, please consult %s/README.\n\n", + " -T text - text banner to show on the screen\n" // 指定在屏幕上显示的文本横幅 + " -M / -S id - distributed mode (see parallel_fuzzing.txt)\n" // 分布式模式(参见parallel_fuzzing.txt) + " -C - crash exploration mode (the peruvian rabbit thing)\n" // 崩溃探索模式(“秘鲁兔子”模式) + " -V - show version number and exit\n\n" // 显示版本号并退出 + " -b cpu_id - bind the fuzzing process to the specified CPU core\n\n" // 将fuzzing进程绑定到指定的CPU核心 - argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); + "For additional tips, please consult %s/README.\n\n", // 提示用户查阅%s/README以获取更多信息 - exit(1); + argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); // 替换格式字符串中的占位符,argv0是程序名,EXEC_TIMEOUT和MEM_LIMIT是常量,doc_path是文档路径 + + exit(1); // 退出程序,返回状态码1 } -/* Prepare output directories and fds. */ -EXP_ST void setup_dirs_fds(void) { +/* Prepare output directories and fds. */ +EXP_ST void setup_dirs_fds(void) { // 定义函数,设置输出目录和文件描述符 - u8* tmp; - s32 fd; + u8* tmp; // 声明临时指针变量,存储路径 + s32 fd; // 声明文件描述符变量 - ACTF("Setting up output directories..."); + ACTF("Setting up output directories..."); // 输出设置目录的提示信息 + // 如果启用了同步标识且尝试创建同步目录,若出错且错误不是"已存在"则致命错误 if (sync_id && mkdir(sync_dir, 0700) && errno != EEXIST) PFATAL("Unable to create '%s'", sync_dir); + // 尝试创建输出目录 if (mkdir(out_dir, 0700)) { + // 如果创建目录出错且错误不是"已存在",则致命错误 if (errno != EEXIST) PFATAL("Unable to create '%s'", out_dir); + // 可能删除输出目录的旧内容 maybe_delete_out_dir(); } else { + // 如果尝试恢复状态,但未找到旧的输出目录,则致命错误 if (in_place_resume) FATAL("Resume attempted but old output directory not found"); + // 打开输出目录,获取文件描述符 out_dir_fd = open(out_dir, O_RDONLY); -#ifndef __sun +#ifndef __sun // 如果不是Sun系统 + // 如果输出目录文件描述符小于0或加锁失败,则致命错误 if (out_dir_fd < 0 || flock(out_dir_fd, LOCK_EX | LOCK_NB)) PFATAL("Unable to flock() output directory."); @@ -7172,50 +7197,57 @@ EXP_ST void setup_dirs_fds(void) { } - /* Queue directory for any starting & discovered paths. */ + /* 为任何起始和发现的路径排队的目录。 */ + // 为队列目录分配内存并创建目录 tmp = alloc_printf("%s/queue", out_dir); - if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); // 创建失败则致命错误 + ck_free(tmp); // 释放临时变量 - /* Top-level directory for queue metadata used for session - resume and related tasks. */ + /* 用于会话恢复和相关任务的队列元数据的顶层目录。 */ + // 创建队列状态目录 tmp = alloc_printf("%s/queue/.state/", out_dir); if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); ck_free(tmp); - /* Directory for flagging queue entries that went through - deterministic fuzzing in the past. */ + /* 标记过去经历过确定性模糊测试的队列条目的目录。 */ + // 创建确定性完成目录 tmp = alloc_printf("%s/queue/.state/deterministic_done/", out_dir); if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); ck_free(tmp); - /* Directory with the auto-selected dictionary entries. */ + /* 自动选择字典条目的目录。 */ + // 创建自动附加条目目录 tmp = alloc_printf("%s/queue/.state/auto_extras/", out_dir); if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); ck_free(tmp); - /* The set of paths currently deemed redundant. */ + /* 当前被认为是冗余的路径集合。 */ + // 创建冗余边缘目录 tmp = alloc_printf("%s/queue/.state/redundant_edges/", out_dir); if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); ck_free(tmp); - /* The set of paths showing variable behavior. */ + /* 显示可变行为的路径集合。 */ + // 创建可变行为目录 tmp = alloc_printf("%s/queue/.state/variable_behavior/", out_dir); if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); ck_free(tmp); - /* Sync directory for keeping track of cooperating fuzzers. */ + /* 用于跟踪合作模糊测试工具的同步目录。 */ + // 如果启用了同步标识 if (sync_id) { + // 分配同步目录路径 tmp = alloc_printf("%s/.synced/", out_dir); + // 创建同步目录,出错则致命错误 if (mkdir(tmp, 0700) && (!in_place_resume || errno != EEXIST)) PFATAL("Unable to create '%s'", tmp); @@ -7223,66 +7255,76 @@ EXP_ST void setup_dirs_fds(void) { } - /* All recorded crashes. */ + /* 所有记录下来的崩溃。 */ + // 创建崩溃目录 tmp = alloc_printf("%s/crashes", out_dir); if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); ck_free(tmp); - /* All recorded hangs. */ + /* 所有记录的挂起。 */ + // 创建挂起目录 tmp = alloc_printf("%s/hangs", out_dir); if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp); ck_free(tmp); - /* Generally useful file descriptors. */ + /* 一般实用的文件描述符。 */ + // 打开/dev/null,以便丢弃数据 dev_null_fd = open("/dev/null", O_RDWR); - if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); + if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); // 打开失败则致命错误 + // 打开/dev/urandom,以便获取随机数据 dev_urandom_fd = open("/dev/urandom", O_RDONLY); - if (dev_urandom_fd < 0) PFATAL("Unable to open /dev/urandom"); + if (dev_urandom_fd < 0) PFATAL("Unable to open /dev/urandom"); // 打开失败则致命错误 - /* Gnuplot output file. */ + /* Gnuplot输出文件。 */ + // 创建plot_data文件以存储绘图数据 tmp = alloc_printf("%s/plot_data", out_dir); - fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", tmp); - ck_free(tmp); + fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); // 以写入方式打开文件 + if (fd < 0) PFATAL("Unable to create '%s'", tmp); // 打开失败则致命错误 + ck_free(tmp); // 释放临时变量 + // 将文件描述符转换为文件流 plot_file = fdopen(fd, "w"); - if (!plot_file) PFATAL("fdopen() failed"); + if (!plot_file) PFATAL("fdopen() failed"); // 转换失败则致命错误 + // 输出文件首行注释 fprintf(plot_file, "# unix_time, cycles_done, cur_path, paths_total, " "pending_total, pending_favs, map_size, unique_crashes, " "unique_hangs, max_depth, execs_per_sec\n"); - /* ignore errors */ + /* 忽略错误 */ } -/* Setup the output file for fuzzed data, if not using -f. */ +/* 如果不使用 -f,设置模糊数据的输出文件。 */ EXP_ST void setup_stdio_file(void) { + // 分配当前输入文件的路径 u8* fn = alloc_printf("%s/.cur_input", out_dir); - unlink(fn); /* Ignore errors */ + unlink(fn); /* 忽略错误 */ // 删除当前输入文件(如果存在) + // 打开当前输入文件 out_fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (out_fd < 0) PFATAL("Unable to create '%s'", fn); // 打开失败则致命错误 - if (out_fd < 0) PFATAL("Unable to create '%s'", fn); - - ck_free(fn); + ck_free(fn); // 释放临时变量 } + /* Make sure that core dumps don't go to a program. */ -static void check_crash_handling(void) { +static void check_crash_handling(void) { // 定义一个静态函数,检查崩溃处理机制 -#ifdef __APPLE__ +#ifdef __APPLE__ // 如果操作系统是苹果的 macOS /* Yuck! There appears to be no simple C API to query for the state of loaded daemons on MacOS X, and I'm a bit hesitant to do something @@ -7290,9 +7332,9 @@ static void check_crash_handling(void) { until I get a box to test the code. So, for now, we check for crash reporting the awful way. */ - if (system("launchctl list 2>/dev/null | grep -q '\\.ReportCrash$'")) return; + if (system("launchctl list 2>/dev/null | grep -q '\\.ReportCrash'")) return; // 检查是否有崩溃报告进程在运行,如果有则直接返回 - SAYF("\n" cLRD "[-] " cRST + SAYF("\n" cLRD "[-] " cRST // 输出警告信息,通知用户有崩溃报告工具 "Whoops, your system is configured to forward crash notifications to an\n" " external crash reporting utility. This will cause issues due to the\n" " extended delay between the fuzzed binary malfunctioning and this fact\n" @@ -7304,24 +7346,24 @@ static void check_crash_handling(void) { " launchctl unload -w ${SL}/LaunchAgents/${PL}.plist\n" " sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist\n"); - if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) + if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) // 如果环境变量未设置,则报告错误 FATAL("Crash reporter detected"); -#else +#else // 如果不是 macOS /* This is Linux specific, but I don't think there's anything equivalent on *BSD, so we can just let it slide for now. */ - s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY); - u8 fchar; + s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY); // 打开核心转储模式文件 + u8 fchar; // 文件字符变量 - if (fd < 0) return; + if (fd < 0) return; // 如果文件打开失败,则返回 - ACTF("Checking core_pattern..."); + ACTF("Checking core_pattern..."); // 检查核心转储模式 - if (read(fd, &fchar, 1) == 1 && fchar == '|') { + if (read(fd, &fchar, 1) == 1 && fchar == '|') { // 判断核心模式是否以管道符开头 - SAYF("\n" cLRD "[-] " cRST + SAYF("\n" cLRD "[-] " cRST // 输出警告信息 "Hmm, your system is configured to send core dump notifications to an\n" " external utility. This will cause issues: there will be an extended delay\n" " between stumbling upon a crash and having this information relayed to the\n" @@ -7332,56 +7374,54 @@ static void check_crash_handling(void) { " echo core >/proc/sys/kernel/core_pattern\n"); - if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) + if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) // 如果环境变量未设置,则报告错误 FATAL("Pipe at the beginning of 'core_pattern'"); } - close(fd); + close(fd); // 关闭文件描述符 #endif /* ^__APPLE__ */ } - -/* Check CPU governor. */ - +/* Check CPU governor. */ // 检查CPU调节器 static void check_cpu_governor(void) { - FILE* f; - u8 tmp[128]; - u64 min = 0, max = 0; + FILE* f; // 文件指针 + u8 tmp[128]; // 临时存储变量 + u64 min = 0, max = 0; // 最小和最大频率 - if (getenv("AFL_SKIP_CPUFREQ")) return; + if (getenv("AFL_SKIP_CPUFREQ")) return; // 如果设置了跳过CPU频率检查的环境变量,则返回 - f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "r"); - if (!f) return; + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "r"); // 打开CPU调节器文件 + if (!f) return; // 如果文件打开失败,则返回 - ACTF("Checking CPU scaling governor..."); + ACTF("Checking CPU scaling governor..."); // 检查CPU频率调节模式 - if (!fgets(tmp, 128, f)) PFATAL("fgets() failed"); + if (!fgets(tmp, 128, f)) PFATAL("fgets() failed"); // 读取调节模式,如果失败则报告致命错误 - fclose(f); + fclose(f); // 关闭文件 - if (!strncmp(tmp, "perf", 4)) return; + if (!strncmp(tmp, "perf", 4)) return; // 如果调节模式是“perf”,则返回 - f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", "r"); + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", "r"); // 打开最小频率文件 - if (f) { - if (fscanf(f, "%llu", &min) != 1) min = 0; - fclose(f); + if (f) { // 如果文件打开成功 + if (fscanf(f, "%llu", &min) != 1) min = 0; // 读取最小频率,如果失败则设为0 + fclose(f); // 关闭文件 } - f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "r"); + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "r"); // 打开最大频率文件 - if (f) { - if (fscanf(f, "%llu", &max) != 1) max = 0; - fclose(f); + if (f) { // 如果文件打开成功 + if (fscanf(f, "%llu", &max) != 1) max = 0; // 读取最大频率,如果失败则设为0 + fclose(f); // 关闭文件 } - if (min == max) return; + if (min == max) return; // 如果最小和最大频率相同,则返回 - SAYF("\n" cLRD "[-] " cRST + SAYF("\n" cLRD "[-] " cRST // 输出警告信息,提示CPU频率调节模式不佳 "Whoops, your system uses on-demand CPU frequency scaling, adjusted\n" " between %llu and %llu MHz. Unfortunately, the scaling algorithm in the\n" " kernel is imperfect and can miss the short-lived processes spawned by\n" @@ -7393,94 +7433,103 @@ static void check_cpu_governor(void) { " You can later go back to the original state by replacing 'performance' with\n" " 'ondemand'. If you don't want to change the settings, set AFL_SKIP_CPUFREQ\n" " to make afl-fuzz skip this check - but expect some performance drop.\n", - min / 1024, max / 1024); - - FATAL("Suboptimal CPU scaling governor"); + min / 1024, max / 1024); // 输出最小最大频率的值转为GHz + FATAL("Suboptimal CPU scaling governor"); // 报告致命错误 } + /* Count the number of logical CPU cores. */ static void get_core_count(void) { - u32 cur_runnable = 0; + u32 cur_runnable = 0; // 当前可运行的任务数 #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) + // 如果是 macOS、FreeBSD 或 OpenBSD 系统 - size_t s = sizeof(cpu_core_count); + size_t s = sizeof(cpu_core_count); // 获取 cpu_core_count 的大小 /* On *BSD systems, we can just use a sysctl to get the number of CPUs. */ + // 在 *BSD 系统上,我们可以使用 sysctl 来获取 CPU 数量 #ifdef __APPLE__ + // 如果是 macOS 系统 if (sysctlbyname("hw.logicalcpu", &cpu_core_count, &s, NULL, 0) < 0) - return; + return; // 使用 sysctlbyname 获取逻辑 CPU 数量,失败则返回 #else + // 如果是 FreeBSD 或 OpenBSD 系统 - int s_name[2] = { CTL_HW, HW_NCPU }; + int s_name[2] = { CTL_HW, HW_NCPU }; // 定义 sysctl 参数 - if (sysctl(s_name, 2, &cpu_core_count, &s, NULL, 0) < 0) return; + if (sysctl(s_name, 2, &cpu_core_count, &s, NULL, 0) < 0) return; // 使用 sysctl 获取 CPU 数量,失败则返回 #endif /* ^__APPLE__ */ #else + // 如果不是 macOS、FreeBSD 或 OpenBSD 系统 #ifdef HAVE_AFFINITY + // 如果支持 CPU 亲和性 - cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN); + cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN); // 使用 sysconf 获取在线 CPU 数量 #else + // 如果不支持 CPU 亲和性 - FILE* f = fopen("/proc/stat", "r"); - u8 tmp[1024]; + FILE* f = fopen("/proc/stat", "r"); // 打开 /proc/stat 文件 + u8 tmp[1024]; // 定义缓冲区 - if (!f) return; + if (!f) return; // 如果文件打开失败,返回 - while (fgets(tmp, sizeof(tmp), f)) - if (!strncmp(tmp, "cpu", 3) && isdigit(tmp[3])) cpu_core_count++; + while (fgets(tmp, sizeof(tmp), f)) // 逐行读取文件内容 + if (!strncmp(tmp, "cpu", 3) && isdigit(tmp[3])) cpu_core_count++; // 如果行以 "cpu" 开头且后面是数字,增加 CPU 核心计数 - fclose(f); + fclose(f); // 关闭文件 #endif /* ^HAVE_AFFINITY */ #endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */ - if (cpu_core_count > 0) { + if (cpu_core_count > 0) { // 如果成功获取到 CPU 核心数量 - cur_runnable = (u32)get_runnable_processes(); + cur_runnable = (u32)get_runnable_processes(); // 获取当前可运行的任务数 #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) + // 如果是 macOS、FreeBSD 或 OpenBSD 系统 /* Add ourselves, since the 1-minute average doesn't include that yet. */ + // 添加当前进程,因为 1 分钟的平均值尚未包含它 - cur_runnable++; + cur_runnable++; // 增加当前可运行任务数 #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ OKF("You have %u CPU core%s and %u runnable tasks (utilization: %0.0f%%).", - cpu_core_count, cpu_core_count > 1 ? "s" : "", - cur_runnable, cur_runnable * 100.0 / cpu_core_count); + cpu_core_count, cpu_core_count > 1 ? "s" : "", // 输出 CPU 核心数和可运行任务数 + cur_runnable, cur_runnable * 100.0 / cpu_core_count); // 计算并输出 CPU 利用率 - if (cpu_core_count > 1) { + if (cpu_core_count > 1) { // 如果 CPU 核心数大于 1 - if (cur_runnable > cpu_core_count * 1.5) { + if (cur_runnable > cpu_core_count * 1.5) { // 如果可运行任务数超过 CPU 核心数的 1.5 倍 - WARNF("System under apparent load, performance may be spotty."); + WARNF("System under apparent load, performance may be spotty."); // 警告系统负载较高,性能可能不稳定 - } else if (cur_runnable + 1 <= cpu_core_count) { + } else if (cur_runnable + 1 <= cpu_core_count) { // 如果可运行任务数加 1 小于等于 CPU 核心数 - OKF("Try parallel jobs - see %s/parallel_fuzzing.txt.", doc_path); + OKF("Try parallel jobs - see %s/parallel_fuzzing.txt.", doc_path); // 建议尝试并行任务 } } - } else { + } else { // 如果无法获取 CPU 核心数量 - cpu_core_count = 0; - WARNF("Unable to figure out the number of CPU cores."); + cpu_core_count = 0; // 将 CPU 核心数量设为 0 + WARNF("Unable to figure out the number of CPU cores."); // 警告无法获取 CPU 核心数量 } @@ -7489,131 +7538,146 @@ static void get_core_count(void) { /* Validate and fix up out_dir and sync_dir when using -S. */ +// 修正同步设置的函数 static void fix_up_sync(void) { + + u8* x = sync_id; // 声明指针x指向同步ID - u8* x = sync_id; - + // 检查是否处于傻瓜模式,若是,显示错误信息并终止 if (dumb_mode) FATAL("-S / -M and -n are mutually exclusive"); + // 如果跳过确定性选项被设置 if (skip_deterministic) { - + + // 如果强制确定性被设置,显示错误信息建议使用-S if (force_deterministic) FATAL("use -S instead of -M -d"); else - FATAL("-S already implies -d"); - + FATAL("-S already implies -d"); // -S已经隐含了-d选项 } + // 验证同步ID中的字符是否有效 while (*x) { - + + // 如果字符不是字母数字或下划线和横线,显示错误信息并终止 if (!isalnum(*x) && *x != '_' && *x != '-') FATAL("Non-alphanumeric fuzzer ID specified via -S or -M"); - x++; - + x++; // 移动到下一个字符 } + // 如果同步ID的长度超过32,显示错误信息并终止 if (strlen(sync_id) > 32) FATAL("Fuzzer ID too long"); + // 分配内存,将输出目录和同步ID组合成新的目录字符串 x = alloc_printf("%s/%s", out_dir, sync_id); + // 设置同步目录 sync_dir = out_dir; + // 更新输出目录为新创建的目录 out_dir = x; + // 如果未强制确定性 if (!force_deterministic) { - skip_deterministic = 1; - use_splicing = 1; + skip_deterministic = 1; // 设置跳过确定性标志 + use_splicing = 1; // 启用拼接功能 } - } - -/* Handle screen resize (SIGWINCH). */ - +/* 处理屏幕大小变化 (SIGWINCH) 的信号 */ static void handle_resize(int sig) { - clear_screen = 1; + clear_screen = 1; // 标记需要清屏 } /* Check ASAN options. */ static void check_asan_opts(void) { + // 获取环境变量 ASAN_OPTIONS 的值 u8* x = getenv("ASAN_OPTIONS"); + // 如果 ASAN_OPTIONS 环境变量存在 if (x) { + // 检查 ASAN_OPTIONS 中是否包含 abort_on_error=1,如果不包含则报错 if (!strstr(x, "abort_on_error=1")) FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + // 检查 ASAN_OPTIONS 中是否包含 symbolize=0,如果不包含则报错 if (!strstr(x, "symbolize=0")) FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } + // 获取环境变量 MSAN_OPTIONS 的值 x = getenv("MSAN_OPTIONS"); + // 如果 MSAN_OPTIONS 环境变量存在 if (x) { + // 检查 MSAN_OPTIONS 中是否包含 exit_code=MSAN_ERROR,如果不包含则报错 if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(MSAN_ERROR) " - please fix!"); + // 检查 MSAN_OPTIONS 中是否包含 symbolize=0,如果不包含则报错 if (!strstr(x, "symbolize=0")) FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); } -} +} + /* Detect @@ in args. */ -EXP_ST void detect_file_args(char** argv) { +EXP_ST void detect_file_args(char** argv) { // 定义一个函数detect_file_args,接受一个字符指针数组argv - u32 i = 0; - u8* cwd = getcwd(NULL, 0); + u32 i = 0; // 初始化索引变量i为0 + u8* cwd = getcwd(NULL, 0); // 获取当前工作目录,并将结果存储在cwd中 - if (!cwd) PFATAL("getcwd() failed"); + if (!cwd) PFATAL("getcwd() failed"); // 如果获取目前工作目录失败,调用PFATAL输出错误信息并终止程序 - while (argv[i]) { + while (argv[i]) { // 当argv[i]不为空(即还有参数时) - u8* aa_loc = strstr(argv[i], "@@"); + u8* aa_loc = strstr(argv[i], "@@"); // 查找当前参数中是否包含"@@" - if (aa_loc) { + if (aa_loc) { // 如果找到了"@@" - u8 *aa_subst, *n_arg; + u8 *aa_subst, *n_arg; // 定义两个指针变量aa_subst和n_arg - /* If we don't have a file name chosen yet, use a safe default. */ + /* 如果还没有选择文件名,使用一个安全的默认值。 */ - if (!out_file) - out_file = alloc_printf("%s/.cur_input", out_dir); + if (!out_file) // 如果out_file为空 + out_file = alloc_printf("%s/.cur_input", out_dir); // 分配内存并设置默认文件名 - /* Be sure that we're always using fully-qualified paths. */ + /* 确保我们始终使用绝对路径。 */ - if (out_file[0] == '/') aa_subst = out_file; - else aa_subst = alloc_printf("%s/%s", cwd, out_file); + if (out_file[0] == '/') aa_subst = out_file; // 如果out_file是绝对路径,直接赋值给aa_subst + else aa_subst = alloc_printf("%s/%s", cwd, out_file); // 否则,将当前工作目录与out_file组合成绝对路径 - /* Construct a replacement argv value. */ + /* 构造一个替换的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; // 将"@@"的第一个字符替换为null,分隔字符串 + n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); // 创建新的参数,通过拼接原参数和替换路径 + argv[i] = n_arg; // 将新的参数赋值回argv[i] + *aa_loc = '@'; // 恢复"@@"的位置,为下一次循环准备 - if (out_file[0] != '/') ck_free(aa_subst); + if (out_file[0] != '/') ck_free(aa_subst); // 如果out_file不是绝对路径,释放分配的内存 } - i++; + i++; // 移动到下一个参数 } - free(cwd); /* not tracked */ - + free(cwd); /* not tracked */ // 释放cwd指针指向的内存,不跟踪该内存 } + /* Set up signal handlers. More complicated that needs to be, because libc on Solaris doesn't resume interrupted reads(), sets SA_RESETHAND when you call siginterrupt(), and does other unnecessary things. */ @@ -7622,87 +7686,100 @@ EXP_ST void setup_signal_handlers(void) { struct sigaction sa; - sa.sa_handler = NULL; - sa.sa_flags = SA_RESTART; - sa.sa_sigaction = NULL; + sa.sa_handler = NULL; // 初始化信号处理函数为空 + sa.sa_flags = SA_RESTART; // 设置信号处理标志为SA_RESTART,使被中断的系统调用自动重启 + sa.sa_sigaction = NULL; // 初始化信号处理函数指针为空 - sigemptyset(&sa.sa_mask); + sigemptyset(&sa.sa_mask); // 清空信号掩码,表示不阻塞任何信号 /* Various ways of saying "stop". */ - sa.sa_handler = handle_stop_sig; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = handle_stop_sig; // 设置处理停止信号的函数 + sigaction(SIGHUP, &sa, NULL); // 设置SIGHUP信号的处理函数 + sigaction(SIGINT, &sa, NULL); // 设置SIGINT信号的处理函数 + sigaction(SIGTERM, &sa, NULL); // 设置SIGTERM信号的处理函数 /* Exec timeout notifications. */ - sa.sa_handler = handle_timeout; - sigaction(SIGALRM, &sa, NULL); + sa.sa_handler = handle_timeout; // 设置处理超时信号的函数 + sigaction(SIGALRM, &sa, NULL); // 设置SIGALRM信号的处理函数 /* Window resize */ - sa.sa_handler = handle_resize; - sigaction(SIGWINCH, &sa, NULL); + sa.sa_handler = handle_resize; // 设置处理窗口大小改变信号的函数 + sigaction(SIGWINCH, &sa, NULL); // 设置SIGWINCH信号的处理函数 /* SIGUSR1: skip entry */ - sa.sa_handler = handle_skipreq; - sigaction(SIGUSR1, &sa, NULL); + sa.sa_handler = handle_skipreq; // 设置处理SIGUSR1信号的函数 + sigaction(SIGUSR1, &sa, NULL); // 设置SIGUSR1信号的处理函数 /* Things we don't care about. */ - sa.sa_handler = SIG_IGN; - sigaction(SIGTSTP, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); + sa.sa_handler = SIG_IGN; // 设置忽略信号的处理方式 + sigaction(SIGTSTP, &sa, NULL); // 忽略SIGTSTP信号 + sigaction(SIGPIPE, &sa, NULL); // 忽略SIGPIPE信号 } + /* Rewrite argv for QEMU. */ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { + // 分配新的参数数组,大小为原始参数数量加上4个额外参数 char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); u8 *tmp, *cp, *rsl, *own_copy; - /* Workaround for a QEMU stability glitch. */ - + /* 设置环境变量 QEMU_LOG 为 "nochain",以解决 QEMU 的稳定性问题 */ setenv("QEMU_LOG", "nochain", 1); + // 将原始参数从 argv[1] 开始复制到 new_argv[3] 开始的位置 memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); + // 设置 new_argv[2] 为目标路径,new_argv[1] 为 "--" new_argv[2] = target_path; new_argv[1] = "--"; - /* Now we need to actually find the QEMU binary to put in argv[0]. */ + /* 现在需要找到 QEMU 二进制文件并将其放入 new_argv[0] */ + // 获取环境变量 AFL_PATH 的值 tmp = getenv("AFL_PATH"); if (tmp) { + // 构建 QEMU 二进制文件的路径 cp = alloc_printf("%s/afl-qemu-trace", tmp); + // 检查该路径是否可执行 if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp); + // 设置目标路径和 new_argv[0] 为找到的 QEMU 二进制文件路径 target_path = new_argv[0] = cp; return new_argv; } + // 复制 own_loc 字符串 own_copy = ck_strdup(own_loc); + // 查找最后一个 '/' 字符的位置 rsl = strrchr(own_copy, '/'); if (rsl) { + // 将 '/' 替换为字符串结束符 *rsl = 0; + // 构建 QEMU 二进制文件的路径 cp = alloc_printf("%s/afl-qemu-trace", own_copy); ck_free(own_copy); + // 检查该路径是否可执行 if (!access(cp, X_OK)) { + // 设置目标路径和 new_argv[0] 为找到的 QEMU 二进制文件路径 target_path = new_argv[0] = cp; return new_argv; @@ -7710,13 +7787,16 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { } else ck_free(own_copy); + // 检查默认路径下的 QEMU 二进制文件是否可执行 if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { + // 设置目标路径和 new_argv[0] 为默认路径下的 QEMU 二进制文件路径 target_path = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace"); return new_argv; } + // 如果找不到 QEMU 二进制文件,输出错误信息 SAYF("\n" cLRD "[-] " cRST "Oops, unable to find the 'afl-qemu-trace' binary. The binary must be built\n" " separately by following the instructions in qemu_mode/README.qemu. If you\n" @@ -7727,43 +7807,43 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { " instrumented at compile time with afl-gcc. It is also possible to use it as a\n" " traditional \"dumb\" fuzzer by specifying '-n' in the command line.\n"); + // 抛出致命错误,提示无法找到 QEMU 二进制文件 FATAL("Failed to locate 'afl-qemu-trace'."); } -/* Make a copy of the current command line. */ - +// 函数: save_cmdline +// 功能: 保存当前命令行的参数 static void save_cmdline(u32 argc, char** argv) { - u32 len = 1, i; - u8* buf; + u32 len = 1, i; // 初始化长度为1,存储循环变量i + u8* buf; // 定义字符指针,用于保存命令行字符串 + // 计算所有命令行参数的总长度 for (i = 0; i < argc; i++) - len += strlen(argv[i]) + 1; - + len += strlen(argv[i]) + 1; // 每个参数长度加1(用于空格) + + // 分配内存,保存命令行参数 buf = orig_cmdline = ck_alloc(len); + // 复制命令行参数到分配的缓冲区 for (i = 0; i < argc; i++) { - u32 l = strlen(argv[i]); - - memcpy(buf, argv[i], l); - buf += l; + u32 l = strlen(argv[i]); // 获取当前参数的长度 - if (i != argc - 1) *(buf++) = ' '; + memcpy(buf, argv[i], l); // 复制参数到缓冲区 + buf += l; // 移动缓冲区指针 + // 如果不是最后一个参数,添加空格 + if (i != argc - 1) *(buf++) = ' '; } - *buf = 0; - + *buf = 0; // 在字符串末尾添加终止符 } - -#ifndef AFL_LIB - -/* Main entry point */ - +#ifndef AFL_LIB // 如果没有定义 AFL_LIB +// 主函数,程序的入口点 int main(int argc, char** argv) { s32 opt; @@ -7777,94 +7857,114 @@ int main(int argc, char** argv) { struct timeval tv; struct timezone tz; - SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by \n"); - - doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; - - gettimeofday(&tv, &tz); - srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); - - // argv 处理 - while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0) - - switch (opt) { - - case 'i': /* input dir */ - - // 初始 corpus 目录 - if (in_dir) FATAL("Multiple -i options not supported"); - in_dir = optarg; - - // 若使用 "-i -",则表示 in-place resume - if (!strcmp(in_dir, "-")) in_place_resume = 1; - - break; - - case 'o': /* output dir */ - - if (out_dir) FATAL("Multiple -o options not supported"); - out_dir = optarg; - break; - - case 'M': { /* master sync ID */ - - u8* c; - - if (sync_id) FATAL("Multiple -S or -M options not supported"); - sync_id = ck_strdup(optarg); - - if ((c = strchr(sync_id, ':'))) { - - *c = 0; - - if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || - !master_id || !master_max || master_id > master_max || - master_max > 1000000) FATAL("Bogus master ID passed to -M"); + SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by \n"); +} + /*************************************************************** + * 函数/方法: 主程序的参数处理 + * 描述: 该段代码负责解析命令行参数,并对输入输出目录、文件、同步ID以及超时等选项进行处理和验证。 + ***************************************************************/ + + doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; + + gettimeofday(&tv, &tz); + srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); + + // argv 处理 + while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0) + + switch (opt) { + + case 'i': /* input dir */ + + // 初始 corpus 目录 + if (in_dir) FATAL("Multiple -i options not supported"); + in_dir = optarg; + + // 若使用 "-i -",则表示 in-place resume + if (!strcmp(in_dir, "-")) in_place_resume = 1; + + break; + + /* 处理输出目录的命令行选项 */ + case 'o': /* output dir */ + + if (out_dir) FATAL("Multiple -o options not supported"); + out_dir = optarg; + break; + + case 'M': { /* master sync ID */ + // 处理选项 'M',用于设定主同步 ID + + u8* c; + + if (sync_id) FATAL("Multiple -S or -M options not supported"); + sync_id = ck_strdup(optarg); + + if ((c = strchr(sync_id, ':'))) { + // 解析同步ID中的主ID和最大ID + + *c = 0; + + if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || + !master_id || !master_max || master_id > master_max || + master_max > 1000000) FATAL("Bogus master ID passed to -M"); + + } + + force_deterministic = 1; + // 强制使用确定性执行 + } - - force_deterministic = 1; - - } - - break; - - case 'S': - - if (sync_id) FATAL("Multiple -S or -M options not supported"); - sync_id = ck_strdup(optarg); - break; - - case 'f': /* target file */ - - if (out_file) FATAL("Multiple -f options not supported"); - out_file = optarg; - break; - - case 'x': /* dictionary */ - - if (extras_dir) FATAL("Multiple -x options not supported"); - extras_dir = optarg; - break; - - case 't': { /* timeout */ - - u8 suffix = 0; - - if (timeout_given) FATAL("Multiple -t options not supported"); - - if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -t"); - - if (exec_tmout < 5) FATAL("Dangerously low value of -t"); - - if (suffix == '+') timeout_given = 2; else timeout_given = 1; - + break; - - } + + // 处理-S选项的代码逻辑 + /************************************************************************************ + * 该代码处理不同命令行选项的解析,包括同步ID、目标文件和字典目录等。 * + * 当检测到重复的选项时,程序会调用FATAL函数报错。 * + ************************************************************************************/ + case 'S': + + if (sync_id) FATAL("Multiple -S or -M options not supported"); + sync_id = ck_strdup(optarg); + break; + + case 'f': /* target file */ + + if (out_file) FATAL("Multiple -f options not supported"); + out_file = optarg; + break; + + case 'x': /* dictionary */ + + if (extras_dir) FATAL("Multiple -x options not supported"); + extras_dir = optarg; + break; + + /************************************** + * 处理命令行选项中与超时相关的参数 + * 支持-t选项,用于设置执行超时时间 + **************************************/ + case 't': { /* timeout */ + + u8 suffix = 0; + + if (timeout_given) FATAL("Multiple -t options not supported"); + + if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 || + optarg[0] == '-') FATAL("Bad syntax used for -t"); + + if (exec_tmout < 5) FATAL("Dangerously low value of -t"); + + if (suffix == '+') timeout_given = 2; else timeout_given = 1; + + break; + + } case 'm': { /* mem limit */ + // 处理内存限制选项的相关逻辑 u8 suffix = 'M'; @@ -7872,15 +7972,16 @@ int main(int argc, char** argv) { mem_limit_given = 1; if (!strcmp(optarg, "none")) { - + // 如果选项参数为"none",则将内存限制设置为0 mem_limit = 0; break; - } + // 解析命令行参数,获取内存限制和单位后缀 if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -m"); + // 根据单位后缀调整内存限制的值 switch (suffix) { case 'T': mem_limit *= 1024 * 1024; break; @@ -7892,8 +7993,10 @@ int main(int argc, char** argv) { } + // 检查内存限制值是否低于安全阈值 if (mem_limit < 5) FATAL("Dangerously low value of -m"); + // 在32位系统上检查内存限制的范围 if (sizeof(rlim_t) == 4 && mem_limit > 2000) FATAL("Value of -m out of range on 32-bit systems"); @@ -7902,10 +8005,11 @@ int main(int argc, char** argv) { break; case 'b': { /* bind CPU core */ - + // 处理 CPU 绑定选项,确保只能使用一次 if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); cpu_to_bind_given = 1; + // 解析用户输入的 CPU 核心编号,检查输入合法性 if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -b"); @@ -7913,6 +8017,7 @@ int main(int argc, char** argv) { } + /* 处理命令行参数,选择跳过确定性测试的选项 */ case 'd': /* skip deterministic */ if (skip_deterministic) FATAL("Multiple -d options not supported"); @@ -7921,24 +8026,19 @@ int main(int argc, char** argv) { break; case 'B': /* load bitmap */ - - /* This is a secret undocumented option! It is useful if you find - an interesting test case during a normal fuzzing process, and want - to mutate it without rediscovering any of the test cases already - found during an earlier run. - - To use this mode, you need to point -B to the fuzz_bitmap produced - by an earlier run for the exact same binary... and that's it. - - I only used this once or twice to get variants of a particular - file, so I'm not making this an official setting. */ - + /* + 该代码段处理加载位图的选项。 + 位图功能是一个未文档化的选项,适用于在正常模糊测试过程中找到有趣的测试用例后,进行变异而不重新发现先前运行中已找到的测试用例。 + 使用此模式时,需要将 -B 指向之前运行相同二进制文件生成的 fuzz_bitmap。 + 该功能仅在少数几次中使用过,因此不做为官方设置。 + */ if (in_bitmap) FATAL("Multiple -B options not supported"); in_bitmap = optarg; read_bitmap(in_bitmap); break; + /* 处理命令行参数选项 'C',用于设置崩溃模式 */ case 'C': /* crash mode */ if (crash_mode) FATAL("Multiple -C options not supported"); @@ -7946,27 +8046,34 @@ int main(int argc, char** argv) { break; case 'n': /* dumb mode */ - + // 处理-dumb模式的选项,当程序收到'-n'时触发 + if (dumb_mode) FATAL("Multiple -n options not supported"); if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1; break; + /* 处理命令行选项 */ case 'T': /* banner */ + /* 检查是否支持多个 -T 选项 */ if (use_banner) FATAL("Multiple -T options not supported"); use_banner = optarg; break; + // 处理QEMU模式的代码段 + // 如果启用QEMU模式,检查是否已指定多个-Q选项 + // 如果未指定内存限制,则使用默认的QEMU内存限制 + // 在处理完成后退出switch语句 + case 'Q': /* QEMU mode */ + + if (qemu_mode) FATAL("Multiple -Q options not supported"); + qemu_mode = 1; + + if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; + + break; - case 'Q': /* QEMU mode */ - - if (qemu_mode) FATAL("Multiple -Q options not supported"); - qemu_mode = 1; - - if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; - - break; - + /* 处理选项 'V' 的情况,显示版本号 */ case 'V': /* Show version number */ /* Version number has been printed already, just quit. */ @@ -7978,44 +8085,57 @@ int main(int argc, char** argv) { } - if (optind == argc || !in_dir || !out_dir) usage(argv[0]); - - setup_signal_handlers(); - check_asan_opts(); - - if (sync_id) fix_up_sync(); - - if (!strcmp(in_dir, out_dir)) - FATAL("Input and output directories can't be the same"); - - if (dumb_mode) { - - if (crash_mode) FATAL("-C and -n are mutually exclusive"); - if (qemu_mode) FATAL("-Q and -n are mutually exclusive"); - - } - - if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; - if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; - if (getenv("AFL_NO_ARITH")) no_arith = 1; - if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; - if (getenv("AFL_FAST_CAL")) fast_cal = 1; - - if (getenv("AFL_HANG_TMOUT")) { - hang_tmout = atoi(getenv("AFL_HANG_TMOUT")); - if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT"); - } - - if (dumb_mode == 2 && no_forkserver) - FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); + // 检查命令行参数以及输入输出目录是否有效,若无效则显示使用方法 + if (optind == argc || !in_dir || !out_dir) usage(argv[0]); + + // 设置信号处理函数 + setup_signal_handlers(); + // 检查地址卫生选项 + check_asan_opts(); + + // 处理同步ID和目录检查的逻辑 + // 该段代码主要用于验证输入输出目录及模式的有效性 + // 在不同的运行模式下进行相应的错误处理 + + if (sync_id) fix_up_sync(); + + if (!strcmp(in_dir, out_dir)) + FATAL("Input and output directories can't be the same"); + + if (dumb_mode) { + + if (crash_mode) FATAL("-C and -n are mutually exclusive"); + if (qemu_mode) FATAL("-Q and -n are mutually exclusive"); + + } - if (getenv("AFL_PRELOAD")) { - setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); - setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); - } + // 该代码段用于读取环境变量并根据其值设置相应的标志和超时值 + // 主要功能是启动时配置这些参数以调整程序的行为 + if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; + if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; + if (getenv("AFL_NO_ARITH")) no_arith = 1; + if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; + if (getenv("AFL_FAST_CAL")) fast_cal = 1; + + if (getenv("AFL_HANG_TMOUT")) { + hang_tmout = atoi(getenv("AFL_HANG_TMOUT")); + if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT"); + } - if (getenv("AFL_LD_PRELOAD")) - FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); + // 此函数用于检查环境变量和启动模式的合法性 + // 如果在不支持的模式下使用互斥选项,则会引发致命错误 + // 处理环境变量以确保正确的库加载 + + if (dumb_mode == 2 && no_forkserver) + FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); + + if (getenv("AFL_PRELOAD")) { + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + } + + if (getenv("AFL_LD_PRELOAD")) + FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); save_cmdline(argc, argv); @@ -8026,104 +8146,142 @@ int main(int argc, char** argv) { get_core_count(); #ifdef HAVE_AFFINITY + // 将当前进程绑定到一个空闲的CPU上,以优化性能 bind_to_free_cpu(); #endif /* HAVE_AFFINITY */ +/* 检查崩溃处理机制 */ check_crash_handling(); +/* 检查CPU调节器设置 */ check_cpu_governor(); +/* 设置后处理操作 */ setup_post(); +/* 设置共享内存 */ setup_shm(); +/* 初始化计数类16 */ init_count_class16(); +/* 设置目录和文件描述符 */ setup_dirs_fds(); +/* 读取测试用例 */ read_testcases(); +/* 加载自动化设置 */ load_auto(); +/* 处理输入的转换 */ pivot_inputs(); + // 此代码段为 afl-fuzz.c 文件中的初始化和参数处理部分 + // 主要功能是配置执行环境并检查传入的文件参数 + // 包括加载额外目录、查找超时、检测文件参数、设置输出文件、检查二进制文件及获取当前时间 + + if (extras_dir) load_extras(extras_dir); + + if (!timeout_given) find_timeout(); + + detect_file_args(argv + optind + 1); + + if (!out_file) setup_stdio_file(); + + check_binary(argv[optind]); + + start_time = get_cur_time(); + + if (qemu_mode) + use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); + else + use_argv = argv + optind; - if (extras_dir) load_extras(extras_dir); - - if (!timeout_given) find_timeout(); - - detect_file_args(argv + optind + 1); - - if (!out_file) setup_stdio_file(); - - check_binary(argv[optind]); - - start_time = get_cur_time(); - - if (qemu_mode) - use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); - else - use_argv = argv + optind; - - perform_dry_run(use_argv); - - cull_queue(); - - show_init_stats(); - - seek_to = find_start_position(); - - write_stats_file(0, 0, 0); - save_auto(); - - if (stop_soon) goto stop_fuzzing; - - /* Woop woop woop */ - - if (!not_on_tty) { - sleep(4); - start_time += 4000; + /* + * 此函数实现了模糊测试的初始步骤,包括执行干运行、清理队列、 + * 显示初始化统计信息、寻找起始位置并写入统计文件。 + */ + perform_dry_run(use_argv); + + cull_queue(); + + show_init_stats(); + + seek_to = find_start_position(); + + write_stats_file(0, 0, 0); + save_auto(); + + /* + * 如果状态指示测试需要停止,则跳转到停止模糊测试的标签。 + */ if (stop_soon) goto stop_fuzzing; - } + + /* Woop woop woop */ + + /* + * 如果不是在终端上运行,则暂停4秒,增加开始时间。 + */ + if (!not_on_tty) { + sleep(4); + start_time += 4000; + /* + * 再次检查是否需要停止模糊测试。 + */ + if (stop_soon) goto stop_fuzzing; + } + // 该段代码实现了一个持续循环,处理模糊测试队列中的条目。 while (1) { - - u8 skipped_fuzz; - - cull_queue(); - - if (!queue_cur) { - - queue_cycle++; - current_entry = 0; - cur_skipped_paths = 0; - queue_cur = queue; - - while (seek_to) { - current_entry++; - seek_to--; - queue_cur = queue_cur->next; - } - - show_stats(); - - if (not_on_tty) { - ACTF("Entering queue cycle %llu.", queue_cycle); - fflush(stdout); - } + + u8 skipped_fuzz; + + // 清理当前模糊测试队列 + cull_queue(); + + if (!queue_cur) { + + // 如果当前队列为空,增加队列周期计数 + queue_cycle++; + current_entry = 0; + cur_skipped_paths = 0; + queue_cur = queue; + + // 根据指定的跳过路径数量,定位到队列中的特定条目 + while (seek_to) { + current_entry++; + seek_to--; + queue_cur = queue_cur->next; + } + + // 显示当前状态统计 + show_stats(); + + if (not_on_tty) { + // 在非终端模式下,输出当前队列周期信息 + ACTF("Entering queue cycle %llu.", queue_cycle); + fflush(stdout); + } /* If we had a full queue cycle with no new finds, try recombination strategies next. */ + // 检查当前队列路径是否与前一个队列路径相同 if (queued_paths == prev_queued) { + // 根据是否使用拼接,决定增加无发现循环次数或启用拼接 if (use_splicing) cycles_wo_finds++; else use_splicing = 1; } else cycles_wo_finds = 0; + // 更新前一个队列路径值 prev_queued = queued_paths; + // 检查同步ID,队列循环和环境变量以同步模糊测试器 if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) sync_fuzzers(use_argv); } + // 执行模糊测试 skipped_fuzz = fuzz_one(use_argv); + // 如果没有停止标志且没有跳过模糊测试,根据同步间隔决定是否同步 if (!stop_soon && sync_id && !skipped_fuzz) { if (!(sync_interval_cnt++ % SYNC_INTERVAL)) @@ -8131,38 +8289,47 @@ int main(int argc, char** argv) { } + // 如果没有停止标志且 exit_1 为真,则设置停止标志 if (!stop_soon && exit_1) stop_soon = 2; + // 如果设置了停止标志,则跳出循环 if (stop_soon) break; - queue_cur = queue_cur->next; - current_entry++; - - } - - if (queue_cur) show_stats(); + // 更新当前队列指针和当前条目计数 + queue_cur = queue_cur->next; + current_entry++; - /* If we stopped programmatically, we kill the forkserver and the current runner. - If we stopped manually, this is done by the signal handler. */ - if (stop_soon == 2) { - if (child_pid > 0) kill(child_pid, SIGKILL); - if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); - } - /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */ - if (waitpid(forksrv_pid, NULL, 0) <= 0) { - WARNF("error waitpid\n"); } - write_bitmap(); - write_stats_file(0, 0, 0); - save_auto(); + /* + * 该段代码用于处理程序终止的情况,包括被程序控制的终止和手动终止。 + * 在终止时会杀死子进程和forkserver,并等待forkserver获取资源使用统计信息。 + * 此外,还会写入位图和统计文件以保存当前状态。 + */ + if (queue_cur) show_stats(); + + /* If we stopped programmatically, we kill the forkserver and the current runner. + If we stopped manually, this is done by the signal handler. */ + if (stop_soon == 2) { + if (child_pid > 0) kill(child_pid, SIGKILL); + if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); + } + /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */ + if (waitpid(forksrv_pid, NULL, 0) <= 0) { + WARNF("error waitpid\n"); + } + + write_bitmap(); + write_stats_file(0, 0, 0); + save_auto(); +/* 停止模糊测试的函数 */ stop_fuzzing: SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST, stop_soon == 2 ? "programmatically" : "by user"); - /* Running for more than 30 minutes but still doing first cycle? */ + /* 运行超过30分钟但仍在进行第一次周期? */ if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) { @@ -8187,4 +8354,3 @@ stop_fuzzing: } #endif /* !AFL_LIB */ -