批注了afl-as.c

main
BagPipeOuO 3 months ago
parent 74366041a3
commit 6a6c239329

@ -14,6 +14,8 @@
limitations under the License.
*/
// 这部分是版权声明和许可协议,声明该文件受到 Apache 2.0 许可证保护。
/*
american fuzzy lop - wrapper for GNU as
---------------------------------------
@ -36,6 +38,10 @@
*/
// 该段注释解释了代码的功能
// 它是一个用于 GNU as 汇编器的包装器
// 目的是预处理由 GCC/Clang 生成的汇编文件并注入必要的仪器代码
#define AFL_MAIN
#include "config.h"
@ -56,20 +62,43 @@
#include <sys/wait.h>
#include <sys/time.h>
// 定义宏 AFL_MAIN通常用于标识主程序或相关模块。
// 包含内部的头文件,这些文件提供了项目的配置、类型定义、调试功能以及内存分配等支持。
// 包含标准的C库和POSIX库头文件用于文件操作、内存管理、时间处理等。
static u8** as_params; /* Parameters passed to the real 'as' */
static u8* input_file; /* Originally specified input file */
static u8* modified_file; /* Instrumented file for the real 'as' */
//声明静态变量:
// as_params保存传递给实际 as 汇编器的参数。
// input_file保存输入的文件路径。
// 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 */
// 声明其他控制程序行为的静态变量:
// 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' */
// 声明静态变量:
// inst_ratio仪器插入的概率百分比
// as_par_cnt传递给汇编器 as 的参数数量。
/* If we don't find --32 or --64 in the command line, default to
instrumentation for whichever mode we were compiled with. This is not
perfect, but should do the trick for almost all use cases. */
@ -88,20 +117,32 @@ static u8 use_64bit = 0;
#endif /* ^WORD_SIZE_64 */
// 根据编译时的定义判断是否使用 64 位模式。
// 如果没有定义 WORD_SIZE_64则默认使用 32 位模式。
// 如果是苹果平台,还会抛出一个错误,表明不支持 32 位 Apple 平台。
/* Examine and modify parameters to pass to 'as'. Note that the file name
is always the last parameter passed by GCC, so we exploit this property
to keep the code simple. */
// 这段注释解释了接下来要做的工作:分析和修改传递给汇编器 as 的参数,特别是文件名,它总是作为最后一个参数传递给 GCC。
static void edit_params(int argc, char** argv) {
// 定义函数 edit_params用于修改传递给 as 汇编器的参数。
u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS");
u32 i;
// 获取环境变量 TMPDIR 和 AFL_AS并声明变量 i。
#ifdef __APPLE__
u8 use_clang_as = 0;
// 如果是在 Apple 平台上,声明一个变量 use_clang_as 来标识是否使用 clang 作为汇编器。
/* On MacOS X, the Xcode cctool 'as' driver is a bit stale and does not work
with the code generated by newer versions of clang that are hand-built
by the user. See the thread here: http://goo.gl/HBWDtn.
@ -122,6 +163,8 @@ static void edit_params(int argc, char** argv) {
if (!afl_as) afl_as = getenv("AFL_CXX");
if (!afl_as) afl_as = "clang";
// 如果在 clang 模式下且没有指定 AFL_AS则尝试使用 clang 作为汇编器。
}
#endif /* __APPLE__ */
@ -134,10 +177,17 @@ static void edit_params(int argc, char** argv) {
if (!tmp_dir) tmp_dir = getenv("TMP");
if (!tmp_dir) tmp_dir = "/tmp";
//如果没有设置 TMPDIR则尝试使用其他环境变量如 TEMP 和 TMP如果都没有设置则默认使用 /tmp。
as_params = ck_alloc((argc + 32) * sizeof(u8*));
// 为汇编器参数分配内存,留出额外的空间。
as_params[0] = afl_as ? afl_as : (u8*)"as";
// 设置 as 汇编器命令,如果没有设置 AFL_AS则默认使用 "as"。
as_params[argc] = 0;
for (i = 1; i < argc - 1; i++) {
@ -169,11 +219,17 @@ static void edit_params(int argc, char** argv) {
}
// 遍历所有传递给程序的参数,如果参数为 --64 或 --32则根据平台设置 use_64bit
// 对于 Apple 平台,还会检查架构并设置为 64 位。
#ifdef __APPLE__
/* When calling clang as the upstream assembler, append -c -x assembler
and hope for the best. */
// 这段注释说明接下来的操作是调用真正的 as 汇编器,并传递适当的参数。
// 如果一切顺利as 会返回一个 0 的退出代码,程序会传播这个退出代码。
if (use_clang_as) {
as_params[as_par_cnt++] = "-c";
@ -182,16 +238,23 @@ static void edit_params(int argc, char** argv) {
}
// 在 exec_real_as 函数中:
// 如果处于静默模式be_quiet则关闭标准错误输出并将其重定向到 /dev/null避免打印调试信息。
// 使用 execvp 执行汇编器命令,传递修改后的参数。
// 如果执行失败,则打印错误信息并退出程序。
#endif /* __APPLE__ */
input_file = argv[argc - 1];
input_file = argv[argc - 1];
if (input_file[0] == '-') {
if (!strcmp(input_file + 1, "-version")) {
just_version = 1;
modified_file = input_file;
goto wrap_things_up;
goto wrap_things_up; // 跳到 wrap_things_up 标签,处理版本信息
}
if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)");
@ -199,122 +262,118 @@ static void edit_params(int argc, char** argv) {
} else {
/* Check if this looks like a standard invocation as a part of an attempt
to compile a program, rather than using gcc on an ad-hoc .s file in
a format we may not understand. This works around an issue compiling
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;
strncmp(input_file, "/tmp/", 5)) pass_thru = 1; // 启用通过模式
}
modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),
(u32)time(NULL));
(u32)time(NULL)); // 为生成的文件名分配内存并格式化
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 结尾
}
// 此段代码注释说明了程序如何检查和处理输入文件,包括:
// 如果文件名以 - 开头,可能是传递版本信息或者其他特殊的命令行参数。
// 如果文件路径看起来是临时目录之外的路径则启用“通过模式”pass_thru这表示不进行修改直接传递给汇编器。
/* Process input file, generate modified_file. Insert instrumentation in all
the appropriate places. */
/* 处理输入文件,生成修改后的文件,并在所有适当的位置插入仪器代码 */
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;
u32 ins_lines = 0; // 记录插入了多少行
u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0,
skip_intel = 0, skip_app = 0, instrument_next = 0;
#ifdef __APPLE__
u8* colon_pos;
u8* colon_pos; // 用于查找冒号位置
#endif /* __APPLE__ */
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"); // 如果 fdopen 失败,打印错误并退出
while (fgets(line, MAX_LINE, inf)) {
while (fgets(line, MAX_LINE, inf)) { // 逐行读取输入文件
/* In some cases, we want to defer writing the instrumentation trampoline
until after all the labels, macros, comments, etc. If we're in this
mode, and if the line starts with a tab followed by a character, dump
the trampoline now. */
/* 在某些情况下,我们希望在写入仪器跳板之前先跳过某些标签、宏或注释。
*/
if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
instrument_next && line[0] == '\t' && isalpha(line[1])) {
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
R(MAP_SIZE));
R(MAP_SIZE)); // 根据使用的位数选择合适的跳板格式
instrument_next = 0;
ins_lines++;
ins_lines++; // 统计插入的行数
}
/* Output the actual line, call it a day in pass-thru mode. */
/* 输出当前行,只有在通过模式下才跳过插入仪器 */
fputs(line, outf);
if (pass_thru) continue;
if (pass_thru) continue; // 如果启用了通过模式,则跳过剩下的处理
/* All right, this is where the actual fun begins. For one, we only want to
instrument the .text section. So, let's keep track of that in processed
files - and let's set instr_ok accordingly. */
/* 以下代码处理实际的插桩逻辑,只在 .text 段插入仪器代码 */
if (line[0] == '\t' && line[1] == '.') {
/* OpenBSD puts jump tables directly inline with the code, which is
a bit annoying. They use a specific format of p2align directives
around them, so we use that as a signal. */
/* OpenBSD 将跳转表直接内联到代码中,这很麻烦。它们使用特定的 p2align 指令格式,
*/
if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
isdigit(line[10]) && line[11] == '\n') skip_next_label = 1;
/* 如果检测到 .text 段或相关段,启用插桩 */
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;
continue; // 继续处理下一行
}
/* 如果是其他段(如 bss、data禁用插桩 */
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;
continue; // 继续处理下一行
}
}
/* Detect off-flavor assembly (rare, happens in gdb). When this is
encountered, we set skip_csect until the opposite directive is
seen, and we do not instrument. */
/* 检测并跳过特定的汇编节(如 .code */
if (strstr(line, ".code")) {
@ -323,14 +382,11 @@ static void add_instrumentation(void) {
}
/* Detect syntax changes, as could happen with hand-written assembly.
Skip Intel blocks, resume instrumentation when back to AT&T. */
/* 检测并跳过 Intel 语法块 */
if (strstr(line, ".intel_syntax")) skip_intel = 1;
if (strstr(line, ".att_syntax")) skip_intel = 0;
/* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */
/* 跳过 ad-hoc 的 __asm__ 块 */
if (line[0] == '#' || line[1] == '#') {
if (strstr(line, "#APP")) skip_app = 1;
@ -338,45 +394,20 @@ static void add_instrumentation(void) {
}
/* If we're in the right mood for instrumenting, check for function
names or conditional labels. This is a bit messy, but in essence,
we want to catch:
^main: - function entry point (always instrumented)
^.L0: - GCC branch label
^.LBB0_0: - clang branch label (but only in clang mode)
^\tjnz foo - conditional branches
...but not:
^# BB#0: - clang comments
^ # BB#0: - ditto
^.Ltmp0: - clang non-branch labels
^.LC0 - GCC non-branch labels
^.LBB0_0: - ditto (when in GCC mode)
^\tjmp foo - non-conditional jumps
Additionally, clang and GCC on MacOS X follow a different convention
with no leading dots on labels, hence the weird maze of #ifdefs
later on.
*/
/* 检查并插入仪器代码:主要是函数标签、条件标签等 */
if (skip_intel || skip_app || skip_csect || !instr_ok ||
line[0] == '#' || line[0] == ' ') continue;
line[0] == '#' || line[0] == ' ') continue; // 跳过不需要插入的行
/* Conditional branch instruction (jnz, etc). We append the instrumentation
right after the branch (to instrument the not-taken path) and at the
branch destination label (handled later on). */
/* 条件分支指令(如 jnz 等)。我们将插入仪器代码到分支指令后面(用于记录未采取的路径) */
if (line[0] == '\t') {
if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) {
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
R(MAP_SIZE));
R(MAP_SIZE)); // 插入仪器代码
ins_lines++;
ins_lines++; // 统计插入的行数
}
@ -384,13 +415,11 @@ static void add_instrumentation(void) {
}
/* Label of some sort. This may be a branch destination, but we need to
tread carefully and account for several different formatting
conventions. */
/* 标签的处理。标签可能是分支目标,我们需要根据格式区分处理 */
#ifdef __APPLE__
/* Apple: L<whatever><digit>: */
/* 苹果系统标签格式:L<whatever><digit>: */
if ((colon_pos = strstr(line, ":"))) {
@ -398,7 +427,7 @@ static void add_instrumentation(void) {
#else
/* Everybody else: .L<whatever>: */
/* 其他平台的标签格式:.L<whatever>: */
if (strstr(line, ":")) {
@ -406,34 +435,25 @@ static void add_instrumentation(void) {
#endif /* __APPLE__ */
/* .L0: or LBB0_0: style jump destination */
/* 处理跳转目标标签(如 .L0: 或 LBB0_0: */
#ifdef __APPLE__
/* Apple: L<num> / LBB<num> */
/* 苹果:L<num> / LBB<num> */
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
&& R(100) < inst_ratio) {
#else
/* Apple: .L<num> / .LBB<num> */
/* 其他平台:.L<num> / .LBB<num> */
if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
&& R(100) < inst_ratio) {
#endif /* __APPLE__ */
/* An optimization is possible here by adding the code only if the
label is mentioned in the code in contexts other than call / jmp.
That said, this complicates the code by requiring two-pass
processing (messy with stdin), and results in a speed gain
typically under 10%, because compilers are generally pretty good
about not generating spurious intra-function jumps.
We use deferred output chiefly to avoid disrupting
.Lfunc_begin0-style exception handling calculations (a problem on
MacOS X). */
/* 如果符合条件,则插入仪器代码 */
if (!skip_next_label) instrument_next = 1; else skip_next_label = 0;
@ -441,39 +461,38 @@ static void add_instrumentation(void) {
} else {
/* Function label (always instrumented, deferred mode). */
/* 函数标签,插入仪器代码 */
instrument_next = 1;
}
}
}
if (ins_lines)
fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
if (input_file) fclose(inf);
fclose(outf);
if (!be_quiet) {
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%%).",
ins_lines, use_64bit ? "64" : "32",
getenv("AFL_HARDEN") ? "hardened" :
(sanitizer ? "ASAN/MSAN" : "non-hardened"),
inst_ratio);
}
fclose(inf); // 关闭输入文件
fclose(outf); // 关闭输出文件
}
/* Main entry point */
// 在 main 函数中:
// 检查是否只需要显示版本信息,如果是,则调用 print_version 函数。
// 解析命令行参数:
// 解析 -q 启用静默模式。
// 解析 --clang 启用 clang 模式。
// 解析 --pass-through 启用数据通过模式。
// 解析 --sanitizer 启用 sanitizer。
// 解析 --inst-ratio 设置仪器插入的概率。
// 解析 --version 仅显示版本信息。
// 确保输入文件已经指定,如果没有指定,则退出并报错。
// 调用 edit_params 函数修改传递给汇编器的参数。
// 最后,调用 exec_real_as 函数执行实际的汇编器命令。
int main(int argc, char** argv) {
s32 pid;

Loading…
Cancel
Save