zoukankan      html  css  js  c++  java
  • 【AFL(三)】afl-tmin修改:添加文件夹操作指令

    前言:

    之前分析tmin工具的时候,提到tmin的命令目前只能对文件进行操作,很多博客也提供了写脚本的方式对文件夹进行操作。本文是想通过修改tmin源代码的方式,实现添加新命令参数就可以对文件夹进行操作。

    本文分为三部分:主要思路、部分实现细节、演示。

    在文章最后给出了git地址,可以pull下来直接替换afl-tmin.c用。


    主要思路:

    【一】分析源码

    在main函数入口之前,有几个自变量、函数需要仔细查看:

    1. 自变量

    static u8 *in_file,                   /* Minimizer input test case         */
              *out_file,                  /* Minimizer output file             */

    in_fileout_file 分别是 -i-o 之后的参数存放的自变量。in_file 就相当于字符串,如果输出,可以直接用来输出文件名。

    2. 文件读取函数 static void read_initial_file(void)

    用 in_file 作为路径,除了读取文件,还会在读取文件之前进行分析路径是否合适等等。

    static void read_initial_file(void) {
    
      struct stat st;
      s32 fd = open(in_file, O_RDONLY);
    
      if (fd < 0) PFATAL("Unable to open '%s'", in_file);
    
      if (fstat(fd, &st) || !st.st_size)
        FATAL("Zero-sized input file.");
    
      if (st.st_size >= TMIN_MAX_FILE)
        FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024);
    
      in_len  = st.st_size;
      in_data = ck_alloc_nozero(in_len);
    
      ck_read(fd, in_data, in_len, in_file);
    
      close(fd);
    
      OKF("Read %u byte%s from '%s'.", in_len, in_len == 1 ? "" : "s", in_file);
    
    }
    点击展开查看函数所有代码 View All Code

    3.文件输出函数 static s32 write_to_file(u8* path, u8* mem, u32 len)

    用 path 作为路径,将处理好的数据,写到文件中。

    static s32 write_to_file(u8* path, u8* mem, u32 len) {
    
      s32 ret;
    
      unlink(path); /* Ignore errors */
    
      ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
    
      if (ret < 0) PFATAL("Unable to create '%s'", path);
    
      ck_write(ret, mem, len, path);
    
      lseek(ret, 0, SEEK_SET);
    
      return ret;
    
    }
    点击展开查看函数所有代码 View All Code

    4.真正的最小化处理函数 static void minimize(char** argv)

    tmin最核心的部分,用来处理读取到的数据。

    static void minimize(char** argv) {
    
      static u32 alpha_map[256];
    
      u8* tmp_buf = ck_alloc_nozero(in_len);
      u32 orig_len = in_len, stage_o_len;
    
      u32 del_len, set_len, del_pos, set_pos, i, alpha_size, cur_pass = 0;
      u32 syms_removed, alpha_del0 = 0, alpha_del1, alpha_del2, alpha_d_total = 0;
      u8  changed_any, prev_del;
    
      /***********************
       * BLOCK NORMALIZATION *
       ***********************/
    
      set_len    = next_p2(in_len / TMIN_SET_STEPS);
      set_pos    = 0;
    
      if (set_len < TMIN_SET_MIN_SIZE) set_len = TMIN_SET_MIN_SIZE;
    
      ACTF(cBRI "Stage #0: " cRST "One-time block normalization...");
    
      while (set_pos < in_len) {
    
        u8  res;
        u32 use_len = MIN(set_len, in_len - set_pos);
    
        for (i = 0; i < use_len; i++)
          if (in_data[set_pos + i] != '0') break;
    
        if (i != use_len) {
    
          memcpy(tmp_buf, in_data, in_len);
          memset(tmp_buf + set_pos, '0', use_len);
      
          res = run_target(argv, tmp_buf, in_len, 0);
    
          if (res) {
    
            memset(in_data + set_pos, '0', use_len);
            changed_any = 1;
            alpha_del0 += use_len;
    
          }
    
        }
    
        set_pos += set_len;
    
      }
    
      alpha_d_total += alpha_del0;
    
      OKF("Block normalization complete, %u byte%s replaced.", alpha_del0,
          alpha_del0 == 1 ? "" : "s");
    
      next_pass:
    
      ACTF(cYEL "--- " cBRI "Pass #%u " cYEL "---", ++cur_pass);
      changed_any = 0;
    
      /******************
       * BLOCK DELETION *
       ******************/
    
      del_len = next_p2(in_len / TRIM_START_STEPS);
      stage_o_len = in_len;
    
      ACTF(cBRI "Stage #1: " cRST "Removing blocks of data...");
    
      next_del_blksize:
    
      if (!del_len) del_len = 1;
      del_pos  = 0;
      prev_del = 1;
    
      SAYF(cGRA "    Block length = %u, remaining size = %u
    " cRST,
           del_len, in_len);
    
      while (del_pos < in_len) {
    
        u8  res;
        s32 tail_len;
    
        tail_len = in_len - del_pos - del_len;
        if (tail_len < 0) tail_len = 0;
    
        /* If we have processed at least one full block (initially, prev_del == 1),
           and we did so without deleting the previous one, and we aren't at the
           very end of the buffer (tail_len > 0), and the current block is the same
           as the previous one... skip this step as a no-op. */
    
        if (!prev_del && tail_len && !memcmp(in_data + del_pos - del_len,
            in_data + del_pos, del_len)) {
    
          del_pos += del_len;
          continue;
    
        }
    
        prev_del = 0;
    
        /* Head */
        memcpy(tmp_buf, in_data, del_pos);
    
        /* Tail */
        memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len);
    
        res = run_target(argv, tmp_buf, del_pos + tail_len, 0);
    
        if (res) {
    
          memcpy(in_data, tmp_buf, del_pos + tail_len);
          prev_del = 1;
          in_len   = del_pos + tail_len;
    
          changed_any = 1;
    
        } else del_pos += del_len;
    
      }
    
      if (del_len > 1 && in_len >= 1) {
    
        del_len /= 2;
        goto next_del_blksize;
    
      }
    
      OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len);
    
      if (!in_len && changed_any)
        WARNF(cLRD "Down to zero bytes - check the command line and mem limit!" cRST);
    
      if (cur_pass > 1 && !changed_any) goto finalize_all;
    
      /*************************
       * ALPHABET MINIMIZATION *
       *************************/
    
      alpha_size   = 0;
      alpha_del1   = 0;
      syms_removed = 0;
    
      memset(alpha_map, 0, 256 * sizeof(u32));
    
      for (i = 0; i < in_len; i++) {
        if (!alpha_map[in_data[i]]) alpha_size++;
        alpha_map[in_data[i]]++;
      }
    
      ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...",
           alpha_size, alpha_size == 1 ? "" : "s");
    
      for (i = 0; i < 256; i++) {
    
        u32 r;
        u8 res;
    
        if (i == '0' || !alpha_map[i]) continue;
    
        memcpy(tmp_buf, in_data, in_len);
    
        for (r = 0; r < in_len; r++)
          if (tmp_buf[r] == i) tmp_buf[r] = '0'; 
    
        res = run_target(argv, tmp_buf, in_len, 0);
    
        if (res) {
    
          memcpy(in_data, tmp_buf, in_len);
          syms_removed++;
          alpha_del1 += alpha_map[i];
          changed_any = 1;
    
        }
    
      }
    
      alpha_d_total += alpha_del1;
    
      OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.",
          syms_removed, syms_removed == 1 ? "" : "s",
          alpha_del1, alpha_del1 == 1 ? "" : "s");
    
      /**************************
       * CHARACTER MINIMIZATION *
       **************************/
    
      alpha_del2 = 0;
    
      ACTF(cBRI "Stage #3: " cRST "Character minimization...");
    
      memcpy(tmp_buf, in_data, in_len);
    
      for (i = 0; i < in_len; i++) {
    
        u8 res, orig = tmp_buf[i];
    
        if (orig == '0') continue;
        tmp_buf[i] = '0';
    
        res = run_target(argv, tmp_buf, in_len, 0);
    
        if (res) {
    
          in_data[i] = '0';
          alpha_del2++;
          changed_any = 1;
    
        } else tmp_buf[i] = orig;
    
      }
    
      alpha_d_total += alpha_del2;
    
      OKF("Character minimization done, %u byte%s replaced.",
          alpha_del2, alpha_del2 == 1 ? "" : "s");
    
      if (changed_any) goto next_pass;
    
      finalize_all:
    
      SAYF("
    "
           cGRA "     File size reduced by : " cRST "%0.02f%% (to %u byte%s)
    "
           cGRA "    Characters simplified : " cRST "%0.02f%%
    "
           cGRA "     Number of execs done : " cRST "%u
    "
           cGRA "          Fruitless execs : " cRST "path=%u crash=%u hang=%s%u
    
    ",
           100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s",
           ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1),
           total_execs, missed_paths, missed_crashes, missed_hangs ? cLRD : "",
           missed_hangs);
    
      if (total_execs > 50 && missed_hangs * 10 > total_execs)
        WARNF(cLRD "Frequent timeouts - results may be skewed." cRST);
    
    }
    点击展开查看函数所有代码 View All Code

    5.显示提示 static void usage(u8* argv0)

    用来显示提示部分,添加新参数之后,在此修改提示。

    static void usage(u8* argv0) {
    
      SAYF("
    %s [ options ] -- /path/to/target_app [ ... ]
    
    "
    
           "Required parameters:
    
    "
    
           "  -d 0/1        - 0:not dir mode, 1:is dir mode
    "
           "  -i file       - input test case to be shrunk by the tool
    "
           "  -o file       - final output location for the minimized data
    
    "
    
           "Execution control settings:
    
    "
    
           "  -f file       - input file read by the tested program (stdin)
    "
           "  -t msec       - timeout for each run (%u ms)
    "
           "  -m megs       - memory limit for child process (%u MB)
    "
           "  -Q            - use binary-only instrumentation (QEMU mode)
    
    "
    
           "Minimization settings:
    
    "
    
           "  -e            - solve for edge coverage only, ignore hit counts
    "
           "  -x            - treat non-zero exit codes as crashes
    
    "
    
           "For additional tips, please consult %s/README.
    
    ",
    
           argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);
    
      exit(1);
    
    }
    点击展开查看函数所有代码 View All Code

    main函数流程简析

    1.第一个主要部分:while循环

    while内的参数 (opt = getopt(argc,argv,"+d:i:o:f:m:t:B:xeQ")) > 0 可见其实我已经加入了 d 参数,while实现对命令行所有参数都能读取,并通过一个switch实现对读取到的命令行参数进行判断。

    2.初步检查参数合法性

    在开始对文件进行处理之前,要对while过程读取到的一些参数进行初步检查,保证之后不能报错。

    3.文件操作

    一直到 close(write_to_file(out_file, in_data, in_len)); 之前,都是对文件的操作部分,这一部分可以当作一个代码块(或者是没有抽象出来的函数)对待,利用读文件函数,最小化函数,输出到文件函数,实现 tmin 核心操作。

    【二】设计代码思路

    经过刚刚的分析,有两点可以确定:第一、最核心的最小化函数是数据为参数进行操作,所有写代码的时候没有设计文件夹操作(要不然一定会把以文件文件的操作单独抽象出来);第二、要想实现功能需要单独自己添加文件夹的操作;

    所以思路就是:先添加命令行新参数 -d -> 然后根据 -d 判断是文件夹操作 -> 文件夹进行循环 -> 循环内对单个文件进行操作 -> 跳出循环结束。


    部分实现细节:

    【一】新参数和对参数的判断

    添加新参数,输入文件夹,输出文件夹,输入模式保存 -d(1代表文件夹模式,0代表文件模式)

    static u8 *in_dir,                    /* Minimizer input direction         */
              *out_dir;                   /* Minimizer output direction        */
    static u8 *dir_mode;                  /* dir mode : 1(yes) / 0(no)         */

    并在 main 函数的 whileswitch 里进行判断

          case 'd':
            if (dir_mode) FATAL("Multiple -d options not supported");
            dir_mode = optarg;
            break;
    
          case 'i':
    
            if (in_file) FATAL("Multiple -i options not supported");
            if (*dir_mode == '1')
              in_dir = optarg;
            else
              in_file = optarg;
            break;
    
          case 'o':
    
            if (out_file) FATAL("Multiple -o options not supported");
            if (*dir_mode == '1')
              out_dir = optarg;
            else
              out_file = optarg;
            break;

    【二】文件夹循环和对单个文件的操作

    利用 goto 实现循环(一开始我是想用while实现,但是发现afl源码里出现很多的 goto,虽然 c 语言老师一再强调不要用 goto,影响可读性,但是现在看来,管他可不可读,在源码基础上改,还是用 goto 舒服)。

    如果是文件操作模式:

    还是按照原计划进行,但是在刚刚提到的文件操作代码块的前面加上 deal_file: 用来跳转,在代码块之后,用判断是否是文件夹操作模式的 goto deal_dir; 实现文件夹模式,跳转回循环

    如果是文件夹操作模式:

        if (*dir_mode == '1'){
        if ( !in_dir || !out_dir){
          usage(argv[0]);
        }
        /* 读取文件夹下的所有文件,每个文件进行操作,在输出文件夹生成相应的文件【暂时不递归文件夹下面的文件】 */
        /* 利用loop实现全部文件循环读取 */
        DIR *d = NULL;
        struct dirent *dp = NULL;
        struct stat st;
        char p_in[256] = {0};
        char p_out[256] = {0};
    
        //检查路径合理性
        if((!(d=opendir(in_dir))) || stat(in_dir, &st) < 0 || !S_ISDIR(st.st_mode)){
          ACTF("invalid path:%s
    ", in_dir);
          goto finish_tmin;
        }
     deal_dir:
        if((dp = readdir(d)) == NULL){
          closedir(d);
          goto finish_tmin;
        }
        //当前路径和上一级路径以及隐藏文件去掉,避免死循环
        if ((!strncmp(dp->d_name, ".", 1)) || (!strncmp(dp->d_name, "..", 2)))
          goto deal_dir;
    
        snprintf(p_in, sizeof(p_in) - 1, "%s/%s", in_dir, dp->d_name);
        stat(p_in, &st);
        snprintf(p_out, sizeof(p_out) - 1, "%s/%s", out_dir, dp->d_name);
        if(!S_ISDIR(st.st_mode)) { 
          in_file = p_in;
          out_file = p_out;
          goto deal_file;
        }
        else{
          //文件夹下面的文件也是文件夹的话,在此操作留作递归操作
        }

    演示:

    重新安装魔改后的afl

    修改afl-tmin之后,利用指令实现重新安装:

    make
    sudo make install

    注意,要在afl源文件路径下进行此操作。

    添加新参数后的测试

    可以看到效果还是很好的,对文件夹每个文件的操作,操作的细则会按文件一块一块展示。

    源码

    GitHub地址:https://github.com/WayneDevMaze/Chinese_noted_AFL


     Reference

    【1】Linux C 编程的文件夹遍历:Linux c 遍历目录及目录下文件 - guotianqing的博客 - CSDN博客

    【2】C语言拼接字符:C语言拼接字符串 -- 使用strcat()函数 - 白菜菜白 - 博客园

    【3】Linux C 编程:linux下c编程 基础 - konglingbin - 博客园

  • 相关阅读:
    springboot+maven+thymeleaf配置实战demo
    报错AbstractStandardExpressionAttributeTagProcessor
    IllegalStateException: Unable to find a @SpringBootConfiguration
    Java装饰模式
    Java容器类解析
    jdk之object源码理解
    osx brew mysql
    java String[] 初始化
    date 常用
    mac mysql
  • 原文地址:https://www.cnblogs.com/wayne-tao/p/11964565.html
Copyright © 2011-2022 走看看