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

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

2025-8-16 15:29
4943

因为是整个AFL的核心区域,函数长度有点长,我们从以下几个部分分析

跟afl-as使用相同的策略,确保整个AFL工具的随机性一致

支持的主要选项:

extern char *optarg;

optarg定义在<unistd.h>头文件中,由 getopt() 函数维护,当 getopt() 解析到一个需要参数的选项时,会将该参数的地址赋值给 optarg,例如此时有-i /tmp/test这个选项,如果getopt的第三个参数有i:的话,那么此时*optarg="/tmp/test"

可以看到是主/从同步模式,afl-fuzz的单个fuzzer只占用一个进程,因此只能占用一个核心,而在多核系统中可以同时运行多个fuzzer实例

实例与实例之间的共享方式是通过将信息输入到同一个文件夹下,每个fuzzer周期性的check文件夹下其他fuzzer生成的用例,将有用的用例加入到自己的用例集中,这里可以详细分析一下:

目录结构大致会是这样的

可以看到同步时机是5次

可以看到,保留用例来自于save_if_interesting函数:

ret=0的话代表没有新覆盖率,ret=1代表发现已知路径的新的命中次数,ret=2代表发现全新的执行路径

bash

Apply to afl-fuzz.c

Run

解读:

0x0000-0x000F: 全部为0xFF,表示这些路径完全未被触及

0x0010: 0xFE,表示这个路径被触及过1次

0x0012: 0xFC,表示这个路径被触及过2-3次

0x0014: 0xF8,表示这个路径被触及过4-7次

以此类推...

接下来就是一些参数的验证和环境的检查

同时dubm模式与crash和qemu模式不兼容

重要环境变量:

设置stop_soon标志为1,表示AFL应该开始优雅退出

终止所有子进程和fork服务器进程

响应SIGHUP(终端断开)、SIGINT(Ctrl+C)和SIGTERM(终止请求)信号

设置child_timed_out标志,表示目标程序执行超时

强制终止超时的子进程

响应SIGALRM信号,用于执行时间限制控制

设置skip_requested标志,允许跳过当前测试用例

响应SIGUSR1信号,提供手动干预模糊测试过程的能力

SIGTSTP(Ctrl+Z):防止AFL被挂起

SIGPIPE(管道破裂):防止在写入已关闭的管道时程序终止

用于验证内存安全工具的环境变量配置是否符合要求。这个函数主要检查ASAN(AddressSanitizer)和MSAN(MemorySanitizer)的配置选项,确保它们以最适合模糊测试的方式设置。

AFL要求ASAN(AddressSanitizer)配置包含以下选项:

abort_on_error=1

功能:当ASAN检测到内存错误时立即终止程序

重要性:确保AFL能够捕获到崩溃,而不是让程序继续执行

如果缺少:AFL会显示致命错误并终止

symbolize=0

功能:禁用ASAN的符号化错误报告

重要性:防止ASAN生成详细的错误报告,这会干扰AFL的崩溃检测机制

如果缺少:AFL会显示致命错误并终止

对于MSAN(MemorySanitizer),AFL要求:

exit_code=MSAN_ERROR

功能:设置MSAN检测到错误时的退出代码为特定值(MSAN_ERROR)

重要性:允许AFL准确识别由未初始化内存使用导致的崩溃

如果缺少:AFL会显示致命错误并终止

symbolize=0

功能:与ASAN相同,禁用符号化错误报告

重要性:确保错误报告不会干扰AFL的崩溃检测

如果缺少:AFL会显示致命错误并终止

当使用AFL与ASAN/MSAN一起进行模糊测试时,应该设置以下环境变量:

主-从模式:

主节点(-M):执行确定性测试,系统地探索输入空间

从节点(-S):专注于随机变异,提高覆盖率和发现率

共享发现:

所有节点共享一个顶级同步目录

每个节点定期检查其他节点的发现

有趣的测试用例会被所有节点采纳

工作分配:

主节点处理计算密集型的确定性阶段

从节点可以更快地进入随机变异阶段

整体提高了模糊测试的效率

该函数将命令行参数数组(argc/argv)转换为单个连续的字符串,并将其存储在全局变量orig_cmdline中,以便在程序的其他部分使用。

就是afl用来设置横幅(banner)的函数

检查是否是在tty即终端下运行的函数

剩下的就挑几个有点意思的分析吧

读取输入目录中的所有测试用例并将其排队等待测试,此函数在启动时调用。

检查是否存在<in-dir>/queue目录,存在的话设置为新的in_dir,为了支持恢复之前的模糊测试会话

使用scandir()函数遍历in_dir,通过alphasort()确保测试用例按字母顺序排序

如果启用了shuffle_queue且同时存在多个测试用例,使用shuffle_ptrs函数随机排列测试用例

过滤非常规文件、空文件和README文件;将有效的测试用例添加到队列

将之前模糊测试会话中发现的有价值的字节序列(token)加入字典中,用于新的模糊测试会话

会发现上述涉及两个概念:token 和 字典

字典里存储着afl-fuzz认定的有效token,存在必要性

例如:png文件的文件头是IHDR,当afl-fuzz变异到I字节后,因为发生了错误,所以走到了一个不同的执行路径,然后继续H D R的变异,会发现与字节I变异走到了同一个执行路径,都发生了同一个错误,那么afl-fuzz会认定IHDR是一个有效的、不可分割的字节单元,将其加入字典后,再次用fuzz遇到它就不会再进行逐字节的变异了

pivot_inputs:规范命名,优先使用硬链接而非复制,记录测试用例之间的派生关系

load_extras:可以通过设置-x选项加载任意一个dict文件(字典)或者目录

find_timeout:设置超时时间

detect_file_args&&setup_stdio_file:检测末尾是否有@@

check_binary:检查二进制文件的合法性

get_cur_time:获取当前的准确时间

get_qemu_argv:解析qemu的参数

例如:./afl-fuzz -Q -i testcases/input -o findings -- ./target_binary @@运行这个命令

那么在该函数中就会将其解析成:./afl-qemu-trace -- ./target_binary @@

/* Main entry point */
 
int main(int argc, char** argv) {
 
  s32 opt;
  u64 prev_queued = 0;
  u32 sync_interval_cnt = 0, seek_to;
  u8  *extras_dir = 0;
  u8  mem_limit_given = 0;
  u8  exit_1 = !!getenv("AFL_BENCH_JUST_ONE");
  char** use_argv;
 
  struct timeval tv;
  struct timezone tz;
 
  SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
 
  doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
 
  gettimeofday(&tv, &tz);
  srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
 
  while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0)
 
    switch (opt) {
 
      case 'i': /* input dir */
 
        if (in_dir) FATAL("Multiple -i options not supported");
        in_dir = optarg;
 
        if (!strcmp(in_dir, "-")) in_place_resume = 1;
 
        break;
 
      case 'o': /* output dir */
 
        if (out_dir) FATAL("Multiple -o options not supported");
        out_dir = optarg;
        break;
 
      case 'M': { /* master sync ID */
 
          u8* c;
 
          if (sync_id) FATAL("Multiple -S or -M options not supported");
          sync_id = ck_strdup(optarg);
 
          if ((c = strchr(sync_id, ':'))) {
 
            *c = 0;
 
            if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
                !master_id || !master_max || master_id > master_max ||
                master_max > 1000000) FATAL("Bogus master ID passed to -M");
 
          }
 
          force_deterministic = 1;
 
        }
 
        break;
 
      case 'S':
 
        if (sync_id) FATAL("Multiple -S or -M options not supported");
        sync_id = ck_strdup(optarg);
        break;
 
      case 'f': /* target file */
 
        if (out_file) FATAL("Multiple -f options not supported");
        out_file = optarg;
        break;
 
      case 'x': /* dictionary */
 
        if (extras_dir) FATAL("Multiple -x options not supported");
        extras_dir = optarg;
        break;
 
      case 't': { /* timeout */
 
          u8 suffix = 0;
 
          if (timeout_given) FATAL("Multiple -t options not supported");
 
          if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -t");
 
          if (exec_tmout < 5) FATAL("Dangerously low value of -t");
 
          if (suffix == '+') timeout_given = 2; else timeout_given = 1;
 
          break;
 
      }
 
      case 'm': { /* mem limit */
 
          u8 suffix = 'M';
 
          if (mem_limit_given) FATAL("Multiple -m options not supported");
          mem_limit_given = 1;
 
          if (!strcmp(optarg, "none")) {
 
            mem_limit = 0;
            break;
 
          }
 
          if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -m");
 
          switch (suffix) {
 
            case 'T': mem_limit *= 1024 * 1024; break;
            case 'G': mem_limit *= 1024; break;
            case 'k': mem_limit /= 1024; break;
            case 'M': break;
 
            default:  FATAL("Unsupported suffix or bad syntax for -m");
 
          }
 
          if (mem_limit < 5) FATAL("Dangerously low value of -m");
 
          if (sizeof(rlim_t) == 4 && mem_limit > 2000)
            FATAL("Value of -m out of range on 32-bit systems");
 
        }
 
        break;
       
      case 'b': { /* bind CPU core */
 
          if (cpu_to_bind_given) FATAL("Multiple -b options not supported");
          cpu_to_bind_given = 1;
 
          if (sscanf(optarg, "%u", &cpu_to_bind) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -b");
 
          break;
 
      }
 
      case 'd': /* skip deterministic */
 
        if (skip_deterministic) FATAL("Multiple -d options not supported");
        skip_deterministic = 1;
        use_splicing = 1;
        break;
 
      case 'B': /* load bitmap */
 
        /* This is a secret undocumented option! It is useful if you find
           an interesting test case during a normal fuzzing process, and want
           to mutate it without rediscovering any of the test cases already
           found during an earlier run.
 
           To use this mode, you need to point -B to the fuzz_bitmap produced
           by an earlier run for the exact same binary... and that's it.
 
           I only used this once or twice to get variants of a particular
           file, so I'm not making this an official setting. */
 
        if (in_bitmap) FATAL("Multiple -B options not supported");
 
        in_bitmap = optarg;
        read_bitmap(in_bitmap);
        break;
 
      case 'C': /* crash mode */
 
        if (crash_mode) FATAL("Multiple -C options not supported");
        crash_mode = FAULT_CRASH;
        break;
 
      case 'n': /* dumb mode */
 
        if (dumb_mode) FATAL("Multiple -n options not supported");
        if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1;
 
        break;
 
      case 'T': /* banner */
 
        if (use_banner) FATAL("Multiple -T options not supported");
        use_banner = optarg;
        break;
 
      case 'Q': /* QEMU mode */
 
        if (qemu_mode) FATAL("Multiple -Q options not supported");
        qemu_mode = 1;
 
        if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
 
        break;
 
      case 'V': /* Show version number */
 
        /* Version number has been printed already, just quit. */
        exit(0);
 
      default:
 
        usage(argv[0]);
 
    }
 
  if (optind == argc || !in_dir || !out_dir) usage(argv[0]);
 
  setup_signal_handlers();
  check_asan_opts();
 
  if (sync_id) fix_up_sync();
 
  if (!strcmp(in_dir, out_dir))
    FATAL("Input and output directories can't be the same");
 
  if (dumb_mode) {
 
    if (crash_mode) FATAL("-C and -n are mutually exclusive");
    if (qemu_mode)  FATAL("-Q and -n are mutually exclusive");
 
  }
 
  if (getenv("AFL_NO_FORKSRV"))    no_forkserver    = 1;
  if (getenv("AFL_NO_CPU_RED"))    no_cpu_meter_red = 1;
  if (getenv("AFL_NO_ARITH"))      no_arith         = 1;
  if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue    = 1;
  if (getenv("AFL_FAST_CAL"))      fast_cal         = 1;
 
  if (getenv("AFL_HANG_TMOUT")) {
    hang_tmout = atoi(getenv("AFL_HANG_TMOUT"));
    if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");
  }
 
  if (dumb_mode == 2 && no_forkserver)
    FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");
 
  if (getenv("AFL_PRELOAD")) {
    setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
    setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
  }
 
  if (getenv("AFL_LD_PRELOAD"))
    FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
 
  save_cmdline(argc, argv);
 
  fix_up_banner(argv[optind]);
 
  check_if_tty();
 
  get_core_count();
 
#ifdef HAVE_AFFINITY
  bind_to_free_cpu();
#endif /* HAVE_AFFINITY */
 
  check_crash_handling();
  check_cpu_governor();
 
  setup_post();
  setup_shm();
  init_count_class16();
 
  setup_dirs_fds();
  read_testcases();
  load_auto();
 
  pivot_inputs();
 
  if (extras_dir) load_extras(extras_dir);
 
  if (!timeout_given) find_timeout();
 
  detect_file_args(argv + optind + 1);
 
  if (!out_file) setup_stdio_file();
 
  check_binary(argv[optind]);
 
  start_time = get_cur_time();
 
  if (qemu_mode)
    use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
  else
    use_argv = argv + optind;
 
  perform_dry_run(use_argv);
 
  cull_queue();
 
  show_init_stats();
 
  seek_to = find_start_position();
 
  write_stats_file(0, 0, 0);
  save_auto();
 
  if (stop_soon) goto stop_fuzzing;
 
  /* Woop woop woop */
 
  if (!not_on_tty) {
    sleep(4);
    start_time += 4000;
    if (stop_soon) goto stop_fuzzing;
  }
 
  while (1) {
 
    u8 skipped_fuzz;
 
    cull_queue();
 
    if (!queue_cur) {
 
      queue_cycle++;
      current_entry     = 0;
      cur_skipped_paths = 0;
      queue_cur         = queue;
 
      while (seek_to) {
        current_entry++;
        seek_to--;
        queue_cur = queue_cur->next;
      }
 
      show_stats();
 
      if (not_on_tty) {
        ACTF("Entering queue cycle %llu.", queue_cycle);
        fflush(stdout);
      }
 
      /* If we had a full queue cycle with no new finds, try
         recombination strategies next. */
 
      if (queued_paths == prev_queued) {
 
        if (use_splicing) cycles_wo_finds++; else use_splicing = 1;
 
      } else cycles_wo_finds = 0;
 
      prev_queued = queued_paths;
 
      if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST"))
        sync_fuzzers(use_argv);
 
    }
 
    skipped_fuzz = fuzz_one(use_argv);
 
    if (!stop_soon && sync_id && !skipped_fuzz) {
       
      if (!(sync_interval_cnt++ % SYNC_INTERVAL))
        sync_fuzzers(use_argv);
 
    }
 
    if (!stop_soon && exit_1) stop_soon = 2;
 
    if (stop_soon) break;
 
    queue_cur = queue_cur->next;
    current_entry++;
 
  }
 
  if (queue_cur) show_stats();
 
  /* If we stopped programmatically, we kill the forkserver and the current runner.
     If we stopped manually, this is done by the signal handler. */
  if (stop_soon == 2) {
      if (child_pid > 0) kill(child_pid, SIGKILL);
      if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL);
  }
  /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */
  if (waitpid(forksrv_pid, NULL, 0) <= 0) {
    WARNF("error waitpid\n");
  }
 
  write_bitmap();
  write_stats_file(0, 0, 0);
  save_auto();
 
stop_fuzzing:
 
  SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
       stop_soon == 2 ? "programmatically" : "by user");
 
  /* Running for more than 30 minutes but still doing first cycle? */
 
  if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) {
 
    SAYF("\n" cYEL "[!] " cRST
           "Stopped during the first cycle, results may be incomplete.\n"
           "    (For info on resuming, see %s/README.)\n", doc_path);
 
  }
 
  fclose(plot_file);
  destroy_queue();
  destroy_extras();
  ck_free(target_path);
  ck_free(sync_id);
 
  alloc_report();
 
  OKF("We're done here. Have a nice day!\n");
 
  exit(0);
 
}
 
#endif /* !AFL_LIB *
/* Main entry point */
 
int main(int argc, char** argv) {
 
  s32 opt;
  u64 prev_queued = 0;
  u32 sync_interval_cnt = 0, seek_to;
  u8  *extras_dir = 0;
  u8  mem_limit_given = 0;
  u8  exit_1 = !!getenv("AFL_BENCH_JUST_ONE");
  char** use_argv;
 
  struct timeval tv;
  struct timezone tz;
 
  SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
 
  doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
 
  gettimeofday(&tv, &tz);
  srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
 
  while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0)
 
    switch (opt) {
 
      case 'i': /* input dir */
 
        if (in_dir) FATAL("Multiple -i options not supported");
        in_dir = optarg;
 
        if (!strcmp(in_dir, "-")) in_place_resume = 1;
 
        break;
 
      case 'o': /* output dir */
 
        if (out_dir) FATAL("Multiple -o options not supported");
        out_dir = optarg;
        break;
 
      case 'M': { /* master sync ID */
 
          u8* c;
 
          if (sync_id) FATAL("Multiple -S or -M options not supported");
          sync_id = ck_strdup(optarg);
 
          if ((c = strchr(sync_id, ':'))) {
 
            *c = 0;
 
            if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
                !master_id || !master_max || master_id > master_max ||
                master_max > 1000000) FATAL("Bogus master ID passed to -M");
 
          }
 
          force_deterministic = 1;
 
        }
 
        break;
 
      case 'S':
 
        if (sync_id) FATAL("Multiple -S or -M options not supported");
        sync_id = ck_strdup(optarg);
        break;
 
      case 'f': /* target file */
 
        if (out_file) FATAL("Multiple -f options not supported");
        out_file = optarg;
        break;
 
      case 'x': /* dictionary */
 
        if (extras_dir) FATAL("Multiple -x options not supported");
        extras_dir = optarg;
        break;
 
      case 't': { /* timeout */
 
          u8 suffix = 0;
 
          if (timeout_given) FATAL("Multiple -t options not supported");
 
          if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -t");
 
          if (exec_tmout < 5) FATAL("Dangerously low value of -t");
 
          if (suffix == '+') timeout_given = 2; else timeout_given = 1;
 
          break;
 
      }
 
      case 'm': { /* mem limit */
 
          u8 suffix = 'M';
 
          if (mem_limit_given) FATAL("Multiple -m options not supported");
          mem_limit_given = 1;
 
          if (!strcmp(optarg, "none")) {
 
            mem_limit = 0;
            break;
 
          }
 
          if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -m");
 
          switch (suffix) {
 
            case 'T': mem_limit *= 1024 * 1024; break;
            case 'G': mem_limit *= 1024; break;
            case 'k': mem_limit /= 1024; break;
            case 'M': break;
 
            default:  FATAL("Unsupported suffix or bad syntax for -m");
 
          }
 
          if (mem_limit < 5) FATAL("Dangerously low value of -m");
 
          if (sizeof(rlim_t) == 4 && mem_limit > 2000)
            FATAL("Value of -m out of range on 32-bit systems");
 
        }
 
        break;
       
      case 'b': { /* bind CPU core */
 
          if (cpu_to_bind_given) FATAL("Multiple -b options not supported");
          cpu_to_bind_given = 1;
 
          if (sscanf(optarg, "%u", &cpu_to_bind) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -b");
 
          break;
 
      }
 
      case 'd': /* skip deterministic */
 
        if (skip_deterministic) FATAL("Multiple -d options not supported");
        skip_deterministic = 1;
        use_splicing = 1;
        break;
 
      case 'B': /* load bitmap */
 
        /* This is a secret undocumented option! It is useful if you find
           an interesting test case during a normal fuzzing process, and want
           to mutate it without rediscovering any of the test cases already
           found during an earlier run.
 
           To use this mode, you need to point -B to the fuzz_bitmap produced
           by an earlier run for the exact same binary... and that's it.
 
           I only used this once or twice to get variants of a particular
           file, so I'm not making this an official setting. */
 
        if (in_bitmap) FATAL("Multiple -B options not supported");
 
        in_bitmap = optarg;
        read_bitmap(in_bitmap);
        break;
 
      case 'C': /* crash mode */
 
        if (crash_mode) FATAL("Multiple -C options not supported");
        crash_mode = FAULT_CRASH;
        break;
 
      case 'n': /* dumb mode */
 
        if (dumb_mode) FATAL("Multiple -n options not supported");
        if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1;
 
        break;
 
      case 'T': /* banner */
 
        if (use_banner) FATAL("Multiple -T options not supported");
        use_banner = optarg;
        break;
 
      case 'Q': /* QEMU mode */
 
        if (qemu_mode) FATAL("Multiple -Q options not supported");
        qemu_mode = 1;
 
        if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
 
        break;
 
      case 'V': /* Show version number */
 
        /* Version number has been printed already, just quit. */
        exit(0);
 
      default:
 
        usage(argv[0]);
 
    }
 
  if (optind == argc || !in_dir || !out_dir) usage(argv[0]);
 
  setup_signal_handlers();
  check_asan_opts();
 
  if (sync_id) fix_up_sync();
 
  if (!strcmp(in_dir, out_dir))
    FATAL("Input and output directories can't be the same");
 
  if (dumb_mode) {
 
    if (crash_mode) FATAL("-C and -n are mutually exclusive");
    if (qemu_mode)  FATAL("-Q and -n are mutually exclusive");
 
  }
 
  if (getenv("AFL_NO_FORKSRV"))    no_forkserver    = 1;
  if (getenv("AFL_NO_CPU_RED"))    no_cpu_meter_red = 1;
  if (getenv("AFL_NO_ARITH"))      no_arith         = 1;
  if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue    = 1;
  if (getenv("AFL_FAST_CAL"))      fast_cal         = 1;
 
  if (getenv("AFL_HANG_TMOUT")) {
    hang_tmout = atoi(getenv("AFL_HANG_TMOUT"));
    if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");
  }
 
  if (dumb_mode == 2 && no_forkserver)
    FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");
 
  if (getenv("AFL_PRELOAD")) {
    setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
    setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
  }
 
  if (getenv("AFL_LD_PRELOAD"))
    FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
 
  save_cmdline(argc, argv);
 
  fix_up_banner(argv[optind]);
 
  check_if_tty();
 
  get_core_count();
 
#ifdef HAVE_AFFINITY
  bind_to_free_cpu();
#endif /* HAVE_AFFINITY */
 
  check_crash_handling();
  check_cpu_governor();
 
  setup_post();
  setup_shm();
  init_count_class16();
 
  setup_dirs_fds();
  read_testcases();
  load_auto();
 
  pivot_inputs();
 
  if (extras_dir) load_extras(extras_dir);
 
  if (!timeout_given) find_timeout();
 
  detect_file_args(argv + optind + 1);
 
  if (!out_file) setup_stdio_file();
 
  check_binary(argv[optind]);
 
  start_time = get_cur_time();
 
  if (qemu_mode)
    use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
  else
    use_argv = argv + optind;
 
  perform_dry_run(use_argv);
 
  cull_queue();
 
  show_init_stats();
 
  seek_to = find_start_position();
 
  write_stats_file(0, 0, 0);
  save_auto();
 
  if (stop_soon) goto stop_fuzzing;
 
  /* Woop woop woop */
 
  if (!not_on_tty) {
    sleep(4);
    start_time += 4000;
    if (stop_soon) goto stop_fuzzing;
  }
 
  while (1) {
 
    u8 skipped_fuzz;
 
    cull_queue();
 
    if (!queue_cur) {
 
      queue_cycle++;
      current_entry     = 0;
      cur_skipped_paths = 0;
      queue_cur         = queue;
 
      while (seek_to) {
        current_entry++;
        seek_to--;
        queue_cur = queue_cur->next;
      }
 
      show_stats();
 
      if (not_on_tty) {
        ACTF("Entering queue cycle %llu.", queue_cycle);
        fflush(stdout);
      }
 
      /* If we had a full queue cycle with no new finds, try
         recombination strategies next. */
 
      if (queued_paths == prev_queued) {
 
        if (use_splicing) cycles_wo_finds++; else use_splicing = 1;
 
      } else cycles_wo_finds = 0;
 
      prev_queued = queued_paths;
 
      if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST"))
        sync_fuzzers(use_argv);
 
    }
 
    skipped_fuzz = fuzz_one(use_argv);
 
    if (!stop_soon && sync_id && !skipped_fuzz) {
       
      if (!(sync_interval_cnt++ % SYNC_INTERVAL))
        sync_fuzzers(use_argv);
 
    }
 
    if (!stop_soon && exit_1) stop_soon = 2;
 
    if (stop_soon) break;
 
    queue_cur = queue_cur->next;
    current_entry++;
 
  }
 
  if (queue_cur) show_stats();
 
  /* If we stopped programmatically, we kill the forkserver and the current runner.
     If we stopped manually, this is done by the signal handler. */
  if (stop_soon == 2) {
      if (child_pid > 0) kill(child_pid, SIGKILL);
      if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL);
  }
  /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */
  if (waitpid(forksrv_pid, NULL, 0) <= 0) {
    WARNF("error waitpid\n");
  }
 
  write_bitmap();
  write_stats_file(0, 0, 0);
  save_auto();
 
stop_fuzzing:
 
  SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
       stop_soon == 2 ? "programmatically" : "by user");
 
  /* Running for more than 30 minutes but still doing first cycle? */
 
  if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) {
 
    SAYF("\n" cYEL "[!] " cRST
           "Stopped during the first cycle, results may be incomplete.\n"
           "    (For info on resuming, see %s/README.)\n", doc_path);
 
  }
 
  fclose(plot_file);
  destroy_queue();
  destroy_extras();
  ck_free(target_path);
  ck_free(sync_id);
 
  alloc_report();
 
  OKF("We're done here. Have a nice day!\n");
 
  exit(0);
 
}
 
#endif /* !AFL_LIB *
s32 opt;                        // getopt选项解析
u64 prev_queued = 0;           // 上一轮队列中的测试用例数
u32 sync_interval_cnt = 0,      // 同步间隔计数器
    seek_to;                   // 队列中的起始位置
u8  *extras_dir = 0;           // 额外字典目录
u8  mem_limit_given = 0;       // 内存限制是否设置
u8  exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); // 基准测试模式
char** use_argv;               // 实际使用的命令行参数
 
struct timeval tv;             // 时间结构
struct timezone tz;            // 时区结构
s32 opt;                        // getopt选项解析
u64 prev_queued = 0;           // 上一轮队列中的测试用例数
u32 sync_interval_cnt = 0,      // 同步间隔计数器
    seek_to;                   // 队列中的起始位置
u8  *extras_dir = 0;           // 额外字典目录
u8  mem_limit_given = 0;       // 内存限制是否设置
u8  exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); // 基准测试模式
char** use_argv;               // 实际使用的命令行参数
 
struct timeval tv;             // 时间结构
struct timezone tz;            // 时区结构
// 1. 显示版本信息
SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
 
// 2. 设置文档路径
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
 
// 3. 初始化随机数生成器
gettimeofday(&tv, &tz);
srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
// 1. 显示版本信息
SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
 
// 2. 设置文档路径
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
 
// 3. 初始化随机数生成器
gettimeofday(&tv, &tz);
srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0)
    switch (opt) {
        // 各种选项处理
    }
while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0)
    switch (opt) {
        // 各种选项处理
    }
case 'i': /* input dir */
 
  if (in_dir) FATAL("Multiple -i options not supported");
  in_dir = optarg;
 
  if (!strcmp(in_dir, "-")) in_place_resume = 1;
 
  break;
 
case 'o': /* output dir */
 
  if (out_dir) FATAL("Multiple -o options not supported");
  out_dir = optarg;
  break;
 
case 'M': { /* master sync ID */
 
    u8* c;
 
    if (sync_id) FATAL("Multiple -S or -M options not supported");
    sync_id = ck_strdup(optarg);
 
    if ((c = strchr(sync_id, ':'))) {
 
      *c = 0;
 
      if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
          !master_id || !master_max || master_id > master_max ||
          master_max > 1000000) FATAL("Bogus master ID passed to -M");
 
    }
 
    force_deterministic = 1;
 
  }
 
  break;
 
case 'S':
 
  if (sync_id) FATAL("Multiple -S or -M options not supported");
  sync_id = ck_strdup(optarg);
  break;
 
case 'f': /* target file */
 
  if (out_file) FATAL("Multiple -f options not supported");
  out_file = optarg;
  break;
 
case 'x': /* dictionary */
 
  if (extras_dir) FATAL("Multiple -x options not supported");
  extras_dir = optarg;
  break;
 
case 't': { /* timeout */
 
    u8 suffix = 0;
 
    if (timeout_given) FATAL("Multiple -t options not supported");
 
    if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
        optarg[0] == '-') FATAL("Bad syntax used for -t");
 
    if (exec_tmout < 5) FATAL("Dangerously low value of -t");
 
    if (suffix == '+') timeout_given = 2; else timeout_given = 1;
 
    break;
 
}
 
case 'm': { /* mem limit */
 
    u8 suffix = 'M';
 
    if (mem_limit_given) FATAL("Multiple -m options not supported");
    mem_limit_given = 1;
 
    if (!strcmp(optarg, "none")) {
 
      mem_limit = 0;
      break;
 
    }
 
    if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
        optarg[0] == '-') FATAL("Bad syntax used for -m");
 
    switch (suffix) {
 
      case 'T': mem_limit *= 1024 * 1024; break;
      case 'G': mem_limit *= 1024; break;
      case 'k': mem_limit /= 1024; break;
      case 'M': break;
 
      default:  FATAL("Unsupported suffix or bad syntax for -m");
 
    }
 
    if (mem_limit < 5) FATAL("Dangerously low value of -m");
 
    if (sizeof(rlim_t) == 4 && mem_limit > 2000)
      FATAL("Value of -m out of range on 32-bit systems");
 
  }
 
  break;
 
case 'b': { /* bind CPU core */
 
    if (cpu_to_bind_given) FATAL("Multiple -b options not supported");
    cpu_to_bind_given = 1;
 
    if (sscanf(optarg, "%u", &cpu_to_bind) < 1 ||
        optarg[0] == '-') FATAL("Bad syntax used for -b");
 
    break;
 
}
 
case 'd': /* skip deterministic */
 
  if (skip_deterministic) FATAL("Multiple -d options not supported");
  skip_deterministic = 1;
  use_splicing = 1;
  break;
 
case 'B': /* load bitmap */
 
  /* This is a secret undocumented option! It is useful if you find
     an interesting test case during a normal fuzzing process, and want
     to mutate it without rediscovering any of the test cases already
     found during an earlier run.
 
     To use this mode, you need to point -B to the fuzz_bitmap produced
     by an earlier run for the exact same binary... and that's it.
 
     I only used this once or twice to get variants of a particular
     file, so I'm not making this an official setting. */
 
  if (in_bitmap) FATAL("Multiple -B options not supported");
 
  in_bitmap = optarg;
  read_bitmap(in_bitmap);
  break;
 
case 'C': /* crash mode */
 
  if (crash_mode) FATAL("Multiple -C options not supported");
  crash_mode = FAULT_CRASH;
  break;
 
case 'n': /* dumb mode */
 
  if (dumb_mode) FATAL("Multiple -n options not supported");
  if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1;
 
  break;
 
case 'T': /* banner */
 
  if (use_banner) FATAL("Multiple -T options not supported");
  use_banner = optarg;
  break;
 
case 'Q': /* QEMU mode */
 
  if (qemu_mode) FATAL("Multiple -Q options not supported");
  qemu_mode = 1;
 
  if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
 
  break;
 
case 'V': /* Show version number */
 
  /* Version number has been printed already, just quit. */
  exit(0);
 
default:
 
  usage(argv[0]);
case 'i': /* input dir */
 
  if (in_dir) FATAL("Multiple -i options not supported");
  in_dir = optarg;
 
  if (!strcmp(in_dir, "-")) in_place_resume = 1;
 
  break;
 
case 'o': /* output dir */
 
  if (out_dir) FATAL("Multiple -o options not supported");
  out_dir = optarg;
  break;
 
case 'M': { /* master sync ID */
 
    u8* c;
 
    if (sync_id) FATAL("Multiple -S or -M options not supported");
    sync_id = ck_strdup(optarg);
 
    if ((c = strchr(sync_id, ':'))) {
 
      *c = 0;
 
      if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
          !master_id || !master_max || master_id > master_max ||
          master_max > 1000000) FATAL("Bogus master ID passed to -M");
 
    }
 
    force_deterministic = 1;
 
  }
 
  break;
 
case 'S':
 
  if (sync_id) FATAL("Multiple -S or -M options not supported");
  sync_id = ck_strdup(optarg);
  break;
 
case 'f': /* target file */
 
  if (out_file) FATAL("Multiple -f options not supported");
  out_file = optarg;
  break;
 
case 'x': /* dictionary */
 
  if (extras_dir) FATAL("Multiple -x options not supported");
  extras_dir = optarg;
  break;
 
case 't': { /* timeout */
 
    u8 suffix = 0;
 
    if (timeout_given) FATAL("Multiple -t options not supported");
 
    if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
        optarg[0] == '-') FATAL("Bad syntax used for -t");
 
    if (exec_tmout < 5) FATAL("Dangerously low value of -t");
 
    if (suffix == '+') timeout_given = 2; else timeout_given = 1;
 
    break;
 
}
 
case 'm': { /* mem limit */
 
    u8 suffix = 'M';
 
    if (mem_limit_given) FATAL("Multiple -m options not supported");
    mem_limit_given = 1;
 
    if (!strcmp(optarg, "none")) {
 
      mem_limit = 0;
      break;
 
    }
 
    if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
        optarg[0] == '-') FATAL("Bad syntax used for -m");
 
    switch (suffix) {
 
      case 'T': mem_limit *= 1024 * 1024; break;
      case 'G': mem_limit *= 1024; break;
      case 'k': mem_limit /= 1024; break;
      case 'M': break;
 
      default:  FATAL("Unsupported suffix or bad syntax for -m");
 
    }
 
    if (mem_limit < 5) FATAL("Dangerously low value of -m");
 
    if (sizeof(rlim_t) == 4 && mem_limit > 2000)
      FATAL("Value of -m out of range on 32-bit systems");
 
  }
 
  break;
 
case 'b': { /* bind CPU core */
 
    if (cpu_to_bind_given) FATAL("Multiple -b options not supported");
    cpu_to_bind_given = 1;
 
    if (sscanf(optarg, "%u", &cpu_to_bind) < 1 ||
        optarg[0] == '-') FATAL("Bad syntax used for -b");
 
    break;
 
}
 
case 'd': /* skip deterministic */
 
  if (skip_deterministic) FATAL("Multiple -d options not supported");
  skip_deterministic = 1;
  use_splicing = 1;
  break;
 
case 'B': /* load bitmap */
 
  /* This is a secret undocumented option! It is useful if you find
     an interesting test case during a normal fuzzing process, and want
     to mutate it without rediscovering any of the test cases already
     found during an earlier run.
 
     To use this mode, you need to point -B to the fuzz_bitmap produced
     by an earlier run for the exact same binary... and that's it.
 
     I only used this once or twice to get variants of a particular
     file, so I'm not making this an official setting. */
 
  if (in_bitmap) FATAL("Multiple -B options not supported");
 
  in_bitmap = optarg;
  read_bitmap(in_bitmap);
  break;
 
case 'C': /* crash mode */
 
  if (crash_mode) FATAL("Multiple -C options not supported");
  crash_mode = FAULT_CRASH;
  break;
 
case 'n': /* dumb mode */
 
  if (dumb_mode) FATAL("Multiple -n options not supported");
  if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1;
 
  break;
 
case 'T': /* banner */
 
  if (use_banner) FATAL("Multiple -T options not supported");
  use_banner = optarg;
  break;
 
case 'Q': /* QEMU mode */
 
  if (qemu_mode) FATAL("Multiple -Q options not supported");
  qemu_mode = 1;
 
  if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
 
  break;
 
case 'V': /* Show version number */
 
  /* Version number has been printed already, just quit. */
  exit(0);
 
default:
 
  usage(argv[0]);
选项 参数 功能 重要性
-i 目录 输入测试用例目录 必需
-o 目录 输出结果目录 必需
-f 文件 目标程序输入文件 可选
-m 内存 内存限制 重要
-t 时间 执行超时 重要
-M/-S ID 主/从同步模式 并行
-x 目录 字典目录 优化
-Q - QEMU模式 特殊
-C - 崩溃模式 调试
-n - 哑模式 特殊
-B bitmap 基于已有覆盖率
# 启动主实例
./afl-fuzz -i input -o sync_dir -M fuzzer01 ./target @@
 
# 启动从实例
./afl-fuzz -i input -o sync_dir -S fuzzer02 ./target @@
./afl-fuzz -i input -o sync_dir -S fuzzer03 ./target @@
# 启动主实例
./afl-fuzz -i input -o sync_dir -M fuzzer01 ./target @@
 
# 启动从实例
./afl-fuzz -i input -o sync_dir -S fuzzer02 ./target @@
./afl-fuzz -i input -o sync_dir -S fuzzer03 ./target @@
sync_dir/
├── fuzzer01/           # 主实例 (-M)
│   ├── queue/          # 测试用例队列
│   ├── crashes/        # 崩溃样本
│   ├── hangs/         # 挂起样本
│   └── .synced/       # 同步状态记录
│       ├── fuzzer02   # 记录从fuzzer02同步到的位置
│       └── fuzzer03   # 记录从fuzzer03同步到的位置
├── fuzzer02/          # 从实例 (-S)
│   ├── queue/
│   └── .synced/
└── fuzzer03/          # 从实例 (-S)
    ├── queue/
    └── .synced/
sync_dir/
├── fuzzer01/           # 主实例 (-M)
│   ├── queue/          # 测试用例队列
│   ├── crashes/        # 崩溃样本
│   ├── hangs/         # 挂起样本
│   └── .synced/       # 同步状态记录
│       ├── fuzzer02   # 记录从fuzzer02同步到的位置
│       └── fuzzer03   # 记录从fuzzer03同步到的位置
├── fuzzer02/          # 从实例 (-S)
│   ├── queue/
│   └── .synced/
└── fuzzer03/          # 从实例 (-S)
    ├── queue/
    └── .synced/
// 主循环中的同步调用
if (!stop_soon && sync_id && !skipped_fuzz) {
    if (!(sync_interval_cnt++ % SYNC_INTERVAL))
        sync_fuzzers(use_argv);
}
#define SYNC_INTERVAL 5  // 每5次fuzz_one()调用后同步一次
// 主循环中的同步调用
if (!stop_soon && sync_id && !skipped_fuzz) {
    if (!(sync_interval_cnt++ % SYNC_INTERVAL))
        sync_fuzzers(use_argv);
}
#define SYNC_INTERVAL 5  // 每5次fuzz_one()调用后同步一次
static void sync_fuzzers(char** argv) {
    DIR* sd;
    struct dirent* sd_ent;
    u32 sync_cnt = 0;
 
    // 1. 打开同步目录
    sd = opendir(sync_dir);
    if (!sd) PFATAL("Unable to open '%s'", sync_dir);
 
    // 2. 遍历同步目录中的所有fuzzer实例
    while ((sd_ent = readdir(sd))) {
        // 跳过自己和隐藏文件
        if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name))
            continue;
 
        // 3. 检查是否有queue子目录
        qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name);
        if (!(qd = opendir(qd_path))) {
            ck_free(qd_path);
            continue;
        }
 
        // 4. 读取同步状态
        qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name);
        id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600);
         
        // 读取上次同步到的测试用例ID
        if (read(id_fd, &min_accept, sizeof(u32)) > 0)
            lseek(id_fd, 0, SEEK_SET);
static void sync_fuzzers(char** argv) {
    DIR* sd;
    struct dirent* sd_ent;
    u32 sync_cnt = 0;

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

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