|
|
|
@ -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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|