Compare commits

..

No commits in common. 'main' and 'dev-zhongfengrong' have entirely different histories.

@ -1,18 +0,0 @@
{
"configurations": [
{
"name": "windows-gcc-x64",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "gcc",
"cStandard": "${default}",
"cppStandard": "${default}",
"intelliSenseMode": "windows-gcc-x64",
"compilerArgs": [
""
]
}
],
"version": 4
}

@ -1,24 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": true,
"cwd": "c:/Users/18249/Desktop/google_afl/src",
"program": "c:/Users/18249/Desktop/google_afl/src/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

@ -4,16 +4,6 @@
"*.wpy": "vue",
"*.wxml": "html",
"*.wxss": "css",
"string.h": "c",
"cstdlib": "c",
"typeinfo": "c",
"stdlib.h": "c",
"alloc-inl.h": "c",
"debug.h": "c",
"android-ashmem.h": "c",
"limits": "c",
"stdio.h": "c",
"file.h": "c",
"config.h": "c"
"string.h": "c"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -47,73 +47,79 @@
#include <stdlib.h>
#include <string.h>
static u8 *as_path; /* AFL“as”包装器的路径 */
static u8 **cc_params; /* 传递给真实C编译器的参数 */
static u32 cc_par_cnt = 1; /* 参数计数包括argv0 */
static u8 be_quiet, /* 静音模式 */
clang_mode; /* 被称为afl clang模式 */
static u8 *as_path; /*AFL“as”包装器的路径*/
static u8 **cc_params; /*传递给真实CC的参数*/
static u32 cc_par_cnt = 1; /*参数计数包括argv0*/
static u8 be_quiet, /*静音模式*/
clang_mode; /*被称为afl clang模式**/
/* 尝试在 AFL_PATH 或从 argv[0] 派生的位置找到我们的“假”GNU 汇编器。
*/
static void find_as(u8 *argv0)
{
u8 *afl_path = getenv("AFL_PATH"); // 获取环境变量AFL_PATH的值
u8 *afl_path = getenv("AFL_PATH");
u8 *slash, *tmp;
if (afl_path) // 如果AFL_PATH环境变量存在
if (afl_path)
{
tmp = alloc_printf("%s/as", afl_path); // 构造路径字符串
if (!access(tmp, X_OK)) // 检查路径是否存在且可执行
tmp = alloc_printf("%s/as", afl_path);
if (!access(tmp, X_OK))
{
as_path = afl_path; // 设置as_path为找到的路径
ck_free(tmp); // 释放临时字符串
as_path = afl_path;
ck_free(tmp);
return;
}
ck_free(tmp); // 如果不可执行,释放临时字符串
ck_free(tmp);
}
slash = strrchr(argv0, '/'); // 在argv0中查找最后一个'/',以获取目录部分
slash = strrchr(argv0, '/');
if (slash) // 如果找到了'/'说明argv0是一个路径
if (slash)
{
u8 *dir;
*slash = 0; // 暂时将'/'替换为'\0',以便截取目录部分
dir = ck_strdup(argv0); // 复制目录部分
*slash = '/'; // 恢复'/'恢复argv0的完整路径
*slash = 0;
dir = ck_strdup(argv0);
*slash = '/';
tmp = alloc_printf("%s/afl-as", dir); // 构造路径字符串
tmp = alloc_printf("%s/afl-as", dir);
if (!access(tmp, X_OK)) // 检查路径是否存在且可执行
if (!access(tmp, X_OK))
{
as_path = dir; // 设置as_path为找到的路径
ck_free(tmp); // 释放临时字符串
as_path = dir;
ck_free(tmp);
return;
}
ck_free(tmp); // 如果不可执行,释放临时字符串
ck_free(dir); // 释放目录字符串
ck_free(tmp);
ck_free(dir);
}
if (!access(AFL_PATH "/as", X_OK)) // 检查默认路径AFL_PATH/as是否存在且可执行
if (!access(AFL_PATH "/as", X_OK))
{
as_path = AFL_PATH; // 设置as_path为默认路径
as_path = AFL_PATH;
return;
}
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH"); // 如果找不到,输出错误信息并中止
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
}
/* 将argv复制到cc_params进行必要的编辑 */
/*将argv复制到cc_params进行必要的编辑*/
static void edit_params(u32 argc, char **argv)
{
u8 fortify_set = 0, asan_set = 0; // 标志变量用于检测是否设置了fortify和asan
u8 fortify_set = 0, asan_set = 0;
u8 *name;
#if defined(__FreeBSD__) && defined(__x86_64__)
u8 m32_set = 0; // FreeBSD x86_64环境下检测是否设置了m32
u8 m32_set = 0;
#endif
/********************************************************************************
@ -121,74 +127,77 @@ static void edit_params(u32 argc, char **argv)
* 使 afl-clang使 C C++
********************************************************************************/
cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); // 分配足够大的空间来存储编译器参数
name = strrchr(argv[0], '/'); // 获取可执行文件名
if (!name)
name = argv[0]; // 如果没有找到'/'则argv0就是可执行文件名
cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
name = strrchr(argv[0], '/');
if (!name)
name = argv[0];
else
name++;
if (!strncmp(name, "afl-clang", 9))
{
clang_mode = 1;
setenv(CLANG_ENV_VAR, "1", 1);
if (!strcmp(name, "afl-clang++"))
{
u8 *alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)"clang++";
}
else
{
u8 *alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8 *)"clang";
}
}
else
name++; // 否则name指向可执行文件名的第一个字符
if (!strncmp(name, "afl-clang", 9)) // 如果可执行文件名以afl-clang开头
{
clang_mode = 1; // 设置clang_mode标志为1表示使用clang模式
setenv(CLANG_ENV_VAR, "1", 1); // 设置环境变量表示正在使用afl-clang
/*安装了GCJ和Eclipse后您实际上可以编译Java这个
abortafl-fuzz使
使Java退
*/
if (!strcmp(name, "afl-clang++")) // 如果是afl-clang++
{
u8 *alt_cxx = getenv("AFL_CXX"); // 获取环境变量AFL_CXX的值
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)"clang++"; // 如果设置了AFL_CXX则使用该值作为编译器否则使用clang++
}
else // 如果是afl-clang
{
u8 *alt_cc = getenv("AFL_CC"); // 获取环境变量AFL_CC的值
cc_params[0] = alt_cc ? alt_cc : (u8 *)"clang"; // 如果设置了AFL_CC则使用该值作为编译器否则使用clang
}
}
else // 如果不是afl-clang
{
// 安装了GCJ和Eclipse后您实际上可以编译Java这个
// 仪器将工作(令人惊讶)。唉,未处理的异常确实如此
// 不调用abort因此需要修改afl-fuzz以使其相等
// 使用Java时具有崩溃条件的非零退出代码
// 二进制文件。嗯
#ifdef __APPLE__ // 如果是在MacOS X系统上
if (!strcmp(name, "afl-g++")) // 如果是afl-g++
cc_params[0] = getenv("AFL_CXX"); // 获取环境变量AFL_CXX的值作为编译器
else if (!strcmp(name, "afl-gcj")) // 如果是afl-gcj
cc_params[0] = getenv("AFL_GCJ"); // 获取环境变量AFL_GCJ的值作为编译器
#ifdef __APPLE__
if (!strcmp(name, "afl-g++"))
cc_params[0] = getenv("AFL_CXX");
else if (!strcmp(name, "afl-gcj"))
cc_params[0] = getenv("AFL_GCJ");
else
cc_params[0] = getenv("AFL_CC"); // 其他情况获取环境变量AFL_CC的值作为编译器
cc_params[0] = getenv("AFL_CC");
if (!cc_params[0]) // 如果没有设置相应的环境变量
if (!cc_params[0])
{
SAYF("\n" cLRD "[-] " cRST // 输出错误信息
SAYF("\n" cLRD "[-] " cRST
"On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
" 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
" set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n");
FATAL("AFL_CC or AFL_CXX required on MacOS X"); // 输出错误信息并中止
FATAL("AFL_CC or AFL_CXX required on MacOS X");
}
#else // 如果不是在MacOS X系统上
#else
if (!strcmp(name, "afl-g++")) // 如果是afl-g++
if (!strcmp(name, "afl-g++"))
{
u8 *alt_cxx = getenv("AFL_CXX"); // 获取环境变量AFL_CXX的值
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)"g++"; // 如果设置了AFL_CXX则使用该值作为编译器否则使用g++
u8 *alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)"g++";
}
else if (!strcmp(name, "afl-gcj")) // 如果是afl-gcj
else if (!strcmp(name, "afl-gcj"))
{
u8 *alt_cc = getenv("AFL_GCJ"); // 获取环境变量AFL_GCJ的值
cc_params[0] = alt_cc ? alt_cc : (u8 *)"gcj"; // 如果设置了AFL_GCJ则使用该值作为编译器否则使用gcj
u8 *alt_cc = getenv("AFL_GCJ");
cc_params[0] = alt_cc ? alt_cc : (u8 *)"gcj";
}
else // 如果是afl-gcc
else
{
u8 *alt_cc = getenv("AFL_CC"); // 获取环境变量AFL_CC的值
cc_params[0] = alt_cc ? alt_cc : (u8 *)"gcc"; // 如果设置了AFL_CC则使用该值作为编译器否则使用gcc
u8 *alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8 *)"gcc";
}
#endif /* __APPLE__ */
@ -224,35 +233,32 @@ static void edit_params(u32 argc, char **argv)
if (!strcmp(cur, "-pipe"))
continue;
/ *
*
*
* FORTIFY_SOURCEASAN/MSAN
* /
#if defined(__FreeBSD__) && defined(__x86_64__)
// 检测是否在FreeBSD的64位系统上使用了-m32选项如果是则设置m32_set为1
if (!strcmp(cur, "-m32"))
m32_set = 1;
#endif
// 检测是否使用了address sanitizer或memory sanitizer选项如果是则设置asan_set为1
if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory"))
asan_set = 1;
// 检测是否设置了_FORTIFY_SOURCE宏如果是则设置fortify_set为1
if (strstr(cur, "FORTIFY_SOURCE"))
fortify_set = 1;
// 将当前命令行参数添加到编译器参数列表中
cc_params[cc_par_cnt++] = cur;
}
// 添加编译器的-B参数指定路径给as_path
cc_params[cc_par_cnt++] = "-B";
cc_params[cc_par_cnt++] = as_path;
// 如果clang_mode为真则添加-no-integrated-as参数
if (clang_mode)
cc_params[cc_par_cnt++] = "-no-integrated-as";
// 如果设置了AFL_HARDEN环境变量则添加-fstack-protector-all参数并在未设置_FORTIFY_SOURCE时定义它
if (getenv("AFL_HARDEN"))
{
@ -262,7 +268,6 @@ static void edit_params(u32 argc, char **argv)
cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
}
// 如果asan_set为真则设置AFL_USE_ASAN环境变量为1用于通知afl使用asan
if (asan_set)
{
@ -272,27 +277,25 @@ static void edit_params(u32 argc, char **argv)
}
else if (getenv("AFL_USE_ASAN"))
{
// 如果设置了AFL_USE_ASAN但未设置asan_set检查是否同时设置了MSAN和AFL_HARDEN如果是则报错
if (getenv("AFL_USE_MSAN"))
FATAL("ASAN和MSAN是互斥的");
FATAL("ASAN and MSAN are mutually exclusive");
if (getenv("AFL_HARDEN"))
FATAL("ASAN和AFL_HARDEN是互斥的");
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
// 取消定义_FORTIFY_SOURCE宏添加address sanitizer参数
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
cc_params[cc_par_cnt++] = "-fsanitize=address";
}
else if (getenv("AFL_USE_MSAN"))
{
// 如果设置了AFL_USE_MSAN检查是否同时设置了ASAN和AFL_HARDEN如果是则报错
if (getenv("AFL_USE_ASAN"))
FATAL("ASAN和MSAN是互斥的");
FATAL("ASAN and MSAN are mutually exclusive");
if (getenv("AFL_HARDEN"))
FATAL("MSAN和AFL_HARDEN是互斥的");
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
// 取消定义_FORTIFY_SOURCE宏添加memory sanitizer参数
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
cc_params[cc_par_cnt++] = "-fsanitize=memory";
}
@ -303,7 +306,6 @@ static void edit_params(u32 argc, char **argv)
* : d:\code\google_AFL\src\afl-gcc.c
********************************************************************************/
// 如果未设置AFL_DONT_OPTIMIZE环境变量则进行优化设置
if (!getenv("AFL_DONT_OPTIMIZE"))
{
@ -313,45 +315,41 @@ static void edit_params(u32 argc, char **argv)
bug*/
// 如果不是clang模式或者未设置m32选项则添加-g参数
if (!clang_mode || !m32_set)
cc_params[cc_par_cnt++] = "-g";
#else
// 添加调试信息参数-g
cc_params[cc_par_cnt++] = "-g";
#endif
// 添加-O3优化级别参数和-unroll-loops参数
cc_params[cc_par_cnt++] = "-O3";
cc_params[cc_par_cnt++] = "-funroll-loops";
/* Two indicators that you're building for fuzzing; one of them is
AFL-specific, the other is shared with libfuzzer. */
// 添加编译器标志,指示正在为模糊测试构建代码
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
}
// 如果设置了AFL_NO_BUILTIN环境变量则添加参数禁用内置的字符串和内存比较函数
if (getenv("AFL_NO_BUILTIN"))
{
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
// 注意:这里重复了两遍-fno-builtin-strncasecmp 和 -fno-builtin-memcmp应去掉重复的部分
// cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
// cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
}
// 设置编译器参数列表的结束标志NULL
/***********************************************
*
* "AFL_NO_BUILTIN"
*
***********************************************/
if (getenv("AFL_NO_BUILTIN"))
{
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
} cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
cc_params[cc_par_cnt] = NULL;
}
@ -360,42 +358,45 @@ static void edit_params(u32 argc, char **argv)
// 主函数,程序的入口点
int main(int argc, char **argv)
{
// 检查标准错误输出是否为终端,以及环境变量是否开启安静模式
if (isatty(2) && !getenv("AFL_QUIET"))
{
SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
}
else
be_quiet = 1;
// 检查传入的参数数量是否少于2,如果是则输出使用说明并退出
// 检查传入的参数数量是否少于2
if (argc < 2)
{
SAYF("\n"
"这是一个辅助afl-fuzz的工具程序。它可以用作gcc或clang的替代品\n"
"让你能够使用必要的运行时检测重新编译第三方代码。\n"
"常见的使用模式如下之一:\n\n"
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
"for gcc or clang, letting you recompile third-party code with the required\n"
"runtime instrumentation. A common use pattern would be one of the following:\n\n"
" CC=%s/afl-gcc ./configure\n"
" CXX=%s/afl-g++ ./configure\n\n"
"你可以通过AFL_CC, AFL_CXX, 和 AFL_AS指定自定义的后续编译工具链。\n"
"设置AFL_HARDEN会在编译代码时启用hardening优化。\n\n",
"You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n"
"Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
BIN_PATH, BIN_PATH);
exit(1);
}
// 查找汇编器根据argv[0]来确定使用的编译器是gcc还是clang
// 查找汇编器
find_as(argv[0]);
// 编辑命令行参数,根据需要添加和调整编译器参数
// 编辑参数
edit_params(argc, argv);
// 执行编译器,并传递编辑后的参数列表
// 执行编译器,并传递参数
execvp(cc_params[0], (char **)cc_params);
// 如果execvp调用失败输出错误信息并退出程序
// 如果执行失败,输出错误信息
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
return 0;

@ -57,108 +57,104 @@
/* Get unix time in microseconds. */
// 定义一个函数来获取当前时间(以微秒为单位)
static u64 get_cur_time_us(void) {
struct timeval tv; // 用于存储时间值
struct timezone tz; // 用于存储时区信息
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz); // 获取当前时间
gettimeofday(&tv, &tz);
// 返回以微秒为单位的时间
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
}
/* 获取CPU使用时间以微秒为单位。 */
/* Get CPU usage in microseconds. */
static u64 get_cpu_usage_us(void) {
struct rusage u; // 用于存储进程资源使用情况
struct rusage u;
getrusage(RUSAGE_SELF, &u); // 获取当前进程的资源使用情况
getrusage(RUSAGE_SELF, &u);
// 返回用户态和内核态CPU使用时间之和以微秒为单位
return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec +
(u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec;
}
/* 测量抢占率。 */
/* Measure preemption rate. */
static u32 measure_preemption(u32 target_ms) {
static volatile u32 v1, v2; // 定义两个静态易失变量用于循环计数
static volatile u32 v1, v2;
u64 st_t, en_t, st_c, en_c, real_delta, slice_delta; // 定义时间变量
s32 loop_repeats = 0; // 定义循环重复次数
u64 st_t, en_t, st_c, en_c, real_delta, slice_delta;
s32 loop_repeats = 0;
st_t = get_cur_time_us(); // 获取开始时间
st_c = get_cpu_usage_us(); // 获取开始时的CPU使用时间
st_t = get_cur_time_us();
st_c = get_cpu_usage_us();
repeat_loop: // 定义循环标签
repeat_loop:
v1 = CTEST_BUSY_CYCLES; // 设置v1为循环次数
v1 = CTEST_BUSY_CYCLES;
// 循环v1次每次v2自增1模拟CPU繁忙
while (v1--) v2++;
sched_yield(); // 让出CPU允许其他进程运行
sched_yield();
en_t = get_cur_time_us(); // 获取结束时间
en_t = get_cur_time_us();
// 如果实际运行时间小于目标时间,则增加循环次数并继续循环
if (en_t - st_t < target_ms * 1000) {
loop_repeats++;
goto repeat_loop;
}
// 获取结束时的CPU使用时间
/* Let's see what percentage of this time we actually had a chance to
run, and how much time was spent in the penalty box. */
en_c = get_cpu_usage_us();
// 计算实际运行时间(以毫秒为单位)
real_delta = (en_t - st_t) / 1000;
// 计算CPU使用时间以毫秒为单位
slice_delta = (en_c - st_c) / 1000;
// 返回实际运行时间占CPU使用时间的百分比作为抢占率
return real_delta * 100 / slice_delta;
}
/* 进行基准测试。 */
/* Do the benchmark thing. */
int main(int argc, char** argv) {
#ifdef HAVE_AFFINITY // 如果支持AFFINITY设置进程CPU亲和性
#ifdef HAVE_AFFINITY
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN), // 获取CPU核心数
idle_cpus = 0, maybe_cpus = 0, i; // 初始化空闲和可能可用的CPU核心数
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN),
idle_cpus = 0, maybe_cpus = 0, i;
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n"); // 输出程序信息
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
// 输出测量核心抢占率的信息,包括预计所需时间
ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...",
((double)CTEST_CORE_TRG_MS) / 1000);
// 对每个CPU核心进行抢占率测量
for (i = 0; i < cpu_cnt; i++) {
s32 fr = fork(); // 创建子进程
s32 fr = fork();
if (fr < 0) PFATAL("fork failed"); // 如果fork失败则输出错误信息
if (fr < 0) PFATAL("fork failed");
if (!fr) { // 子进程中
if (!fr) {
cpu_set_t c; // 定义CPU亲和性集合
u32 util_perc; // 定义CPU利用率
cpu_set_t c;
u32 util_perc;
CPU_ZERO(&c); // 清空CPU亲和性集合
CPU_SET(i, &c); // 设置亲和性为当前核心
CPU_ZERO(&c);
CPU_SET(i, &c);
// 设置进程亲和性到指定的核心
if (sched_setaffinity(0, sizeof(c), &c))
PFATAL("sched_setaffinity failed for cpu %d", i);
util_perc = measure_preemption(CTEST_CORE_TRG_MS); // 测量抢占率
util_perc = measure_preemption(CTEST_CORE_TRG_MS);
// 根据抢占率输出核心状态
if (util_perc < 110) {
SAYF(" Core #%u: " cLGN "AVAILABLE " cRST "(%u%%)\n", i, util_perc);
@ -179,21 +175,18 @@ int main(int argc, char** argv) {
}
// 等待所有子进程结束,并统计结果
for (i = 0; i < cpu_cnt; i++) {
int ret;
if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed"); // 等待子进程结束
if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed");
// 根据子进程退出状态判断核心是否可用
if (WEXITSTATUS(ret) == 0) idle_cpus++;
if (WEXITSTATUS(ret) <= 1) maybe_cpus++;
}
SAYF(cGRA "\n>>> "); // 输出结果标题
SAYF(cGRA "\n>>> ");
// 根据统计结果输出最终判断
if (idle_cpus) {
if (maybe_cpus == idle_cpus) {
@ -208,30 +201,24 @@ int main(int argc, char** argv) {
}
SAYF(cGRA " <<<" cRST "\n\n"); // 输出结果结束标志
SAYF(cGRA " <<<" cRST "\n\n");
return 0;
}
// 如果有可能可用的核心,但没有完全空闲的核心
if (maybe_cpus) {
SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.",
maybe_cpus, maybe_cpus > 1 ? "s" : "");
SAYF(cGRA " <<<" cRST "\n\n"); // 输出结果结束标志
SAYF(cGRA " <<<" cRST "\n\n");
return 1;
}
// 如果所有核心都过载
SAYF(cLRD "FAIL: " cRST "All cores are overbooked.");
SAYF(cGRA " <<<" cRST "\n\n"); // 输出结果结束标志
SAYF(cGRA " <<<" cRST "\n\n");
return 2;
#endif
}
#else
u32 util_perc;

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

File diff suppressed because it is too large Load Diff

@ -33,31 +33,24 @@
# task.
#
# 输出脚本的作者信息
echo "cgroup tool for afl-fuzz by <samir.hakim@nyu.edu> and <dwheeler@ida.org>"
echo
# 清除NEW_USER变量的值
unset NEW_USER
# 设置默认内存限制为50MB
MEM_LIMIT="50"
# 解析命令行参数
while getopts "+u:m:" opt; do
case "$opt" in
# -u 参数用于指定运行fuzzer的用户
"u")
NEW_USER="$OPTARG"
;;
# -m 参数用于设置内存限制单位为MB
"m")
MEM_LIMIT="$OPTARG"
MEM_LIMIT="$[OPTARG]"
;;
# 如果遇到未知参数,退出脚本
"?")
exit 1
;;
@ -66,22 +59,17 @@ while getopts "+u:m:" opt; do
done
# 检查内存限制是否低于安全阈值
if [ "$MEM_LIMIT" -lt "5" ]; then
echo "[-] Error: malformed or dangerously low value of -m." 1>&2
exit 1
fi
# 移除已解析的选项保留fuzz命令
shift $((OPTIND-1))
# 获取目标二进制文件路径
TARGET_BIN="$1"
# 检查是否提供了必要的参数
if [ "$TARGET_BIN" = "" -o "$NEW_USER" = "" ]; then
# 输出使用说明
cat 1>&2 <<_EOF_
Usage: $0 [ options ] -- /path/to/afl-fuzz [ ...afl options... ]
@ -101,81 +89,75 @@ conjunction with '-m none' passed to the afl-fuzz binary itself, say:
_EOF_
# 因为缺少必要的参数,退出脚本
exit 1
fi
# 基本的系统检查
# 检查是否为Linux系统
# Basic sanity checks
if [ ! "`uname -s`" = "Linux" ]; then
echo "[-] Error: this tool does not support non-Linux systems." 1>&2
exit 1
fi
# 检查是否以root用户运行脚本
if [ ! "`id -u`" = "0" ]; then
echo "[-] Error: you need to run this script as root (sorry!)." 1>&2
exit 1
fi
# 检查是否安装了cgroup工具
if ! type cgcreate 2>/dev/null 1>&2; then
echo "[-] Error: you need to install cgroup tools first." 1>&2
# 根据包管理器提供安装命令建议
if type apt-get 2>/dev/null 1>&2; then
echo " (Perhaps 'apt-get install cgroup-bin' will work.)" 1>&2
elif type yum 2>/dev/null 1>&2; then
echo " (Perhaps 'yum install libcgroup-tools' will work.)" 1>&2
fi
# 因为缺少必要的工具,退出脚本
exit 1
fi
# 检查指定的用户是否存在
if ! id -u "$NEW_USER" 2>/dev/null 1>&2; then
echo "[-] Error: user '$NEW_USER' does not seem to exist." 1>&2
exit 1
fi
# 创建一个新的cgroup路径如果必要使用PID键值组来确保并行的afl-fuzz任务相互独立
CID="afl-$NEW_USER-$"
# Create a new cgroup path if necessary... We used PID-keyed groups to keep
# parallel afl-fuzz tasks separate from each other.
CID="afl-$NEW_USER-$$"
CPATH="/sys/fs/cgroup/memory/$CID"
# 如果路径不存在则创建cgroup
if [ ! -d "$CPATH" ]; then
cgcreate -a "$NEW_USER" -g memory:"$CID" || exit 1
fi
# 设置内存限制
# 如果系统支持交换空间限制,则同时设置内存和交换空间限制
# Set the appropriate limit...
if [ -f "$CPATH/memory.memsw.limit_in_bytes" ]; then
echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" 2>/dev/null
echo "${MEM_LIMIT}M" > "$CPATH/memory.memsw.limit_in_bytes" || exit 1
echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1
# 如果系统有启用交换空间,则要求先禁用交换空间
elif grep -qE 'partition|file' /proc/swaps; then
echo "[-] Error: your system requires swap to be disabled first (swapoff -a)." 1>&2
exit 1
# 如果系统不支持交换空间限制,则仅设置内存限制
else
echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1
fi
# 运行fuzz命令并确保其在设置的cgroup内存限制下执行
# All right. At this point, we can just run the command.
cgexec -g "memory:$CID" su -c "$*" "$NEW_USER"
# 删除cgroup以清理资源
cgdelete -g "memory:$CID"

@ -36,19 +36,19 @@
#include "../types.h"
#ifndef PAGE_SIZE
# define PAGE_SIZE 4096 // 定义页面大小为4096字节
# define PAGE_SIZE 4096
#endif /* !PAGE_SIZE */
#ifndef MAP_ANONYMOUS
# define MAP_ANONYMOUS MAP_ANON // 定义MAP_ANONYMOUS为MAP_ANON用于匿名映射
# define MAP_ANONYMOUS MAP_ANON
#endif /* !MAP_ANONYMOUS */
/* 错误/消息处理: */
/* Error / message handling: */
#define DEBUGF(_x...) do { \
if (alloc_verbose) { \
if (++call_depth == 1) { \
fprintf(stderr, "[AFL] " _x); // 输出调试信息
fprintf(stderr, "[AFL] " _x); \
fprintf(stderr, "\n"); \
} \
call_depth--; \
@ -57,97 +57,101 @@
#define FATAL(_x...) do { \
if (++call_depth == 1) { \
fprintf(stderr, "*** [AFL] " _x); // 输出致命错误信息
fprintf(stderr, "*** [AFL] " _x); \
fprintf(stderr, " ***\n"); \
abort(); // 终止程序
abort(); \
} \
call_depth--; \
} while (0)
/* 宏来计算存储缓冲区所需的页面数量: */
/* Macro to count the number of pages needed to store a buffer: */
#define PG_COUNT(_l) (((_l) + (PAGE_SIZE - 1)) / PAGE_SIZE) // 计算所需页面数,向上取整
#define PG_COUNT(_l) (((_l) + (PAGE_SIZE - 1)) / PAGE_SIZE)
/* Canary & clobber bytes: */
#define ALLOC_CANARY 0xAACCAACC // 定义canary值
#define ALLOC_CLOBBER 0xCC // 定义clobber值
#define ALLOC_CANARY 0xAACCAACC
#define ALLOC_CLOBBER 0xCC
#define PTR_C(_p) (((u32*)(_p))[-1]) // 获取canary值的指针
#define PTR_L(_p) (((u32*)(_p))[-2]) // 获取分配长度值的指针
#define PTR_C(_p) (((u32*)(_p))[-1])
#define PTR_L(_p) (((u32*)(_p))[-2])
/* 可配置项使用AFL_LD_*来设置): */
/* Configurable stuff (use AFL_LD_* to set): */
static u32 max_mem = MAX_ALLOC; /* 允许的最大堆使用量 */
static u8 alloc_verbose, /* 是否显示额外的调试消息 */
hard_fail, /* 当超过max_mem时是否使用abort() */
no_calloc_over; /* 对calloc()溢出是否使用abort() */
static u32 max_mem = MAX_ALLOC; /* Max heap usage to permit */
static u8 alloc_verbose, /* Additional debug messages */
hard_fail, /* abort() when max_mem exceeded? */
no_calloc_over; /* abort() on calloc() overflows? */
static __thread size_t total_mem; /* 当前已分配的内存 */
static __thread size_t total_mem; /* Currently allocated mem */
static __thread u32 call_depth; /* 避免通过fprintf()引起的递归 */
static __thread u32 call_depth; /* To avoid recursion via fprintf() */
/* 这是主要的分配函数。它分配比必要多一个页面的内存,
PROT_NONE
使使mmap()
*/
/* This is the main alloc function. It allocates one page more than necessary,
sets that tailing page to PROT_NONE, and then increments the return address
so that it is right-aligned to that boundary. Since it always uses mmap(),
the returned memory will be zeroed. */
static void* __dislocator_alloc(size_t len) {
void* ret;
if (total_mem + len > max_mem || total_mem + len < total_mem) {
if (hard_fail)
FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024); // 如果超过最大内存且hard_fail为真输出错误并终止程序
FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024);
DEBUGF("total allocs exceed %u MB, returning NULL",
max_mem / 1024 / 1024); // 如果超过最大内存且hard_fail为假输出调试信息并返回NULL
max_mem / 1024 / 1024);
return NULL;
}
/* 我们还会在实际缓冲区下面存储缓冲区长度和canary
8 */
/* We will also store buffer length and a canary below the actual buffer, so
let's add 8 bytes for that. */
ret = mmap(NULL, (1 + PG_COUNT(len + 8)) * PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); // 使用mmap分配内存
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ret == (void*)-1) {
if (hard_fail) FATAL("mmap() failed on alloc (OOM?)"); // 如果mmap失败且hard_fail为真输出错误并终止程序
if (hard_fail) FATAL("mmap() failed on alloc (OOM?)");
DEBUGF("mmap() failed on alloc (OOM?)"); // 如果mmap失败且hard_fail为假输出调试信息并返回NULL
DEBUGF("mmap() failed on alloc (OOM?)");
return NULL;
}
/* 在最后一个页面设置PROT_NONE。 */
/* Set PROT_NONE on the last page. */
if (mprotect(ret + PG_COUNT(len + 8) * PAGE_SIZE, PAGE_SIZE, PROT_NONE))
FATAL("mprotect() failed when allocating memory"); // 如果mprotect失败输出错误并终止程序
FATAL("mprotect() failed when allocating memory");
/* 增加返回指针,使其对齐到页面边界。 */
/* Offset the return pointer so that it's right-aligned to the page
boundary. */
ret += PAGE_SIZE * PG_COUNT(len + 8) - len - 8;
/* 存储分配元数据。 */
/* Store allocation metadata. */
ret += 8;
PTR_L(ret) = len; // 存储分配长度
PTR_C(ret) = ALLOC_CANARY; // 存储canary值
PTR_L(ret) = len;
PTR_C(ret) = ALLOC_CANARY;
total_mem += len; // 增加已分配内存计数
total_mem += len;
return ret;
}
/* 面向用户的calloc()包装器。这只是一个溢出检查和
*/
/* The "user-facing" wrapper for calloc(). This just checks for overflows and
displays debug messages if requested. */
void* calloc(size_t elem_len, size_t elem_cnt) {
@ -155,40 +159,42 @@ void* calloc(size_t elem_len, size_t elem_cnt) {
size_t len = elem_len * elem_cnt;
/* 进行一些简单的检查,以检测明显的错误... */
/* Perform some sanity checks to detect obvious issues... */
if (elem_cnt && len / elem_cnt != elem_len) {
if (no_calloc_over) {
DEBUGF("calloc(%zu, %zu) would overflow, returning NULL", elem_len, elem_cnt); // 如果no_calloc_over为真输出调试信息并返回NULL
DEBUGF("calloc(%zu, %zu) would overflow, returning NULL", elem_len, elem_cnt);
return NULL;
}
FATAL("calloc(%zu, %zu) would overflow", elem_len, elem_cnt); // 如果no_calloc_over为假输出错误并终止程序
FATAL("calloc(%zu, %zu) would overflow", elem_len, elem_cnt);
}
ret = __dislocator_alloc(len); // 调用内部分配函数
ret = __dislocator_alloc(len);
DEBUGF("calloc(%zu, %zu) = %p [%zu total]", elem_len, elem_cnt, ret,
total_mem); // 输出调试信息
total_mem);
return ret;
}
/* malloc()的包装器。大致相同,
calloc()malloc() */
/* The wrapper for malloc(). Roughly the same, also clobbers the returned
memory (unlike calloc(), malloc() is not guaranteed to return zeroed
memory). */
void* malloc(size_t len) {
void* ret;
ret = __dislocator_alloc(len); // 调用内部分配函数
ret = __dislocator_alloc(len);
DEBUGF("malloc(%zu) = %p [%zu total]", len, ret, total_mem); // 输出调试信息
DEBUGF("malloc(%zu) = %p [%zu total]", len, ret, total_mem);
if (ret && len) memset(ret, ALLOC_CLOBBER, len); // 使用clobber值填充内存
if (ret && len) memset(ret, ALLOC_CLOBBER, len);
return ret;
@ -200,84 +206,70 @@ void* malloc(size_t len) {
read the canary. Not very graceful, but works, right? */
void free(void* ptr) {
// 定义一个变量len用于存储要释放的内存块的长度
u32 len;
// 调试信息,打印正在释放的内存指针地址
DEBUGF("free(%p)", ptr);
u32 len;
DEBUGF("free(%p)", ptr);
if (!ptr) return;
if (PTR_C(ptr) != ALLOC_CANARY) FATAL("bad allocator canary on free()");
// 如果指针为NULL直接返回不进行任何操作
if (!ptr) return;
len = PTR_L(ptr);
// 检查指针的canary值是否正确如果不正确程序将致命错误并退出
if (PTR_C(ptr) != ALLOC_CANARY) FATAL("bad allocator canary on free()");
total_mem -= len;
// 获取指针所指向的内存块的实际长度
len = PTR_L(ptr);
/* Protect everything. Note that the extra page at the end is already
set as PROT_NONE, so we don't need to touch that. */
// 减少全局变量total_mem的值表示当前分配的内存总大小减少
total_mem -= len;
ptr -= PAGE_SIZE * PG_COUNT(len + 8) - len - 8;
// 计算出内存块的实际起始地址,以便后续对整个内存块进行操作
// 减去len+8是因为在分配内存时内存块的前面8个字节用于存储canary和长度信息
ptr -= PAGE_SIZE * PG_COUNT(len + 8) - len - 8;
if (mprotect(ptr - 8, PG_COUNT(len + 8) * PAGE_SIZE, PROT_NONE))
FATAL("mprotect() failed when freeing memory");
// 使用mprotect系统调用来将内存块的权限设置为PROT_NONE即无法读写执行
// 这样可以防止内存块被再次使用,增加了程序的安全性
if (mprotect(ptr - 8, PG_COUNT(len + 8) * PAGE_SIZE, PROT_NONE))
FATAL("mprotect() failed when freeing memory");
/* Keep the mapping; this is wasteful, but prevents ptr reuse. */
// 保持内存映射的存在,虽然这样做会浪费一些内存,但是防止内存地址被重复使用
// 这是一种保护机制,防止使用已经释放的内存
}
/* realloc函数用于重新分配内存其逻辑是
1.
2.
3. freefreemprotect
*/
/* Realloc is pretty straightforward, too. We forcibly reallocate the buffer,
move data, and then free (aka mprotect()) the original one. */
void* realloc(void* ptr, size_t len) {
// 定义一个指针ret用于存储新分配的内存地址
void* ret;
// 为新的长度分配内存分配失败时ret为NULL
ret = malloc(len);
void* ret;
// 如果新内存分配成功且原始指针不为NULL则进行数据复制和原始内存释放
if (ret && ptr) {
// 检查原始指针的canary值是否正确如果不正确程序将致命错误并退出
if (PTR_C(ptr) != ALLOC_CANARY) FATAL("bad allocator canary on realloc()");
ret = malloc(len);
// 将原始内存中的数据复制到新分配的内存中,复制的数据长度为原始内存和新内存长度的最小值
memcpy(ret, ptr, MIN(len, PTR_L(ptr)));
// 释放原始内存free函数中同样会调用mprotect来保护原始内存
free(ptr);
}
if (ret && ptr) {
if (PTR_C(ptr) != ALLOC_CANARY) FATAL("bad allocator canary on realloc()");
// 调试信息,打印原始指针地址、新长度、新内存地址以及当前分配的总内存大小
DEBUGF("realloc(%p, %zu) = %p [%zu total]", ptr, len, ret, total_mem);
memcpy(ret, ptr, MIN(len, PTR_L(ptr)));
free(ptr);
}
DEBUGF("realloc(%p, %zu) = %p [%zu total]", ptr, len, ret, total_mem);
return ret;
// 返回新分配的内存地址
return ret;
}
// __dislocator_init函数在程序加载时通过构造函数属性自动执行
__attribute__((constructor)) void __dislocator_init(void) {
// 定义一个临时变量tmp用于存储环境变量AFL_LD_LIMIT_MB的值
u8* tmp = getenv("AFL_LD_LIMIT_MB");
// 如果环境变量AFL_LD_LIMIT_MB存在则将其转换为max_mem的值以字节为单位
if (tmp) {
// atoi将字符串转换为整数乘以1024*1024表示将MB转换为字节
max_mem = atoi(tmp) * 1024 * 1024;
// 如果转换后的max_mem为0表示环境变量设置不正确程序将致命错误并退出
if (!max_mem) FATAL("Bad value for AFL_LD_LIMIT_MB");
}
// 检查环境变量AFL_LD_VERBOSE是否存在存在则将alloc_verbose设置为1否则为0
alloc_verbose = !!getenv("AFL_LD_VERBOSE");
// 检查环境变量AFL_LD_HARD_FAIL是否存在存在则将hard_fail设置为1否则为0
hard_fail = !!getenv("AFL_LD_HARD_FAIL");
// 检查环境变量AFL_LD_NO_CALLOC_OVER是否存在存在则将no_calloc_over设置为1否则为0
no_calloc_over = !!getenv("AFL_LD_NO_CALLOC_OVER");
u8* tmp = getenv("AFL_LD_LIMIT_MB");
if (tmp) {
max_mem = atoi(tmp) * 1024 * 1024;
if (!max_mem) FATAL("Bad value for AFL_LD_LIMIT_MB");
}
alloc_verbose = !!getenv("AFL_LD_VERBOSE");
hard_fail = !!getenv("AFL_LD_HARD_FAIL");
no_calloc_over = !!getenv("AFL_LD_NO_CALLOC_OVER");
}

@ -33,95 +33,89 @@
#include "../types.h"
#include "../config.h"
// 检查是否为Linux系统如果不是则报错
#ifndef __linux__
# error "Sorry, this library is Linux-specific for now!"
#endif /* !__linux__ */
// 定义最大映射数量
/* Mapping data and such */
#define MAX_MAPPINGS 1024
// 定义映射结构体,存储只读内存区域的起始和结束地址
static struct mapping {
void *st, *en;
} __tokencap_ro[MAX_MAPPINGS];
// 定义当前加载的只读映射数量
static u32 __tokencap_ro_cnt;
// 定义只读映射是否已加载标志
static u8 __tokencap_ro_loaded;
// 定义输出文件指针
static FILE* __tokencap_out_file;
// 功能:加载只读内存区域的映射信息
// 通过读取/proc/self/maps文件识别出只读且不可写的内存区域并存储在__tokencap_ro数组中
/* Identify read-only regions in memory. Only parameters that fall into these
ranges are worth dumping when passed to strcmp() and so on. Read-write
regions are far more likely to contain user input instead. */
static void __tokencap_load_mappings(void) {
u8 buf[MAX_LINE]; // 用于存储每行读取的内存映射信息
FILE* f = fopen("/proc/self/maps", "r"); // 打开/proc/self/maps文件该文件包含了当前进程的内存映射信息
u8 buf[MAX_LINE];
FILE* f = fopen("/proc/self/maps", "r");
__tokencap_ro_loaded = 1; // 标记只读映射已加载
__tokencap_ro_loaded = 1;
if (!f) return; // 如果文件打开失败,则直接返回
if (!f) return;
// 逐行读取文件内容
while (fgets(buf, MAX_LINE, f)) {
u8 rf, wf; // rf表示是否可读wf表示是否可写
void* st, *en; // st表示内存区域的起始地址en表示内存区域的结束地址
u8 rf, wf;
void* st, *en;
// 解析每行内存映射信息,提取起始地址、结束地址、是否可读和是否可写
if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue;
if (wf == 'w' || rf != 'r') continue; // 跳过可写或不可读的内存区域
if (wf == 'w' || rf != 'r') continue;
// 将只读内存区域的起始和结束地址存储到数组中
__tokencap_ro[__tokencap_ro_cnt].st = (void*)st;
__tokencap_ro[__tokencap_ro_cnt].en = (void*)en;
// 如果已达到最大映射数量,则停止加载
if (++__tokencap_ro_cnt == MAX_MAPPINGS) break;
}
fclose(f); // 关闭文件
fclose(f);
}
// 功能:检查给定地址是否位于只读内存区域
// 如果未加载映射信息则先调用__tokencap_load_mappings加载映射信息
/* Check an address against the list of read-only mappings. */
static u8 __tokencap_is_ro(const void* ptr) {
u32 i;
if (!__tokencap_ro_loaded) __tokencap_load_mappings(); // 如果只读映射未加载,则加载
if (!__tokencap_ro_loaded) __tokencap_load_mappings();
// 遍历只读映射数组,检查给定地址是否在任一只读内存区域内
for (i = 0; i < __tokencap_ro_cnt; i++)
if (ptr >= __tokencap_ro[i].st && ptr <= __tokencap_ro[i].en) return 1;
return 0; // 如果不在任何只读内存区域内则返回0
return 0;
}
// 功能:将感兴趣的数据转储到输出文件中
// 数据会被正确引用和转义,例如,非打印字符会被转义为\xXX的形式
/* Dump an interesting token to output file, quoting and escaping it
properly. */
static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
u8 buf[MAX_AUTO_EXTRA * 4 + 1]; // 存储转义后的数据
u8 buf[MAX_AUTO_EXTRA * 4 + 1];
u32 i;
u32 pos = 0; // 当前写入buf的位置
u32 pos = 0;
// 如果数据长度不符合要求或输出文件未打开,则直接返回
if (len < MIN_AUTO_EXTRA || len > MAX_AUTO_EXTRA || !__tokencap_out_file)
return;
// 遍历数据,进行转义处理
for (i = 0; i < len; i++) {
// 如果是文本数据且遇到空字符,则停止处理
if (is_text && !ptr[i]) break;
// 根据字符类型进行处理
switch (ptr[i]) {
case 0 ... 31:
@ -129,237 +123,189 @@ static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
case '\"':
case '\\':
// 对于非打印字符、双引号和反斜杠进行转义
sprintf(buf + pos, "\\x%02x", ptr[i]);
pos += 4; // 转义后的字符串长度为4
pos += 4;
break;
default:
// 对于可打印字符直接复制到buf中
buf[pos++] = ptr[i];
}
}
buf[pos] = 0; // 添加字符串结束符
buf[pos] = 0;
// 将转义后的字符串写入输出文件
fprintf(__tokencap_out_file, "\"%s\"\n", buf);
}
// 功能替换strcmp函数用于识别并转储只读内存区域中的字符串
// 如果目标程序编译时使用了-fno-builtins选项并动态链接则会使用此函数
/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used
only if the target is compiled with -fno-builtins and linked dynamically. */
#undef strcmp
int strcmp(const char* str1, const char* str2) {
// 检查str1是否位于只读内存区域如果是则转储其内容
if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1);
// 检查str2是否位于只读内存区域如果是则转储其内容
if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1);
// 实现strcmp函数的核心功能比较两个字符串
while (1) {
unsigned char c1 = *str1, c2 = *str2;
// 如果两个字符不同,则返回比较结果
if (c1 != c2) return (c1 > c2) ? 1 : -1;
// 如果遇到字符串结束符则返回0
if (!c1) return 0;
// 指向下一个字符
str1++; str2++;
}
}
// 取消定义原本的strncmp函数以便重新定义
#undef strncmp
// 自定义的strncmp函数用于比较两个字符串的前len个字符
int strncmp(const char* str1, const char* str2, size_t len) {
// 检查str1是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1);
// 检查str2是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1);
// 循环比较两个字符串的前len个字符
while (len--) {
unsigned char c1 = *str1, c2 = *str2; // 获取当前字符
unsigned char c1 = *str1, c2 = *str2;
// 如果str1的第一个字符为0即字符串结束则返回0
if (!c1) return 0;
// 如果当前字符不相等根据字符的ASCII值大小返回1或-1
if (c1 != c2) return (c1 > c2) ? 1 : -1;
str1++; str2++; // 移动到下一个字符
str1++; str2++;
}
// 如果循环结束说明前len个字符都相等返回0
return 0;
}
// 取消定义原本的strcasecmp函数以便重新定义
#undef strcasecmp
// 自定义的strcasecmp函数用于忽略大小写的字符串比较
int strcasecmp(const char* str1, const char* str2) {
// 检查str1是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1);
// 检查str2是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1);
// 无限循环,逐字符比较两个字符串(忽略大小写)
while (1) {
unsigned char c1 = tolower(*str1), c2 = tolower(*str2); // 转换为小写后比较
unsigned char c1 = tolower(*str1), c2 = tolower(*str2);
// 如果当前字符不相等根据字符的ASCII值大小返回1或-1
if (c1 != c2) return (c1 > c2) ? 1 : -1;
// 如果str1的第一个字符为0即字符串结束则返回0
if (!c1) return 0;
str1++; str2++; // 移动到下一个字符
str1++; str2++;
}
}
// 取消定义原本的strncasecmp函数以便重新定义
#undef strncasecmp
// 自定义的strncasecmp函数用于忽略大小写的字符串前len个字符比较
int strncasecmp(const char* str1, const char* str2, size_t len) {
// 检查str1是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1);
// 检查str2是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1);
// 循环比较两个字符串的前len个字符忽略大小写
while (len--) {
unsigned char c1 = tolower(*str1), c2 = tolower(*str2); // 转换为小写后比较
unsigned char c1 = tolower(*str1), c2 = tolower(*str2);
// 如果str1的第一个字符为0即字符串结束则返回0
if (!c1) return 0;
// 如果当前字符不相等根据字符的ASCII值大小返回1或-1
if (c1 != c2) return (c1 > c2) ? 1 : -1;
str1++; str2++; // 移动到下一个字符
str1++; str2++;
}
// 如果循环结束说明前len个字符都相等返回0
return 0;
}
// 取消定义原本的memcmp函数以便重新定义
#undef memcmp
// 自定义的memcmp函数用于比较两个内存区域的前len个字节
int memcmp(const void* mem1, const void* mem2, size_t len) {
// 检查mem1是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0);
// 检查mem2是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0);
// 循环比较两个内存区域的前len个字节
while (len--) {
unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2; // 获取当前字节
// 如果当前字节不相等根据字节的ASCII值大小返回1或-1
unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2;
if (c1 != c2) return (c1 > c2) ? 1 : -1;
mem1++; mem2++; // 移动到下一个字节
mem1++; mem2++;
}
// 如果循环结束说明前len个字节都相等返回0
return 0;
}
// 取消定义原本的strstr函数以便重新定义
#undef strstr
// 自定义的strstr函数用于在haystack字符串中查找needle字符串
char* strstr(const char* haystack, const char* needle) {
// 检查haystack是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(haystack))
__tokencap_dump(haystack, strlen(haystack), 1);
// 检查needle是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(needle))
__tokencap_dump(needle, strlen(needle), 1);
// 在haystack中查找needle
do {
const char* n = needle;
const char* h = haystack;
// 逐字符比较haystack和needle
while(*n && *h && *n == *h) n++, h++;
// 如果needle的所有字符都被匹配返回匹配开始的位置
if(!*n) return (char*)haystack;
} while (*(haystack++)); // 移动到haystack的下一个字符并继续查找
} while (*(haystack++));
// 如果没有找到needle返回NULL
return 0;
}
// 取消定义原本的strcasestr函数以便重新定义
#undef strcasestr
// 自定义的strcasestr函数用于忽略大小写的字符串查找
char* strcasestr(const char* haystack, const char* needle) {
// 检查haystack是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(haystack))
__tokencap_dump(haystack, strlen(haystack), 1);
// 检查needle是否为只读内存如果是则输出内存内容
if (__tokencap_is_ro(needle))
__tokencap_dump(needle, strlen(needle), 1);
// 在haystack中查找needle忽略大小写
do {
const char* n = needle;
const char* h = haystack;
// 逐字符比较haystack和needle忽略大小写
while(*n && *h && tolower(*n) == tolower(*h)) n++, h++;
// 如果needle的所有字符都被匹配返回匹配开始的位置
if(!*n) return (char*)haystack;
} while(*(haystack++)); // 移动到haystack的下一个字符并继续查找
} while(*(haystack++));
// 如果没有找到needle返回NULL
return 0;
}
// 程序启动时执行的初始化代码
/* Init code to open the output file (or default to stderr). */
__attribute__((constructor)) void __tokencap_init(void) {
u8* fn = getenv("AFL_TOKEN_FILE"); // 获取环境变量AFL_TOKEN_FILE的值
// 如果环境变量存在,则以追加模式打开文件
u8* fn = getenv("AFL_TOKEN_FILE");
if (fn) __tokencap_out_file = fopen(fn, "a");
// 如果文件打开失败,则将输出重定向到标准错误输出
if (!__tokencap_out_file) __tokencap_out_file = stderr;
}

@ -28,470 +28,209 @@
of flags, and then calls the real compiler.
*/
#define AFL_MAIN
#define AFL_MAIN // 定义宏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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h> // 引入标准输入输出库
#include <unistd.h> // 引入UNIX标准库
#include <stdlib.h> // 引入标准库
#include <string.h> // 引入字符串处理库
static u8* obj_path; /* Path to runtime libraries */
static u8** cc_params; /* Parameters passed to the real CC */
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
static u8* obj_path; /* 运行时库的路径 */
static u8** cc_params; /* 传递给真实编译器的参数 */
static u32 cc_par_cnt = 1; /* 参数计数初始包含argv0 */
/*
*/
/* 尝试查找运行时库。如果失败,则中止。 */
static void find_obj(u8* argv0) {
u8 *afl_path = getenv("AFL_PATH"); // 获取环境变量 AFL_PATH 的值
u8 *slash, *tmp; // 定义用于存储路径分隔符和临时路径的变量
if (afl_path) { // 如果 AFL_PATH 环境变量存在
tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path); // 构造运行时库的路径
if (!access(tmp, R_OK)) { // 检查路径是否可读
obj_path = afl_path; // 如果可读,设置 obj_path 为 AFL_PATH
ck_free(tmp); // 释放临时路径的内存
return; // 返回,结束函数
}
ck_free(tmp); // 如果路径不可读,释放临时路径的内存
}
slash = strrchr(argv0, '/'); // 查找 argv0 中最后一个斜杠的位置
if (slash) { // 如果找到斜杠
u8 *dir; // 定义用于存储目录路径的变量
*slash = 0; // 将斜杠位置置为字符串结束符,以便提取目录路径
dir = ck_strdup(argv0); // 复制 argv0 的目录路径
*slash = '/'; // 恢复斜杠
tmp = alloc_printf("%s/afl-llvm-rt.o", dir); // 构造运行时库的路径
if (!access(tmp, R_OK)) { // 检查路径是否可读
obj_path = dir; // 如果可读,设置 obj_path 为当前目录
ck_free(tmp); // 释放临时路径的内存
return; // 返回,结束函数
}
ck_free(tmp); // 如果路径不可读,释放临时路径的内存
ck_free(dir); // 释放目录路径的内存
}
// 检查默认的 AFL_PATH 目录下是否存在 'afl-llvm-rt.o' 文件
if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) {
obj_path = AFL_PATH; // 如果存在,设置 obj_path 为 AFL_PATH
return; // 返回,结束函数
}
// 如果以上方法都未找到运行时库,抛出致命错误
FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH");
}
/*
argv cc_params
*/
static void edit_params(u32 argc, char** argv) {
u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; // 初始化标志变量
u8 *name; // 定义用于存储程序名的变量
// 为 cc_params 分配足够的内存,以容纳传入的参数加上额外的空间
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
// 提取程序名
name = strrchr(argv[0], '/'); // 查找 argv0 中最后一个斜杠的位置
if (!name) name = argv[0]; else name++; // 如果没有斜杠,使用完整的 argv[0]
// 根据程序名确定使用的编译器
if (!strcmp(name, "afl-clang-fast++")) { // 如果程序名是 afl-clang-fast++
u8* alt_cxx = getenv("AFL_CXX"); // 获取环境变量 AFL_CXX 的值
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++"; // 如果存在则使用其值,否则默认使用 clang++
} else { // 如果程序名不是 afl-clang-fast++
u8* alt_cc = getenv("AFL_CC"); // 获取环境变量 AFL_CC 的值
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; // 如果存在则使用其值,否则默认使用 clang
}
#ifdef USE_TRACE_PC // 如果定义了 USE_TRACE_PC
// 添加用于 sanitization 的覆盖率参数
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // 启用 trace-pc-guard 插桩
#ifndef __ANDROID__ // 如果不是 Android 平台
cc_params[cc_par_cnt++] = "-mllvm"; // 添加 LLVM 相关参数
cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; // 设置目标覆盖率的阈值为 0
#endif
#else // 如果没有定义 USE_TRACE_PC
// 添加 LLVM 插件的加载参数
cc_params[cc_par_cnt++] = "-Xclang"; // 添加 Clang 参数
cc_params[cc_par_cnt++] = "-load"; // 指定加载插件
cc_params[cc_par_cnt++] = "-Xclang"; // 添加 Clang 参数
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
// 追加 AFL 关键依赖项 afl-llvm-pass.so 到 cc_params用于插桩分析
#endif /* ^USE_TRACE_PC */
cc_params[cc_par_cnt++] = "-Qunused-arguments";
// 添加一个参数,告诉编译器忽略未使用的命令行参数
while (--argc) { // 循环处理剩余的命令行参数
u8* cur = *(++argv); // 获取当前参数
if (!strcmp(cur, "-m32")) bit_mode = 32; // 如果参数是 -m32设置 bit_mode 为 32
if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; // 针对特定架构的设置
if (!strcmp(cur, "-m64")) bit_mode = 64; // 如果参数是 -m64设置 bit_mode 为 64
if (!strcmp(cur, "-x")) x_set = 1; // 如果参数为 -x设置 x_set 为 1表示启用此选项
// 检查是否使用地址或内存的安全检测
if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
// 检查是否启用了 FORTIFY_SOURCE
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
// 如果链接器选项是 -Wl,-z,defs 或 -Wl,--no-undefined跳过该参数
if (!strcmp(cur, "-Wl,-z,defs") ||
!strcmp(cur, "-Wl,--no-undefined")) continue;
cc_params[cc_par_cnt++] = cur; // 将当前参数添加到 cc_params
}
// 如果环境变量 AFL_HARDEN 存在
if (getenv("AFL_HARDEN")) {
cc_params[cc_par_cnt++] = "-fstack-protector-all"; // 启用所有堆栈保护
// 如果未启用 FORTIFY_SOURCE添加定义以启用其功能
if (!fortify_set)
cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
}
// 如果尚未启用 AddressSanitizer
if (!asan_set) {
// 检查是否设置了使用 AddressSanitizer 的环境变量
if (getenv("AFL_USE_ASAN")) {
// 检查 MSAN 和 ASAN 互斥
if (getenv("AFL_USE_MSAN"))
FATAL("ASAN and MSAN are mutually exclusive");
// 检查 AFL_HARDEN 是否与 ASAN 互斥
if (getenv("AFL_HARDEN"))
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 取消定义 FORTIFY_SOURCE
cc_params[cc_par_cnt++] = "-fsanitize=address"; // 启用 AddressSanitizer
}
// 检查是否设置了使用 MemorySanitizer 的环境变量
else if (getenv("AFL_USE_MSAN")) {
// 检查 ASAN 和 MSAN 互斥
if (getenv("AFL_USE_ASAN"))
FATAL("ASAN and MSAN are mutually exclusive");
// 检查 AFL_HARDEN 是否与 MSAN 互斥
if (getenv("AFL_HARDEN"))
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 取消定义 FORTIFY_SOURCE
cc_params[cc_par_cnt++] = "-fsanitize=memory"; // 启用 MemorySanitizer
}
}
// 如果定义了 USE_TRACE_PC
#ifdef USE_TRACE_PC
// 检查 AFL_INST_RATIO 环境变量是否可用
if (getenv("AFL_INST_RATIO"))
FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'.");
#endif /* USE_TRACE_PC */
// 如果未设置 AFL_DONT_OPTIMIZE则启用优化选项
if (!getenv("AFL_DONT_OPTIMIZE")) {
cc_params[cc_par_cnt++] = "-g"; // 添加调试信息
cc_params[cc_par_cnt++] = "-O3"; // 启用高优化级别
cc_params[cc_par_cnt++] = "-funroll-loops"; // 启用循环展开
}
// 如果设置了 AFL_NO_BUILTIN则禁用特定的内置函数
if (getenv("AFL_NO_BUILTIN")) {
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; // 禁用 strcmp 的内置实现
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; // 禁用 strncmp 的内置实现
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; // 禁用 strcasecmp 的内置实现
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; // 禁用 strncasecmp 的内置实现
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; // 禁用 memcmp 的内置实现
}
// 添加 AFL 控制宏,确保手动控制和编译器的定义
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; // 指示支持手动控制
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; // 编译器标识
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; // 表示为不安全的生产模式
/*
使 forkserver
(便 afl-fuzz )
.o
1)
__attribute__((used))
2) -Wl,--gc-sections
'volatile'
3) __afl_persistent_loop()
- :: extern "C"
__attribute__((alias(...))) 使 __asm__
*/
// 定义一个宏,用于处理 AFL 循环的持久化
cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)="
// 开始一个代码块,其中静态的、易失的字符指针用于存放持久化的信号
"({ static volatile char *_B __attribute__((used)); "
" _B = (char*)\"" PERSIST_SIG "\"; "; // 将持久化信号字符串赋值给指针 _B
#ifdef __APPLE__ // 根据系统平台选择合适的函数名称
"__attribute__((visibility(\"default\"))) "
"int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " // Apple 平台下的持久化函数
#else
"__attribute__((visibility(\"default\"))) "
"int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " // 其他平台下的持久化函数
#endif /* ^__APPLE__ */
"_L(_A); })"; // 调用持久化函数,并结束代码块
// 定义一个宏,用于初始化 AFL
cc_params[cc_par_cnt++] = "-D__AFL_INIT()="
"do { static volatile char *_A __attribute__((used)); "
" _A = (char*)\"" DEFER_SIG "\"; "; // 将延迟信号字符串赋值给指针 _A
#ifdef __APPLE__ // 根据系统平台选择合适的初始化函数名称
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"___afl_manual_init\"); " // Apple 平台下的初始化函数
#else
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"__afl_manual_init\"); " // 其他平台下的初始化函数
#endif /* ^__APPLE__ */
"_I(); } while (0)"; // 调用初始化函数,并结束循环结构
// 如果 x_set 被设置,添加参数来指示关闭类型检查
if (x_set) {
cc_params[cc_par_cnt++] = "-x"; // 添加-x参数
cc_params[cc_par_cnt++] = "none"; // 指示后续没有特定类型
}
// 如果不是在 Android 平台下
#ifndef __ANDROID__
switch (bit_mode) { // 根据位数选择合适的运行时库
case 0: // 如果未设置位模式
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); // 使用默认的 afl-llvm-rt.o 路径
break;
case 32: // 如果设置了 32 位模式
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); // 使用 32 位运行时库路径
// 检查路径是否可读,如果不可读则抛出错误
if (access(cc_params[cc_par_cnt - 1], R_OK))
FATAL("-m32 is not supported by your compiler"); // 抛出错误信息
break;
case 64: // 如果设置了 64 位模式
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); // 使用 64 位运行时库路径
/* 查找运行时库的路径。如果找不到,则中止程序。 */
static void find_obj(u8* argv0) {
// 获取环境变量 AFL_PATH
u8 *afl_path = getenv("AFL_PATH");
u8 *afl_path = getenv("AFL_PATH"); // 获取环境变量AFL_PATH
u8 *slash, *tmp;
// 如果找到 AFL_PATH
if (afl_path) {
if (afl_path) { // 如果设置了AFL_PATH
// 生成 afl-llvm-rt.o 的完整路径
tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path);
tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path); // 格式化路径
// 检查文件是否可读
if (!access(tmp, R_OK)) {
obj_path = afl_path; // 设置对象路径为 AFL_PATH
ck_free(tmp); // 释放临时路径内存
return; // 找到文件,结束函数
if (!access(tmp, R_OK)) { // 检查文件是否可读
obj_path = afl_path; // 设置运行时库路径
ck_free(tmp); // 释放临时字符串
return;
}
ck_free(tmp); // 释放临时路径内存
ck_free(tmp); // 释放临时字符串
}
// 查找 argv0 中最后一个 '/' 的位置
slash = strrchr(argv0, '/');
slash = strrchr(argv0, '/'); // 查找路径中的最后一个斜杠
// 如果找到 '/'
if (slash) {
if (slash) { // 如果找到斜杠
u8 *dir;
*slash = 0; // 将 '/' 替换为结束符,以获取目录
dir = ck_strdup(argv0); // 复制目录名
*slash = '/'; // 恢复原来的 '/' 字符
*slash = 0; // 将斜杠替换为结束符
dir = ck_strdup(argv0); // 复制路径
*slash = '/'; // 恢复斜杠
// 生成 afl-llvm-rt.o 的完整路径
tmp = alloc_printf("%s/afl-llvm-rt.o", dir);
tmp = alloc_printf("%s/afl-llvm-rt.o", dir); // 格式化路径
// 检查文件是否可读
if (!access(tmp, R_OK)) {
obj_path = dir; // 设置对象路径为找到的目录
ck_free(tmp); // 释放临时路径内存
return; // 找到文件,结束函数
if (!access(tmp, R_OK)) { // 检查文件是否可读
obj_path = dir; // 设置运行时库路径
ck_free(tmp); // 释放临时字符串
return;
}
ck_free(tmp); // 释放临时路径内存
ck_free(dir); // 释放目录名内存
ck_free(tmp); // 释放临时字符串
ck_free(dir); // 释放目录字符串
}
// 检查默认路径下的 afl-llvm-rt.o 是否可读
if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) {
obj_path = AFL_PATH; // 设置对象路径为默认的 AFL_PATH
return; // 找到文件,结束函数
if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) { // 检查默认路径
obj_path = AFL_PATH; // 设置运行时库路径
return;
}
// 如果都找不到,则抛出致命错误
FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH");
}
FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH"); // 查找失败时提示错误
}
/* 复制 argv cc_params并进行必要的编辑。 */
/* 复制argv到cc_params并进行必要的编辑。 */
static void edit_params(u32 argc, char** argv) {
// 初始化标志变量
u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0;
u8 *name;
u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; // 初始化标志变量
u8 *name; // 当前文件名
// 分配空间以存储参数
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
cc_params = ck_alloc((argc + 128) * sizeof(u8*)); // 分配参数数组内存
// 获取执行的程序名称
name = strrchr(argv[0], '/');
if (!name) name = argv[0]; else name++;
name = strrchr(argv[0], '/'); // 获取程序名
if (!name) name = argv[0]; else name++; // 如果没有斜杠,则使用完整路径
// 根据程序名称选择合适的编译器
// 根据程序名设置编译器
if (!strcmp(name, "afl-clang-fast++")) {
u8* alt_cxx = getenv("AFL_CXX"); // 获取环境变量 AFL_CXX
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++"; // 设置 C++ 编译器
u8* alt_cxx = getenv("AFL_CXX"); // 获取环境变量AFL_CXX
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++"; // 设置C++编译器
} else {
u8* alt_cc = getenv("AFL_CC"); // 获取环境变量 AFL_CC
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; // 设置 C 编译器
u8* alt_cc = getenv("AFL_CC"); // 获取环境变量AFL_CC
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; // 设置C编译器
}
/* 有两种编译afl-clang-fast的方式。传统模式下我们使用afl-llvm-pass.so注入插桩。
'trace-pc-guard'使LLVM */
#ifdef USE_TRACE_PC
// 启用跟踪 PC 的覆盖率
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // 添加Trace PC Guard标志
#ifndef __ANDROID__
cc_params[cc_par_cnt++] = "-mllvm"; // LLVM 选项
cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; // 设定阈值
cc_params[cc_par_cnt++] = "-mllvm"; // 添加LLVM的命令行标志
cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0"; // 设置阈值为0
#endif
#else
// 加载 afl-llvm-pass.so 插件
cc_params[cc_par_cnt++] = "-Xclang";
cc_params[cc_par_cnt++] = "-load";
cc_params[cc_par_cnt++] = "-Xclang";
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
cc_params[cc_par_cnt++] = "-Xclang"; // 添加编译器的命令行标志
cc_params[cc_par_cnt++] = "-load"; // 加载指定共享库
cc_params[cc_par_cnt++] = "-Xclang"; // 添加编译器的命令行标志
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path); // 添加动态库路径
#endif /* ^USE_TRACE_PC */
cc_params[cc_par_cnt++] = "-Qunused-arguments"; // 忽略未使用的参数
cc_params[cc_par_cnt++] = "-Qunused-arguments"; // 添加未使用参数警告
// 循环处理输入参数
// 处理命令行参数
while (--argc) {
u8* cur = *(++argv); // 获取当前参数
u8* cur = *(++argv); // 当前参数
// 检查位模式
if (!strcmp(cur, "-m32")) bit_mode = 32;
if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;
if (!strcmp(cur, "-m64")) bit_mode = 64;
if (!strcmp(cur, "-m32")) bit_mode = 32; // 检查32位模式
if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; // ARM平台
if (!strcmp(cur, "-m64")) bit_mode = 64; // 检查64位模式
// 检查其他编译选项
if (!strcmp(cur, "-x")) x_set = 1;
if (!strcmp(cur, "-x")) x_set = 1; // 检查-x标志
// 检查是否启用地址/内存的 sanitization
// 检查Address Sanitizer
if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
// 检查 FORTIFY_SOURCE 是否启用
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; // 检查FORTIFY_SOURCE
// 跳过某些链接器选项
if (!strcmp(cur, "-Wl,-z,defs") ||
!strcmp(cur, "-Wl,--no-undefined")) continue;
!strcmp(cur, "-Wl,--no-undefined")) continue; // 跳过链接器选项
// 将当前参数添加到 cc_params
cc_params[cc_par_cnt++] = cur;
cc_params[cc_par_cnt++] = cur; // 添加当前参数到编译参数数组
}
// 如果启用了硬化选项
// 检查环境变量AFL_HARDEN
if (getenv("AFL_HARDEN")) {
cc_params[cc_par_cnt++] = "-fstack-protector-all"; // 启用栈保护
// 如果没有启用 FORTIFY_SOURCE 则添加对应宏定义
if (!fortify_set)
if (!fortify_set) // 如果没有FORTIFY_SOURCE则添加相关标志
cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
}
// 检查地址或内存 sanitization 的设置
// 检查Address Sanitizer和Memory Sanitizer的互斥
if (!asan_set) {
if (getenv("AFL_USE_ASAN")) {
if (getenv("AFL_USE_ASAN")) { // 如果启用Address Sanitizer
if (getenv("AFL_USE_MSAN"))
FATAL("ASAN and MSAN are mutually exclusive"); // 互斥检查
FATAL("ASAN和MSAN是互斥的"); // 互斥检查
if (getenv("AFL_HARDEN"))
FATAL("ASAN and AFL_HARDEN are mutually exclusive"); // 互斥检查
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 禁用 FORTIFY_SOURCE
cc_params[cc_par_cnt++] = "-fsanitize=address"; // 启用地址条件检测
} else if (getenv("AFL_USE_MSAN")) {
FATAL("ASAN和AFL_HARDEN是互斥的"); // 互斥检查
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 添加标志
cc_params[cc_par_cnt++] = "-fsanitize=address"; // 启用Address Sanitizer
} else if (getenv("AFL_USE_MSAN")) { // 如果启用Memory Sanitizer
if (getenv("AFL_USE_ASAN"))
FATAL("ASAN and MSAN are mutually exclusive"); // 互斥检查
FATAL("ASAN和MSAN是互斥的"); // 互斥检查
if (getenv("AFL_HARDEN"))
FATAL("MSAN and AFL_HARDEN are mutually exclusive"); // 互斥检查
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 禁用 FORTIFY_SOURCE
cc_params[cc_par_cnt++] = "-fsanitize=memory"; // 启用内存条件检测
FATAL("MSAN和AFL_HARDEN是互斥的"); // 互斥检查
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; // 添加标志
cc_params[cc_par_cnt++] = "-fsanitize=memory"; // 启用Memory Sanitizer
}
}
#ifdef USE_TRACE_PC
// 检查 AFL_INST_RATIO 环境变量的设置
if (getenv("AFL_INST_RATIO"))
FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'.");
if (getenv("AFL_INST_RATIO"))
FATAL("AFL_INST_RATIO在'trace-pc'模式下不可用"); // 提示用户
#endif /* USE_TRACE_PC */
// 检查是否不优化
if (!getenv("AFL_DONT_OPTIMIZE")) {
cc_params[cc_par_cnt++] = "-g"; // 启用调试信息
cc_params[cc_par_cnt++] = "-O3"; // 启用最高级别的优化
cc_params[cc_par_cnt++] = "-funroll-loops"; // 循环展开以提高性能
}
if (!getenv("AFL_DONT_OPTIMIZE")) { // 如果没有设置不优化选项
cc_params[cc_par_cnt++] = "-g"; // 添加调试信息
cc_params[cc_par_cnt++] = "-O3"; // 添加优化等级
cc_params[cc_par_cnt++] = "-funroll-loops"; // 启用循环展开
}
// 检查是否禁用内置函数的使用
if (getenv("AFL_NO_BUILTIN")) {
// 禁用特定的内置字符串比较和内存比较函数
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
}
if (getenv("AFL_NO_BUILTIN")) { // 检查是否使用内建函数
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; // 禁用strcmp内建
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; // 禁用strncmp内建
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; // 禁用strcasecmp内建
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; // 禁用strncasecmp内建
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; // 禁用memcmp内建
}
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; // 添加手动控制标志
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; // 添加编译器标志
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; // 添加不安全模式标志
/* 当用户尝试使用持久化或延迟fork服务器模式时
.o
// 添加 AFL 相关的宏定义
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; // 手动控制
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; // 标记为 AFL 编译器
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; // 不适合生产的模糊构建模式
1) 使__attribute__((used))
2) 使-Wl,--gc-sections
'volatile'
3) __afl_persistent_loop()
使__asm__
// 添加 AFL 循环的实现
cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)="
*/
cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)="
"({ static volatile char *_B __attribute__((used)); "
" _B = (char*)\"" PERSIST_SIG "\"; "
#ifdef __APPLE__
@ -501,10 +240,9 @@ cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)="
"__attribute__((visibility(\"default\"))) "
"int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
#endif /* ^__APPLE__ */
"_L(_A); })"; // 定义 AFL 循环的宏
"_L(_A); })";
// 添加 AFL 初始化函数的实现
cc_params[cc_par_cnt++] = "-D__AFL_INIT()="
cc_params[cc_par_cnt++] = "-D__AFL_INIT()="
"do { static volatile char *_A __attribute__((used)); "
" _A = (char*)\"" DEFER_SIG "\"; "
#ifdef __APPLE__
@ -514,59 +252,57 @@ cc_params[cc_par_cnt++] = "-D__AFL_INIT()="
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"__afl_manual_init\"); "
#endif /* ^__APPLE__ */
"_I(); } while (0)"; // 定义 AFL 初始化的宏
"_I(); } while (0)";
// 如果启用 -x 选项
if (x_set) {
cc_params[cc_par_cnt++] = "-x"; // 添加 -x 选项
cc_params[cc_par_cnt++] = "none"; // 设置为 none表示不对输入进行特定语言解析
}
if (x_set) { // 检查是否有-x标志
cc_params[cc_par_cnt++] = "-x"; // 添加-x标志
cc_params[cc_par_cnt++] = "none"; // 设置参数
}
#ifndef __ANDROID__
// 根据位模式选择不同的运行时库文件
switch (bit_mode) {
switch (bit_mode) { // 根据位模式选择运行时库
case 0:
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); // 默认情况下使用通用的运行时文件
break;
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); // 添加默认路径
break;
case 32:
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); // 使用 32 位版本的运行时文件
// 检查此文件是否可读
if (access(cc_params[cc_par_cnt - 1], R_OK))
FATAL("-m32 is not supported by your compiler"); // 如果不可读则报错
break;
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); // 32位库路径
if (access(cc_params[cc_par_cnt - 1], R_OK)) // 检查权限
FATAL("-m32不被你的编译器支持"); // 提示不支持
break;
case 64:
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); // 使用 64 位版本的运行时文件
// 检查此文件是否可读
if (access(cc_params[cc_par_cnt - 1], R_OK))
FATAL("-m64 is not supported by your compiler"); // 如果不可读则报错
break;
}
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); // 64位库路径
// 结束参数数组,以空指针结尾
cc_params[cc_par_cnt] = NULL;
if (access(cc_params[cc_par_cnt - 1], R_OK)) // 检查权限
FATAL("-m64不被你的编译器支持"); // 提示不支持
break;
}
#endif
cc_params[cc_par_cnt] = NULL; // 结束参数数组
}
/* 主入口点 */
int main(int argc, char** argv) {
// 检查标准错误是否连接到终端且 AFL_QUIET 环境变量未设置
if (isatty(2) && !getenv("AFL_QUIET")) {
if (isatty(2) && !getenv("AFL_QUIET")) { // 检查终端和环境变量
#ifdef USE_TRACE_PC
// 如果启用了 USE_TRACE_PC打印版本信息
SAYF(cCYA "afl-clang-fast [tpcg] " cBRI VERSION cRST " by <lszekeres@google.com>\n");
SAYF(cCYA "afl-clang-fast [tpcg] " cBRI VERSION cRST " by <lszekeres@google.com>\n"); // 输出版本信息
#else
// 否则,仅打印版本信息
SAYF(cCYA "afl-clang-fast " cBRI VERSION cRST " by <lszekeres@google.com>\n");
SAYF(cCYA "afl-clang-fast " cBRI VERSION cRST " by <lszekeres@google.com>\n"); // 输出版本信息
#endif /* ^USE_TRACE_PC */
}
// 如果参数数量小于 2打印帮助信息并退出
if (argc < 2) {
if (argc < 2) { // 检查参数数量
SAYF("\n"
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
"for clang, letting you recompile third-party code with the required runtime\n"
@ -580,25 +316,22 @@ int main(int argc, char** argv) {
"You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. Setting\n"
"AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
BIN_PATH, BIN_PATH);
BIN_PATH, BIN_PATH); // 输出用法说明
exit(1); // 退出程序,返回错误码 1
exit(1); // 退出程序
}
#ifndef __ANDROID__
// 在非 Android 平台下调用 find_obj 函数查找运行时库
find_obj(argv[0]);
find_obj(argv[0]); // 查找运行时库
#endif
// 调用 edit_params 函数处理命令行参数并设置编译参数
edit_params(argc, argv);
edit_params(argc, argv); // 编辑参数
execvp(cc_params[0], (char**)cc_params); // 执行真实编译器
// 使用 execvp 执行指定的编译器命令
execvp(cc_params[0], (char**)cc_params);
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); // 执行失败提示
// 如果 execvp 失败,抛出致命错误并打印相关信息
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
return 0; // 返回0表示成功
return 0; // 正常结束程序
}

Loading…
Cancel
Save