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

前言

通过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;

}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值