首页
社区
课程
招聘
[原创]AFLNET源码分析(二)
发表于: 2023-3-4 13:10 9355

[原创]AFLNET源码分析(二)

2023-3-4 13:10
9355

在AFLNET源码分析(一)中,我们主要分析了send_over_network()函数,弄清楚了为什么AFL多用于文件处理类程序而AFLNET却可以应用于网络协议服务端的模糊测试。

本篇文章将着眼于模糊测试的整体流程

首先从main函数入手,AFLNET与AFL在main函数中相同的部分这里就直接略过不讲了,网上分析AFL源码的文章有很多。

直接从main里的fuzz主循环开始看:

首先查看是否开启了状态感知模式,我们通过在命令中增加- E 选项就可以开启,如果不开启的话则会走AFL原有的循环逻辑

满足if条件则会进入整个fuzzing的主循环

在主循环中首先初始化selected_seed为NULL,然后判断种子是否未选择以及选择的种子的region数量是否为0,满足任意一个则进入一个用于选择状态的循环

在状态选择的循环中,执行如下逻辑:

具体AFLNET是如何选择状态和选择种子的我们等到主循环分析结束之后再来详细分析

接着往下看

如果selected_seed不为NULL,就要在queue里寻找这个种子并将其设置为queue_cur

接下来调用fuzz_one函数进行一轮fuzz,然后根据三个值来判断是否要调用sync_fuzzers

其中skipped_fuzz表示是否开启了IGNORE_FINDS模式,如果开启则fuzzer只会使用原始种子文件进行fuzz

stop_soon则是由用户的ctrl+c操作进行设置,表示用户是否要停止fuzzing

sync_id表示fuzzer 的id

如果三个条件都满足,则将sync_interval_cnt加1模5并判断是否为0,为0则调用sync_fuzzers。即在一切正常的情况下每SYNC_INTERVAL(默认为5)次调用一次sync_fuzzers。

这里注意到一点,上述代码和AFL中没有区别,少了两句:

这是AFL中有而AFLNET中没有的,并且只是状态感知模式下没有,没开状态感知的话仍然是有这两句代码的。原因在于AFL的fuzzing队列是顺序往下走的,但是状态感知模式会在开头通过选择状态和种子来确定本次fuzzing所用的queue是什么

后面的代码AFLNET和AFL就没有任何区别了,都是一些信息展示和退出的功能,这里就不再赘述。

接下来分析在开头暂时略过的一些函数

choose_target_state:

函数接收一个单字节参数,表示选用哪种状态选择策略,默认为2轮询模式,不过官方给的例子用的是3偏好模式(也许翻译的有偏差)

如果是第一种随机模式,则selected_state_index直接从UR(即random函数)中获取,范围用state_ids_count来限制。

如果是第二种轮询模式,则selected_state_index不断加一按顺序进行轮转,如果等于状态总数了就回到0继续下一轮

如果是第三种偏好模式,在state_cycles小于5的时候会首先按照轮询的模式进行选择,因为需要先获取足够的状态信息,如果大于5则调用update_scores_and_select_next_state获取result

继续深入update_scores_and_select_next_state看看其逻辑:

由于下面的代码中涉及到了一些state的属性,所以我们先来熟悉一下state结构(注释已进行标注)

如果state_ids_count为0则直接返回0,为state_scores申请内存,每个state申请4字节

然后更新每个状态的分数,更新的时候会依据fuzz的次数,被选择过的次数以及发现路径的条数这些指标来计算分数,分数越高说明这个状态越好。

接下来的逻辑一眼看上去可能会有些奇怪,按照惯性思维我们应该将分数排序然后选取最高的,但是作者采用的方式是将得分累计起来,然后在所有分数之和的范围内获取一个随机数。

然后调用index_search来获取最终的idx,这个index_search的实现也较为简单,就是找到第一个大于上一步获取的随机数的分数,将它的序号作为最后选出的idx

这么实现的道理是,保证了分数越高,被随机数命中的概率就越大,从而实现分数越高越容易选中,同时省去了排序操作效率更高,并且兼顾了随机性,还是比较巧妙的。

然后再来看看choose_seed,由于代码较长这里分开叙述

首先通过传入的状态id将之前选出的状态找到

随机模式与轮询模式

随机模式同样以种子数量为范围获取一个随机数然后赋给result,轮询模式也和之前状态选择策略差不多

偏好模式:

如果种子数量小于10则按照轮询模式的逻辑来,如果大于是则进入以下逻辑

以下逻辑总体来讲就是为不同条件的种子设置了不同的概率去选择。

 
 
 
 
if (state_aware_mode) {
    if (state_ids_count == 0) {
      PFATAL("No server states have been detected. Server responses are likely empty!");
    }
if (state_aware_mode) {
    if (state_ids_count == 0) {
      PFATAL("No server states have been detected. Server responses are likely empty!");
    }
 
 
while (1) {
      u8 skipped_fuzz;
 
      struct queue_entry *selected_seed = NULL;
      while(!selected_seed || selected_seed->region_count == 0) {
        target_state_id = choose_target_state(state_selection_algo);
 
        /* Update favorites based on the selected state */
        cull_queue();
 
        /* Update number of times a state has been selected for targeted fuzzing */
        khint_t k = kh_get(hms, khms_states, target_state_id);
        if (k != kh_end(khms_states)) {
          kh_val(khms_states, k)->selected_times++;
        }
 
        selected_seed = choose_seed(target_state_id, seed_selection_algo);
      }
while (1) {
      u8 skipped_fuzz;
 
      struct queue_entry *selected_seed = NULL;
      while(!selected_seed || selected_seed->region_count == 0) {
        target_state_id = choose_target_state(state_selection_algo);
 
        /* Update favorites based on the selected state */
        cull_queue();
 
        /* Update number of times a state has been selected for targeted fuzzing */
        khint_t k = kh_get(hms, khms_states, target_state_id);
        if (k != kh_end(khms_states)) {
          kh_val(khms_states, k)->selected_times++;
        }
 
        selected_seed = choose_seed(target_state_id, seed_selection_algo);
      }
 
 
if (selected_seed) {
        if (!queue_cur) {
            current_entry     = 0;
            cur_skipped_paths = 0;
            queue_cur         = queue;
            queue_cycle++;
        }
        while (queue_cur != selected_seed) {
          queue_cur = queue_cur->next;
          current_entry++;
          if (!queue_cur) {
            current_entry     = 0;
            cur_skipped_paths = 0;
            queue_cur         = queue;
            queue_cycle++;
          }
        }
      }
if (selected_seed) {
        if (!queue_cur) {
            current_entry     = 0;
            cur_skipped_paths = 0;
            queue_cur         = queue;
            queue_cycle++;
        }
        while (queue_cur != selected_seed) {
          queue_cur = queue_cur->next;
          current_entry++;
          if (!queue_cur) {
            current_entry     = 0;
            cur_skipped_paths = 0;
            queue_cur         = queue;
            queue_cycle++;
          }
        }
      }
 
 
 
 
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;
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++;
queue_cur = queue_cur->next;
    current_entry++;
 
 
 
 
 
 
 
unsigned int choose_target_state(u8 mode) {
  u32 result = 0;
  switch (mode) {
    case RANDOM_SELECTION: //Random state selection
      selected_state_index = UR(state_ids_count);
      result = state_ids[selected_state_index];
      break;
    case ROUND_ROBIN: //Round-robin state selection
      result = state_ids[selected_state_index];
      selected_state_index++;
      if (selected_state_index == state_ids_count) selected_state_index = 0;
      break;
    case FAVOR:
      /* Do ROUND_ROBIN for a few cycles to get enough statistical information*/
      if (state_cycles < 5) {
        result = state_ids[selected_state_index];
        selected_state_index++;
        if (selected_state_index == state_ids_count) {
          selected_state_index = 0;
          state_cycles++;
        }
        break;
      }
      result = update_scores_and_select_next_state(FAVOR);
      break;
    default:
      break;
  }
  return result;
}
unsigned int choose_target_state(u8 mode) {
  u32 result = 0;
  switch (mode) {
    case RANDOM_SELECTION: //Random state selection
      selected_state_index = UR(state_ids_count);
      result = state_ids[selected_state_index];
      break;
    case ROUND_ROBIN: //Round-robin state selection
      result = state_ids[selected_state_index];
      selected_state_index++;
      if (selected_state_index == state_ids_count) selected_state_index = 0;
      break;
    case FAVOR:
      /* Do ROUND_ROBIN for a few cycles to get enough statistical information*/
      if (state_cycles < 5) {
        result = state_ids[selected_state_index];
        selected_state_index++;
        if (selected_state_index == state_ids_count) {
          selected_state_index = 0;
          state_cycles++;
        }
        break;

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-3-30 08:48 被Ayakaaa编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (1)
雪    币: 2267
活跃值: (1523)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
2
tql
2023-3-4 13:15
0
游客
登录 | 注册 方可回帖
返回
//