From d7a37d69d4069d0aa26358b80a2d4dff36746c0e Mon Sep 17 00:00:00 2001 From: yinao <1872767794@qq.com> Date: Wed, 8 Jan 2025 20:47:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86afl-as.h=EF=BC=8Cafl?= =?UTF-8?q?-as.c=EF=BC=8Cafl-analyze.c=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 7 +- src/afl-analyze.c | 735 ++++++++++++++++++------------------ src/afl-as.c | 485 ++++++++++++------------ src/afl-as.h | 842 +++++++++++++++++++++--------------------- 4 files changed, 1035 insertions(+), 1034 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f9ff0ee..925fab1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,11 @@ { "files.associations": { "stdio.h": "c", - "alloc-inl.h": "c" + "alloc-inl.h": "c", + "config.h": "c", + "afl-as.h": "c", + "types.h": "c", + "fcntl.h": "c", + "android-ashmem.h": "c" } } \ No newline at end of file diff --git a/src/afl-analyze.c b/src/afl-analyze.c index c97b73d..bcb429b 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -27,79 +27,79 @@ */ -#define AFL_MAIN -#include "android-ashmem.h" - -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" -#include "hash.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static s32 child_pid; /* PID of the tested program */ - -static u8* trace_bits; /* SHM with instrumentation bitmap */ - -static u8 *in_file, /* Analyzer input test case */ - *prog_in, /* Targeted program input file */ - *target_path, /* Path to target binary */ - *doc_path; /* Path to docs */ - -static u8 *in_data; /* Input data for analysis */ - -static u32 in_len, /* Input data length */ - orig_cksum, /* Original checksum */ - total_execs, /* Total number of execs */ - exec_hangs, /* Total number of hangs */ - exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */ - -static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ - -static s32 shm_id, /* ID of the SHM region */ - dev_null_fd = -1; /* FD to /dev/null */ - -static u8 edges_only, /* Ignore hit counts? */ - use_hex_offsets, /* Show hex offsets? */ - use_stdin = 1; /* Use stdin for program input? */ +#define AFL_MAIN // 定义主程序宏 +#include "android-ashmem.h" // 包含Android共享内存支持 + +#include "config.h" // 包含配置文件 +#include "types.h" // 包含类型定义 +#include "debug.h" // 包含调试工具 +#include "alloc-inl.h" // 包含内存分配工具 +#include "hash.h" // 包含哈希函数 + +#include // 标准输入输出 +#include // POSIX标准库 +#include // 标准库 +#include // 字符串处理 +#include // 时间处理 +#include // 错误处理 +#include // 信号处理 +#include // 目录操作 +#include // 文件控制 +#include // 字符处理 + +#include // 进程等待 +#include // 时间处理 +#include // 共享内存 +#include // 文件状态 +#include // 类型定义 +#include // 资源限制 + +static s32 child_pid; // 测试程序的PID + +static u8* trace_bits; // 共享内存中的插桩位图 + +static u8 *in_file, // 分析器输入测试用例 + *prog_in, // 目标程序的输入文件 + *target_path, // 目标二进制文件的路径 + *doc_path; // 文档路径 + +static u8 *in_data; // 用于分析的输入数据 + +static u32 in_len, // 输入数据长度 + orig_cksum, // 原始校验和 + total_execs, // 总执行次数 + exec_hangs, // 总挂起次数 + exec_tmout = EXEC_TIMEOUT; // 执行超时时间(毫秒) + +static u64 mem_limit = MEM_LIMIT; // 内存限制(MB) + +static s32 shm_id, // 共享内存区域的ID + dev_null_fd = -1; // /dev/null的文件描述符 + +static u8 edges_only, // 是否忽略命中计数 + use_hex_offsets, // 是否显示十六进制偏移量 + use_stdin = 1; // 是否使用stdin作为程序输入 static volatile u8 - stop_soon, /* Ctrl-C pressed? */ - child_timed_out; /* Child timed out? */ + stop_soon, // 是否按下Ctrl-C + child_timed_out; // 子进程是否超时 /* Constants used for describing byte behavior. */ -#define RESP_NONE 0x00 /* Changing byte is a no-op. */ -#define RESP_MINOR 0x01 /* Some changes have no effect. */ -#define RESP_VARIABLE 0x02 /* Changes produce variable paths. */ -#define RESP_FIXED 0x03 /* Changes produce fixed patterns. */ +#define RESP_NONE 0x00 // 改变字节无影响 +#define RESP_MINOR 0x01 // 某些改变无影响 +#define RESP_VARIABLE 0x02 // 改变产生可变路径 +#define RESP_FIXED 0x03 // 改变产生固定模式 -#define RESP_LEN 0x04 /* Potential length field */ -#define RESP_CKSUM 0x05 /* Potential checksum */ -#define RESP_SUSPECT 0x06 /* Potential "suspect" blob */ +#define RESP_LEN 0x04 // 可能是长度字段 +#define RESP_CKSUM 0x05 // 可能是校验和 +#define RESP_SUSPECT 0x06 // 可能是“可疑”块 /* Classify tuple counts. This is a slow & naive version, but good enough here. */ -static u8 count_class_lookup[256] = { +static u8 count_class_lookup[256] = { // 计数分类查找表 [0] = 0, [1] = 1, @@ -113,21 +113,21 @@ static u8 count_class_lookup[256] = { }; -static void classify_counts(u8* mem) { +static void classify_counts(u8* mem) { // 分类计数 u32 i = MAP_SIZE; - if (edges_only) { + if (edges_only) { // 如果只关注边缘 while (i--) { - if (*mem) *mem = 1; + if (*mem) *mem = 1; // 如果有计数,设置为1 mem++; } - } else { + } else { // 否则 while (i--) { - *mem = count_class_lookup[*mem]; + *mem = count_class_lookup[*mem]; // 使用查找表分类 mem++; } @@ -138,107 +138,107 @@ static void classify_counts(u8* mem) { /* See if any bytes are set in the bitmap. */ -static inline u8 anything_set(void) { +static inline u8 anything_set(void) { // 检查位图中是否有任何字节被设置 u32* ptr = (u32*)trace_bits; u32 i = (MAP_SIZE >> 2); - while (i--) if (*(ptr++)) return 1; + while (i--) if (*(ptr++)) return 1; // 如果有设置,返回1 - return 0; + return 0; // 否则返回0 } /* Get rid of shared memory and temp files (atexit handler). */ -static void remove_shm(void) { +static void remove_shm(void) { // 删除共享内存和临时文件 - unlink(prog_in); /* Ignore errors */ - shmctl(shm_id, IPC_RMID, NULL); + unlink(prog_in); // 忽略错误 + shmctl(shm_id, IPC_RMID, NULL); // 删除共享内存 } /* Configure shared memory. */ -static void setup_shm(void) { +static void setup_shm(void) { // 配置共享内存 u8* shm_str; - shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); + shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); // 创建共享内存 - if (shm_id < 0) PFATAL("shmget() failed"); + if (shm_id < 0) PFATAL("shmget() failed"); // 如果失败,报错 - atexit(remove_shm); + atexit(remove_shm); // 注册退出处理函数 - 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); // 设置环境变量 - 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"); // 如果失败,报错 } /* Read initial file. */ -static void read_initial_file(void) { +static void read_initial_file(void) { // 读取初始文件 struct stat st; - s32 fd = open(in_file, O_RDONLY); + s32 fd = open(in_file, O_RDONLY); // 打开文件 - if (fd < 0) PFATAL("Unable to open '%s'", in_file); + if (fd < 0) PFATAL("Unable to open '%s'", in_file); // 如果失败,报错 - if (fstat(fd, &st) || !st.st_size) - FATAL("Zero-sized input file."); + if (fstat(fd, &st) || !st.st_size) // 获取文件状态 + FATAL("Zero-sized input file."); // 如果文件大小为0,报错 - if (st.st_size >= TMIN_MAX_FILE) - FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024); + if (st.st_size >= TMIN_MAX_FILE) // 如果文件过大 + FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024); // 报错 - in_len = st.st_size; - in_data = ck_alloc_nozero(in_len); + in_len = st.st_size; // 设置输入数据长度 + in_data = ck_alloc_nozero(in_len); // 分配内存 - ck_read(fd, in_data, in_len, in_file); + ck_read(fd, in_data, in_len, in_file); // 读取文件内容 - close(fd); + close(fd); // 关闭文件 - OKF("Read %u byte%s from '%s'.", in_len, in_len == 1 ? "" : "s", in_file); + OKF("Read %u byte%s from '%s'.", in_len, in_len == 1 ? "" : "s", in_file); // 输出读取成功信息 } /* Write output file. */ -static s32 write_to_file(u8* path, u8* mem, u32 len) { +static s32 write_to_file(u8* path, u8* mem, u32 len) { // 写入输出文件 s32 ret; - unlink(path); /* Ignore errors */ + unlink(path); // 忽略错误 - ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); + ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); // 创建文件 - if (ret < 0) PFATAL("Unable to create '%s'", path); + if (ret < 0) PFATAL("Unable to create '%s'", path); // 如果失败,报错 - ck_write(ret, mem, len, path); + ck_write(ret, mem, len, path); // 写入数据 - lseek(ret, 0, SEEK_SET); + lseek(ret, 0, SEEK_SET); // 重置文件指针 - return ret; + return ret; // 返回文件描述符 } /* Handle timeout signal. */ -static void handle_timeout(int sig) { +static void handle_timeout(int sig) { // 处理超时信号 - child_timed_out = 1; - if (child_pid > 0) kill(child_pid, SIGKILL); + child_timed_out = 1; // 设置超时标志 + if (child_pid > 0) kill(child_pid, SIGKILL); // 杀死子进程 } @@ -246,7 +246,7 @@ static void handle_timeout(int sig) { /* Execute target application. Returns exec checksum, or 0 if program times out. */ -static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { +static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { // 执行目标程序 static struct itimerval it; int status = 0; @@ -254,115 +254,115 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { s32 prog_in_fd; u32 cksum; - memset(trace_bits, 0, MAP_SIZE); - MEM_BARRIER(); + memset(trace_bits, 0, MAP_SIZE); // 清空位图 + MEM_BARRIER(); // 内存屏障 - prog_in_fd = write_to_file(prog_in, mem, len); + prog_in_fd = write_to_file(prog_in, mem, len); // 写入输入文件 - child_pid = fork(); + child_pid = fork(); // 创建子进程 - if (child_pid < 0) PFATAL("fork() failed"); + if (child_pid < 0) PFATAL("fork() failed"); // 如果失败,报错 - if (!child_pid) { + if (!child_pid) { // 子进程 struct rlimit r; - if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 || - dup2(dev_null_fd, 1) < 0 || - dup2(dev_null_fd, 2) < 0) { + if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 || // 重定向stdin + dup2(dev_null_fd, 1) < 0 || // 重定向stdout + dup2(dev_null_fd, 2) < 0) { // 重定向stderr - *(u32*)trace_bits = EXEC_FAIL_SIG; - PFATAL("dup2() failed"); + *(u32*)trace_bits = EXEC_FAIL_SIG; // 设置失败标志 + PFATAL("dup2() failed"); // 如果失败,报错 } - close(dev_null_fd); - close(prog_in_fd); + close(dev_null_fd); // 关闭/dev/null + close(prog_in_fd); // 关闭输入文件 - if (mem_limit) { + if (mem_limit) { // 如果设置了内存限制 - r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; + r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; // 设置内存限制 #ifdef RLIMIT_AS - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + setrlimit(RLIMIT_AS, &r); // 忽略错误 #else - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ + setrlimit(RLIMIT_DATA, &r); // 忽略错误 #endif /* ^RLIMIT_AS */ } r.rlim_max = r.rlim_cur = 0; - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + setrlimit(RLIMIT_CORE, &r); // 忽略错误 - execv(target_path, argv); + execv(target_path, argv); // 执行目标程序 - *(u32*)trace_bits = EXEC_FAIL_SIG; - exit(0); + *(u32*)trace_bits = EXEC_FAIL_SIG; // 设置失败标志 + exit(0); // 退出 } - close(prog_in_fd); + close(prog_in_fd); // 关闭输入文件 /* Configure timeout, wait for child, cancel timeout. */ - child_timed_out = 0; - it.it_value.tv_sec = (exec_tmout / 1000); + 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; + child_pid = 0; // 重置子进程PID + it.it_value.tv_sec = 0; // 重置定时器 it.it_value.tv_usec = 0; - setitimer(ITIMER_REAL, &it, NULL); + setitimer(ITIMER_REAL, &it, NULL); // 取消定时器 - MEM_BARRIER(); + MEM_BARRIER(); // 内存屏障 /* Clean up bitmap, analyze exit condition, etc. */ - if (*(u32*)trace_bits == EXEC_FAIL_SIG) - FATAL("Unable to execute '%s'", argv[0]); + if (*(u32*)trace_bits == EXEC_FAIL_SIG) // 如果执行失败 + FATAL("Unable to execute '%s'", argv[0]); // 报错 - classify_counts(trace_bits); - total_execs++; + classify_counts(trace_bits); // 分类计数 + total_execs++; // 增加执行次数 - if (stop_soon) { - SAYF(cRST cLRD "\n+++ Analysis aborted by user +++\n" cRST); - exit(1); + if (stop_soon) { // 如果用户中止 + SAYF(cRST cLRD "\n+++ Analysis aborted by user +++\n" cRST); // 输出中止信息 + exit(1); // 退出 } /* Always discard inputs that time out. */ - if (child_timed_out) { + if (child_timed_out) { // 如果超时 - exec_hangs++; - return 0; + exec_hangs++; // 增加挂起次数 + return 0; // 返回0 } - cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); + cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); // 计算校验和 /* We don't actually care if the target is crashing or not, except that when it does, the checksum should be different. */ - if (WIFSIGNALED(status) || - (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) || - (WIFEXITED(status) && WEXITSTATUS(status))) { + if (WIFSIGNALED(status) || // 如果子进程被信号终止 + (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) || // 如果MSAN错误 + (WIFEXITED(status) && WEXITSTATUS(status))) { // 如果非正常退出 - cksum ^= 0xffffffff; + cksum ^= 0xffffffff; // 反转校验和 } - if (first_run) orig_cksum = cksum; + if (first_run) orig_cksum = cksum; // 如果是第一次运行,保存原始校验和 - return cksum; + return cksum; // 返回校验和 } @@ -371,14 +371,14 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { /* Helper function to display a human-readable character. */ -static void show_char(u8 val) { +static void show_char(u8 val) { // 显示可读字符 switch (val) { case 0 ... 32: - case 127 ... 255: SAYF("#%02x", val); break; + case 127 ... 255: SAYF("#%02x", val); break; // 显示十六进制值 - default: SAYF(" %c ", val); + default: SAYF(" %c ", val); // 显示字符 } @@ -387,7 +387,7 @@ static void show_char(u8 val) { /* Show the legend */ -static void show_legend(void) { +static void show_legend(void) { // 显示图例 SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block " cBLK bgLGN " 01 " cRST " - suspected length field\n" @@ -404,7 +404,7 @@ static void show_legend(void) { /* Interpret and report a pattern in the input file. */ -static void dump_hex(u8* buf, u32 len, u8* b_data) { +static void dump_hex(u8* buf, u32 len, u8* b_data) { // 解释并报告输入文件中的模式 u32 i; @@ -420,7 +420,7 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { /* Look ahead to determine the length of run. */ - while (i + rlen < len && (b_data[i] >> 7) == (b_data[i + rlen] >> 7)) { + while (i + rlen < len && (b_data[i] >> 7) == (b_data[i + rlen] >> 7)) { // 查找连续相同类型 if (rtype < (b_data[i + rlen] & 0x0f)) rtype = b_data[i + rlen] & 0x0f; rlen++; @@ -429,24 +429,24 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { /* Try to do some further classification based on length & value. */ - if (rtype == RESP_FIXED) { + if (rtype == RESP_FIXED) { // 如果是固定模式 switch (rlen) { - case 2: { + case 2: { // 2字节 u16 val = *(u16*)(in_data + i); /* Small integers may be length fields. */ - if (val && (val <= in_len || SWAP16(val) <= in_len)) { + if (val && (val <= in_len || SWAP16(val) <= in_len)) { // 可能是长度字段 rtype = RESP_LEN; break; } /* Uniform integers may be checksums. */ - if (val && abs(in_data[i] - in_data[i + 1]) > 32) { + if (val && abs(in_data[i] - in_data[i + 1]) > 32) { // 可能是校验和 rtype = RESP_CKSUM; break; } @@ -455,13 +455,13 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { } - case 4: { + case 4: { // 4字节 u32 val = *(u32*)(in_data + i); /* Small integers may be length fields. */ - if (val && (val <= in_len || SWAP32(val) <= in_len)) { + if (val && (val <= in_len || SWAP32(val) <= in_len)) { // 可能是长度字段 rtype = RESP_LEN; break; } @@ -470,7 +470,7 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { if (val && (in_data[i] >> 7 != in_data[i + 1] >> 7 || in_data[i] >> 7 != in_data[i + 2] >> 7 || - in_data[i] >> 7 != in_data[i + 3] >> 7)) { + in_data[i] >> 7 != in_data[i + 3] >> 7)) { // 可能是校验和 rtype = RESP_CKSUM; break; } @@ -481,7 +481,7 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { case 1: case 3: case 5 ... MAX_AUTO_EXTRA - 1: break; - default: rtype = RESP_SUSPECT; + default: rtype = RESP_SUSPECT; // 可能是可疑块 } @@ -491,22 +491,22 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { #ifdef USE_COLOR - for (off = 0; off < rlen; off++) { + for (off = 0; off < rlen; off++) { // 打印整个连续块 /* Every 16 digits, display offset. */ - if (!((i + off) % 16)) { + if (!((i + off) % 16)) { // 每16字节显示偏移量 if (off) SAYF(cRST cLCY ">"); - if (use_hex_offsets) + if (use_hex_offsets) // 如果使用十六进制偏移量 SAYF(cRST cGRA "%s[%06x] " cRST, (i + off) ? "\n" : "", i + off); else SAYF(cRST cGRA "%s[%06u] " cRST, (i + off) ? "\n" : "", i + off); } - switch (rtype) { + switch (rtype) { // 根据类型设置颜色 case RESP_NONE: SAYF(cLGR bgGRA); break; case RESP_MINOR: SAYF(cBRI bgGRA); break; @@ -518,20 +518,20 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { } - show_char(in_data[i + off]); + show_char(in_data[i + off]); // 显示字符 - if (off != rlen - 1 && (i + off + 1) % 16) SAYF(" "); else SAYF(cRST " "); + if (off != rlen - 1 && (i + off + 1) % 16) SAYF(" "); else SAYF(cRST " "); // 处理空格 } #else - if (use_hex_offsets) + if (use_hex_offsets) // 如果使用十六进制偏移量 SAYF(" Offset %x, length %u: ", i, rlen); else SAYF(" Offset %u, length %u: ", i, rlen); - switch (rtype) { + switch (rtype) { // 根据类型输出描述 case RESP_NONE: SAYF("no-op block\n"); break; case RESP_MINOR: SAYF("superficial content\n"); break; @@ -545,37 +545,36 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { #endif /* ^USE_COLOR */ - i += rlen - 1; + i += rlen - 1; // 跳过已处理的字节 } #ifdef USE_COLOR - SAYF(cRST "\n"); + SAYF(cRST "\n"); // 重置颜色 #endif /* USE_COLOR */ } - /* Actually analyze! */ -static void analyze(char** argv) { +static void analyze(char** argv) { // 实际分析函数 u32 i; u32 boring_len = 0, prev_xff = 0, prev_x01 = 0, prev_s10 = 0, prev_a10 = 0; - u8* b_data = ck_alloc(in_len + 1); + u8* b_data = ck_alloc(in_len + 1); // 分配行为数据数组 u8 seq_byte = 0; - b_data[in_len] = 0xff; /* Intentional terminator. */ + b_data[in_len] = 0xff; /* Intentional terminator. */ // 设置终止符 - ACTF("Analyzing input file (this may take a while)...\n"); + ACTF("Analyzing input file (this may take a while)...\n"); // 输出分析提示 #ifdef USE_COLOR - show_legend(); + show_legend(); // 显示图例 #endif /* USE_COLOR */ - for (i = 0; i < in_len; i++) { + for (i = 0; i < in_len; i++) { // 遍历输入文件的每个字节 u32 xor_ff, xor_01, sub_10, add_10; u8 xff_orig, x01_orig, s10_orig, a10_orig; @@ -584,149 +583,148 @@ static void analyze(char** argv) { operations designed to elicit some response from the underlying code. */ - in_data[i] ^= 0xff; - xor_ff = run_target(argv, in_data, in_len, 0); + in_data[i] ^= 0xff; // 对当前字节进行异或操作 + xor_ff = run_target(argv, in_data, in_len, 0); // 运行目标程序并获取校验和 - in_data[i] ^= 0xfe; - xor_01 = run_target(argv, in_data, in_len, 0); + in_data[i] ^= 0xfe; // 再次异或操作 + xor_01 = run_target(argv, in_data, in_len, 0); // 运行目标程序并获取校验和 - in_data[i] = (in_data[i] ^ 0x01) - 0x10; - sub_10 = run_target(argv, in_data, in_len, 0); + in_data[i] = (in_data[i] ^ 0x01) - 0x10; // 对当前字节进行减操作 + sub_10 = run_target(argv, in_data, in_len, 0); // 运行目标程序并获取校验和 - in_data[i] += 0x20; - add_10 = run_target(argv, in_data, in_len, 0); - in_data[i] -= 0x10; + in_data[i] += 0x20; // 对当前字节进行加操作 + add_10 = run_target(argv, in_data, in_len, 0); // 运行目标程序并获取校验和 + in_data[i] -= 0x10; // 恢复原始值 /* Classify current behavior. */ - xff_orig = (xor_ff == orig_cksum); - x01_orig = (xor_01 == orig_cksum); - s10_orig = (sub_10 == orig_cksum); - a10_orig = (add_10 == orig_cksum); + xff_orig = (xor_ff == orig_cksum); // 检查异或操作后的校验和是否与原始校验和相同 + x01_orig = (xor_01 == orig_cksum); // 检查异或操作后的校验和是否与原始校验和相同 + s10_orig = (sub_10 == orig_cksum); // 检查减操作后的校验和是否与原始校验和相同 + a10_orig = (add_10 == orig_cksum); // 检查加操作后的校验和是否与原始校验和相同 - if (xff_orig && x01_orig && s10_orig && a10_orig) { + if (xff_orig && x01_orig && s10_orig && a10_orig) { // 如果所有操作后的校验和都与原始校验和相同 - b_data[i] = RESP_NONE; - boring_len++; + b_data[i] = RESP_NONE; // 标记为无影响 + boring_len++; // 增加无影响字节计数 - } else if (xff_orig || x01_orig || s10_orig || a10_orig) { + } else if (xff_orig || x01_orig || s10_orig || a10_orig) { // 如果部分操作后的校验和与原始校验和相同 - b_data[i] = RESP_MINOR; - boring_len++; + b_data[i] = RESP_MINOR; // 标记为轻微影响 + boring_len++; // 增加无影响字节计数 - } else if (xor_ff == xor_01 && xor_ff == sub_10 && xor_ff == add_10) { + } else if (xor_ff == xor_01 && xor_ff == sub_10 && xor_ff == add_10) { // 如果所有操作后的校验和相同 - b_data[i] = RESP_FIXED; + b_data[i] = RESP_FIXED; // 标记为固定模式 - } else b_data[i] = RESP_VARIABLE; + } else b_data[i] = RESP_VARIABLE; // 否则标记为可变模式 /* When all checksums change, flip most significant bit of b_data. */ - if (prev_xff != xor_ff && prev_x01 != xor_01 && - prev_s10 != sub_10 && prev_a10 != add_10) seq_byte ^= 0x80; + if (prev_xff != xor_ff && prev_x01 != xor_01 && // 如果所有操作后的校验和都发生变化 + prev_s10 != sub_10 && prev_a10 != add_10) seq_byte ^= 0x80; // 翻转最高位 - b_data[i] |= seq_byte; + b_data[i] |= seq_byte; // 更新行为数据 - prev_xff = xor_ff; - prev_x01 = xor_01; - prev_s10 = sub_10; - prev_a10 = add_10; + prev_xff = xor_ff; // 更新前一个异或操作后的校验和 + prev_x01 = xor_01; // 更新前一个异或操作后的校验和 + prev_s10 = sub_10; // 更新前一个减操作后的校验和 + prev_a10 = add_10; // 更新前一个加操作后的校验和 } - dump_hex(in_data, in_len, b_data); + dump_hex(in_data, in_len, b_data); // 输出分析结果 SAYF("\n"); - OKF("Analysis complete. Interesting bits: %0.02f%% of the input file.", + OKF("Analysis complete. Interesting bits: %0.02f%% of the input file.", // 输出分析完成信息 100.0 - ((double)boring_len * 100) / in_len); - if (exec_hangs) - WARNF(cLRD "Encountered %u timeouts - results may be skewed." cRST, + if (exec_hangs) // 如果有超时 + WARNF(cLRD "Encountered %u timeouts - results may be skewed." cRST, // 输出警告 exec_hangs); - ck_free(b_data); + ck_free(b_data); // 释放行为数据数组 } - /* Handle Ctrl-C and the like. */ -static void handle_stop_sig(int sig) { +static void handle_stop_sig(int sig) { // 处理停止信号 - stop_soon = 1; + stop_soon = 1; // 设置停止标志 - if (child_pid > 0) kill(child_pid, SIGKILL); + if (child_pid > 0) kill(child_pid, SIGKILL); // 杀死子进程 } /* Do basic preparations - persistent fds, filenames, etc. */ -static void set_up_environment(void) { +static void set_up_environment(void) { // 设置环境 u8* x; - dev_null_fd = open("/dev/null", O_RDWR); - if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); + dev_null_fd = open("/dev/null", O_RDWR); // 打开/dev/null + if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); // 如果失败,报错 - if (!prog_in) { + if (!prog_in) { // 如果没有指定输入文件 u8* use_dir = "."; - if (access(use_dir, R_OK | W_OK | X_OK)) { + if (access(use_dir, R_OK | W_OK | X_OK)) { // 检查当前目录是否可访问 - use_dir = getenv("TMPDIR"); - if (!use_dir) use_dir = "/tmp"; + use_dir = getenv("TMPDIR"); // 获取临时目录 + if (!use_dir) use_dir = "/tmp"; // 如果没有设置,使用/tmp } - prog_in = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, getpid()); + prog_in = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, getpid()); // 生成临时文件路径 } /* Set sane defaults... */ - x = getenv("ASAN_OPTIONS"); + x = getenv("ASAN_OPTIONS"); // 获取ASAN选项 - if (x) { + if (x) { // 如果设置了ASAN选项 - if (!strstr(x, "abort_on_error=1")) + if (!strstr(x, "abort_on_error=1")) // 检查是否设置了abort_on_error FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); - if (!strstr(x, "symbolize=0")) + if (!strstr(x, "symbolize=0")) // 检查是否设置了symbolize FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } - x = getenv("MSAN_OPTIONS"); + x = getenv("MSAN_OPTIONS"); // 获取MSAN选项 - if (x) { + if (x) { // 如果设置了MSAN选项 - if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) // 检查是否设置了exit_code FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(MSAN_ERROR) " - please fix!"); - if (!strstr(x, "symbolize=0")) + if (!strstr(x, "symbolize=0")) // 检查是否设置了symbolize FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); } - setenv("ASAN_OPTIONS", "abort_on_error=1:" + setenv("ASAN_OPTIONS", "abort_on_error=1:" // 设置ASAN选项 "detect_leaks=0:" "symbolize=0:" "allocator_may_return_null=1", 0); - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" // 设置MSAN选项 "symbolize=0:" "abort_on_error=1:" "allocator_may_return_null=1:" "msan_track_origins=0", 0); - if (getenv("AFL_PRELOAD")) { - setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); - setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + if (getenv("AFL_PRELOAD")) { // 如果设置了AFL_PRELOAD + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); // 设置LD_PRELOAD + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); // 设置DYLD_INSERT_LIBRARIES } } @@ -734,7 +732,7 @@ static void set_up_environment(void) { /* Setup signal handlers, duh. */ -static void setup_signal_handlers(void) { +static void setup_signal_handlers(void) { // 设置信号处理函数 struct sigaction sa; @@ -746,14 +744,14 @@ static void setup_signal_handlers(void) { /* Various ways of saying "stop". */ - sa.sa_handler = handle_stop_sig; + 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; + sa.sa_handler = handle_timeout; // 设置超时信号处理函数 sigaction(SIGALRM, &sa, NULL); } @@ -761,34 +759,34 @@ static void setup_signal_handlers(void) { /* Detect @@ in args. */ -static void detect_file_args(char** argv) { +static void detect_file_args(char** argv) { // 检测参数中的@@ u32 i = 0; - u8* cwd = getcwd(NULL, 0); + u8* cwd = getcwd(NULL, 0); // 获取当前工作目录 - if (!cwd) PFATAL("getcwd() failed"); + if (!cwd) PFATAL("getcwd() failed"); // 如果失败,报错 - while (argv[i]) { + while (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; /* Be sure that we're always using fully-qualified paths. */ - if (prog_in[0] == '/') aa_subst = prog_in; - else aa_subst = alloc_printf("%s/%s", cwd, prog_in); + if (prog_in[0] == '/') aa_subst = prog_in; // 如果输入文件是绝对路径,直接使用 + else aa_subst = alloc_printf("%s/%s", cwd, prog_in); // 否则拼接当前目录 /* Construct a replacement argv value. */ - *aa_loc = 0; - n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); - argv[i] = n_arg; - *aa_loc = '@'; + *aa_loc = 0; // 截断字符串 + n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); // 构造新参数 + argv[i] = n_arg; // 替换参数 + *aa_loc = '@'; // 恢复@@ - if (prog_in[0] != '/') ck_free(aa_subst); + if (prog_in[0] != '/') ck_free(aa_subst); // 释放内存 } @@ -796,14 +794,14 @@ static void detect_file_args(char** argv) { } - free(cwd); /* not tracked */ + free(cwd); /* not tracked */ // 释放当前工作目录 } /* Display usage hints. */ -static void usage(u8* argv0) { +static void usage(u8* argv0) { // 显示用法提示 SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" @@ -837,51 +835,51 @@ static void usage(u8* argv0) { /* Find binary. */ -static void find_binary(u8* fname) { +static void find_binary(u8* fname) { // 查找目标二进制文件 u8* env_path = 0; struct stat st; - if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { + if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { // 如果文件名包含路径或没有PATH环境变量 - target_path = ck_strdup(fname); + target_path = ck_strdup(fname); // 直接使用文件名 - if (stat(target_path, &st) || !S_ISREG(st.st_mode) || + 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 { + } else { // 否则在PATH中查找 - while (env_path) { + while (env_path) { // 遍历PATH - u8 *cur_elem, *delim = strchr(env_path, ':'); + u8 *cur_elem, *delim = strchr(env_path, ':'); // 查找分隔符 - if (delim) { + if (delim) { // 如果找到分隔符 - cur_elem = ck_alloc(delim - env_path + 1); - memcpy(cur_elem, env_path, delim - env_path); + 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; - if (cur_elem[0]) - target_path = alloc_printf("%s/%s", cur_elem, fname); + if (cur_elem[0]) // 如果路径不为空 + target_path = alloc_printf("%s/%s", cur_elem, fname); // 拼接路径 else - target_path = ck_strdup(fname); + target_path = ck_strdup(fname); // 否则直接使用文件名 - ck_free(cur_elem); + ck_free(cur_elem); // 释放内存 - if (!stat(target_path, &st) && S_ISREG(st.st_mode) && - (st.st_mode & 0111) && st.st_size >= 4) break; + if (!stat(target_path, &st) && S_ISREG(st.st_mode) && // 检查文件状态 + (st.st_mode & 0111) && st.st_size >= 4) break; // 如果文件存在且可执行,退出循环 - ck_free(target_path); + 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); // 如果未找到文件,报错 } @@ -890,206 +888,205 @@ static void find_binary(u8* fname) { /* Fix up argv for QEMU. */ -static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { +static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { // 为QEMU模式修复argv - char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); + char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); // 分配内存 u8 *tmp, *cp, *rsl, *own_copy; /* Workaround for a QEMU stability glitch. */ - setenv("QEMU_LOG", "nochain", 1); + setenv("QEMU_LOG", "nochain", 1); // 设置QEMU日志 - memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); + memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); // 复制参数 /* Now we need to actually find qemu for argv[0]. */ - new_argv[2] = target_path; - new_argv[1] = "--"; + new_argv[2] = target_path; // 设置目标路径 + new_argv[1] = "--"; // 设置分隔符 - tmp = getenv("AFL_PATH"); + tmp = getenv("AFL_PATH"); // 获取AFL路径 - if (tmp) { + if (tmp) { // 如果设置了AFL路径 - cp = alloc_printf("%s/afl-qemu-trace", tmp); + cp = alloc_printf("%s/afl-qemu-trace", tmp); // 拼接路径 - if (access(cp, X_OK)) - FATAL("Unable to find '%s'", tmp); + if (access(cp, X_OK)) // 检查文件是否可执行 + FATAL("Unable to find '%s'", tmp); // 如果不可执行,报错 - target_path = new_argv[0] = cp; - return new_argv; + target_path = new_argv[0] = cp; // 设置目标路径 + return new_argv; // 返回新参数 } - own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); + own_copy = ck_strdup(own_loc); // 复制当前路径 + rsl = strrchr(own_copy, '/'); // 查找最后一个斜杠 - if (rsl) { + if (rsl) { // 如果找到斜杠 - *rsl = 0; + *rsl = 0; // 截断路径 - cp = alloc_printf("%s/afl-qemu-trace", own_copy); - ck_free(own_copy); + cp = alloc_printf("%s/afl-qemu-trace", own_copy); // 拼接路径 + ck_free(own_copy); // 释放内存 - if (!access(cp, X_OK)) { + if (!access(cp, X_OK)) { // 检查文件是否可执行 - target_path = new_argv[0] = cp; - return new_argv; + target_path = new_argv[0] = cp; // 设置目标路径 + return new_argv; // 返回新参数 } - } else ck_free(own_copy); + } else ck_free(own_copy); // 释放内存 - if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { + if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { // 检查默认路径 - target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; - return new_argv; + target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; // 设置目标路径 + return new_argv; // 返回新参数 } - FATAL("Unable to find 'afl-qemu-trace'."); + FATAL("Unable to find 'afl-qemu-trace'."); // 如果未找到,报错 } /* Main entry point */ -int main(int argc, char** argv) { +int main(int argc, char** argv) { // 主函数 s32 opt; u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0; char** use_argv; - doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; + doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; // 设置文档路径 - SAYF(cCYA "afl-analyze " cBRI VERSION cRST " by \n"); + SAYF(cCYA "afl-analyze " cBRI VERSION cRST " by \n"); // 输出版本信息 - while ((opt = getopt(argc,argv,"+i:f:m:t:eQV")) > 0) + while ((opt = getopt(argc,argv,"+i:f:m:t:eQV")) > 0) // 解析命令行参数 switch (opt) { - case 'i': + case 'i': // 输入文件 - if (in_file) FATAL("Multiple -i options not supported"); - in_file = optarg; + if (in_file) FATAL("Multiple -i options not supported"); // 如果多次指定,报错 + in_file = optarg; // 设置输入文件 break; - case 'f': + case 'f': // 目标程序输入文件 - if (prog_in) FATAL("Multiple -f options not supported"); - use_stdin = 0; - prog_in = optarg; + if (prog_in) FATAL("Multiple -f options not supported"); // 如果多次指定,报错 + use_stdin = 0; // 不使用stdin + prog_in = optarg; // 设置输入文件 break; - case 'e': + case 'e': // 只关注边缘覆盖率 - if (edges_only) FATAL("Multiple -e options not supported"); - edges_only = 1; + if (edges_only) FATAL("Multiple -e options not supported"); // 如果多次指定,报错 + edges_only = 1; // 设置只关注边缘覆盖率 break; - case 'm': { + case 'm': { // 内存限制 u8 suffix = 'M'; - if (mem_limit_given) FATAL("Multiple -m options not supported"); + if (mem_limit_given) FATAL("Multiple -m options not supported"); // 如果多次指定,报错 mem_limit_given = 1; - if (!strcmp(optarg, "none")) { + if (!strcmp(optarg, "none")) { // 如果设置为none - mem_limit = 0; + mem_limit = 0; // 不限制内存 break; } - if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -m"); + if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || // 解析内存限制 + optarg[0] == '-') FATAL("Bad syntax used for -m"); // 如果语法错误,报错 - switch (suffix) { + switch (suffix) { // 根据后缀调整内存限制 - case 'T': mem_limit *= 1024 * 1024; break; - case 'G': mem_limit *= 1024; break; - case 'k': mem_limit /= 1024; break; - case 'M': break; + 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"); + default: FATAL("Unsupported suffix or bad syntax for -m"); // 不支持的后缀,报错 } - if (mem_limit < 5) FATAL("Dangerously low value of -m"); + if (mem_limit < 5) FATAL("Dangerously low value of -m"); // 如果内存限制过低,报错 - if (sizeof(rlim_t) == 4 && mem_limit > 2000) - FATAL("Value of -m out of range on 32-bit systems"); + if (sizeof(rlim_t) == 4 && mem_limit > 2000) // 如果是32位系统且内存限制过大 + FATAL("Value of -m out of range on 32-bit systems"); // 报错 } break; - case 't': + case 't': // 超时时间 - if (timeout_given) FATAL("Multiple -t options not supported"); + if (timeout_given) FATAL("Multiple -t options not supported"); // 如果多次指定,报错 timeout_given = 1; - exec_tmout = atoi(optarg); + exec_tmout = atoi(optarg); // 设置超时时间 - if (exec_tmout < 10 || optarg[0] == '-') - FATAL("Dangerously low value of -t"); + if (exec_tmout < 10 || optarg[0] == '-') // 如果超时时间过小 + FATAL("Dangerously low value of -t"); // 报错 break; - case 'Q': + case 'Q': // QEMU模式 - if (qemu_mode) FATAL("Multiple -Q options not supported"); - if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; + if (qemu_mode) FATAL("Multiple -Q options not supported"); // 如果多次指定,报错 + if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; // 如果没有设置内存限制,使用默认值 - qemu_mode = 1; + qemu_mode = 1; // 设置QEMU模式 break; - case 'V': /* Show version number */ + case 'V': /* Show version number */ // 显示版本号 - /* Version number has been printed already, just quit. */ + /* Version number has been printed already, just quit. */ // 版本号已输出,退出 exit(0); default: - usage(argv[0]); + usage(argv[0]); // 显示用法提示 } - if (optind == argc || !in_file) usage(argv[0]); + if (optind == argc || !in_file) usage(argv[0]); // 如果没有目标程序或输入文件,显示用法提示 - use_hex_offsets = !!getenv("AFL_ANALYZE_HEX"); + use_hex_offsets = !!getenv("AFL_ANALYZE_HEX"); // 设置是否使用十六进制偏移量 - setup_shm(); - setup_signal_handlers(); + setup_shm(); // 设置共享内存 + setup_signal_handlers(); // 设置信号处理函数 - set_up_environment(); + set_up_environment(); // 设置环境 - find_binary(argv[optind]); - detect_file_args(argv + optind); + find_binary(argv[optind]); // 查找目标二进制文件 + detect_file_args(argv + optind); // 检测参数中的@@ - if (qemu_mode) - use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); + if (qemu_mode) // 如果是QEMU模式 + use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); // 获取QEMU参数 else - use_argv = argv + optind; + use_argv = argv + optind; // 否则直接使用参数 SAYF("\n"); - read_initial_file(); + read_initial_file(); // 读取初始文件 - ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", + ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", // 输出预运行信息 mem_limit, exec_tmout, edges_only ? ", edges only" : ""); - run_target(use_argv, in_data, in_len, 1); + run_target(use_argv, in_data, in_len, 1); // 运行目标程序 - if (child_timed_out) - FATAL("Target binary times out (adjusting -t may help)."); + if (child_timed_out) // 如果超时 + FATAL("Target binary times out (adjusting -t may help)."); // 报错 - if (!anything_set()) FATAL("No instrumentation detected."); + if (!anything_set()) FATAL("No instrumentation detected."); // 如果没有插桩,报错 - analyze(use_argv); + analyze(use_argv); // 分析输入文件 - OKF("We're done here. Have a nice day!\n"); + OKF("We're done here. Have a nice day!\n"); // 输出完成信息 - exit(0); - -} + exit(0); // 退出 +} \ No newline at end of file diff --git a/src/afl-as.c b/src/afl-as.c index 0e31b1c..567054d 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -36,379 +36,378 @@ */ -#define AFL_MAIN +#define AFL_MAIN // 定义主程序宏 -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" +#include "config.h" // 包含配置文件 +#include "types.h" // 包含类型定义 +#include "debug.h" // 包含调试工具 +#include "alloc-inl.h" // 包含内存分配工具 -#include "afl-as.h" +#include "afl-as.h" // 包含AFL汇编插桩头文件 -#include -#include -#include -#include -#include -#include -#include +#include // 标准输入输出 +#include // POSIX标准库 +#include // 标准库 +#include // 字符串处理 +#include // 时间处理 +#include // 字符处理 +#include // 文件控制 -#include -#include +#include // 进程等待 +#include // 时间处理 -static u8** as_params; /* Parameters passed to the real 'as' */ +static u8** as_params; // 传递给真实 'as' 的参数 -static u8* input_file; /* Originally specified input file */ -static u8* modified_file; /* Instrumented file for the real 'as' */ +static u8* input_file; // 原始输入文件 +static u8* modified_file; // 插桩后的文件 -static u8 be_quiet, /* Quiet mode (no stderr output) */ - clang_mode, /* Running in clang mode? */ - pass_thru, /* Just pass data through? */ - just_version, /* Just show version? */ - sanitizer; /* Using ASAN / MSAN */ +static u8 be_quiet, // 静默模式(不输出错误信息) + clang_mode, // 是否在clang模式下运行 + pass_thru, // 是否直接传递数据 + just_version, // 是否只显示版本 + sanitizer; // 是否使用ASAN / MSAN -static u32 inst_ratio = 100, /* Instrumentation probability (%) */ - as_par_cnt = 1; /* Number of params to 'as' */ +static u32 inst_ratio = 100, // 插桩概率(%) + as_par_cnt = 1; // 传递给 'as' 的参数数量 -/* 如果命令行中没有找到 --32 或 --64 参数,则默认对编译该工具时使用的模式进行插桩。 - 这不是完美的,但对于大多数使用场景来说已经足够了。 */ +// 如果命令行中没有找到 --32 或 --64 参数,则默认对编译该工具时使用的模式进行插桩。 +// 这不是完美的,但对于大多数使用场景来说已经足够了。 -#ifdef WORD_SIZE_64 +#ifdef WORD_SIZE_64 // 如果是64位系统 -static u8 use_64bit = 1; +static u8 use_64bit = 1; // 使用64位模式 -#else +#else // 否则 -static u8 use_64bit = 0; +static u8 use_64bit = 0; // 使用32位模式 -#ifdef __APPLE__ -# error "Sorry, 32-bit Apple platforms are not supported." +#ifdef __APPLE__ // 如果是苹果系统 +# error "Sorry, 32-bit Apple platforms are not supported." // 不支持32位苹果平台 #endif /* __APPLE__ */ #endif /* ^WORD_SIZE_64 */ -/* 检查并修改传递给 'as' 的参数。注意 GCC 总是将文件名作为最后一个参数传递给 'as', - 因此我们利用这一特性来简化代码。 */ +// 检查并修改传递给 'as' 的参数。注意 GCC 总是将文件名作为最后一个参数传递给 'as', +// 因此我们利用这一特性来简化代码。 static void edit_params(int argc, char** argv) { - u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS"); - u32 i; + u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS"); // 获取临时目录和AFL_AS环境变量 + u32 i; // 循环变量 -#ifdef __APPLE__ +#ifdef __APPLE__ // 如果是苹果系统 - u8 use_clang_as = 0; + u8 use_clang_as = 0; // 是否使用clang作为汇编器 - /* 在 MacOS X 上,Xcode cctool 'as' 驱动程序有点过时,无法处理由用户自己编译的较新版本的 clang - 生成的代码。详见此线程:http://goo.gl/HBWDtn. + // 在 MacOS X 上,Xcode cctool 'as' 驱动程序有点过时,无法处理由用户自己编译的较新版本的 clang + // 生成的代码。详见此线程:http://goo.gl/HBWDtn. - 为了绕过这一问题,当使用 clang 且未指定 AFL_AS 时,我们将实际调用 'clang -c' 而不是 'as -q' - 来编译汇编文件. + // 为了绕过这一问题,当使用 clang 且未指定 AFL_AS 时,我们将实际调用 'clang -c' 而不是 'as -q' + // 来编译汇编文件. - 虽然这两个工具不是命令行兼容的,但我们可以通过进行一些小的修改来让它们在某些情况下似乎可以很好地协同工作。 - 感谢 Nico Weber 提出这一思路。 */ + // 虽然这两个工具不是命令行兼容的,但我们可以通过进行一些小的修改来让它们在某些情况下似乎可以很好地协同工作。 + // 感谢 Nico Weber 提出这一思路。 - if (clang_mode && !afl_as) { + if (clang_mode && !afl_as) { // 如果是clang模式且没有指定AFL_AS - use_clang_as = 1; + use_clang_as = 1; // 使用clang作为汇编器 - afl_as = getenv("AFL_CC"); - if (!afl_as) afl_as = getenv("AFL_CXX"); - if (!afl_as) afl_as = "clang"; + afl_as = getenv("AFL_CC"); // 获取AFL_CC环境变量 + if (!afl_as) afl_as = getenv("AFL_CXX"); // 如果没有AFL_CC,则获取AFL_CXX + if (!afl_as) afl_as = "clang"; // 如果都没有,则默认使用clang } #endif /* __APPLE__ */ - /* 虽然这在文档中没有提及,但 GCC 实际上也使用 TEMP 和 TMP(当 TMPDIR 未设置时)。 - 我们需要检查这些非常规变量以正确处理 pass_thru 逻辑。 */ + // 虽然这在文档中没有提及,但 GCC 实际上也使用 TEMP 和 TMP(当 TMPDIR 未设置时)。 + // 我们需要检查这些非常规变量以正确处理 pass_thru 逻辑。 - if (!tmp_dir) tmp_dir = getenv("TEMP"); - if (!tmp_dir) tmp_dir = getenv("TMP"); - if (!tmp_dir) tmp_dir = "/tmp"; + if (!tmp_dir) tmp_dir = getenv("TEMP"); // 如果没有TMPDIR,则获取TEMP + if (!tmp_dir) tmp_dir = getenv("TMP"); // 如果没有TEMP,则获取TMP + if (!tmp_dir) tmp_dir = "/tmp"; // 如果都没有,则默认使用/tmp - as_params = ck_alloc((argc + 32) * sizeof(u8*)); + as_params = ck_alloc((argc + 32) * sizeof(u8*)); // 分配参数数组内存 - as_params[0] = afl_as ? afl_as : (u8*)"as"; + as_params[0] = afl_as ? afl_as : (u8*)"as"; // 设置第一个参数为AFL_AS或默认的as - as_params[argc] = 0; + as_params[argc] = 0; // 设置最后一个参数为NULL - for (i = 1; i < argc - 1; i++) { + for (i = 1; i < argc - 1; i++) { // 遍历参数 - if (!strcmp(argv[i], "--64")) use_64bit = 1; - else if (!strcmp(argv[i], "--32")) use_64bit = 0; + if (!strcmp(argv[i], "--64")) use_64bit = 1; // 如果参数是--64,则使用64位模式 + else if (!strcmp(argv[i], "--32")) use_64bit = 0; // 如果参数是--32,则使用32位模式 -#ifdef __APPLE__ +#ifdef __APPLE__ // 如果是苹果系统 - /* MacOS X 的情况有点不同... */ + // MacOS X 的情况有点不同... - if (!strcmp(argv[i], "-arch") && i + 1 < argc) { + if (!strcmp(argv[i], "-arch") && i + 1 < argc) { // 如果参数是-arch - if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1; - else if (!strcmp(argv[i + 1], "i386")) - FATAL("Sorry, 32-bit Apple platforms are not supported."); + if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1; // 如果架构是x86_64,则使用64位模式 + else if (!strcmp(argv[i + 1], "i386")) // 如果架构是i386 + FATAL("Sorry, 32-bit Apple platforms are not supported."); // 不支持32位苹果平台 } - /* 移除 Xcode 中设置特定上游汇编器的选项 */ + // 移除 Xcode 中设置特定上游汇编器的选项 - if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q"))) - continue; + if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q"))) // 如果是clang模式且参数是-q或-Q + continue; // 跳过这些参数 #endif /* __APPLE__ */ - as_params[as_par_cnt++] = argv[i]; + as_params[as_par_cnt++] = argv[i]; // 将参数添加到as_params数组中 } -#ifdef __APPLE__ +#ifdef __APPLE__ // 如果是苹果系统 - /* 当调用 clang 作为上游汇编器时,追加 -c -x assembler 选项并希望一切顺利。 */ + // 当调用 clang 作为上游汇编器时,追加 -c -x assembler 选项并希望一切顺利。 - if (use_clang_as) { + if (use_clang_as) { // 如果使用clang作为汇编器 - as_params[as_par_cnt++] = "-c"; - as_params[as_par_cnt++] = "-x"; - as_params[as_par_cnt++] = "assembler"; + as_params[as_par_cnt++] = "-c"; // 添加-c参数 + as_params[as_par_cnt++] = "-x"; // 添加-x参数 + as_params[as_par_cnt++] = "assembler"; // 添加assembler参数 } #endif /* __APPLE__ */ - input_file = argv[argc - 1]; + input_file = argv[argc - 1]; // 获取输入文件 - if (input_file[0] == '-') { + if (input_file[0] == '-') { // 如果输入文件是标准输入 - if (!strcmp(input_file + 1, "-version")) { - just_version = 1; - modified_file = input_file; - goto wrap_things_up; + if (!strcmp(input_file + 1, "-version")) { // 如果参数是-version + just_version = 1; // 设置只显示版本 + modified_file = input_file; // 设置修改后的文件为输入文件 + goto wrap_things_up; // 跳转到结束处理 } - if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)"); - else input_file = NULL; + if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)"); // 如果输入文件不是标准输入 + else input_file = NULL; // 否则设置为NULL - } else { + } else { // 如果输入文件不是标准输入 - /* 检查是否为标准调用,作为编译程序的一部分,而不是使用 gcc 对一个独立的 .s 文件进行编译。 - 这解决了在编译 NSS 时遇到的问题。 */ + // 检查是否为标准调用,作为编译程序的一部分,而不是使用 gcc 对一个独立的 .s 文件进行编译。 + // 这解决了在编译 NSS 时遇到的问题。 - if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) && - strncmp(input_file, "/var/tmp/", 9) && - strncmp(input_file, "/tmp/", 5)) pass_thru = 1; + if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) && // 如果输入文件不在临时目录 + strncmp(input_file, "/var/tmp/", 9) && // 且不在/var/tmp/ + strncmp(input_file, "/tmp/", 5)) pass_thru = 1; // 且不在/tmp/,则设置为pass_thru模式 } - modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(), + modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(), // 生成修改后的文件名 (u32)time(NULL)); -wrap_things_up: +wrap_things_up: // 结束处理 - as_params[as_par_cnt++] = modified_file; - as_params[as_par_cnt] = NULL; + as_params[as_par_cnt++] = modified_file; // 将修改后的文件添加到参数数组 + as_params[as_par_cnt] = NULL; // 设置最后一个参数为NULL } -/* 处理输入文件并生成 modified_file。在所有适当的位置插入插桩代码。 */ +// 处理输入文件并生成 modified_file。在所有适当的位置插入插桩代码。 static void add_instrumentation(void) { - static u8 line[MAX_LINE]; + static u8 line[MAX_LINE]; // 读取文件的缓冲区 - FILE* inf; - FILE* outf; - s32 outfd; - u32 ins_lines = 0; + FILE* inf; // 输入文件指针 + FILE* outf; // 输出文件指针 + s32 outfd; // 输出文件描述符 + u32 ins_lines = 0; // 插桩的行数 - u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0, + u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0, // 插桩状态标志 skip_intel = 0, skip_app = 0, instrument_next = 0; -#ifdef __APPLE__ +#ifdef __APPLE__ // 如果是苹果系统 - u8* colon_pos; + u8* colon_pos; // 冒号位置 #endif /* __APPLE__ */ - if (input_file) { + if (input_file) { // 如果有输入文件 - inf = fopen(input_file, "r"); - if (!inf) PFATAL("Unable to read '%s'", input_file); + inf = fopen(input_file, "r"); // 打开输入文件 + if (!inf) PFATAL("Unable to read '%s'", input_file); // 如果打开失败则报错 - } else inf = stdin; + } else inf = stdin; // 否则使用标准输入 - outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600); + outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600); // 打开输出文件 - if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file); + if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file); // 如果打开失败则报错 - outf = fdopen(outfd, "w"); + outf = fdopen(outfd, "w"); // 将文件描述符转换为文件指针 - if (!outf) PFATAL("fdopen() failed"); + if (!outf) PFATAL("fdopen() failed"); // 如果转换失败则报错 - while (fgets(line, MAX_LINE, inf)) { + while (fgets(line, MAX_LINE, inf)) { // 逐行读取输入文件 - /* 在某些情况下,我们希望在所有标签、宏、注释等之后再插入插桩跳板代码。 - 如果处于这一模式,且行以制表符开头,后跟一个字母,则现在插入跳板代码。 */ + // 在某些情况下,我们希望在所有标签、宏、注释等之后再插入插桩跳板代码。 + // 如果处于这一模式,且行以制表符开头,后跟一个字母,则现在插入跳板代码。 if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok && - instrument_next && line[0] == '\t' && isalpha(line[1])) { + instrument_next && line[0] == '\t' && isalpha(line[1])) { // 如果满足插桩条件 - fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, + fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, // 插入跳板代码 R(MAP_SIZE)); - instrument_next = 0; - ins_lines++; + instrument_next = 0; // 重置插桩标志 + ins_lines++; // 增加插桩行数 } - /* 输出实际的行,在 pass-thru 模式下结束操作。 */ + // 输出实际的行,在 pass-thru 模式下结束操作。 - fputs(line, outf); + fputs(line, outf); // 输出当前行 - if (pass_thru) continue; + if (pass_thru) continue; // 如果是pass-thru模式则跳过 - /* 现在开始真正的插桩操作。首先,我们只希望插桩 .text 部分。 - 因此,我们需要跟踪处理的汇编文件中各部分的状态,并据此设置 instr_ok。 */ + // 现在开始真正的插桩操作。首先,我们只希望插桩 .text 部分。 + // 因此,我们需要跟踪处理的汇编文件中各部分的状态,并据此设置 instr_ok。 - if (line[0] == '\t' && line[1] == '.') { + if (line[0] == '\t' && line[1] == '.') { // 如果行以制表符和点开头 - /* OpenBSD 在代码中直接放置跳转表,这稍微有点麻烦。 - 它们使用特定格式的 p2align 指令围绕它们,因此我们可以使用该格式作为信号。 */ + // OpenBSD 在代码中直接放置跳转表,这稍微有点麻烦。 + // 它们使用特定格式的 p2align 指令围绕它们,因此我们可以使用该格式作为信号。 - if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) && - isdigit(line[10]) && line[11] == '\n') skip_next_label = 1; + if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) && // 如果是OpenBSD的p2align指令 + isdigit(line[10]) && line[11] == '\n') skip_next_label = 1; // 设置跳过下一个标签 - if (!strncmp(line + 2, "text\n", 5) || - !strncmp(line + 2, "section\t.text", 13) || - !strncmp(line + 2, "section\t__TEXT,__text", 21) || - !strncmp(line + 2, "section __TEXT,__text", 21)) { - instr_ok = 1; - continue; + if (!strncmp(line + 2, "text\n", 5) || // 如果是.text部分 + !strncmp(line + 2, "section\t.text", 13) || // 或者section .text + !strncmp(line + 2, "section\t__TEXT,__text", 21) || // 或者section __TEXT,__text + !strncmp(line + 2, "section __TEXT,__text", 21)) { // 或者section __TEXT,__text + instr_ok = 1; // 设置插桩标志 + continue; // 继续 } - if (!strncmp(line + 2, "section\t", 8) || - !strncmp(line + 2, "section ", 8) || - !strncmp(line + 2, "bss\n", 4) || - !strncmp(line + 2, "data\n", 5)) { - instr_ok = 0; - continue; + if (!strncmp(line + 2, "section\t", 8) || // 如果是其他section + !strncmp(line + 2, "section ", 8) || // 或者其他section + !strncmp(line + 2, "bss\n", 4) || // 或者.bss部分 + !strncmp(line + 2, "data\n", 5)) { // 或者.data部分 + instr_ok = 0; // 重置插桩标志 + continue; // 继续 } } - /* 检测非常规汇编(罕见,例如在 gdb 中)。当遇到这种汇编时,我们设置 skip_csect - 直到遇到相反的指令,此时我们不进行插桩。 */ + // 检测非常规汇编(罕见,例如在 gdb 中)。当遇到这种汇编时,我们设置 skip_csect + // 直到遇到相反的指令,此时我们不进行插桩。 - if (strstr(line, ".code")) { + if (strstr(line, ".code")) { // 如果行包含.code - if (strstr(line, ".code32")) skip_csect = use_64bit; - if (strstr(line, ".code64")) skip_csect = !use_64bit; + if (strstr(line, ".code32")) skip_csect = use_64bit; // 如果是.code32,则根据64位模式设置skip_csect + if (strstr(line, ".code64")) skip_csect = !use_64bit; // 如果是.code64,则根据64位模式设置skip_csect } - /* 检测并跳过手写汇编块(__asm__),同样不进行插桩。 */ + // 检测并跳过手写汇编块(__asm__),同样不进行插桩。 - if (line[0] == '#' || line[1] == '#') { + if (line[0] == '#' || line[1] == '#') { // 如果行以#开头 - if (strstr(line, "#APP")) skip_app = 1; - if (strstr(line, "#NO_APP")) skip_app = 0; + if (strstr(line, "#APP")) skip_app = 1; // 如果包含#APP,则设置skip_app + if (strstr(line, "#NO_APP")) skip_app = 0; // 如果包含#NO_APP,则重置skip_app } - /* 如果我们处于插桩模式,检查函数名或条件标签。这里逻辑有些复杂,但基本目标是捕获: + // 如果我们处于插桩模式,检查函数名或条件标签。这里逻辑有些复杂,但基本目标是捕获: - ^main: - 函数入口点(总是插桩) - ^.L0: - GCC 分支标签 - ^.LBB0_0: - clang 分支标签(但仅在 clang 模式下) - ^\tjnz foo - 条件分支 + // ^main: - 函数入口点(总是插桩) + // ^.L0: - GCC 分支标签 + // ^.LBB0_0: - clang 分支标签(但仅在 clang 模式下) + // ^\tjnz foo - 条件分支 - ...而不捕获: + // ...而不捕获: - ^# BB#0: - clang 注释 - ^ # BB#0: - 同上 - ^.Ltmp0: - clang 非分支标签 - ^.LC0 - GCC 非分支标签 - ^.LBB0_0: - 同上(当处于 GCC 模式下) - ^\tjmp foo - 非条件跳转 + // ^# BB#0: - clang 注释 + // ^ # BB#0: - 同上 + // ^.Ltmp0: - clang 非分支标签 + // ^.LC0 - GCC 非分支标签 + // ^.LBB0_0: - 同上(当处于 GCC 模式下) + // ^\tjmp foo - 非条件跳转 - 此外,MacOS X 上的 clang 和 GCC 使用不同的标签格式,没有前导点,因此我们根据这一情况处理。 - */ + // 此外,MacOS X 上的 clang 和 GCC 使用不同的标签格式,没有前导点,因此我们根据这一情况处理。 - if (skip_intel || skip_app || skip_csect || !instr_ok || - line[0] == '#' || line[0] == ' ') continue; + if (skip_intel || skip_app || skip_csect || !instr_ok || // 如果跳过插桩 + line[0] == '#' || line[0] == ' ') continue; // 或者行以#或空格开头,则跳过 - /* 条件分支指令(jnz 等)。我们会在分支之后插入插桩(以插桩不执行路径), - 并在分支目标标签处插入(稍后处理)。 */ + // 条件分支指令(jnz 等)。我们会在分支之后插入插桩(以插桩不执行路径), + // 并在分支目标标签处插入(稍后处理)。 - if (line[0] == '\t') { + if (line[0] == '\t') { // 如果行以制表符开头 - if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) { + if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) { // 如果是条件分支指令 - fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, + fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, // 插入跳板代码 R(MAP_SIZE)); - ins_lines++; + ins_lines++; // 增加插桩行数 } - continue; + continue; // 继续 } - /* 某类标签。这可能是分支目标,但我们需要小心处理不同的格式约定。 */ + // 某类标签。这可能是分支目标,但我们需要小心处理不同的格式约定。 -#ifdef __APPLE__ +#ifdef __APPLE__ // 如果是苹果系统 - /* MacOS X: L: */ + // MacOS X: L: - if ((colon_pos = strstr(line, ":"))) { + if ((colon_pos = strstr(line, ":"))) { // 如果行包含冒号 - if (line[0] == 'L' && isdigit(*(colon_pos - 1))) { + if (line[0] == 'L' && isdigit(*(colon_pos - 1))) { // 如果标签以L开头且冒号前是数字 -#else +#else // 否则 - /* 其他人:.L: */ + // 其他人:.L: - if (strstr(line, ":")) { + if (strstr(line, ":")) { // 如果行包含冒号 - if (line[0] == '.') { + if (line[0] == '.') { // 如果标签以点开头 #endif /* __APPLE__ */ - /* .L0: 或 LBB0_0: 风格的分支目标 */ + // .L0: 或 LBB0_0: 风格的分支目标 -#ifdef __APPLE__ +#ifdef __APPLE__ // 如果是苹果系统 - /* MacOS X: L / LBB */ + // MacOS X: L / LBB - if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) - && R(100) < inst_ratio) { + if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) // 如果标签是L或LBB + && R(100) < inst_ratio) { // 并且随机数小于插桩概率 -#else +#else // 否则 - /* MacOS X: .L / .LBB */ + // MacOS X: .L / .LBB - if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3))) - && R(100) < inst_ratio) { + if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3))) // 如果标签是.L或.LBB + && R(100) < inst_ratio) { // 并且随机数小于插桩概率 #endif /* __APPLE__ */ - /* 在仅需要在标签被引用时(非调用/跳转上下文)才添加代码的情况下可以进行优化。 - 这会引入两遍处理过程的复杂性(当使用 stdin 时尤其麻烦),并且通常只能带来不到 10% 的速度提升。 - 因为编译器通常不会生成不相关的函数内跳转。 + // 在仅需要在标签被引用时(非调用/跳转上下文)才添加代码的情况下可以进行优化。 + // 这会引入两遍处理过程的复杂性(当使用 stdin 时尤其麻烦),并且通常只能带来不到 10% 的速度提升。 + // 因为编译器通常不会生成不相关的函数内跳转。 - 我们使用延迟输出主要是为了避免干扰 MacOS X 上 .Lfunc_begin0 风格异常处理计算的问题。 */ + // 我们使用延迟输出主要是为了避免干扰 MacOS X 上 .Lfunc_begin0 风格异常处理计算的问题。 - if (!skip_next_label) instrument_next = 1; else skip_next_label = 0; + if (!skip_next_label) instrument_next = 1; else skip_next_label = 0; // 设置插桩标志 } - } else { + } else { // 否则 - /* 函数标签(总是插桩,延迟模式)。 */ + // 函数标签(总是插桩,延迟模式)。 - instrument_next = 1; + instrument_next = 1; // 设置插桩标志 } @@ -416,17 +415,17 @@ static void add_instrumentation(void) { } - if (ins_lines) - fputs(use_64bit ? main_payload_64 : main_payload_32, outf); + if (ins_lines) // 如果有插桩行 + fputs(use_64bit ? main_payload_64 : main_payload_32, outf); // 插入主插桩代码 - if (input_file) fclose(inf); - fclose(outf); + if (input_file) fclose(inf); // 关闭输入文件 + fclose(outf); // 关闭输出文件 - if (!be_quiet) { + if (!be_quiet) { // 如果不是静默模式 - if (!ins_lines) WARNF("No instrumentation targets found%s.", + if (!ins_lines) WARNF("No instrumentation targets found%s.", // 如果没有插桩目标 pass_thru ? " (pass-thru mode)" : ""); - else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", + else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", // 输出插桩信息 ins_lines, use_64bit ? "64" : "32", getenv("AFL_HARDEN") ? "hardened" : (sanitizer ? "ASAN/MSAN" : "non-hardened"), @@ -437,27 +436,27 @@ static void add_instrumentation(void) { } -/* 程序主入口点 */ +// 程序主入口点 int main(int argc, char** argv) { - s32 pid; - u32 rand_seed; - int status; - u8* inst_ratio_str = getenv("AFL_INST_RATIO"); + s32 pid; // 进程ID + u32 rand_seed; // 随机种子 + int status; // 进程状态 + u8* inst_ratio_str = getenv("AFL_INST_RATIO"); // 获取插桩概率环境变量 - struct timeval tv; - struct timezone tz; + struct timeval tv; // 时间结构 + struct timezone tz; // 时区结构 - clang_mode = !!getenv(CLANG_ENV_VAR); + clang_mode = !!getenv(CLANG_ENV_VAR); // 设置clang模式 - if (isatty(2) && !getenv("AFL_QUIET")) { + if (isatty(2) && !getenv("AFL_QUIET")) { // 如果是终端且没有设置AFL_QUIET - SAYF(cCYA "afl-as " cBRI VERSION cRST " by \n"); + SAYF(cCYA "afl-as " cBRI VERSION cRST " by \n"); // 输出版本信息 - } else be_quiet = 1; + } else be_quiet = 1; // 否则设置为静默模式 - if (argc < 2) { + if (argc < 2) { // 如果参数少于2个 SAYF("\n" "This is a helper application for afl-fuzz. It is a wrapper around GNU 'as',\n" @@ -466,55 +465,55 @@ int main(int argc, char** argv) { "Rarely, when dealing with extremely complex projects, it may be advisable to\n" "set AFL_INST_RATIO to a value less than 100 in order to reduce the odds of\n" - "instrumenting every discovered branch.\n\n"); + "instrumenting every discovered branch.\n\n"); // 输出帮助信息 - exit(1); + exit(1); // 退出 } - gettimeofday(&tv, &tz); + gettimeofday(&tv, &tz); // 获取当前时间 - rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); + rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); // 生成随机种子 - srandom(rand_seed); + srandom(rand_seed); // 设置随机种子 - edit_params(argc, argv); + edit_params(argc, argv); // 编辑参数 - if (inst_ratio_str) { + if (inst_ratio_str) { // 如果有插桩概率环境变量 - if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100) - FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)"); + if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100) // 如果解析失败或大于100 + FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)"); // 报错 } - if (getenv(AS_LOOP_ENV_VAR)) - FATAL("Endless loop when calling 'as' (remove '.' from your PATH)"); + if (getenv(AS_LOOP_ENV_VAR)) // 如果设置了AS_LOOP_ENV_VAR + FATAL("Endless loop when calling 'as' (remove '.' from your PATH)"); // 报错 - setenv(AS_LOOP_ENV_VAR, "1", 1); + setenv(AS_LOOP_ENV_VAR, "1", 1); // 设置AS_LOOP_ENV_VAR - /* 使用 ASAN 时,我们没有特别优雅的方法来跳过 ASAN 特定的分支。 - 但可以通过按概率补偿来解决这个问题... */ + // 使用 ASAN 时,我们没有特别优雅的方法来跳过 ASAN 特定的分支。 + // 但可以通过按概率补偿来解决这个问题... - if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) { - sanitizer = 1; - inst_ratio /= 3; + if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) { // 如果使用ASAN或MSAN + sanitizer = 1; // 设置sanitizer标志 + inst_ratio /= 3; // 降低插桩概率 } - if (!just_version) add_instrumentation(); + if (!just_version) add_instrumentation(); // 如果不是只显示版本,则进行插桩 - if (!(pid = fork())) { + if (!(pid = fork())) { // 创建子进程 - execvp(as_params[0], (char**)as_params); - FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]); + execvp(as_params[0], (char**)as_params); // 执行as命令 + FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]); // 如果执行失败则报错 } - if (pid < 0) PFATAL("fork() failed"); + if (pid < 0) PFATAL("fork() failed"); // 如果fork失败则报错 - if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); // 等待子进程结束 - if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file); + if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file); // 如果没有设置AFL_KEEP_ASSEMBLY,则删除修改后的文件 - exit(WEXITSTATUS(status)); + exit(WEXITSTATUS(status)); // 退出 } \ No newline at end of file diff --git a/src/afl-as.h b/src/afl-as.h index 276b8a4..3275a70 100644 --- a/src/afl-as.h +++ b/src/afl-as.h @@ -39,11 +39,11 @@ */ -#ifndef _HAVE_AFL_AS_H -#define _HAVE_AFL_AS_H +#ifndef _HAVE_AFL_AS_H // 如果没有定义 _HAVE_AFL_AS_H +#define _HAVE_AFL_AS_H // 定义 _HAVE_AFL_AS_H -#include "config.h" -#include "types.h" +#include "config.h" // 包含配置文件 +#include "types.h" // 包含类型定义 /* ------------------ @@ -112,264 +112,264 @@ */ -static const u8* trampoline_fmt_32 = +static const u8* trampoline_fmt_32 = // 32位跳板代码模板 "\n" - "/* --- AFL TRAMPOLINE (32-BIT) --- */\n" + "/* --- AFL TRAMPOLINE (32-BIT) --- */\n" // 32位跳板代码开始 "\n" - ".align 4\n" + ".align 4\n" // 对齐到4字节 "\n" - "leal -16(%%esp), %%esp\n" - "movl %%edi, 0(%%esp)\n" - "movl %%edx, 4(%%esp)\n" - "movl %%ecx, 8(%%esp)\n" - "movl %%eax, 12(%%esp)\n" - "movl $0x%08x, %%ecx\n" - "call __afl_maybe_log\n" - "movl 12(%%esp), %%eax\n" - "movl 8(%%esp), %%ecx\n" - "movl 4(%%esp), %%edx\n" - "movl 0(%%esp), %%edi\n" - "leal 16(%%esp), %%esp\n" + "leal -16(%%esp), %%esp\n" // 调整栈指针,预留16字节空间 + "movl %%edi, 0(%%esp)\n" // 保存edi寄存器到栈 + "movl %%edx, 4(%%esp)\n" // 保存edx寄存器到栈 + "movl %%ecx, 8(%%esp)\n" // 保存ecx寄存器到栈 + "movl %%eax, 12(%%esp)\n" // 保存eax寄存器到栈 + "movl $0x%08x, %%ecx\n" // 将当前分支位置ID存入ecx + "call __afl_maybe_log\n" // 调用日志记录函数 + "movl 12(%%esp), %%eax\n" // 恢复eax寄存器 + "movl 8(%%esp), %%ecx\n" // 恢复ecx寄存器 + "movl 4(%%esp), %%edx\n" // 恢复edx寄存器 + "movl 0(%%esp), %%edi\n" // 恢复edi寄存器 + "leal 16(%%esp), %%esp\n" // 恢复栈指针 "\n" - "/* --- END --- */\n" + "/* --- END --- */\n" // 32位跳板代码结束 "\n"; -static const u8* trampoline_fmt_64 = +static const u8* trampoline_fmt_64 = // 64位跳板代码模板 "\n" - "/* --- AFL TRAMPOLINE (64-BIT) --- */\n" + "/* --- AFL TRAMPOLINE (64-BIT) --- */\n" // 64位跳板代码开始 "\n" - ".align 4\n" + ".align 4\n" // 对齐到4字节 "\n" - "leaq -(128+24)(%%rsp), %%rsp\n" - "movq %%rdx, 0(%%rsp)\n" - "movq %%rcx, 8(%%rsp)\n" - "movq %%rax, 16(%%rsp)\n" - "movq $0x%08x, %%rcx\n" - "call __afl_maybe_log\n" - "movq 16(%%rsp), %%rax\n" - "movq 8(%%rsp), %%rcx\n" - "movq 0(%%rsp), %%rdx\n" - "leaq (128+24)(%%rsp), %%rsp\n" + "leaq -(128+24)(%%rsp), %%rsp\n" // 调整栈指针,预留152字节空间 + "movq %%rdx, 0(%%rsp)\n" // 保存rdx寄存器到栈 + "movq %%rcx, 8(%%rsp)\n" // 保存rcx寄存器到栈 + "movq %%rax, 16(%%rsp)\n" // 保存rax寄存器到栈 + "movq $0x%08x, %%rcx\n" // 将当前分支位置ID存入rcx + "call __afl_maybe_log\n" // 调用日志记录函数 + "movq 16(%%rsp), %%rax\n" // 恢复rax寄存器 + "movq 8(%%rsp), %%rcx\n" // 恢复rcx寄存器 + "movq 0(%%rsp), %%rdx\n" // 恢复rdx寄存器 + "leaq (128+24)(%%rsp), %%rsp\n" // 恢复栈指针 "\n" - "/* --- END --- */\n" + "/* --- END --- */\n" // 64位跳板代码结束 "\n"; -static const u8* main_payload_32 = +static const u8* main_payload_32 = // 32位主插桩代码 "\n" - "/* --- AFL MAIN PAYLOAD (32-BIT) --- */\n" + "/* --- AFL MAIN PAYLOAD (32-BIT) --- */\n" // 32位主插桩代码开始 "\n" - ".text\n" - ".att_syntax\n" - ".code32\n" - ".align 8\n" + ".text\n" // 代码段 + ".att_syntax\n" // AT&T语法 + ".code32\n" // 32位代码 + ".align 8\n" // 对齐到8字节 "\n" - "__afl_maybe_log:\n" + "__afl_maybe_log:\n" // 日志记录函数 "\n" - " lahf\n" - " seto %al\n" + " lahf\n" // 将标志寄存器加载到AH + " seto %al\n" // 设置溢出标志到AL "\n" - " /* Check if SHM region is already mapped. */\n" + " /* Check if SHM region is already mapped. */\n" // 检查SHM区域是否已映射 "\n" - " movl __afl_area_ptr, %edx\n" - " testl %edx, %edx\n" - " je __afl_setup\n" + " movl __afl_area_ptr, %edx\n" // 加载SHM区域指针到EDX + " testl %edx, %edx\n" // 测试EDX是否为0 + " je __afl_setup\n" // 如果为0,跳转到设置代码 "\n" - "__afl_store:\n" + "__afl_store:\n" // 存储命中信息 "\n" " /* Calculate and store hit for the code location specified in ecx. There\n" " is a double-XOR way of doing this without tainting another register,\n" " and we use it on 64-bit systems; but it's slower for 32-bit ones. */\n" "\n" -#ifndef COVERAGE_ONLY - " movl __afl_prev_loc, %edi\n" - " xorl %ecx, %edi\n" - " shrl $1, %ecx\n" - " movl %ecx, __afl_prev_loc\n" -#else - " movl %ecx, %edi\n" +#ifndef COVERAGE_ONLY // 如果不是仅覆盖率模式 + " movl __afl_prev_loc, %edi\n" // 加载前一个位置到EDI + " xorl %ecx, %edi\n" // 计算当前和前一个位置的XOR + " shrl $1, %ecx\n" // 右移ECX,减少冲突 + " movl %ecx, __afl_prev_loc\n" // 存储当前位置 +#else // 否则 + " movl %ecx, %edi\n" // 直接使用ECX #endif /* ^!COVERAGE_ONLY */ "\n" -#ifdef SKIP_COUNTS - " orb $1, (%edx, %edi, 1)\n" -#else - " incb (%edx, %edi, 1)\n" +#ifdef SKIP_COUNTS // 如果跳过计数 + " orb $1, (%edx, %edi, 1)\n" // 设置位 +#else // 否则 + " incb (%edx, %edi, 1)\n" // 增加计数 #endif /* ^SKIP_COUNTS */ "\n" - "__afl_return:\n" + "__afl_return:\n" // 返回 "\n" - " addb $127, %al\n" - " sahf\n" - " ret\n" + " addb $127, %al\n" // 恢复溢出标志 + " sahf\n" // 将AH存储回标志寄存器 + " ret\n" // 返回 "\n" - ".align 8\n" + ".align 8\n" // 对齐到8字节 "\n" - "__afl_setup:\n" + "__afl_setup:\n" // 设置代码 "\n" - " /* Do not retry setup if we had previous failures. */\n" + " /* Do not retry setup if we had previous failures. */\n" // 如果之前失败,不再重试 "\n" - " cmpb $0, __afl_setup_failure\n" - " jne __afl_return\n" + " cmpb $0, __afl_setup_failure\n" // 检查设置失败标志 + " jne __afl_return\n" // 如果失败,跳转到返回 "\n" " /* Map SHM, jumping to __afl_setup_abort if something goes wrong.\n" " We do not save FPU/MMX/SSE registers here, but hopefully, nobody\n" - " will notice this early in the game. */\n" + " will notice this early in the game. */\n" // 映射SHM区域 "\n" - " pushl %eax\n" - " pushl %ecx\n" + " pushl %eax\n" // 保存EAX + " pushl %ecx\n" // 保存ECX "\n" - " pushl $.AFL_SHM_ENV\n" - " call getenv\n" - " addl $4, %esp\n" + " pushl $.AFL_SHM_ENV\n" // 压入SHM环境变量名 + " call getenv\n" // 调用getenv + " addl $4, %esp\n" // 恢复栈 "\n" - " testl %eax, %eax\n" - " je __afl_setup_abort\n" + " testl %eax, %eax\n" // 检查返回值 + " je __afl_setup_abort\n" // 如果为0,跳转到设置失败 "\n" - " pushl %eax\n" - " call atoi\n" - " addl $4, %esp\n" + " pushl %eax\n" // 保存返回值 + " call atoi\n" // 调用atoi + " addl $4, %esp\n" // 恢复栈 "\n" - " pushl $0 /* shmat flags */\n" - " pushl $0 /* requested addr */\n" - " pushl %eax /* SHM ID */\n" - " call shmat\n" - " addl $12, %esp\n" + " pushl $0 /* shmat flags */\n" // 压入shmat标志 + " pushl $0 /* requested addr */\n" // 压入请求地址 + " pushl %eax /* SHM ID */\n" // 压入SHM ID + " call shmat\n" // 调用shmat + " addl $12, %esp\n" // 恢复栈 "\n" - " cmpl $-1, %eax\n" - " je __afl_setup_abort\n" + " cmpl $-1, %eax\n" // 检查返回值 + " je __afl_setup_abort\n" // 如果为-1,跳转到设置失败 "\n" - " /* Store the address of the SHM region. */\n" + " /* Store the address of the SHM region. */\n" // 存储SHM区域地址 "\n" - " movl %eax, __afl_area_ptr\n" - " movl %eax, %edx\n" + " movl %eax, __afl_area_ptr\n" // 存储到全局变量 + " movl %eax, %edx\n" // 存储到EDX "\n" - " popl %ecx\n" - " popl %eax\n" + " popl %ecx\n" // 恢复ECX + " popl %eax\n" // 恢复EAX "\n" - "__afl_forkserver:\n" + "__afl_forkserver:\n" // 进入fork服务器模式 "\n" - " /* Enter the fork server mode to avoid the overhead of execve() calls. */\n" + " /* Enter the fork server mode to avoid the overhead of execve() calls. */\n" // 进入fork服务器模式 "\n" - " pushl %eax\n" - " pushl %ecx\n" - " pushl %edx\n" + " pushl %eax\n" // 保存EAX + " pushl %ecx\n" // 保存ECX + " pushl %edx\n" // 保存EDX "\n" " /* Phone home and tell the parent that we're OK. (Note that signals with\n" " no SA_RESTART will mess it up). If this fails, assume that the fd is\n" " closed because we were execve()d from an instrumented binary, or because\n" - " the parent doesn't want to use the fork server. */\n" + " the parent doesn't want to use the fork server. */\n" // 通知父进程 "\n" - " pushl $4 /* length */\n" - " pushl $__afl_temp /* data */\n" - " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" - " call write\n" - " addl $12, %esp\n" + " pushl $4 /* length */\n" // 压入长度 + " pushl $__afl_temp /* data */\n" // 压入数据 + " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" // 压入文件描述符 + " call write\n" // 调用write + " addl $12, %esp\n" // 恢复栈 "\n" - " cmpl $4, %eax\n" - " jne __afl_fork_resume\n" + " cmpl $4, %eax\n" // 检查返回值 + " jne __afl_fork_resume\n" // 如果不等于4,跳转到恢复 "\n" - "__afl_fork_wait_loop:\n" + "__afl_fork_wait_loop:\n" // fork等待循环 "\n" - " /* Wait for parent by reading from the pipe. Abort if read fails. */\n" + " /* Wait for parent by reading from the pipe. Abort if read fails. */\n" // 等待父进程 "\n" - " pushl $4 /* length */\n" - " pushl $__afl_temp /* data */\n" - " pushl $" STRINGIFY(FORKSRV_FD) " /* file desc */\n" - " call read\n" - " addl $12, %esp\n" + " pushl $4 /* length */\n" // 压入长度 + " pushl $__afl_temp /* data */\n" // 压入数据 + " pushl $" STRINGIFY(FORKSRV_FD) " /* file desc */\n" // 压入文件描述符 + " call read\n" // 调用read + " addl $12, %esp\n" // 恢复栈 "\n" - " cmpl $4, %eax\n" - " jne __afl_die\n" + " cmpl $4, %eax\n" // 检查返回值 + " jne __afl_die\n" // 如果不等于4,跳转到死亡 "\n" " /* Once woken up, create a clone of our process. This is an excellent use\n" " case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly\n" " caches getpid() results and offers no way to update the value, breaking\n" - " abort(), raise(), and a bunch of other things :-( */\n" + " abort(), raise(), and a bunch of other things :-( */\n" // 创建子进程 "\n" - " call fork\n" + " call fork\n" // 调用fork "\n" - " cmpl $0, %eax\n" - " jl __afl_die\n" - " je __afl_fork_resume\n" + " cmpl $0, %eax\n" // 检查返回值 + " jl __afl_die\n" // 如果小于0,跳转到死亡 + " je __afl_fork_resume\n" // 如果等于0,跳转到恢复 "\n" - " /* In parent process: write PID to pipe, then wait for child. */\n" + " /* In parent process: write PID to pipe, then wait for child. */\n" // 在父进程中 "\n" - " movl %eax, __afl_fork_pid\n" + " movl %eax, __afl_fork_pid\n" // 存储子进程PID "\n" - " pushl $4 /* length */\n" - " pushl $__afl_fork_pid /* data */\n" - " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" - " call write\n" - " addl $12, %esp\n" + " pushl $4 /* length */\n" // 压入长度 + " pushl $__afl_fork_pid /* data */\n" // 压入数据 + " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" // 压入文件描述符 + " call write\n" // 调用write + " addl $12, %esp\n" // 恢复栈 "\n" - " pushl $0 /* no flags */\n" - " pushl $__afl_temp /* status */\n" - " pushl __afl_fork_pid /* PID */\n" - " call waitpid\n" - " addl $12, %esp\n" + " pushl $0 /* no flags */\n" // 压入标志 + " pushl $__afl_temp /* status */\n" // 压入状态 + " pushl __afl_fork_pid /* PID */\n" // 压入PID + " call waitpid\n" // 调用waitpid + " addl $12, %esp\n" // 恢复栈 "\n" - " cmpl $0, %eax\n" - " jle __afl_die\n" + " cmpl $0, %eax\n" // 检查返回值 + " jle __afl_die\n" // 如果小于等于0,跳转到死亡 "\n" - " /* Relay wait status to pipe, then loop back. */\n" + " /* Relay wait status to pipe, then loop back. */\n" // 传递等待状态 "\n" - " pushl $4 /* length */\n" - " pushl $__afl_temp /* data */\n" - " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" - " call write\n" - " addl $12, %esp\n" + " pushl $4 /* length */\n" // 压入长度 + " pushl $__afl_temp /* data */\n" // 压入数据 + " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" // 压入文件描述符 + " call write\n" // 调用write + " addl $12, %esp\n" // 恢复栈 "\n" - " jmp __afl_fork_wait_loop\n" + " jmp __afl_fork_wait_loop\n" // 跳转到等待循环 "\n" - "__afl_fork_resume:\n" + "__afl_fork_resume:\n" // 恢复 "\n" - " /* In child process: close fds, resume execution. */\n" + " /* In child process: close fds, resume execution. */\n" // 在子进程中 "\n" - " pushl $" STRINGIFY(FORKSRV_FD) "\n" - " call close\n" + " pushl $" STRINGIFY(FORKSRV_FD) "\n" // 压入文件描述符 + " call close\n" // 调用close "\n" - " pushl $" STRINGIFY((FORKSRV_FD + 1)) "\n" - " call close\n" + " pushl $" STRINGIFY((FORKSRV_FD + 1)) "\n" // 压入文件描述符 + " call close\n" // 调用close "\n" - " addl $8, %esp\n" + " addl $8, %esp\n" // 恢复栈 "\n" - " popl %edx\n" - " popl %ecx\n" - " popl %eax\n" - " jmp __afl_store\n" + " popl %edx\n" // 恢复EDX + " popl %ecx\n" // 恢复ECX + " popl %eax\n" // 恢复EAX + " jmp __afl_store\n" // 跳转到存储 "\n" - "__afl_die:\n" + "__afl_die:\n" // 死亡 "\n" - " xorl %eax, %eax\n" - " call _exit\n" + " xorl %eax, %eax\n" // 清零EAX + " call _exit\n" // 调用_exit "\n" - "__afl_setup_abort:\n" + "__afl_setup_abort:\n" // 设置失败 "\n" " /* Record setup failure so that we don't keep calling\n" - " shmget() / shmat() over and over again. */\n" + " shmget() / shmat() over and over again. */\n" // 记录设置失败 "\n" - " incb __afl_setup_failure\n" - " popl %ecx\n" - " popl %eax\n" - " jmp __afl_return\n" + " incb __afl_setup_failure\n" // 增加设置失败标志 + " popl %ecx\n" // 恢复ECX + " popl %eax\n" // 恢复EAX + " jmp __afl_return\n" // 跳转到返回 "\n" - ".AFL_VARS:\n" + ".AFL_VARS:\n" // AFL变量 "\n" - " .comm __afl_area_ptr, 4, 32\n" - " .comm __afl_setup_failure, 1, 32\n" -#ifndef COVERAGE_ONLY - " .comm __afl_prev_loc, 4, 32\n" + " .comm __afl_area_ptr, 4, 32\n" // 定义全局变量 + " .comm __afl_setup_failure, 1, 32\n" // 定义全局变量 +#ifndef COVERAGE_ONLY // 如果不是仅覆盖率模式 + " .comm __afl_prev_loc, 4, 32\n" // 定义全局变量 #endif /* !COVERAGE_ONLY */ - " .comm __afl_fork_pid, 4, 32\n" - " .comm __afl_temp, 4, 32\n" + " .comm __afl_fork_pid, 4, 32\n" // 定义全局变量 + " .comm __afl_temp, 4, 32\n" // 定义全局变量 "\n" - ".AFL_SHM_ENV:\n" - " .asciz \"" SHM_ENV_VAR "\"\n" + ".AFL_SHM_ENV:\n" // SHM环境变量 + " .asciz \"" SHM_ENV_VAR "\"\n" // 定义字符串 "\n" - "/* --- END --- */\n" + "/* --- END --- */\n" // 32位主插桩代码结束 "\n"; /* The OpenBSD hack is due to lahf and sahf not being recognized by some @@ -380,348 +380,348 @@ static const u8* main_payload_32 = to work around the crash issue with .lcomm and the fact that they don't recognize .string. */ -#ifdef __APPLE__ -# define CALL_L64(str) "call _" str "\n" -#else -# define CALL_L64(str) "call " str "@PLT\n" +#ifdef __APPLE__ // 如果是苹果系统 +# define CALL_L64(str) "call _" str "\n" // 定义调用宏 +#else // 否则 +# define CALL_L64(str) "call " str "@PLT\n" // 定义调用宏 #endif /* ^__APPLE__ */ -static const u8* main_payload_64 = +static const u8* main_payload_64 = // 64位主插桩代码 "\n" - "/* --- AFL MAIN PAYLOAD (64-BIT) --- */\n" + "/* --- AFL MAIN PAYLOAD (64-BIT) --- */\n" // 64位主插桩代码开始 "\n" - ".text\n" - ".att_syntax\n" - ".code64\n" - ".align 8\n" + ".text\n" // 代码段 + ".att_syntax\n" // AT&T语法 + ".code64\n" // 64位代码 + ".align 8\n" // 对齐到8字节 "\n" - "__afl_maybe_log:\n" + "__afl_maybe_log:\n" // 日志记录函数 "\n" -#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9)) - " .byte 0x9f /* lahf */\n" -#else - " lahf\n" +#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9)) // 如果是OpenBSD或旧版FreeBSD + " .byte 0x9f /* lahf */\n" // 使用字节码 +#else // 否则 + " lahf\n" // 加载标志寄存器 #endif /* ^__OpenBSD__, etc */ - " seto %al\n" + " seto %al\n" // 设置溢出标志 "\n" - " /* Check if SHM region is already mapped. */\n" + " /* Check if SHM region is already mapped. */\n" // 检查SHM区域是否已映射 "\n" - " movq __afl_area_ptr(%rip), %rdx\n" - " testq %rdx, %rdx\n" - " je __afl_setup\n" + " movq __afl_area_ptr(%rip), %rdx\n" // 加载SHM区域指针 + " testq %rdx, %rdx\n" // 测试RDX是否为0 + " je __afl_setup\n" // 如果为0,跳转到设置 "\n" - "__afl_store:\n" + "__afl_store:\n" // 存储命中信息 "\n" - " /* Calculate and store hit for the code location specified in rcx. */\n" + " /* Calculate and store hit for the code location specified in rcx. */\n" // 计算并存储命中信息 "\n" -#ifndef COVERAGE_ONLY - " xorq __afl_prev_loc(%rip), %rcx\n" - " xorq %rcx, __afl_prev_loc(%rip)\n" - " shrq $1, __afl_prev_loc(%rip)\n" +#ifndef COVERAGE_ONLY // 如果不是仅覆盖率模式 + " xorq __afl_prev_loc(%rip), %rcx\n" // 计算XOR + " xorq %rcx, __afl_prev_loc(%rip)\n" // 更新前一个位置 + " shrq $1, __afl_prev_loc(%rip)\n" // 右移减少冲突 #endif /* ^!COVERAGE_ONLY */ "\n" -#ifdef SKIP_COUNTS - " orb $1, (%rdx, %rcx, 1)\n" -#else - " incb (%rdx, %rcx, 1)\n" +#ifdef SKIP_COUNTS // 如果跳过计数 + " orb $1, (%rdx, %rcx, 1)\n" // 设置位 +#else // 否则 + " incb (%rdx, %rcx, 1)\n" // 增加计数 #endif /* ^SKIP_COUNTS */ "\n" - "__afl_return:\n" + "__afl_return:\n" // 返回 "\n" - " addb $127, %al\n" -#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9)) - " .byte 0x9e /* sahf */\n" -#else - " sahf\n" + " addb $127, %al\n" // 恢复溢出标志 +#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9)) // 如果是OpenBSD或旧版FreeBSD + " .byte 0x9e /* sahf */\n" // 使用字节码 +#else // 否则 + " sahf\n" // 存储标志寄存器 #endif /* ^__OpenBSD__, etc */ - " ret\n" + " ret\n" // 返回 "\n" - ".align 8\n" + ".align 8\n" // 对齐到8字节 "\n" - "__afl_setup:\n" + "__afl_setup:\n" // 设置 "\n" - " /* Do not retry setup if we had previous failures. */\n" + " /* Do not retry setup if we had previous failures. */\n" // 如果之前失败,不再重试 "\n" - " cmpb $0, __afl_setup_failure(%rip)\n" - " jne __afl_return\n" + " cmpb $0, __afl_setup_failure(%rip)\n" // 检查设置失败标志 + " jne __afl_return\n" // 如果失败,跳转到返回 "\n" - " /* Check out if we have a global pointer on file. */\n" + " /* Check out if we have a global pointer on file. */\n" // 检查是否有全局指针 "\n" -#ifndef __APPLE__ - " movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n" - " movq (%rdx), %rdx\n" -#else - " movq __afl_global_area_ptr(%rip), %rdx\n" +#ifndef __APPLE__ // 如果不是苹果系统 + " movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n" // 加载全局指针 + " movq (%rdx), %rdx\n" // 加载指针值 +#else // 否则 + " movq __afl_global_area_ptr(%rip), %rdx\n" // 加载全局指针 #endif /* !^__APPLE__ */ - " testq %rdx, %rdx\n" - " je __afl_setup_first\n" + " testq %rdx, %rdx\n" // 测试RDX是否为0 + " je __afl_setup_first\n" // 如果为0,跳转到首次设置 "\n" - " movq %rdx, __afl_area_ptr(%rip)\n" - " jmp __afl_store\n" + " movq %rdx, __afl_area_ptr(%rip)\n" // 存储指针 + " jmp __afl_store\n" // 跳转到存储 "\n" - "__afl_setup_first:\n" + "__afl_setup_first:\n" // 首次设置 "\n" " /* Save everything that is not yet saved and that may be touched by\n" - " getenv() and several other libcalls we'll be relying on. */\n" - "\n" - " leaq -352(%rsp), %rsp\n" - "\n" - " movq %rax, 0(%rsp)\n" - " movq %rcx, 8(%rsp)\n" - " movq %rdi, 16(%rsp)\n" - " movq %rsi, 32(%rsp)\n" - " movq %r8, 40(%rsp)\n" - " movq %r9, 48(%rsp)\n" - " movq %r10, 56(%rsp)\n" - " movq %r11, 64(%rsp)\n" - "\n" - " movq %xmm0, 96(%rsp)\n" - " movq %xmm1, 112(%rsp)\n" - " movq %xmm2, 128(%rsp)\n" - " movq %xmm3, 144(%rsp)\n" - " movq %xmm4, 160(%rsp)\n" - " movq %xmm5, 176(%rsp)\n" - " movq %xmm6, 192(%rsp)\n" - " movq %xmm7, 208(%rsp)\n" - " movq %xmm8, 224(%rsp)\n" - " movq %xmm9, 240(%rsp)\n" - " movq %xmm10, 256(%rsp)\n" - " movq %xmm11, 272(%rsp)\n" - " movq %xmm12, 288(%rsp)\n" - " movq %xmm13, 304(%rsp)\n" - " movq %xmm14, 320(%rsp)\n" - " movq %xmm15, 336(%rsp)\n" - "\n" - " /* Map SHM, jumping to __afl_setup_abort if something goes wrong. */\n" + " getenv() and several other libcalls we'll be relying on. */\n" // 保存所有可能被修改的寄存器 + "\n" + " leaq -352(%rsp), %rsp\n" // 调整栈指针 + "\n" + " movq %rax, 0(%rsp)\n" // 保存RAX + " movq %rcx, 8(%rsp)\n" // 保存RCX + " movq %rdi, 16(%rsp)\n" // 保存RDI + " movq %rsi, 32(%rsp)\n" // 保存RSI + " movq %r8, 40(%rsp)\n" // 保存R8 + " movq %r9, 48(%rsp)\n" // 保存R9 + " movq %r10, 56(%rsp)\n" // 保存R10 + " movq %r11, 64(%rsp)\n" // 保存R11 + "\n" + " movq %xmm0, 96(%rsp)\n" // 保存XMM0 + " movq %xmm1, 112(%rsp)\n" // 保存XMM1 + " movq %xmm2, 128(%rsp)\n" // 保存XMM2 + " movq %xmm3, 144(%rsp)\n" // 保存XMM3 + " movq %xmm4, 160(%rsp)\n" // 保存XMM4 + " movq %xmm5, 176(%rsp)\n" // 保存XMM5 + " movq %xmm6, 192(%rsp)\n" // 保存XMM6 + " movq %xmm7, 208(%rsp)\n" // 保存XMM7 + " movq %xmm8, 224(%rsp)\n" // 保存XMM8 + " movq %xmm9, 240(%rsp)\n" // 保存XMM9 + " movq %xmm10, 256(%rsp)\n" // 保存XMM10 + " movq %xmm11, 272(%rsp)\n" // 保存XMM11 + " movq %xmm12, 288(%rsp)\n" // 保存XMM12 + " movq %xmm13, 304(%rsp)\n" // 保存XMM13 + " movq %xmm14, 320(%rsp)\n" // 保存XMM14 + " movq %xmm15, 336(%rsp)\n" // 保存XMM15 + "\n" + " /* Map SHM, jumping to __afl_setup_abort if something goes wrong. */\n" // 映射SHM区域 "\n" " /* The 64-bit ABI requires 16-byte stack alignment. We'll keep the\n" - " original stack ptr in the callee-saved r12. */\n" + " original stack ptr in the callee-saved r12. */\n" // 64位ABI要求16字节对齐 "\n" - " pushq %r12\n" - " movq %rsp, %r12\n" - " subq $16, %rsp\n" - " andq $0xfffffffffffffff0, %rsp\n" + " pushq %r12\n" // 保存R12 + " movq %rsp, %r12\n" // 保存栈指针 + " subq $16, %rsp\n" // 调整栈指针 + " andq $0xfffffffffffffff0, %rsp\n" // 对齐栈 "\n" - " leaq .AFL_SHM_ENV(%rip), %rdi\n" - CALL_L64("getenv") + " leaq .AFL_SHM_ENV(%rip), %rdi\n" // 加载SHM环境变量名 + CALL_L64("getenv") // 调用getenv "\n" - " testq %rax, %rax\n" - " je __afl_setup_abort\n" + " testq %rax, %rax\n" // 检查返回值 + " je __afl_setup_abort\n" // 如果为0,跳转到设置失败 "\n" - " movq %rax, %rdi\n" - CALL_L64("atoi") + " movq %rax, %rdi\n" // 加载返回值 + CALL_L64("atoi") // 调用atoi "\n" - " xorq %rdx, %rdx /* shmat flags */\n" - " xorq %rsi, %rsi /* requested addr */\n" - " movq %rax, %rdi /* SHM ID */\n" - CALL_L64("shmat") + " xorq %rdx, %rdx /* shmat flags */\n" // 清零RDX + " xorq %rsi, %rsi /* requested addr */\n" // 清零RSI + " movq %rax, %rdi /* SHM ID */\n" // 加载SHM ID + CALL_L64("shmat") // 调用shmat "\n" - " cmpq $-1, %rax\n" - " je __afl_setup_abort\n" + " cmpq $-1, %rax\n" // 检查返回值 + " je __afl_setup_abort\n" // 如果为-1,跳转到设置失败 "\n" - " /* Store the address of the SHM region. */\n" + " /* Store the address of the SHM region. */\n" // 存储SHM区域地址 "\n" - " movq %rax, %rdx\n" - " movq %rax, __afl_area_ptr(%rip)\n" + " movq %rax, %rdx\n" // 存储到RDX + " movq %rax, __afl_area_ptr(%rip)\n" // 存储到全局变量 "\n" -#ifdef __APPLE__ - " movq %rax, __afl_global_area_ptr(%rip)\n" -#else - " movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n" - " movq %rax, (%rdx)\n" +#ifdef __APPLE__ // 如果是苹果系统 + " movq %rax, __afl_global_area_ptr(%rip)\n" // 存储到全局变量 +#else // 否则 + " movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n" // 加载全局指针 + " movq %rax, (%rdx)\n" // 存储到全局变量 #endif /* ^__APPLE__ */ - " movq %rax, %rdx\n" + " movq %rax, %rdx\n" // 存储到RDX "\n" - "__afl_forkserver:\n" + "__afl_forkserver:\n" // 进入fork服务器模式 "\n" " /* Enter the fork server mode to avoid the overhead of execve() calls. We\n" - " push rdx (area ptr) twice to keep stack alignment neat. */\n" + " push rdx (area ptr) twice to keep stack alignment neat. */\n" // 进入fork服务器模式 "\n" - " pushq %rdx\n" - " pushq %rdx\n" + " pushq %rdx\n" // 保存RDX + " pushq %rdx\n" // 保存RDX "\n" " /* Phone home and tell the parent that we're OK. (Note that signals with\n" " no SA_RESTART will mess it up). If this fails, assume that the fd is\n" " closed because we were execve()d from an instrumented binary, or because\n" - " the parent doesn't want to use the fork server. */\n" + " the parent doesn't want to use the fork server. */\n" // 通知父进程 "\n" - " movq $4, %rdx /* length */\n" - " leaq __afl_temp(%rip), %rsi /* data */\n" - " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" - CALL_L64("write") + " movq $4, %rdx /* length */\n" // 设置长度 + " leaq __afl_temp(%rip), %rsi /* data */\n" // 加载数据 + " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" // 加载文件描述符 + CALL_L64("write") // 调用write "\n" - " cmpq $4, %rax\n" - " jne __afl_fork_resume\n" + " cmpq $4, %rax\n" // 检查返回值 + " jne __afl_fork_resume\n" // 如果不等于4,跳转到恢复 "\n" - "__afl_fork_wait_loop:\n" + "__afl_fork_wait_loop:\n" // fork等待循环 "\n" - " /* Wait for parent by reading from the pipe. Abort if read fails. */\n" + " /* Wait for parent by reading from the pipe. Abort if read fails. */\n" // 等待父进程 "\n" - " movq $4, %rdx /* length */\n" - " leaq __afl_temp(%rip), %rsi /* data */\n" - " movq $" STRINGIFY(FORKSRV_FD) ", %rdi /* file desc */\n" - CALL_L64("read") - " cmpq $4, %rax\n" - " jne __afl_die\n" + " movq $4, %rdx /* length */\n" // 设置长度 + " leaq __afl_temp(%rip), %rsi /* data */\n" // 加载数据 + " movq $" STRINGIFY(FORKSRV_FD) ", %rdi /* file desc */\n" // 加载文件描述符 + CALL_L64("read") // 调用read + " cmpq $4, %rax\n" // 检查返回值 + " jne __afl_die\n" // 如果不等于4,跳转到死亡 "\n" " /* Once woken up, create a clone of our process. This is an excellent use\n" " case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly\n" " caches getpid() results and offers no way to update the value, breaking\n" - " abort(), raise(), and a bunch of other things :-( */\n" + " abort(), raise(), and a bunch of other things :-( */\n" // 创建子进程 "\n" - CALL_L64("fork") - " cmpq $0, %rax\n" - " jl __afl_die\n" - " je __afl_fork_resume\n" + CALL_L64("fork") // 调用fork + " cmpq $0, %rax\n" // 检查返回值 + " jl __afl_die\n" // 如果小于0,跳转到死亡 + " je __afl_fork_resume\n" // 如果等于0,跳转到恢复 "\n" - " /* In parent process: write PID to pipe, then wait for child. */\n" + " /* In parent process: write PID to pipe, then wait for child. */\n" // 在父进程中 "\n" - " movl %eax, __afl_fork_pid(%rip)\n" + " movl %eax, __afl_fork_pid(%rip)\n" // 存储子进程PID "\n" - " movq $4, %rdx /* length */\n" - " leaq __afl_fork_pid(%rip), %rsi /* data */\n" - " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" - CALL_L64("write") + " movq $4, %rdx /* length */\n" // 设置长度 + " leaq __afl_fork_pid(%rip), %rsi /* data */\n" // 加载数据 + " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" // 加载文件描述符 + CALL_L64("write") // 调用write "\n" - " movq $0, %rdx /* no flags */\n" - " leaq __afl_temp(%rip), %rsi /* status */\n" - " movq __afl_fork_pid(%rip), %rdi /* PID */\n" - CALL_L64("waitpid") - " cmpq $0, %rax\n" - " jle __afl_die\n" + " movq $0, %rdx /* no flags */\n" // 设置标志 + " leaq __afl_temp(%rip), %rsi /* status */\n" // 加载状态 + " movq __afl_fork_pid(%rip), %rdi /* PID */\n" // 加载PID + CALL_L64("waitpid") // 调用waitpid + " cmpq $0, %rax\n" // 检查返回值 + " jle __afl_die\n" // 如果小于等于0,跳转到死亡 "\n" - " /* Relay wait status to pipe, then loop back. */\n" + " /* Relay wait status to pipe, then loop back. */\n" // 传递等待状态 "\n" - " movq $4, %rdx /* length */\n" - " leaq __afl_temp(%rip), %rsi /* data */\n" - " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" - CALL_L64("write") + " movq $4, %rdx /* length */\n" // 设置长度 + " leaq __afl_temp(%rip), %rsi /* data */\n" // 加载数据 + " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" // 加载文件描述符 + CALL_L64("write") // 调用write "\n" - " jmp __afl_fork_wait_loop\n" + " jmp __afl_fork_wait_loop\n" // 跳转到等待循环 "\n" - "__afl_fork_resume:\n" + "__afl_fork_resume:\n" // 恢复 "\n" - " /* In child process: close fds, resume execution. */\n" + " /* In child process: close fds, resume execution. */\n" // 在子进程中 "\n" - " movq $" STRINGIFY(FORKSRV_FD) ", %rdi\n" - CALL_L64("close") + " movq $" STRINGIFY(FORKSRV_FD) ", %rdi\n" // 加载文件描述符 + CALL_L64("close") // 调用close "\n" - " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi\n" - CALL_L64("close") + " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi\n" // 加载文件描述符 + CALL_L64("close") // 调用close "\n" - " popq %rdx\n" - " popq %rdx\n" + " popq %rdx\n" // 恢复RDX + " popq %rdx\n" // 恢复RDX "\n" - " movq %r12, %rsp\n" - " popq %r12\n" + " movq %r12, %rsp\n" // 恢复栈指针 + " popq %r12\n" // 恢复R12 "\n" - " movq 0(%rsp), %rax\n" - " movq 8(%rsp), %rcx\n" - " movq 16(%rsp), %rdi\n" - " movq 32(%rsp), %rsi\n" - " movq 40(%rsp), %r8\n" - " movq 48(%rsp), %r9\n" - " movq 56(%rsp), %r10\n" - " movq 64(%rsp), %r11\n" + " movq 0(%rsp), %rax\n" // 恢复RAX + " movq 8(%rsp), %rcx\n" // 恢复RCX + " movq 16(%rsp), %rdi\n" // 恢复RDI + " movq 32(%rsp), %rsi\n" // 恢复RSI + " movq 40(%rsp), %r8\n" // 恢复R8 + " movq 48(%rsp), %r9\n" // 恢复R9 + " movq 56(%rsp), %r10\n" // 恢复R10 + " movq 64(%rsp), %r11\n" // 恢复R11 "\n" - " movq 96(%rsp), %xmm0\n" - " movq 112(%rsp), %xmm1\n" - " movq 128(%rsp), %xmm2\n" - " movq 144(%rsp), %xmm3\n" - " movq 160(%rsp), %xmm4\n" - " movq 176(%rsp), %xmm5\n" - " movq 192(%rsp), %xmm6\n" - " movq 208(%rsp), %xmm7\n" - " movq 224(%rsp), %xmm8\n" - " movq 240(%rsp), %xmm9\n" - " movq 256(%rsp), %xmm10\n" - " movq 272(%rsp), %xmm11\n" - " movq 288(%rsp), %xmm12\n" - " movq 304(%rsp), %xmm13\n" - " movq 320(%rsp), %xmm14\n" - " movq 336(%rsp), %xmm15\n" + " movq 96(%rsp), %xmm0\n" // 恢复XMM0 + " movq 112(%rsp), %xmm1\n" // 恢复XMM1 + " movq 128(%rsp), %xmm2\n" // 恢复XMM2 + " movq 144(%rsp), %xmm3\n" // 恢复XMM3 + " movq 160(%rsp), %xmm4\n" // 恢复XMM4 + " movq 176(%rsp), %xmm5\n" // 恢复XMM5 + " movq 192(%rsp), %xmm6\n" // 恢复XMM6 + " movq 208(%rsp), %xmm7\n" // 恢复XMM7 + " movq 224(%rsp), %xmm8\n" // 恢复XMM8 + " movq 240(%rsp), %xmm9\n" // 恢复XMM9 + " movq 256(%rsp), %xmm10\n" // 恢复XMM10 + " movq 272(%rsp), %xmm11\n" // 恢复XMM11 + " movq 288(%rsp), %xmm12\n" // 恢复XMM12 + " movq 304(%rsp), %xmm13\n" // 恢复XMM13 + " movq 320(%rsp), %xmm14\n" // 恢复XMM14 + " movq 336(%rsp), %xmm15\n" // 恢复XMM15 "\n" - " leaq 352(%rsp), %rsp\n" + " leaq 352(%rsp), %rsp\n" // 恢复栈指针 "\n" - " jmp __afl_store\n" + " jmp __afl_store\n" // 跳转到存储 "\n" - "__afl_die:\n" + "__afl_die:\n" // 死亡 "\n" - " xorq %rax, %rax\n" - CALL_L64("_exit") + " xorq %rax, %rax\n" // 清零RAX + CALL_L64("_exit") // 调用_exit "\n" - "__afl_setup_abort:\n" + "__afl_setup_abort:\n" // 设置失败 "\n" " /* Record setup failure so that we don't keep calling\n" - " shmget() / shmat() over and over again. */\n" - "\n" - " incb __afl_setup_failure(%rip)\n" - "\n" - " movq %r12, %rsp\n" - " popq %r12\n" - "\n" - " movq 0(%rsp), %rax\n" - " movq 8(%rsp), %rcx\n" - " movq 16(%rsp), %rdi\n" - " movq 32(%rsp), %rsi\n" - " movq 40(%rsp), %r8\n" - " movq 48(%rsp), %r9\n" - " movq 56(%rsp), %r10\n" - " movq 64(%rsp), %r11\n" - "\n" - " movq 96(%rsp), %xmm0\n" - " movq 112(%rsp), %xmm1\n" - " movq 128(%rsp), %xmm2\n" - " movq 144(%rsp), %xmm3\n" - " movq 160(%rsp), %xmm4\n" - " movq 176(%rsp), %xmm5\n" - " movq 192(%rsp), %xmm6\n" - " movq 208(%rsp), %xmm7\n" - " movq 224(%rsp), %xmm8\n" - " movq 240(%rsp), %xmm9\n" - " movq 256(%rsp), %xmm10\n" - " movq 272(%rsp), %xmm11\n" - " movq 288(%rsp), %xmm12\n" - " movq 304(%rsp), %xmm13\n" - " movq 320(%rsp), %xmm14\n" - " movq 336(%rsp), %xmm15\n" - "\n" - " leaq 352(%rsp), %rsp\n" - "\n" - " jmp __afl_return\n" - "\n" - ".AFL_VARS:\n" - "\n" - -#ifdef __APPLE__ - - " .comm __afl_area_ptr, 8\n" -#ifndef COVERAGE_ONLY - " .comm __afl_prev_loc, 8\n" + " shmget() / shmat() over and over again. */\n" // 记录设置失败 + "\n" + " incb __afl_setup_failure(%rip)\n" // 增加设置失败标志 + "\n" + " movq %r12, %rsp\n" // 恢复栈指针 + " popq %r12\n" // 恢复R12 + "\n" + " movq 0(%rsp), %rax\n" // 恢复RAX + " movq 8(%rsp), %rcx\n" // 恢复RCX + " movq 16(%rsp), %rdi\n" // 恢复RDI + " movq 32(%rsp), %rsi\n" // 恢复RSI + " movq 40(%rsp), %r8\n" // 恢复R8 + " movq 48(%rsp), %r9\n" // 恢复R9 + " movq 56(%rsp), %r10\n" // 恢复R10 + " movq 64(%rsp), %r11\n" // 恢复R11 + "\n" + " movq 96(%rsp), %xmm0\n" // 恢复XMM0 + " movq 112(%rsp), %xmm1\n" // 恢复XMM1 + " movq 128(%rsp), %xmm2\n" // 恢复XMM2 + " movq 144(%rsp), %xmm3\n" // 恢复XMM3 + " movq 160(%rsp), %xmm4\n" // 恢复XMM4 + " movq 176(%rsp), %xmm5\n" // 恢复XMM5 + " movq 192(%rsp), %xmm6\n" // 恢复XMM6 + " movq 208(%rsp), %xmm7\n" // 恢复XMM7 + " movq 224(%rsp), %xmm8\n" // 恢复XMM8 + " movq 240(%rsp), %xmm9\n" // 恢复XMM9 + " movq 256(%rsp), %xmm10\n" // 恢复XMM10 + " movq 272(%rsp), %xmm11\n" // 恢复XMM11 + " movq 288(%rsp), %xmm12\n" // 恢复XMM12 + " movq 304(%rsp), %xmm13\n" // 恢复XMM13 + " movq 320(%rsp), %xmm14\n" // 恢复XMM14 + " movq 336(%rsp), %xmm15\n" // 恢复XMM15 + "\n" + " leaq 352(%rsp), %rsp\n" // 恢复栈指针 + "\n" + " jmp __afl_return\n" // 跳转到返回 + "\n" + ".AFL_VARS:\n" // AFL变量 + "\n" + +#ifdef __APPLE__ // 如果是苹果系统 + + " .comm __afl_area_ptr, 8\n" // 定义全局变量 +#ifndef COVERAGE_ONLY // 如果不是仅覆盖率模式 + " .comm __afl_prev_loc, 8\n" // 定义全局变量 #endif /* !COVERAGE_ONLY */ - " .comm __afl_fork_pid, 4\n" - " .comm __afl_temp, 4\n" - " .comm __afl_setup_failure, 1\n" + " .comm __afl_fork_pid, 4\n" // 定义全局变量 + " .comm __afl_temp, 4\n" // 定义全局变量 + " .comm __afl_setup_failure, 1\n" // 定义全局变量 -#else +#else // 否则 - " .lcomm __afl_area_ptr, 8\n" -#ifndef COVERAGE_ONLY - " .lcomm __afl_prev_loc, 8\n" + " .lcomm __afl_area_ptr, 8\n" // 定义局部变量 +#ifndef COVERAGE_ONLY // 如果不是仅覆盖率模式 + " .lcomm __afl_prev_loc, 8\n" // 定义局部变量 #endif /* !COVERAGE_ONLY */ - " .lcomm __afl_fork_pid, 4\n" - " .lcomm __afl_temp, 4\n" - " .lcomm __afl_setup_failure, 1\n" + " .lcomm __afl_fork_pid, 4\n" // 定义局部变量 + " .lcomm __afl_temp, 4\n" // 定义局部变量 + " .lcomm __afl_setup_failure, 1\n" // 定义局部变量 #endif /* ^__APPLE__ */ - " .comm __afl_global_area_ptr, 8, 8\n" + " .comm __afl_global_area_ptr, 8, 8\n" // 定义全局变量 "\n" - ".AFL_SHM_ENV:\n" - " .asciz \"" SHM_ENV_VAR "\"\n" + ".AFL_SHM_ENV:\n" // SHM环境变量 + " .asciz \"" SHM_ENV_VAR "\"\n" // 定义字符串 "\n" - "/* --- END --- */\n" + "/* --- END --- */\n" // 64位主插桩代码结束 "\n"; -#endif /* !_HAVE_AFL_AS_H */ +#endif /* !_HAVE_AFL_AS_H */ // 结束条件编译 \ No newline at end of file