首页
社区
课程
招聘
[原创]AFL源码分析(1)
发表于: 2025-8-16 15:28 4265

[原创]AFL源码分析(1)

2025-8-16 15:28
4265

basic block(基本块)、edge(边)、代码覆盖率

edge就被用来表示在基本块之间的跳转,知道了每个基本块和跳转的执行次数,就可以知道程序中的每个语句和分支的执行次数,从而获得比记录BB更细粒度的覆盖率信息

代码覆盖率是一种度量代码的覆盖程度的方式,也就是指源代码中的某行代码是否已执行;对二进制程序,还可将此概念理解为汇编代码中的某条指令是否已执行。

覆盖率劫持:

劫持汇编器(afl-gcc、afl-clang、afl-g++),通过识别跳转指令,然后在其中插入一段汇编用于与AFL之间的通信(__afl_maybe_log)

clang内置,LLVM 内置了一个简单的代码覆盖率检测 (SanitizerCoverage),它在函数、基本块和边缘级别插入对用户定义函数的调用。提供了这些回调的默认实现,并实现了简单的覆盖率报告和可视化

编译器将在 -fsanitize-coverage=trace-pc-guard 每个边缘插入以下代码:

每条边都有自己的 guard_variable,每当对应的edge被执行到时,都会向共享内存中对list[*guard_variable]进行++操作

这个函数会在可执行文件开始执行时会调用一次,每一个有向边对应一个*guard值,参数start是第一个guard指针,代表第一条边;stop是最后一个guard指针,代表最后一条边,遍历start到stop就可以给每个guard指针指向的值初始化一个随机数(但是实际上这个值可以由用户来决定)

但是不可避免的,会有概率出现冲突的情况,因此Sakura师傅对其进行了替换:

要想对每条edge进行插桩,需要先在make afl时传入AFL_TRACE_PC=1来定义DUSE_TRACE_PC宏(make AFL_TRACE_PC=1),然后在执行afl-clang-fast的时候传入-fsanitize-coverage=trace-pc-guard参数即可。正常make会启用afl-llvm-pass而非afl-llvm-rt

GCOV、LCOV

覆盖率记录的实现除了前两种方式之外还可以使用GCOV、LCOV这两个东西可视化展示代码覆盖率,但是不能使用到fuzzer上。

一些fuzz的覆盖率反馈和引导变异的一些概念:

fuzz有生成式fuzz变异式fuzz两种,生成式fuzz就是一开始什么种子也没有,纯根据fuzzer的逻辑来生成种子(输入),fuzz过程中没有任何的反馈,也不会根据某时某刻的代码覆盖率来把interesting case变异发现新的路径。 AFL采用的是变异式fuzz

进行fuzz时,对样本进行变异,然后在找到新的路径时,将当前变异后数据当成一个新的样本继续变异。经过逐代的变异杂交选优,直到达到一个局部最优解为止(已发现的路径达到max),这就是所谓的**遗传算法**。

因为遗传算法的特征总是来自于初始种子样本变异策略,所以改进也主要在这两方面。种子也可以是一些代码语句:

如果变异器不会引入新的token(特征),那永远就只会生成select这一种特征,而不会生成如SQL语句中的insert、delete等。所以种子的变异策略也十分的重要,它极大的影响着fuzz的代码覆盖率。

除了对种子的不断变异,我们还需要了解AFL如何将自动或半自动生成的随机数据输入到被fuzzer的程序中并监视如崩溃,断言(assertion)等程序异常这个过程,也就是进行输入和捕获crash。AFL采用的是fork server这种机制,具体方式如下:

进程关系:fuzzer进程 -> fork server -> 被fuzz的程序

常用的AFL魔改技巧有哪些?

write_to_testcase函数会将变异生成的新样本写入到.cur_input隐藏文件夹中,所以我们在文件写入之前可以在外面套上一层函数以便让AFL的原始字节码的变异适用于更多的场景。afl-fuzz适合基于字节粒度变异的fuzz(如fuzz一个简单的输入输出程序),但并不是所有目标都可以直接进行字节粒度的fuzz。有些是因为文件解析部分的代码不能单独抽出来,有些是因为文件解析仅仅是逻辑的开始。那么为变异器加层就是在这方面扩展afl-fuzz的最简单方法,最经典的例子如webassembly

为了fuzz这些结构化的东西(类似SQL)我们需要进行结构感知(structure-aware),即针对特定输入类型的语法进行感知

如何使用AFL fuzz client-server模式的程序,或者如何使用AFL去fuzz网络协议呢?

lient-server模式中的client -> server大致流程如下:

这篇文章值得参考:05eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2X3j5i4y4@1L8s2W2Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8r3!0Y4i4K6u0r3K9r3!0%4i4K6u0V1k6Y4g2*7P5W2)9J5k6s2y4W2M7Y4k6W2M7W2)9J5k6r3q4E0k6i4u0A6j5$3q4F1i4K6u0V1k6Y4g2*7P5Y4W2Q4x3X3c8D9L8%4l9`.

文章中提到一个为Persistent mode(持久模式)的概念,如果要向AFL集成这种功能,我们可以按照如下流程编写自己的代码:

该方法需要用afl直接启动server程序,patch server程序接收请求包的代码,改为直接从标准输入里读取,server执行足够多次请求后退出,AFL再启动一个新的server再次fuzz。

fuzz要真正解决的问题和一些Sakura师傅的建议

Address Sanitizer又名ASan,是一个快速的C/C++内存错误检查器。它可以检测到:UAF、Heap buffer overflow、Stack buffer overflow、Global buffer overflow、Use after return、Use after scpoe、Initialization order bugs、Memory leak等漏洞类型。asan可以简单理解成对malloc和free以及存取指令等的hook,从而在发现分配出来的内存的大小,小于要存取的index的大小时,检测出越界读写问题。

要理解被测试程序的代码,并不是说随便拿到一个程序拿fuzz跑起来是有意义的,例如传入数据的parser(解析器、语法分析器),必要时通过逆向来单独取出一部分代码来测试功能。比如Windows的media player,这是一个图形化的应用程序,我们不能通过命令行窗口来对该程序进行流程的控制与输入,但是这些功能的实现肯定都是存在于某一个dll(动态链接库)中的,所以可以利用dlopen函数写harness来加载dll:

harness作用:如果我们想fuzz dll (加载的库)中的函数输入,因为没有定义入口点,我们需要编写测试工具来将输入从命令行传递到 DLL 函数。

善于根据场景改进fuzz,以将只用与文件格式fuzz的AFL利用加层或映射让其扩展到更多的场景中,实现自己的自定义编译。

根据不同的目标掌握不同的知识,找到主流使用的fuzzer并进行改进

自定义检测工具

Address Sanitizer又名ASan,是一个快速的C/C++内存错误检查器。它可以检测到:UAF、Heap buffer overflow、Stack buffer overflow、Global buffer overflow、Use after return、Use after scpoe、Initialization order bugs、Memory leak等漏洞类型。asan可以简单理解成对malloc和free以及存取指令等的hook,从而在发现分配出来的内存的大小,小于要存取的index的大小时,检测出越界读写问题。

asan是对malloc、free等指令的hook,但是如果像js引擎这种直接mmap一块大内存然后自己来管理内存的情况,那么如何检测出潜在的越界读写问题呢?这种时候就需要理解代码,然后自己实现一套针对性的自定义asan了。

调用find_as命令,argv[0]是afl-gcc的路径

这个宏定义实现了一个动态内存分配的格式化字符串函数,类似于 sprintf,但它会自动分配足够的内存来存储格式化后的字符串,其中调用ck_alloc函数

此处要求DEBUG_BUILD为假

接下来还有这个定义

因此得到:

非调试模式下的执行流程:

调试模式下的执行流程:

主要看DFL_ck_alloc函数

在对应位置写了magic token以及size:

DFL_ck_alloc_nozero函数返回后会调用memset将堆内存清零,说白了DFL_ck_alloc就是外面包了一个memset得到了DFL_ck_alloc_nozero

主要的还是DFL_ck_alloc函数

然后调用TRK_alloc_buf函数

首先看到以下的数据结构:

TRK_obj结构体:

哈希桶系统:

哈希函数:

查找空闲槽位:

动态扩容(如果没有足够的空间):

内存布局大致如下:

释放前的内存布局:

释放后的内存布局:

说白了就是在free外面套一层wrapper

宏AFL_PATH(非环境变量AFL_PATH)在编译时由Makefile文件确定,值默认为/usr/local/lib/afl/,拼接后就有:/usr/local/lib/afl/as

该函数会按照一定的规则在系统中寻找afl-as汇编器的位置:

edit_params是AFL编译器包装器的核心函数,负责处理和修改编译参数,将用户的编译命令转换为带有AFL插桩功能的编译命令。

功能:

初始化状态标志变量

分配参数数组内存(原参数数 + 128个额外槽位)

使用AFL的安全内存分配函数

编译器选择逻辑:

原因:在macOS上,gcc通常是clang的别名,需要明确指定编译器路径。

参数处理策略:

跳过的参数:-B, -integrated-as, -pipe

检测的参数:-fsanitize=*, FORTIFY_SOURCE

特殊标记:FreeBSD x64下的-m32

保留的参数:其他所有参数

功能:

强制使用AFL的汇编器包装器

Clang模式下禁用集成汇编器

加固措施:

启用栈保护:-fstack-protector-all

启用FORTIFY_SOURCE:-D_FORTIFY_SOURCE=2(如果未设置)

互斥性检查:

ASAN与MSAN不能同时使用

ASAN/MSAN与AFL_HARDEN不能同时使用

默认优化设置:

启用调试信息:-g

高级优化:-O3

循环展开:-funroll-loops

定义模糊测试宏

FreeBSD特殊处理:在64位FreeBSD系统上,clang -g -m32有bug,需要特殊处理。

目的:

禁用字符串比较函数的编译器优化

确保这些函数调用能被AFL的插桩捕获

就是执行编译相应的源文件

至此afl-gcc就分析完毕

这里用cyberangel师傅提供的例子做一个记录

会发现最后execvp的命令是这一串,我们可以通过-B来指定我们自己的as(汇编器)

gcc编译大多都认为是以下四个步骤:

会发现上述的gcc只指定了特殊的as(即汇编器),因此可以猜测出是通过劫持汇编器来达到在特殊位置插入特殊函数的结果,因此接下来看一手afl的as源码

核心数据结构和全局变量:

功能:

解析命令行参数,准备传递给真实汇编器的参数

检测目标架构(32位/64位)

处理 macOS 特殊情况(使用 clang 而不是 as)

确定临时文件路径

环境变量TEMPTMP的使用均需要用户在执行afl-as前手动设置,如果均不存在则默认设置tmp_dir变量为/tmp目录

生成临时文件名:/tmp/.afl-<pid>-<timestamp>.s

以下是核心数据结构:

插桩注入,这是程序的核心函数:

文件打开和初始化

这是延迟插桩的机制,fgets的第一个参数line被定义为static u8 line[MAX_LINE];,宏MAX_LINEconfig.h中被默认定义为8192。将line展开,可以得到static uint8_t line[8192],也就是说fgets函数会读取input_file的一行代码存放到line数组(最多读取MAX_LINE个字符):image-20250728201118243

image-20250728201145951

触发条件:

如果满足以上所有条件,就会直接将插桩代码写入outf变量,然后再通过fputs函数写入对应文件中

如果是直通模式就会直接将汇编写入进入下一行了

段类型处理:

假设当前是.text段,那么会标记instr_ok=1,说明当前段是可以插桩的代码,那么continue之后,接下来的片段就是.text段即代码段的内容了,就可以进行插桩了

接下来就是特殊情况的处理:

OpenBSD的特殊性:

OpenBSD 将跳转表(jump tables)直接内联在代码中

跳转表是编译器生成的用于 switch 语句优化的数据结构

这些表被放在 .text 段中,但不应该被当作普通代码插桩

例如:

如果不进行特殊处理,会出现以下情况:

会破坏p2align指令的对齐效果;跳转表是数据,不是代码,不应该被插桩;错误的插桩会导致跳转表寻址错误

混合架构代码:

同一个汇编文件可能包含32位和64位代码段(比如armv8向前兼容)

在调试器(如GDB)中经常遇到

需要根据当前编译目标跳过不匹配的代码段

由于gcc本身默认使用AT&T语法,AFL的插桩代码也是用AT&T语法写的

或许我可以写一个patch来兼容这部分()

例子:

内联汇编一般都是

因为内联汇编一般都是程序员手写的,不符合编译器生成代码的模式;同时#还检测了注释

接下来就是一个流程图:

然后回到main函数

fork一个子进程执行as --64 -o /home/cyberangel/Desktop/test/exec_obj.o /tmp/.afl-27115-1673249415.s命令

__sanitizer_cov_trace_pc_guard(uint32_t* guard_variable)
__sanitizer_cov_trace_pc_guard(uint32_t* guard_variable)
//该函数处于AFL/llvm_mode/afl-llvm-rt.o.c
/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard.
   It remains non-operational in the traditional, plugin-backed LLVM mode.
   For more info about 'trace-pc-guard', see README.llvm.
 
   The first function (__sanitizer_cov_trace_pc_guard) is called back on every
   edge (as opposed to every basic block). */
 
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
  __afl_area_ptr[*guard]++;                             //__afl_area_ptr是共享内存的指针
}
//该函数处于AFL/llvm_mode/afl-llvm-rt.o.c
/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard.
   It remains non-operational in the traditional, plugin-backed LLVM mode.
   For more info about 'trace-pc-guard', see README.llvm.
 
   The first function (__sanitizer_cov_trace_pc_guard) is called back on every
   edge (as opposed to every basic block). */
 
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
  __afl_area_ptr[*guard]++;                             //__afl_area_ptr是共享内存的指针
}
__sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop);
__sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop);
//AFL/llvm_mode/afl-llvm-rt.o.c
 
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
  __afl_area_ptr[*guard]++;
}
 
/* Init callback. Populates instrumentation IDs. Note that we're using
   ID of 0 as a special value to indicate non-instrumented bits. That may
   still touch the bitmap, but in a fairly harmless way. */
 
void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
 
  u32 inst_ratio = 100;
  u8* x;
 
  if (start == stop || *start) return;
 
  x = getenv("AFL_INST_RATIO");
  if (x) inst_ratio = atoi(x);
 
  if (!inst_ratio || inst_ratio > 100) {
    fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n");
    abort();
  }
 
  /* Make sure that the first element in the range is always set - we use that
     to avoid duplicate calls (which can happen as an artifact of the underlying
     implementation in LLVM). */
 
  *(start++) = R(MAP_SIZE - 1) + 1;                             //初始化初始值
 
  while (start < stop) {                                     //为每一个*guard赋初始值*start = R(MAP_SIZE - 1) + 1;
 
    if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1;       //  #  define R(x) (random() % (x))【random随机数】
    else *start = 0;
 
    start++;                                                    //指针遍历(uint32_t)
 
  }
 
}
//AFL/llvm_mode/afl-llvm-rt.o.c
 
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
  __afl_area_ptr[*guard]++;
}
 
/* Init callback. Populates instrumentation IDs. Note that we're using
   ID of 0 as a special value to indicate non-instrumented bits. That may
   still touch the bitmap, but in a fairly harmless way. */
 
void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
 
  u32 inst_ratio = 100;
  u8* x;
 
  if (start == stop || *start) return;
 
  x = getenv("AFL_INST_RATIO");
  if (x) inst_ratio = atoi(x);
 
  if (!inst_ratio || inst_ratio > 100) {
    fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n");
    abort();
  }
 
  /* Make sure that the first element in the range is always set - we use that
     to avoid duplicate calls (which can happen as an artifact of the underlying
     implementation in LLVM). */
 
  *(start++) = R(MAP_SIZE - 1) + 1;                             //初始化初始值
 
  while (start < stop) {                                     //为每一个*guard赋初始值*start = R(MAP_SIZE - 1) + 1;
 
    if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1;       //  #  define R(x) (random() % (x))【random随机数】
    else *start = 0;
 
    start++;                                                    //指针遍历(uint32_t)
 
  }
 
}
void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
    unsigned int N = 0 ;
    printf("@sakura in __sanitizer_cov_trace_pc_guard_init\n");
    if(start == stop || *start) return;
    for (uint32* x = start ; x < stop ; x++){
        *x = (++N) % MAP_SIZE;  //mod map_size(1 << 16 == 65536)
    }
    printf("@sakura there are %u guards in total\n",N);
}
void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
    unsigned int N = 0 ;
    printf("@sakura in __sanitizer_cov_trace_pc_guard_init\n");
    if(start == stop || *start) return;
    for (uint32* x = start ; x < stop ; x++){
        *x = (++N) % MAP_SIZE;  //mod map_size(1 << 16 == 65536)
    }
    printf("@sakura there are %u guards in total\n",N);
}
select * from cyberangel;
select * from cyberangel;
//afl-fuzz.c
 
/* Write modified data to file for testing. If out_file is set, the old file
   is unlinked and a new one is created. Otherwise, out_fd is rewound and
   truncated. */
 
static void write_to_testcase(void* mem, u32 len) {
 
  s32 fd = out_fd;
 
  if (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);
 
  } else lseek(fd, 0, SEEK_SET);
 
  ck_write(fd, mem, len, out_file);
 
  if (!out_file) {
 
    if (ftruncate(fd, len)) PFATAL("ftruncate() failed");
    lseek(fd, 0, SEEK_SET);
 
  } else close(fd);
 
}
//afl-fuzz.c
 
/* Write modified data to file for testing. If out_file is set, the old file
   is unlinked and a new one is created. Otherwise, out_fd is rewound and
   truncated. */
 
static void write_to_testcase(void* mem, u32 len) {
 
  s32 fd = out_fd;
 
  if (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);
 
  } else lseek(fd, 0, SEEK_SET);
 
  ck_write(fd, mem, len, out_file);
 
  if (!out_file) {
 
    if (ftruncate(fd, len)) PFATAL("ftruncate() failed");
    lseek(fd, 0, SEEK_SET);
 
  } else close(fd);
 
}
client ---> server(){
    接收包();
    处理包();
    返回响应包();
}
client ---> server(){
    接收包();
    处理包();
    返回响应包();
}
while (go)                  //while loop
    put_request(read(file)) //将client要发送的请求包写入到文件中
    req = get_request()     //并让服务端从文件中获取请求
    process(req)            //服务端处理请求
    notify_fuzzer()         //通知fuzzer(AFL)
while (go)                  //while loop
    put_request(read(file)) //将client要发送的请求包写入到文件中
    req = get_request()     //并让服务端从文件中获取请求
    process(req)            //服务端处理请求
    notify_fuzzer()         //通知fuzzer(AFL)
/* Main entry point */
 
int main(int argc, char** argv) {
 
  if (isatty(2) && !getenv("AFL_QUIET")) { //检查stderr是否连接/dev/
 
    SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
 
  } else be_quiet = 1;
 
  if (argc < 2) {
 
    SAYF("\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"
 
         "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);
 
  }
 
  find_as(argv[0]);
 
  edit_params(argc, argv);
 
  execvp(cc_params[0], (char**)cc_params);
 
  FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
 
  return 0;
 
}
/* Main entry point */
 
int main(int argc, char** argv) {
 
  if (isatty(2) && !getenv("AFL_QUIET")) { //检查stderr是否连接/dev/
 
    SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
 
  } else be_quiet = 1;
 
  if (argc < 2) {
 
    SAYF("\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"
 
         "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);
 
  }
 
  find_as(argv[0]);
 
  edit_params(argc, argv);
 
  execvp(cc_params[0], (char**)cc_params);
 
  FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
 
  return 0;
 
}
find_as(argv[0]);
find_as(argv[0]);
/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
   from argv[0]. If that fails, abort. */
 
static void find_as(u8* argv0) {
 
  u8 *afl_path = getenv("AFL_PATH");
  u8 *slash, *tmp;
 
  if (afl_path) {
 
    tmp = alloc_printf("%s/as", afl_path);
 
    if (!access(tmp, X_OK)) {
      as_path = afl_path;
      ck_free(tmp);
      return;
    }
 
    ck_free(tmp);
 
  }
 
  slash = strrchr(argv0, '/');
 
  if (slash) {
 
    u8 *dir;
 
    *slash = 0;
    dir = ck_strdup(argv0);
    *slash = '/';
 
    tmp = alloc_printf("%s/afl-as", dir);
 
    if (!access(tmp, X_OK)) {
      as_path = dir;
      ck_free(tmp);
      return;
    }
 
    ck_free(tmp);
    ck_free(dir);
 
  }
 
  if (!access(AFL_PATH "/as", X_OK)) {
    as_path = AFL_PATH;
    return;
  }
 
  FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
  
}
/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
   from argv[0]. If that fails, abort. */
 
static void find_as(u8* argv0) {
 
  u8 *afl_path = getenv("AFL_PATH");
  u8 *slash, *tmp;
 
  if (afl_path) {
 
    tmp = alloc_printf("%s/as", afl_path);
 
    if (!access(tmp, X_OK)) {
      as_path = afl_path;
      ck_free(tmp);
      return;
    }
 
    ck_free(tmp);
 
  }
 
  slash = strrchr(argv0, '/');
 
  if (slash) {
 
    u8 *dir;
 
    *slash = 0;
    dir = ck_strdup(argv0);
    *slash = '/';
 
    tmp = alloc_printf("%s/afl-as", dir);
 
    if (!access(tmp, X_OK)) {
      as_path = dir;
      ck_free(tmp);
      return;
    }
 
    ck_free(tmp);
    ck_free(dir);
 
  }
 
  if (!access(AFL_PATH "/as", X_OK)) {
    as_path = AFL_PATH;
    return;
  }
 
  FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
  
}
u8 *afl_path = getenv("AFL_PATH");
u8 *slash, *tmp;
 
if (afl_path) {
 
  tmp = alloc_printf("%s/as", afl_path); //@@@@
 
  if (!access(tmp, X_OK)) {
    as_path = afl_path;
    ck_free(tmp);
    return;
  }
 
  ck_free(tmp);
 
}
u8 *afl_path = getenv("AFL_PATH");
u8 *slash, *tmp;
 
if (afl_path) {
 
  tmp = alloc_printf("%s/as", afl_path); //@@@@
 
  if (!access(tmp, X_OK)) {
    as_path = afl_path;
    ck_free(tmp);
    return;
  }
 
  ck_free(tmp);
 
}
#define alloc_printf(_str...) ({ \
    u8* _tmp; \
    s32 _len = snprintf(NULL, 0, _str); \
    if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
    _tmp = ck_alloc(_len + 1); \
    snprintf((char*)_tmp, _len + 1, _str); \
    _tmp; \
  })
#define alloc_printf(_str...) ({ \
    u8* _tmp; \
    s32 _len = snprintf(NULL, 0, _str); \
    if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
    _tmp = ck_alloc(_len + 1); \
    snprintf((char*)_tmp, _len + 1, _str); \
    _tmp; \
  })
#ifndef DEBUG_BUILD
 
/* In non-debug mode, we just do straightforward aliasing of the above functions
   to user-visible names such as ck_alloc(). */
/* 在非调试模式下,我们只需将上述函数直接别名为用户可见的名称,例如 ck_alloc() */
 
#define ck_alloc          DFL_ck_alloc
#define ck_alloc_nozero   DFL_ck_alloc_nozero
#define ck_realloc        DFL_ck_realloc
#define ck_realloc_block  DFL_ck_realloc_block
#define ck_strdup         DFL_ck_strdup
#define ck_memdup         DFL_ck_memdup
#define ck_memdup_str     DFL_ck_memdup_str
#define ck_free           DFL_ck_free
 
#define alloc_report()
 
#else
#ifndef DEBUG_BUILD
 
/* In non-debug mode, we just do straightforward aliasing of the above functions
   to user-visible names such as ck_alloc(). */
/* 在非调试模式下,我们只需将上述函数直接别名为用户可见的名称,例如 ck_alloc() */
 
#define ck_alloc          DFL_ck_alloc
#define ck_alloc_nozero   DFL_ck_alloc_nozero
#define ck_realloc        DFL_ck_realloc
#define ck_realloc_block  DFL_ck_realloc_block
#define ck_strdup         DFL_ck_strdup
#define ck_memdup         DFL_ck_memdup
#define ck_memdup_str     DFL_ck_memdup_str
#define ck_free           DFL_ck_free
 
#define alloc_report()
 
#else
#define ck_alloc(_p1) \
  TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
#define ck_alloc(_p1) \
  TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
ck_alloc(size)
  → DFL_ck_alloc(size)
    → DFL_ck_alloc_nozero(size)  // 实际分配
    memset(mem, 0, size)       // 清零
    → 返回内存指针
ck_alloc(size)
  → DFL_ck_alloc(size)
    → DFL_ck_alloc_nozero(size)  // 实际分配
    memset(mem, 0, size)       // 清零
    → 返回内存指针
ck_alloc(size)
  → TRK_ck_alloc(size, __FILE__, __FUNCTION__, __LINE__)
    → DFL_ck_alloc(size)         // 实际分配
    → TRK_alloc_buf(ret, file, func, line)  // 记录分配信息
    → 返回内存指针
ck_alloc(size)
  → TRK_ck_alloc(size, __FILE__, __FUNCTION__, __LINE__)
    → DFL_ck_alloc(size)         // 实际分配
    → TRK_alloc_buf(ret, file, func, line)  // 记录分配信息
    → 返回内存指针
/* Allocate a buffer, returning zeroed memory. */
 
static inline void* DFL_ck_alloc(u32 size) {
 
  void* mem;
 
  if (!size) return NULL;
  mem = DFL_ck_alloc_nozero(size);
 
  return memset(mem, 0, size);
 
}
/* Allocate a buffer, returning zeroed memory. */
 
static inline void* DFL_ck_alloc(u32 size) {
 
  void* mem;
 
  if (!size) return NULL;
  mem = DFL_ck_alloc_nozero(size);
 
  return memset(mem, 0, size);
 
}
static inline void* DFL_ck_alloc_nozero(u32 size) {
 
  void* ret;
 
  if (!size) return NULL;
 
  ALLOC_CHECK_SIZE(size);
  ret = malloc(size + ALLOC_OFF_TOTAL);
  ALLOC_CHECK_RESULT(ret, size);
 
  ret += ALLOC_OFF_HEAD;
 
  ALLOC_C1(ret) = ALLOC_MAGIC_C1;
  ALLOC_S(ret)  = size;
  ALLOC_C2(ret) = ALLOC_MAGIC_C2;
 
  return ret;
 
}
---------------------------------------------------------------------------------------------------
/* Magic tokens used to mark used / freed chunks. */
 
#define ALLOC_MAGIC_C1  0xFF00FF00 /* Used head (dword)  */
#define ALLOC_MAGIC_F   0xFE00FE00 /* Freed head (dword) */
#define ALLOC_MAGIC_C2  0xF0       /* Used tail (byte)   */
 
/* Positions of guard tokens in relation to the user-visible pointer. */
 
#define ALLOC_C1(_ptr)  (((u32*)(_ptr))[-2])
#define ALLOC_S(_ptr)   (((u32*)(_ptr))[-1])
#define ALLOC_C2(_ptr)  (((u8*)(_ptr))[ALLOC_S(_ptr)])
static inline void* DFL_ck_alloc_nozero(u32 size) {
 
  void* ret;
 
  if (!size) return NULL;
 
  ALLOC_CHECK_SIZE(size);
  ret = malloc(size + ALLOC_OFF_TOTAL);
  ALLOC_CHECK_RESULT(ret, size);
 
  ret += ALLOC_OFF_HEAD;
 
  ALLOC_C1(ret) = ALLOC_MAGIC_C1;
  ALLOC_S(ret)  = size;
  ALLOC_C2(ret) = ALLOC_MAGIC_C2;
 
  return ret;
 
}
---------------------------------------------------------------------------------------------------
/* Magic tokens used to mark used / freed chunks. */
 
#define ALLOC_MAGIC_C1  0xFF00FF00 /* Used head (dword)  */
#define ALLOC_MAGIC_F   0xFE00FE00 /* Freed head (dword) */
#define ALLOC_MAGIC_C2  0xF0       /* Used tail (byte)   */
 
/* Positions of guard tokens in relation to the user-visible pointer. */
 
#define ALLOC_C1(_ptr)  (((u32*)(_ptr))[-2])
#define ALLOC_S(_ptr)   (((u32*)(_ptr))[-1])
#define ALLOC_C2(_ptr)  (((u8*)(_ptr))[ALLOC_S(_ptr)])
((u32*)(_ptr))[-2] = 0xFF00FF00 ;
((u32*)(_ptr))[-1] = size ;             // size ->【字符串长度】
((u8*)(_ptr))[((u32*)(_ptr))[-1]] =  0xF0 ;
((u32*)(_ptr))[-2] = 0xFF00FF00 ;
((u32*)(_ptr))[-1] = size ;             // size ->【字符串长度】
((u8*)(_ptr))[((u32*)(_ptr))[-1]] =  0xF0 ;
// 执行前------------------------------------------------------------------------
(gdb) x/16gx ret-0x10
0x555555758270: 0xf0006e69622f6c61  0x0000000000000031
0x555555758280: 0x0000000000000000  0x0000000000000000
                                    # ret
0x555555758290: 0x0000000000000000  0x0000000000000000
0x5555557582a0: 0x0000000000000000  0x0000000000020d61
// 执行后------------------------------------------------------------------------
(gdb) x/16gx ret-0x10
0x555555758270: 0xf0006e69622f6c61  0x0000000000000031
0x555555758280: 0x00000016ff00ff00  0x0000000000000000
                # 【2】、【1】        # ret
0x555555758290: 0x0000000000000000  0x00f0000000000000
                                    # 【3】
0x5555557582a0: 0x0000000000000000  0x0000000000020d61
// 执行前------------------------------------------------------------------------
(gdb) x/16gx ret-0x10
0x555555758270: 0xf0006e69622f6c61  0x0000000000000031
0x555555758280: 0x0000000000000000  0x0000000000000000
                                    # ret
0x555555758290: 0x0000000000000000  0x0000000000000000
0x5555557582a0: 0x0000000000000000  0x0000000000020d61
// 执行后------------------------------------------------------------------------
(gdb) x/16gx ret-0x10
0x555555758270: 0xf0006e69622f6c61  0x0000000000000031
0x555555758280: 0x00000016ff00ff00  0x0000000000000000
                # 【2】、【1】        # ret
0x555555758290: 0x0000000000000000  0x00f0000000000000
                                    # 【3】
0x5555557582a0: 0x0000000000000000  0x0000000000020d61
/* Simple wrappers for non-debugging functions: */
 
static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func,
                                 u32 line) {
 
  void* ret = DFL_ck_alloc(size);
  TRK_alloc_buf(ret, file, func, line);
  return ret;
 
}
/* Simple wrappers for non-debugging functions: */
 
static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func,
                                 u32 line) {
 
  void* ret = DFL_ck_alloc(size);
  TRK_alloc_buf(ret, file, func, line);
  return ret;
 
}
/* Add a new entry to the list of allocated objects. */
 
static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
                                 u32 line) {
 
  u32 i, bucket;
 
  if (!ptr) return;
 
  bucket = TRKH(ptr);                        //计算哈希值
 
  /* Find a free slot in the list of entries for that bucket. */
 
  for (i = 0; i < TRK_cnt[bucket]; i++)
 
    if (!TRK[bucket][i].ptr) {
 
      TRK[bucket][i].ptr  = ptr;
      TRK[bucket][i].file = (char*)file;
      TRK[bucket][i].func = (char*)func;
      TRK[bucket][i].line = line;
      return;
 
    }
 
  /* No space available - allocate more. */
 
  TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
    (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
 
  TRK[bucket][i].ptr  = ptr;
  TRK[bucket][i].file = (char*)file;
  TRK[bucket][i].func = (char*)func;
  TRK[bucket][i].line = line;
 
  TRK_cnt[bucket]++;
 
}
/* Add a new entry to the list of allocated objects. */
 
static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
                                 u32 line) {
 
  u32 i, bucket;
 
  if (!ptr) return;
 
  bucket = TRKH(ptr);                        //计算哈希值
 
  /* Find a free slot in the list of entries for that bucket. */
 
  for (i = 0; i < TRK_cnt[bucket]; i++)
 
    if (!TRK[bucket][i].ptr) {
 
      TRK[bucket][i].ptr  = ptr;
      TRK[bucket][i].file = (char*)file;
      TRK[bucket][i].func = (char*)func;
      TRK[bucket][i].line = line;
      return;
 
    }
 
  /* No space available - allocate more. */
 
  TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
    (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
 
  TRK[bucket][i].ptr  = ptr;
  TRK[bucket][i].file = (char*)file;
  TRK[bucket][i].func = (char*)func;
  TRK[bucket][i].line = line;
 
  TRK_cnt[bucket]++;
 
}
struct TRK_obj {
  void *ptr;      // 分配的内存指针
  char *file;     // 分配时的源文件名
  char *func;     // 分配时的函数名
  u32  line;      // 分配时的行号
};
struct TRK_obj {
  void *ptr;      // 分配的内存指针
  char *file;     // 分配时的源文件名
  char *func;     // 分配时的函数名
  u32  line;      // 分配时的行号
};
#define ALLOC_BUCKETS 4096
struct TRK_obj* TRK[ALLOC_BUCKETS];    // 哈希桶数组
u32 TRK_cnt[ALLOC_BUCKETS];            // 每个桶的元素计数
#define ALLOC_BUCKETS 4096
struct TRK_obj* TRK[ALLOC_BUCKETS];    // 哈希桶数组
u32 TRK_cnt[ALLOC_BUCKETS];            // 每个桶的元素计数
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
if (!ptr) return;           // 空指针检查
bucket = TRKH(ptr);         // 计算哈希桶索引
if (!ptr) return;           // 空指针检查
bucket = TRKH(ptr);         // 计算哈希桶索引
for (i = 0; i < TRK_cnt[bucket]; i++)
    if (!TRK[bucket][i].ptr) {
        // 找到空闲槽位,记录分配信息
        TRK[bucket][i].ptr  = ptr;
        TRK[bucket][i].file = (char*)file;
        TRK[bucket][i].func = (char*)func;
        TRK[bucket][i].line = line;
        return;
    }
for (i = 0; i < TRK_cnt[bucket]; i++)
    if (!TRK[bucket][i].ptr) {
        // 找到空闲槽位,记录分配信息
        TRK[bucket][i].ptr  = ptr;
        TRK[bucket][i].file = (char*)file;
        TRK[bucket][i].func = (char*)func;
        TRK[bucket][i].line = line;
        return;
    }
// 重新分配更大的桶空间
TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
    (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
 
// 在新分配的位置记录信息
TRK[bucket][i].ptr  = ptr;
TRK[bucket][i].file = (char*)file;
TRK[bucket][i].func = (char*)func;
TRK[bucket][i].line = line;
 
TRK_cnt[bucket]++;          // 增加桶的元素计数
// 重新分配更大的桶空间
TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
    (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
 
// 在新分配的位置记录信息
TRK[bucket][i].ptr  = ptr;
TRK[bucket][i].file = (char*)file;
TRK[bucket][i].func = (char*)func;
TRK[bucket][i].line = line;
 
TRK_cnt[bucket]++;          // 增加桶的元素计数
slash = strrchr(argv0, '/');  // 获取"/afl-gcc"的起始地址
 
if (slash) {
 
  u8 *dir;
 
  *slash = 0;                // 对argv0进行\x00截断,使其变为/usr/local/bin
  dir = ck_strdup(argv0);
  *slash = '/';
 
  tmp = alloc_printf("%s/afl-as", dir);
 
  if (!access(tmp, X_OK)) {
    as_path = dir;
    ck_free(tmp);
    return;
  }
 
  ck_free(tmp);
  ck_free(dir);
 
}
 
if (!access(AFL_PATH "/as", X_OK)) {
  as_path = AFL_PATH;
  return;
}
 
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
slash = strrchr(argv0, '/');  // 获取"/afl-gcc"的起始地址
 
if (slash) {
 
  u8 *dir;
 
  *slash = 0;                // 对argv0进行\x00截断,使其变为/usr/local/bin
  dir = ck_strdup(argv0);
  *slash = '/';
 
  tmp = alloc_printf("%s/afl-as", dir);
 
  if (!access(tmp, X_OK)) {
    as_path = dir;
    ck_free(tmp);
    return;
  }
 
  ck_free(tmp);
  ck_free(dir);
 
}
 
if (!access(AFL_PATH "/as", X_OK)) {
  as_path = AFL_PATH;
  return;
}
 
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
static inline u8* DFL_ck_strdup(u8* str) {
 
  void* ret;
  u32   size;
 
  if (!str) return NULL;
 
  size = strlen((char*)str) + 1;
 
  ALLOC_CHECK_SIZE(size);
     
  ret = malloc(size + ALLOC_OFF_TOTAL);
  ALLOC_CHECK_RESULT(ret, size);
 
  ret += ALLOC_OFF_HEAD;
 
  ALLOC_C1(ret) = ALLOC_MAGIC_C1;
  ALLOC_S(ret)  = size;
  ALLOC_C2(ret) = ALLOC_MAGIC_C2;
 
  return memcpy(ret, str, size);
 
}
static inline u8* DFL_ck_strdup(u8* str) {
 
  void* ret;
  u32   size;
 
  if (!str) return NULL;
 
  size = strlen((char*)str) + 1;
 
  ALLOC_CHECK_SIZE(size);
     
  ret = malloc(size + ALLOC_OFF_TOTAL);
  ALLOC_CHECK_RESULT(ret, size);
 
  ret += ALLOC_OFF_HEAD;
 
  ALLOC_C1(ret) = ALLOC_MAGIC_C1;
  ALLOC_S(ret)  = size;
  ALLOC_C2(ret) = ALLOC_MAGIC_C2;
 
  return memcpy(ret, str, size);
 
}
ALLOC_CHECK_SIZE(size);
// 展开为:
if (size > MAX_ALLOC)  // MAX_ALLOC = 0x40000000 (1GB)
    ABORT("Bad alloc request: %u bytes", size);
ALLOC_CHECK_SIZE(size);
// 展开为:
if (size > MAX_ALLOC)  // MAX_ALLOC = 0x40000000 (1GB)
    ABORT("Bad alloc request: %u bytes", size);
ALLOC_C1(ret) = ALLOC_MAGIC_C1;  // ((u32*)ret)[-2] = 0xFF00FF00
ALLOC_S(ret)  = size;            // ((u32*)ret)[-1] = size
ALLOC_C2(ret) = ALLOC_MAGIC_C2;  // ((u8*)ret)[size] = 0xF0
ALLOC_C1(ret) = ALLOC_MAGIC_C1;  // ((u32*)ret)[-2] = 0xFF00FF00
ALLOC_S(ret)  = size;            // ((u32*)ret)[-1] = size
ALLOC_C2(ret) = ALLOC_MAGIC_C2;  // ((u8*)ret)[size] = 0xF0
地址偏移:  -8    -4     0           size
内容:    [C1]  [SIZE] [用户数据...] [C2]
值:    0xFF00FF00  size   字符串内容   0xF0
地址偏移:  -8    -4     0           size
内容:    [C1]  [SIZE] [用户数据...] [C2]
值:    0xFF00FF00  size   字符串内容   0xF0
static inline void DFL_ck_free(void* mem) {
 
  if (!mem) return;
 
  CHECK_PTR(mem);
 
#ifdef DEBUG_BUILD
 
  /* Catch pointer issues sooner. */
  memset(mem, 0xFF, ALLOC_S(mem));
 
#endif /* DEBUG_BUILD */
 
  ALLOC_C1(mem) = ALLOC_MAGIC_F;  // 标记为已经释放
 
  free(mem - ALLOC_OFF_HEAD);
 
}
static inline void DFL_ck_free(void* mem) {
 
  if (!mem) return;
 
  CHECK_PTR(mem);
 
#ifdef DEBUG_BUILD
 
  /* Catch pointer issues sooner. */
  memset(mem, 0xFF, ALLOC_S(mem));
 
#endif /* DEBUG_BUILD */
 
  ALLOC_C1(mem) = ALLOC_MAGIC_F;  // 标记为已经释放
 
  free(mem - ALLOC_OFF_HEAD);
 
}
if (mem) {
    // 检查头部magic number
    if (ALLOC_C1(mem) ^ ALLOC_MAGIC_C1) {
        if (ALLOC_C1(mem) == ALLOC_MAGIC_F)
            ABORT("Use after free.");           // 重复释放
        else
            ABORT("Corrupted head alloc canary."); // 头部损坏
    }
    // 检查尾部magic number
    if (ALLOC_C2(mem) ^ ALLOC_MAGIC_C2)
        ABORT("Corrupted tail alloc canary.");     // 尾部损坏
}
if (mem) {
    // 检查头部magic number
    if (ALLOC_C1(mem) ^ ALLOC_MAGIC_C1) {
        if (ALLOC_C1(mem) == ALLOC_MAGIC_F)
            ABORT("Use after free.");           // 重复释放
        else
            ABORT("Corrupted head alloc canary."); // 头部损坏
    }
    // 检查尾部magic number
    if (ALLOC_C2(mem) ^ ALLOC_MAGIC_C2)
        ABORT("Corrupted tail alloc canary.");     // 尾部损坏
}
实际分配的内存块:
[Guard1: 4字节] [Size: 4字节] [用户数据: size字节] [Guard2: 1字节]
0xFF00FF00        size          字符串内容           0xF0
     ↑                              ↑
malloc返回的地址              mem指向的位置
实际分配的内存块:
[Guard1: 4字节] [Size: 4字节] [用户数据: size字节] [Guard2: 1字节]
0xFF00FF00        size          字符串内容           0xF0
     ↑                              ↑
malloc返回的地址              mem指向的位置
[Guard1: 4字节] [Size: 4字节] [污染数据: size字节] [Guard2: 1字节]
0xFE00FE00        size       0xFF...0xFF         0xF0
(已释放标记)                  (调试模式下)
[Guard1: 4字节] [Size: 4字节] [污染数据: size字节] [Guard2: 1字节]
0xFE00FE00        size       0xFF...0xFF         0xF0
(已释放标记)                  (调试模式下)
if (!access(AFL_PATH "/as", X_OK)) {
  as_path = AFL_PATH;
  return;
}
 
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
if (!access(AFL_PATH "/as", X_OK)) {
  as_path = AFL_PATH;
  return;
}
 
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
edit_params(argc, argv);
edit_params(argc, argv);
/* Copy argv to cc_params, making the necessary edits. */
 
static void edit_params(u32 argc, char** argv) {
 
  u8 fortify_set = 0, asan_set = 0;
  u8 *name;
 
#if defined(__FreeBSD__) && defined(__x86_64__)
  u8 m32_set = 0;
#endif
 
  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 {
 
    /* With GCJ and Eclipse installed, you can actually compile Java! The
       instrumentation will work (amazingly). Alas, unhandled exceptions do
       not call abort(), so afl-fuzz would need to be modified to equate
       non-zero exit codes with crash conditions when working with Java
       binaries. Meh. */
 
#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");
 
    if (!cc_params[0]) {
 
      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");
 
    }
 
#else
 
    if (!strcmp(name, "afl-g++")) {
      u8* alt_cxx = getenv("AFL_CXX");
      cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
    } else if (!strcmp(name, "afl-gcj")) {
      u8* alt_cc = getenv("AFL_GCJ");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
    } else {
      u8* alt_cc = getenv("AFL_CC");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
    }
 
#endif /* __APPLE__ */
 
  }
 
  while (--argc) {
    u8* cur = *(++argv);
 
    if (!strncmp(cur, "-B", 2)) {
 
      if (!be_quiet) WARNF("-B is already set, overriding");
 
      if (!cur[2] && argc > 1) { argc--; argv++; }
      continue;
 
    }
 
    if (!strcmp(cur, "-integrated-as")) continue;
 
    if (!strcmp(cur, "-pipe")) continue;
 
#if defined(__FreeBSD__) && defined(__x86_64__)
    if (!strcmp(cur, "-m32")) m32_set = 1;
#endif
 
    if (!strcmp(cur, "-fsanitize=address") ||
        !strcmp(cur, "-fsanitize=memory")) asan_set = 1;
 
    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
 
    cc_params[cc_par_cnt++] = cur;
 
  }
 
  cc_params[cc_par_cnt++] = "-B";
  cc_params[cc_par_cnt++] = as_path;
 
  if (clang_mode)
    cc_params[cc_par_cnt++] = "-no-integrated-as";
 
  if (getenv("AFL_HARDEN")) {
 
    cc_params[cc_par_cnt++] = "-fstack-protector-all";
 
    if (!fortify_set)
      cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
 
  }
 
  if (asan_set) {
 
    /* Pass this on to afl-as to adjust map density. */
 
    setenv("AFL_USE_ASAN", "1", 1);
 
  } else if (getenv("AFL_USE_ASAN")) {
 
    if (getenv("AFL_USE_MSAN"))
      FATAL("ASAN and MSAN are mutually exclusive");
 
    if (getenv("AFL_HARDEN"))
      FATAL("ASAN and AFL_HARDEN are mutually exclusive");
 
    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=address";
 
  } else if (getenv("AFL_USE_MSAN")) {
 
    if (getenv("AFL_USE_ASAN"))
      FATAL("ASAN and MSAN are mutually exclusive");
 
    if (getenv("AFL_HARDEN"))
      FATAL("MSAN and AFL_HARDEN are mutually exclusive");
 
    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=memory";
 
 
  }
 
  if (!getenv("AFL_DONT_OPTIMIZE")) {
 
#if defined(__FreeBSD__) && defined(__x86_64__)
 
    /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself
       works OK. This has nothing to do with us, but let's avoid triggering
       that bug. */
 
    if (!clang_mode || !m32_set)
      cc_params[cc_par_cnt++] = "-g";
 
#else
 
      cc_params[cc_par_cnt++] = "-g";
 
#endif
 
    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";
 
  }
 
  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] = NULL;
 
}
/* Copy argv to cc_params, making the necessary edits. */
 
static void edit_params(u32 argc, char** argv) {
 
  u8 fortify_set = 0, asan_set = 0;
  u8 *name;
 
#if defined(__FreeBSD__) && defined(__x86_64__)
  u8 m32_set = 0;
#endif
 
  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 {
 
    /* With GCJ and Eclipse installed, you can actually compile Java! The
       instrumentation will work (amazingly). Alas, unhandled exceptions do
       not call abort(), so afl-fuzz would need to be modified to equate
       non-zero exit codes with crash conditions when working with Java
       binaries. Meh. */
 
#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");
 
    if (!cc_params[0]) {
 
      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");
 
    }
 
#else
 
    if (!strcmp(name, "afl-g++")) {
      u8* alt_cxx = getenv("AFL_CXX");
      cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
    } else if (!strcmp(name, "afl-gcj")) {
      u8* alt_cc = getenv("AFL_GCJ");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
    } else {
      u8* alt_cc = getenv("AFL_CC");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
    }
 
#endif /* __APPLE__ */
 
  }
 
  while (--argc) {
    u8* cur = *(++argv);
 
    if (!strncmp(cur, "-B", 2)) {
 
      if (!be_quiet) WARNF("-B is already set, overriding");
 
      if (!cur[2] && argc > 1) { argc--; argv++; }
      continue;
 
    }
 
    if (!strcmp(cur, "-integrated-as")) continue;
 
    if (!strcmp(cur, "-pipe")) continue;
 
#if defined(__FreeBSD__) && defined(__x86_64__)
    if (!strcmp(cur, "-m32")) m32_set = 1;
#endif
 
    if (!strcmp(cur, "-fsanitize=address") ||
        !strcmp(cur, "-fsanitize=memory")) asan_set = 1;
 
    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
 
    cc_params[cc_par_cnt++] = cur;
 
  }
 
  cc_params[cc_par_cnt++] = "-B";
  cc_params[cc_par_cnt++] = as_path;
 
  if (clang_mode)
    cc_params[cc_par_cnt++] = "-no-integrated-as";
 
  if (getenv("AFL_HARDEN")) {
 
    cc_params[cc_par_cnt++] = "-fstack-protector-all";
 
    if (!fortify_set)
      cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
 
  }
 
  if (asan_set) {
 
    /* Pass this on to afl-as to adjust map density. */
 
    setenv("AFL_USE_ASAN", "1", 1);
 
  } else if (getenv("AFL_USE_ASAN")) {
 
    if (getenv("AFL_USE_MSAN"))
      FATAL("ASAN and MSAN are mutually exclusive");
 
    if (getenv("AFL_HARDEN"))
      FATAL("ASAN and AFL_HARDEN are mutually exclusive");
 
    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=address";
 
  } else if (getenv("AFL_USE_MSAN")) {
 
    if (getenv("AFL_USE_ASAN"))
      FATAL("ASAN and MSAN are mutually exclusive");
 
    if (getenv("AFL_HARDEN"))
      FATAL("MSAN and AFL_HARDEN are mutually exclusive");
 
    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=memory";
 
 
  }
 
  if (!getenv("AFL_DONT_OPTIMIZE")) {
 
#if defined(__FreeBSD__) && defined(__x86_64__)
 
    /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself
       works OK. This has nothing to do with us, but let's avoid triggering
       that bug. */
 
    if (!clang_mode || !m32_set)
      cc_params[cc_par_cnt++] = "-g";
 
#else
 
      cc_params[cc_par_cnt++] = "-g";
 
#endif
 
    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";
 
  }
 

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回