AFL源码分析之afl-clang-fast(学习笔记)

本文介绍了afl-clang-fast工具的工作原理与源码解析,该工具作为afl-fuzz的辅助程序,能够替代传统的clang编译器,帮助第三方代码进行必要的运行时插桩。相较于afl-gcc,afl-clang-fast通过LLVM pass实现了更高效的性能。

前言

通过afl-gcc来插桩这种做法已经属于不建议,更好的就是afl-clang-fast工具是通过llvm pass来插桩。

#ifdef 是判断某个宏是否被定义,若已定义,执行随后的语句

#endif 是#if, #ifdef, #ifndef这些条件命令的结束标志.

一、头文件名解析

1、config.h

属于配置类的头文件,包含大量的宏定义,用来规定。
宏定义大概分三个部分,刚开始是一些通用的参数,如输入输出文件的大小,用户的颜色配置;第二部分是跟变异有关的,比如在文件变异时候的interest过程,所用到的预设特殊值,就是在这个头文件中定义的,还有bitflip阶段的 token 长度定义也是在这个头文件中;第三部分是特殊用途的宏定义,比如 HASH_CONST 定义了哈希函数里的一个长度常量,等等

2、types.h

其是对一些数据类型的重命名或者是定义,以及对DMA通用地址的定义以及其64为的特性。

3、debug.h

调试,宏定义各种参数及函数

4、alloc-inl.h

内存相关,提供错误检查、内存清零、内存分配等常规操作,“内存器的设计初衷不是为了抵抗恶意攻击,但是它确实提供了便携健壮的内存处理方式,可以检查 use-after-free 等”

二、afl-clang-fast源码

afl-clang-fast.c 文件本质上是 clang 的 wrapper,最终调用的还是 clang 。但是与 afl-gcc 一样,会进行一些参数处理。

#define AFL_MAIN

#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>

static u8*  obj_path;               /* Path to runtime libraries 运行库的路径   */  
static u8** cc_params;              /* Parameters passed to the real CC 传递给实际CC的运行时库参数的路径*/ 
static u32  cc_par_cnt = 1;         /* Param count, including argv0 参数计数,包括argv0  */


/* Try to find the runtime libraries. If that fails, abort. 尝试查找运行库。如果失败,中止。*/

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);//alloc_printf函数动态分配一段空间存储路径

    if (!access(tmp, R_OK)) { //读取AFL_PATH/afl-llvm-rt.o是否可以访问
      obj_path = afl_path;  //如果可以就设置这个目录为obj_path,然后直接返回
      ck_free(tmp);//释放掉alloc_printf分配的内存
      return;
    }

    ck_free(tmp);//释放掉alloc_printf分配的内存

  }
 //如果不存在环境变量AFL_PATH的值
  slash = strrchr(argv0, '/');//检查argv0里有没有’/‘,如果没有就赋值’argv0’到slash,如果有就找到最后一个’/‘所在的位置,然后跳过这个’/‘,将后面的字符串赋值给slash

  if (slash) {

    u8 *dir;

    *slash = 0;
    dir = ck_strdup(argv0);//取其前面的字符串作为dir
    *slash = '/';

    tmp = alloc_printf("%s/afl-llvm-rt.o", dir);

    if (!access(tmp, R_OK)) { //如果可以就设置这个目录为obj_path,然后直接返回
      obj_path = dir;
      ck_free(tmp);
      return;
    }

    ck_free(tmp);
    ck_free(dir);

  }

  if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) { //如果上面两种都找不到,因为默认的AFL的MakeFile在编译的时候,会定义一个名为AFL_PATH的宏,其指向/usr/local/lib/afl,会到这里找是否存在afl-llvm-rt.o,如果存在设置obj_path并直接返回
    obj_path = AFL_PATH;
    return;
  }

  FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH");//如果上述三种方式都找不到,那么就会抛出异常

}


/* Copy argv to cc_params, making the necessary edits.  将argv复制到cc_参数,进行必要的编辑 */

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 = ck_alloc((argc + 128) * sizeof(u8*));//通过ck_alloc来为cc_params分配内存,分配的长度为(argc+128)*8

  name = strrchr(argv[0], '/');//检查argv[0]里有没有’/‘,如果没有就赋值’argv[0]’到name,如果有就找到最后一个’/‘所在的位置,然后跳过这个’/‘,将后面的字符串赋值给name
  if (!name) name = argv[0]; else name++;

  if (!strcmp(name, "afl-clang-fast++")) {//如果name变量中的字符是afl-clang++
    u8* alt_cxx = getenv("AFL_CXX");//读取环境变量AFL_CXX
    cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";//如果存在,就将其值设置为cc_params[0],如果不存在,就直接设置成clang++
  } else {
    u8* alt_cc = getenv("AFL_CC");//读取环境变量AFL_CC
    cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";//如果存在,就将其值设置为cc_params[0],如果不存在,就直接设置成clang
  }

  /* There are two ways to compile afl-clang-fast. In the traditional mode, we
     use afl-llvm-pass.so to inject instrumentation. In the experimental
     'trace-pc-guard' mode, we use native LLVM instrumentation callbacks
     instead. The latter is a very recent addition - see:

     http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards */
   /*有两种方法可以编译afl-clang-fast。在传统模式中,我们使用afl-llvm-pass。所以要注入仪器。在实验中“trace pc guard”模式下,我们使用本机LLVM检测回调相反后者是最近添加的一项功能,请参见:
  http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards */
#ifdef USE_TRACE_PC  //如果定义了USE_TRACE_PC宏
//就将-fsanitize-coverage=trace-pc-guard -mllvm -sanitizer-coverage-block-threshold=0添加到参数里
  cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
#ifndef __ANDROID__
  cc_params[cc_par_cnt++] = "-mllvm";
  cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
#endif
#else
  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 */
//依次读取我们传给afl-clang-fast的参数,并添加到cc_params里,不过这里会做一些检查和设置
  cc_params[cc_par_cnt++] = "-Qunused-arguments";

  while (--argc) {
    u8* cur = *(++argv);

    if (!strcmp(cur, "-m32")) bit_mode = 32;
    if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;//如果传入参数里有-m32或者armv7a-linux-androideabi,就设置bit_mode为32
    if (!strcmp(cur, "-m64")) bit_mode = 64;//如果传入参数里有-m32或者armv7a-linux-androideabi,就设置bit_mode为32

    if (!strcmp(cur, "-x")) x_set = 1;//如果传入参数里有-x,就设置x_set为1

    if (!strcmp(cur, "-fsanitize=address") ||
        !strcmp(cur, "-fsanitize=memory")) asan_set = 1; //如果传入参数里有-fsanitize=address或者-fsanitize=memory,就设置asan_set为1

    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; //如果传入参数里有FORTIFY_SOURCE就设置fortify_set = 1

    if (!strcmp(cur, "-Wl,-z,defs") ||
        !strcmp(cur, "-Wl,--no-undefined")) continue; //如果传入参数里有-Wl,-z,defs或者-Wl,--no-undefined,就直接pass掉,不传给clang。

    cc_params[cc_par_cnt++] = cur;

  }

  if (getenv("AFL_HARDEN")) {//读取环境变量AFL_HARDEN

    cc_params[cc_par_cnt++] = "-fstack-protector-all";//如果存在,就在cc_params里添加-fstack-protector-all

    if (!fortify_set)
      cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";//如果不存在fortify_set,就在cc_params里添加-D_FORTIFY_SOURCE=2

  }

  if (!asan_set) {//如果不存在asan_set

    if (getenv("AFL_USE_ASAN")) {//获取环境变量AFL_USE_ASAN

      if (getenv("AFL_USE_MSAN"))
        FATAL("ASAN and MSAN are mutually exclusive");//如果环境变量AFL_USE_MSAN不存在,报错

      if (getenv("AFL_HARDEN"))
        FATAL("ASAN and AFL_HARDEN are mutually exclusive");//如果环境变量AFL_HARDEN不存在,报错

      cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";//在cc_params里添加-U_FORTIFY_SOURCE
      cc_params[cc_par_cnt++] = "-fsanitize=address";//在cc_params里添加-fsanitize=address
    } else if (getenv("AFL_USE_MSAN")) {//获取环境变量AFL_USE_MSAN

      if (getenv("AFL_USE_ASAN"))
        FATAL("ASAN and MSAN are mutually exclusive");//如果环境变量AFL_USE_ASAN不存在,报错

      if (getenv("AFL_HARDEN"))
        FATAL("MSAN and AFL_HARDEN are mutually exclusive");//如果环境变量AFL_HARDEN不存在,报错

      cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";//在cc_params里添加-U_FORTIFY_SOURCE
      cc_params[cc_par_cnt++] = "-fsanitize=memory";//在cc_params里添加-fsanitize=memory

    }

  }

#ifdef USE_TRACE_PC //如果定义了USE_TRACE_PC宏

  if (getenv("AFL_INST_RATIO"))
    FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'.");//检查是否存在环境变量AFL_INST_RATIO,如果存在就抛出异常AFL_INST_RATIO not available at compile time with 'trace-pc'.

#endif /* USE_TRACE_PC */

  if (!getenv("AFL_DONT_OPTIMIZE")) {  //读取环境变量AFL_DONT_OPTIMIZE,如果不存在就添加-g -O3 -funroll-loops到参数里

    cc_params[cc_par_cnt++] = "-g";
    cc_params[cc_par_cnt++] = "-O3";
    cc_params[cc_par_cnt++] = "-funroll-loops";

  }

  if (getenv("AFL_NO_BUILTIN")) {//读取环境变量AFL_NO_BUILTIN,如果存在就添加-fno-builtin-strcmp等。

    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++] = "-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";

  /* When the user tries to use persistent or deferred forkserver modes by
     appending a single line to the program, we want to reliably inject a
     signature into the binary (to be picked up by afl-fuzz) and we want
     to call a function from the runtime .o file. This is unnecessarily
     painful for three reasons:

     1) We need to convince the compiler not to optimize out the signature.
        This is done with __attribute__((used)).

     2) We need to convince the linker, when called with -Wl,--gc-sections,
        not to do the same. This is done by forcing an assignment to a
        'volatile' pointer.

     3) We need to declare __afl_persistent_loop() in the global namespace,
        but doing this within a method in a class is hard - :: and extern "C"
        are forbidden and __attribute__((alias(...))) doesn't work. Hence the
        __asm__ aliasing trick.

   */

  cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)="
    "({ static volatile char *_B __attribute__((used)); "
    " _B = (char*)\"" PERSIST_SIG "\"; "
#ifdef __APPLE__
    "__attribute__((visibility(\"default\"))) "
    "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
#else
    "__attribute__((visibility(\"default\"))) "
    "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
#endif /* ^__APPLE__ */
    "_L(_A); })";

  cc_params[cc_par_cnt++] = "-D__AFL_INIT()="
    "do { static volatile char *_A __attribute__((used)); "
    " _A = (char*)\"" DEFER_SIG "\"; "
#ifdef __APPLE__
    "__attribute__((visibility(\"default\"))) "
    "void _I(void) __asm__(\"___afl_manual_init\"); "
#else
    "__attribute__((visibility(\"default\"))) "
    "void _I(void) __asm__(\"__afl_manual_init\"); "
#endif /* ^__APPLE__ */
    "_I(); } while (0)";

  if (x_set) {
    cc_params[cc_par_cnt++] = "-x";//如果x_set为1,则添加参数-x none
    cc_params[cc_par_cnt++] = "none";
  }

#ifndef __ANDROID__
  switch (bit_mode) {//根据bit_mode的值选择afl-llvm-rt

    case 0://如果为0,即没有-m32和-m64选项,就向参数里添加obj_path/afl-llvm-rt.o
      cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path);
      break;

    case 32://如果为32,添加obj_path/afl-llvm-rt-32.o
      cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path);

      if (access(cc_params[cc_par_cnt - 1], R_OK))
        FATAL("-m32 is not supported by your compiler");

      break;

    case 64://如果为64,添加obj_path/afl-llvm-rt-64.o
      cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path);

      if (access(cc_params[cc_par_cnt - 1], R_OK))
        FATAL("-m64 is not supported by your compiler");

      break;

  }
#endif

  cc_params[cc_par_cnt] = NULL;

}


/* Main entry point */

int main(int argc, char** argv) {

  if (isatty(2) && !getenv("AFL_QUIET")) {

#ifdef USE_TRACE_PC
    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");
#endif /* ^USE_TRACE_PC */

  }

  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"
         "instrumentation. A common use pattern would be one of the following:\n\n"

         "  CC=%s/afl-clang-fast ./configure\n"
         "  CXX=%s/afl-clang-fast++ ./configure\n\n"

         "In contrast to the traditional afl-clang tool, this version is implemented as\n"
         "an LLVM pass and tends to offer improved performance with slow programs.\n\n"

         "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);

    exit(1);

  }


#ifndef __ANDROID__
  find_obj(argv[0]);
#endif

  edit_params(argc, argv);

  execvp(cc_params[0], (char**)cc_params);//替换进程空间,执行要调用的clang和为其传递参数

  FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);

  return 0;

}
<think>根据用户的问题,用户遇到了AFL(American Fuzzy Lop)工具在编译时使用afl-gcc或afl-clang时,因为这些编译器被移除而导致的程序中止报错(报错位置:main(), src/afl-cc.c:3605)。用户提供了多个引用,其中引用[1]提到清理编译文件并修改Makefile,引用[2]提到AFL的编译器是gcc/clang的包装器,引用[4]提到afl-gcc.c的源码分析,引用[5]提到afl-as.c的源码分析。 问题分析: 报错信息指向`src/afl-cc.c:3605`,这通常意味着AFL的cc组件在尝试调用afl-gcc或afl-clang时发现它们已经被移除或不可用。从引用[2]中我们知道,afl-gcc等实际上是gcc/clang的包装器。在最新版本的AFL(如AFL++)中,afl-gcc和afl-clang可能已经被弃用,取而代之的是统一的`afl-cc`工具。 解决方案: 1. 首先,确认你使用的AFL版本。如果是较新的AFL++版本(推荐),那么afl-gcc和afl-clang已经被整合到`afl-cc`中。因此,你应该使用`afl-cc`来编译你的程序。 2. 在编译时,使用`afl-cc`代替afl-gcc或afl-clang。例如,将原来的编译命令: afl-gcc -g -O0 test.c -o test 改为: afl-cc -g -O0 test.c -o test 3. 如果使用的是Makefile,可以修改Makefile中的CC变量,例如: CC = afl-cc 4. 清理之前的编译文件(如引用[1]所述),确保重新编译: make clean rm -rf $HOME/example/install/ 5. 设置环境变量(如果需要),确保AFL的编译器被正确使用。例如,在编译时指定编译器: CC=afl-cc CXX=afl-c++ make 6. 如果上述方法不起作用,可以尝试使用AFL的LLVM模式(如果系统支持)。首先确保安装了clang(引用[2]中提到),然后使用: afl-clang-fast # 代替afl-gcc 或者 afl-clang-fast++ # 代替afl-g++ 注意:afl-clang-fast是LLVM模式,需要系统安装clang和llvm(版本要求通常较高)。 如果用户使用的AFL版本较旧,建议升级到AFL++(最新版本),因为旧版本的AFL已经不再维护。 具体步骤总结: 1. 升级AFL到最新版(AFL++): git clone https://github.com/AFLplusplus/AFLplusplus cd AFLplusplus make sudo make install 2. 使用`afl-cc`代替afl-gcc/afl-clang,或者使用`afl-clang-fast`(需要安装LLVM)。 3. 清理之前的编译环境,重新编译目标程序。 如果用户不想升级,可以尝试在旧版本中寻找是否有`afl-cc`,或者检查环境变量是否设置正确,但升级是最推荐的解决方案。 引用说明: 根据用户提供的引用,引用[1]中提到了清理和修改Makefile的方法,引用[2]提到了AFL编译器是gcc/clang的包装器,这些信息帮助我们理解AFL的工作方式。引用[4]和[5]是源码分析,说明AFL内部实现,但当前问题更可能是由于版本更新导致的接口变化。 因此,解决该问题的核心是使用新版本的AFL++并调整编译命令。 生成相关问题: 为了帮助用户更好地理解和使用AFL,生成以下相关问题: 1. 如何安装最新版的AFL++? 2. 如何使用AFL的LLVM模式进行模糊测试? 3. 在编译目标程序时,如何设置AFL的编译选项以获取更好的覆盖率? 4. AFL++有哪些改进和新增功能? 5. 如何解决AFL编译时出现的其他常见错误? 下面将按照用户要求的结构化方式回答。 </think> ### 解决 AFL-GCC/AFL-CLang 因过时被移除导致的程序中止报错 #### 问题分析 根据报错信息 `PROGRAM ABORT (Location: main(), src/afl-cc.c:3605)`,该问题是由于 AFL 工具链中 `afl-gcc/afl-clang` 被标记为废弃并在新版本中被移除导致的。错误发生在 AFL 的编译控制模块(`afl-cc.c`),表明当前使用的 AFL 版本已不再支持传统的 GCC/Clang 包装器[^2][^4]。 #### 解决方案 **核心思路**:使用 AFL++(AFL 的增强分支)并切换到其现代化的编译器接口 `afl-cc`,或启用 LLVM 模式。 ##### 步骤 1:升级到 AFL++ 并清理环境 ```bash # 卸载旧版 AFL sudo apt purge afl # Debian/Ubuntu sudo yum remove afl # CentOS/RHEL # 安装 AFL++ git clone https://github.com/AFLplusplus/AFLplusplus cd AFLplusplus make sudo make install # 清理历史编译文件(关键!) make clean rm -rf $HOME/example/install # 移除旧版残留[^1] ``` ##### 步骤 2:使用 `afl-cc` 替代传统编译器 修改项目 Makefile,将编译器替换为统一接口: ```makefile # 修改前 CC = afl-gcc CXX = afl-g++ # 修改后 CC = afl-cc CXX = afl-c++ ``` 或通过环境变量指定: ```bash export CC=afl-cc export CXX=afl-c++ make ``` ##### 步骤 3:启用 LLVM 模式(推荐) ```bash # 安装 LLVM 依赖 sudo apt install clang llvm # Debian/Ubuntu sudo yum install clang llvm # CentOS/RHEL # 编译时使用 LLVM 模式 afl-clang-fast -g -O0 test.c -o test # C 语言 afl-clang-fast++ -g -O0 test.cpp -o test # C++[^2] ``` ##### 步骤 4:验证安装 ```bash # 检查编译器版本 afl-cc --version afl-clang-fast --version # 运行测试 afl-fuzz -i input/ -o output/ ./target_program ``` #### 关键说明 1. **废弃原因**:传统 `afl-gcc/afl-clang` 已被整合进 `afl-cc` 统一接口,旧调用会触发强制报错。 2. **LLVM 优势**: - 更快的插桩速度 - 支持更复杂的代码覆盖分析 - 兼容最新 Clang 工具链[^5] 3. **环境清理**:必须删除旧版 AFL 的编译残留(特别是 `$HOME/example/install`),否则可能引发冲突[^1]。 #### 常见错误排查 若仍报错,检查: 1. 系统是否安装 LLVM:`clang --version` 2. Makefile 中是否残留 `afl-gcc` 引用 3. 环境变量是否覆盖:`unset CC CXX` 后重试
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值