AFLGO源码解析

介绍AFLGO的文章提到AFLGO在AFL的LLVM基础上做出了修改。
首先说一下AFL的LLVM模式。

AFL LLVM

不同于afl-gcc对gcc做了一个rapper,在编译的时候会识别jmp、call之类的跳转并插入跳板代码实现插桩,AFL的LLVM则利用了编写pass文件的方法实现的插桩。

LLVM的pass介绍

pass文件可以在LLVM的clang进行编译的时候同时对函数、基本块等元素进行操作,详情可见(pass编写

afl-clang-fast

在CC改成afl-clang-fast,就可以使用改过的afl-clang-fast对源码进行编译。把afl-llvm-rt.o中的函数载入,
在这里插入图片描述
然后在编译选项是加入afl-llvm-rt.o。
在这里插入图片描述

afl-llvm-pass.so

在pass文件中先是重载了ModulePass类里面的runOnModule方法。

class AFLCoverage : public ModulePass {

    public:

      static char ID;
      AFLCoverage() : ModulePass(ID) { }

      bool runOnModule(Module &M) override;

      // StringRef getPassName() const override {
      //  return "American Fuzzy Lop Instrumentation";
      // }

  };

然后设置全局变量bitmap地址和上一个BB地址。

  GlobalVariable *AFLMapPtr =
      new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
                         GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");

  GlobalVariable *AFLPrevLoc = new GlobalVariable(
      M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc",
      0, GlobalVariable::GeneralDynamicTLSModel, 0, false);

然后循环,对每一个函数,每一个BB进行插桩操作。

  for (auto &F : M)
    for (auto &BB : F) {
    ...
	}
  }

在运行程序的时候同时会执行pass文件里面的程序,即运行的每一个BB都会update全局的bitmap。

afl-llvm-rt.o

这个相当于afl-gcc里面的mainpload。里面有init,shm_setup和persistent_loop的函数代码,插入在目标程序后,跳板就可以调用这些函数。

AFLGO编译

bitmap在原先的65526大小的基础上加了16个字节,前四个字节放置距离的倒数和,后四个字节放置触发的基本块的数量。

afl-llvm-pass.so统计

在pass文件中,在编译时先检查是否有Targets文件或者绘制好的distance文件。如果有Targets文件,就载入Targets文件并把变量is_aflgo_preprocessing置为1,即处于编译状态。

if (!TargetsFile.empty()) {

    if (OutDirectory.empty()) {
      FATAL("Provide output directory '-outdir <directory>'");
      return false;
    }

    std::ifstream targetsfile(TargetsFile);
    std::string line;
    while (std::getline(targetsfile, line))
      targets.push_back(line);
    targetsfile.close();

    is_aflgo_preprocessing = true;

  }

创建了四个文件。

	std::ofstream bbnames(OutDirectory + "/BBnames.txt", std::ofstream::out | std::ofstream::app);
    std::ofstream bbcalls(OutDirectory + "/BBcalls.txt", std::ofstream::out | std::ofstream::app);
    std::ofstream fnames(OutDirectory + "/Fnames.txt", std::ofstream::out | std::ofstream::app);
    std::ofstream ftargets(OutDirectory + "/Ftargets.txt", std::ofstream::out | std::ofstream::app);

然后对每个函数、每个BB、每个指令进行循环。
识别指令是否时tagets文件中的XX源文件中的第几行,如果是,所在的BB、F设置为Tb,Tf,把function写入fnames和ftargets。
识别指令是否为跳转指令,是的话把当前BB和跳转函数的tuple写入BBcalls文件。然后把文件写入dotfile文件夹。

AFLGO运行

生成distance文件

在插桩后,运行script/getDistanse.sh

动态运行

已有distance文件就把distance载入到全局变量bb_to_dis表中并把is_aflgo置为1。

else if (!DistanceFile.empty()) {

    std::ifstream cf(DistanceFile);
    if (cf.is_open()) {

      std::string line;
      while (getline(cf, line)) {

        std::size_t pos = line.find(",");
        std::string bb_name = line.substr(0, pos);
        int bb_dis = (int) (100.0 * atof(line.substr(pos + 1, line.length()).c_str()));

        bb_to_dis.emplace(bb_name, bb_dis);
        basic_blocks.push_back(bb_name);

      }
      cf.close();

      is_aflgo = true;

    }

然后插桩后,运行的每个BB都可以得到distance文件中计算好的距离。
运行过程中维持着这些数据:

static double cur_distance = -1.0;     /* Distance of executed input       */
static double max_distance = -1.0;     /* Maximal distance for any input   */
static double min_distance = -1.0;     /* Minimal distance for any input   */
static u32 t_x = 10;                  /* Time to exploitation (Default: 10 min) */

每次把新种子放入队列的时候,计算其距离。

  /* Calculate distance of current input to targets */
  u32* total_distance = (u32*)(trace_bits + MAP_SIZE);
  u32* total_count = (u32*)(trace_bits + MAP_SIZE + 4);

  if (*total_count > 0) {
    cur_distance = (double) (*total_distance) / (double) (*total_count);
  else
    cur_distance = -1.0;

然后设置退火时间t_x,调度算法计算种子的havoc能量。在这个调度算法下实现了基于距离对种子的调度。

u64 cur_ms = get_cur_time();
  u64 t = (cur_ms - start_time) / 1000;
  double progress_to_tx = ((double) t) / ((double) t_x * 60.0);
  double T;

  switch (cooling_schedule) {
    case SAN_EXP:
      T = 1.0 / pow(20.0, progress_to_tx);
      break;
    case SAN_LOG:
      T = 1.0 / (1.0 + 2.0 * log(1.0 + progress_to_tx * 13358.7268297));
      break;

    case SAN_LIN:
      T = 1.0 / (1.0 + 19.0 * progress_to_tx);
      break;

    case SAN_QUAD:
      T = 1.0 / (1.0 + 19.0 * pow(progress_to_tx, 2));
      break;

    default:
      PFATAL ("Unkown Power Schedule for Directed Fuzzing");
  }

  double power_factor = 1.0;
  if (q->distance > 0) {
    double normalized_d = q->distance;
    if (max_distance != min_distance)
      normalized_d = (q->distance - min_distance) / (max_distance - min_distance);

    if (normalized_d >= 0) {
        double p = (1.0 - normalized_d) * (1.0 - T) + 0.5 * T;
        power_factor = pow(2.0, 2.0 * (double) log2(MAX_FACTOR) * (p - 0.5));
    }// else WARNF ("Normalized distance negative: %f", normalized_d);
  }
  perf_score *= power_factor;
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值