em0 8 months ago
parent 13b181ea84
commit 8f017a1f4c

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

Loading…
Cancel
Save