栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

AFL源码分析之afl-as.c详细注释

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

AFL源码分析之afl-as.c详细注释

前言

afl-as注释好了,明白了是如何进行插桩的,以及插在哪里

本文主要参考Sakura大佬文章:https://eternalsakura13.com/2020/08/23/afl/

编写不易,如果能够帮助到你,希望能够点赞收藏加关注哦Thanks♪(・ω・)ノ

模糊测试系列往期回顾:
AFL源码分析之afl-gcc.c详细注释




#define AFL_MAIN

#include "config.h"
#include "types.h"
#include "debug.h"
#include "alloc-inl.h"

#include "afl-as.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

static u8** as_params;          

static u8*  input_file;         
static u8*  modified_file;      

static u8   be_quiet,           
            clang_mode,         
            pass_thru,          
            just_version,       
            sanitizer;          

static u32  inst_ratio = 100,   
            as_par_cnt = 1;     



#ifdef WORD_SIZE_64

static u8   use_64bit = 1; //64位标志

#else

static u8   use_64bit = 0; //32位标志

#ifdef __APPLE__ //如果是苹果平台
#  error "Sorry, 32-bit Apple platforms are not supported." //提示苹果平台不支持32位
#endif 

#endif 




static void edit_params(int argc, char** argv) {

  u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS"); //获取环境变量“TMPDIR”和“AFL_AS”
  u32 i;

#ifdef __APPLE__ //如果是APPLE平台

  u8 use_clang_as = 0;

  

  if (clang_mode && !afl_as) { //如果使用clang模式并且没有获取到“AFL_AS”环境变量则进入分支

    use_clang_as = 1; //设置use_clang_as变量为1

    afl_as = getenv("AFL_CC"); 
    if (!afl_as) afl_as = getenv("AFL_CXX");
    if (!afl_as) afl_as = "clang"; //将afl_as赋值为“AFL_CC”、“AFL_CXX”环境变量或“clang”中的一种

  }

#endif 

  

  if (!tmp_dir) tmp_dir = getenv("TEMP");
  if (!tmp_dir) tmp_dir = getenv("TMP");
  if (!tmp_dir) tmp_dir = "/tmp"; //为tmp_dir赋值为"TMPDIR"、“TEMP”、“TMP”环境变量或“/tmp”中的一种

  as_params = ck_alloc((argc + 32) * sizeof(u8*)); //为as_params开辟空间

  as_params[0] = afl_as ? afl_as : (u8*)"as"; //afl_as是否已经获取到“AFL_AS”环境变量,如果获取到了就将环境变量值赋值给as_param[0],如果没有获取到,“as”字符串赋值给afl_as

  as_params[argc] = 0; //设置最后一个参数为0

  for (i = 1; i < argc - 1; i++) { //从第一个参数开始遍历,到最后一个参数

    if (!strcmp(argv[i], "--64")) use_64bit = 1; //如果遍历到“--64”参数,则设置use_64bit变量为1
    else if (!strcmp(argv[i], "--32")) use_64bit = 0; //如果遍历到“--32”参数,则设置use_64bit变量为0

#ifdef __APPLE__ //如果是APPLE平台

    

    if (!strcmp(argv[i], "-arch") && i + 1 < argc) { //如果遍历到“-arch”参数

      if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1; //如果是"-arch x86_64",则设置use_64bit为1
      else if (!strcmp(argv[i + 1], "i386")) //如果是“-arch i386”,则报错
        FATAL("Sorry, 32-bit Apple platforms are not supported.");

    }

    

    if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q"))) 
      continue; //如果是clang模式,并且遍历的是“-q”或者“-Q”参数,直接跳出循环

#endif 

    as_params[as_par_cnt++] = argv[i]; 

  }

#ifdef __APPLE__ //APPLE平台

  

  if (use_clang_as) { 

    as_params[as_par_cnt++] = "-c";
    as_params[as_par_cnt++] = "-x";
    as_params[as_par_cnt++] = "assembler"; //如果使用的时clang模式,追加参数“-c -x assembler”

  }

#endif 

  input_file = argv[argc - 1]; //将最后一个参数的值赋给input_file变量

  if (input_file[0] == '-') { //如果input_file的首字母为“-”

    if (!strcmp(input_file + 1, "-version")) { //如果是“-version”
      just_version = 1; //设置just_version值为1
      modified_file = input_file; //modified设置为“-version”
      goto wrap_things_up; //跳转到参数组合结尾
    }

    if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)"); //如果“-”后不是version,则抛出异常
      else input_file = NULL;

  } else {

    

    if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
        strncmp(input_file, "/var/tmp/", 9) &&
        strncmp(input_file, "/tmp/", 5)) pass_thru = 1;
        //如果首字母不是“-”,则比对input_file的前strlen(tmp_dir)、9、5个字节是否与tmp_dir、"/var/tmp/"、"/tmp/"是否相同,如果都不相同则设置pass_thru为1
  }

  modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(), (u32)time(NULL)); 
  //设置modified_file为类似tmp_dir/.afl-pid-time.s这样的字符串

wrap_things_up:

  as_params[as_par_cnt++] = modified_file; //接收参数为modified最后一个参数
  as_params[as_par_cnt]   = NULL; //参数接收结束

}




static void add_instrumentation(void) { //处理输入文件,生成modified_file,将桩插入所有释放的位置

  static u8 line[MAX_LINE];

  FILE* inf;
  FILE* outf;
  s32 outfd;
  u32 ins_lines = 0;

  u8  instr_ok = 0, skip_csect = 0, skip_next_label = 0,
      skip_intel = 0, skip_app = 0, instrument_next = 0;

#ifdef __APPLE__

  u8* colon_pos;

#endif 

  if (input_file) { //如果存在输入文件名称

    inf = fopen(input_file, "r"); //尝试获取input_file句柄,将fd赋值给inf
    if (!inf) PFATAL("Unable to read '%s'", input_file); //如果获取不到,抛异常

  } else inf = stdin; //如果不存在文件名则赋值标准输入

  outfd = open(modified_file, O_WRonLY | O_EXCL | O_CREAT, 0600); //以写的方式打开modified_file,如果文件已存在就直接打开,如果没有就创建一个

  if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file); //如果文件没有写权限,则抛出异常

  outf = fdopen(outfd, "w"); //尝试打开

  if (!outf) PFATAL("fdopen() failed");  //打不开抛异常

  while (fgets(line, MAX_LINE, inf)) { //循环读取inf指向的文件的每一行到line数组,每行最多MAX_LINE(8192)个字节,含末尾“”

    

    if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
        instrument_next && line[0] == 't' && isalpha(line[1])) { //446行判断是否为defered mode模式

      fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
              R(MAP_SIZE)); //插桩

      instrument_next = 0; //instrument_next重新设置为1
      ins_lines++; //插桩计数器+1

    }

    

    fputs(line, outf);

    if (pass_thru) continue;

    

    if (line[0] == 't' && line[1] == '.') { //判断读入的行是否以“t”开头,并且line[1]是否为"."

      

      if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
          isdigit(line[10]) && line[11] == 'n') skip_next_label = 1; //检查是否为p2align指令,如果是则设置skip_next_label为1
          //instr_ok变量是一个flag,如果为1表示位于.text段,如果为0表示不再.text段
      if (!strncmp(line + 2, "textn", 5) ||
          !strncmp(line + 2, "sectiont.text", 13) ||
          !strncmp(line + 2, "sectiont__TEXT,__text", 21) ||
          !strncmp(line + 2, "section __TEXT,__text", 21)) {
        instr_ok = 1;
        continue; //匹配"textn"、"sectiont.text"、"sectiont__TEXT,__text"、"section __TEXT,__text",如果匹配成功则设置instr_ok为1。跳出本次循环
      }

      if (!strncmp(line + 2, "sectiont", 8) ||
          !strncmp(line + 2, "section ", 8) ||
          !strncmp(line + 2, "bssn", 4) ||
          !strncmp(line + 2, "datan", 5)) {
        instr_ok = 0;
        continue; //匹配"sectiont"、"section "、"bssn"、"datan",如果匹配成功说明不是在.text段,设置instr_ok变量为0
      }

    }

    

    if (strstr(line, ".code")) { //判断架构

      if (strstr(line, ".code32")) skip_csect = use_64bit;
      if (strstr(line, ".code64")) skip_csect = !use_64bit;

    }

    

    if (strstr(line, ".intel_syntax")) skip_intel = 1; //判断是否为Intel汇编语法
    if (strstr(line, ".att_syntax")) skip_intel = 0; //判断是否为att汇编语法

    

    if (line[0] == '#' || line[1] == '#') { //ad-hoc __asm__块是否跳过

      if (strstr(line, "#APP")) skip_app = 1; 
      if (strstr(line, "#NO_APP")) skip_app = 0;

    }

    

    if (skip_intel || skip_app || skip_csect || !instr_ok ||
        line[0] == '#' || line[0] == ' ') continue;

    

    if (line[0] == 't') { 

      if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) { //对于形如tj[^m].格式的指令,即条件跳转指令,且R()函数创建的随机数小于插桩密度inst_ratio

        fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
                R(MAP_SIZE)); //判断是否为64位程序,使用fprintf函数将桩插在outf只想的文件的tj[^m].跳转指令位置,插入长度为R函数创建的小于MAP_SIZE的随机数

        ins_lines++; //插桩计数器+1,跳出循环进行下一次遍历

      }

      continue;

    }

    

#ifdef __APPLE__

    

    if ((colon_pos = strstr(line, ":"))) { //检查line中是否存在“:”

      if (line[0] == 'L' && isdigit(*(colon_pos - 1))) { //检查是否以“.”开始

#else

    

    if (strstr(line, ":")) { //检查line中是否存在“:”

      if (line[0] == '.') { //检查是否以“.”开始

#endif 

        

#ifdef __APPLE__

        

        if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
            && R(100) < inst_ratio) {

#else

        

        if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
            && R(100) < inst_ratio) { //检查line[2]是否为数字,或者在clang模式下从line[1]开始的三个字节是否为LBB,并且随机数小于插桩密度
 
#endif 

          

          if (!skip_next_label) instrument_next = 1; else skip_next_label = 0; //设置instrument_next为1

        }

      } else {

        

        instrument_next = 1; //否则代表这是一个function label,插桩^func,设置instrument_next为1(defered mode)
    
      }

    }

  }

  if (ins_lines) //如果插桩计数器不为0
    fputs(use_64bit ? main_payload_64 : main_payload_32, outf); //向outf中写入main_payload_64或main_payload_32

  if (input_file) fclose(inf); //关闭文件
  fclose(outf); //关闭文件

  if (!be_quiet) { //如果使用的不是静默模式

    if (!ins_lines) WARNF("No instrumentation targets found%s.", //如果插桩计数器为空,抛异常
                          pass_thru ? " (pass-thru mode)" : "");
    else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", //插桩成功输出
             ins_lines, use_64bit ? "64" : "32",
             getenv("AFL_HARDEN") ? "hardened" : 
             (sanitizer ? "ASAN/MSAN" : "non-hardened"),
             inst_ratio);
 
  }

}




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

  s32 pid;
  u32 rand_seed;
  int status;
  u8* inst_ratio_str = getenv("AFL_INST_RATIO"); //该环境变量主要控制检测每个分支的概率,取值为0到100%,设置为0时值检测函数入口的跳转,而不会检测函数分支的跳转

  struct timeval tv;
  struct timezone tz;

  clang_mode = !!getenv(CLANG_ENV_VAR); 

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

    SAYF(cCYA "afl-as " cBRI VERSION cRST " by n");
 
  } else be_quiet = 1;

  if (argc < 2) {

    SAYF("n"
         "This is a helper hollk application for afl-fuzz. It is a wrapper around GNU 'as',n"
         "executed by the toolchain whenever using afl-gcc or afl-clang. You probablyn"
         "don't want to run this program directly.nn"

         "Rarely, when dealing with extremely hollk complex projects, it may be advisable ton"
         "set AFL_INST_RATIO to a value less than 100 in order to reduce the odds ofn"
         "instrumenting every discovered branch.nn");

    exit(1);

  }

  gettimeofday(&tv, &tz); //获取当前精确时间

  rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();  //通过当前时间与进程pid进行亦或处理

  srandom(rand_seed); //获得随机化种子

  edit_params(argc, argv); //检查并修改参数以传递给“as”。文件名是以GCC传递的最后一个参数决定的。此函数主要设置变量as_params的值,以及use_64bit/modified_file的值

  if (inst_ratio_str) { //如果获取到"AFL_INST_RATIO"环境变量则进入分支

    if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100) //如果没有将覆盖率写入inst_ratio变量或者inst_ratio中的值超过100的话,则进入分支抛出异常
      FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)");

  }

  if (getenv(AS_LOOP_ENV_VAR)) //如果获取到"__AFL_AS_LOOPCHECK"环境变量值,则进入分支
    FATAL("Endless loop when calling 'as' (remove '.' from your PATH)"); //抛出异常

  setenv(AS_LOOP_ENV_VAR, "1", 1); //设置"__AFL_AS_LOOPCHECK"环境变量为1

  

  if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) { //如果获取到"AFL_USE_ASAN"或"AFL_USE_MSAN"环境变量,如果其中有一个值为1则进入分支
    sanitizer = 1; //sanitizer设置为1
    inst_ratio /= 3; //inst_ratio除以3
  }

  if (!just_version) add_instrumentation(); //如果不是只查询version,那么就会进入add_instrumentastion()函数,该函数主要处理输入文件,生成modified_file,将桩插入释放的位置

  if (!(pid = fork())) { //调用fork函数创建一个子进程。在执行execvp函数执行是

    execvp(as_params[0], (char**)as_params); //执行命令和参数
    FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]); //不成功抛异常

  }

  if (pid < 0) PFATAL("fork() failed"); //如果创建子进程失败,抛出异常

  if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); //等待子进程结束

  if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file); //读取环境变量"AFL_KEEP_ASSEMBLY"失败,则unlink掉modified_file
  //设置该环境变量主要是为了防止afl-as删掉插桩后的汇编文件,设置为1会保留插桩后的汇编文件
  exit(WEXITSTATUS(status));

}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/511866.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号