AFL-Fuzz源码解析第一部分:afl-gcc代码插桩

先看afl-gcc的实现

当调用afl-g++或afl-gcc对目标代码进行插桩编译时,首先调用g++或gcc对目前程序进行编译,

如下,通过cc_params[0]指定编译器

    if (!strcmp(name, "afl-g++")) {
      u8* alt_cxx = getenv("AFL_CXX");
      cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
    } else if (!strcmp(name, "afl-gcj")) {
      u8* alt_cc = getenv("AFL_GCJ");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
    } else {
      u8* alt_cc = getenv("AFL_CC");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
    }

编译过程中通过-B参数指定汇编器afl-as所在的路径as_path,正是通过afl-as在汇编层面对g++或gcc编译生成的汇编代码进行插桩(汇编器afl-as的路径as_path通过find_as()函数找到,一般和afl-g++或afl-gcc在同一个目录下)。

  cc_params[cc_par_cnt++] = "-B";
  cc_params[cc_par_cnt++] = as_path;

执行cc_params[0],编译完成后,就会找到afl-as进行插桩处理:

  execvp(cc_params[0], (char**)cc_params);

再看afl-as的实现

首先,调用add_instrumentation()进行插桩

  if (!just_version) add_instrumentation();

add_instrumentation()的实现大致如下,在汇编代码中找到.text代码,然后在函数入口、分支标签、条件跳转语句的下面(这里也是分支,条件不成立会走这里)调用fprintf()进行插桩,插入的语句在变量trampoline_fmt_64或trampoline_fmt_32中定义

    /* If we're in the right mood for instrumenting, check for function
       names or conditional labels. This is a bit messy, but in essence,
       we want to catch:

         ^main:      - function entry point (always instrumented)
         ^.L0:       - GCC branch label
         ^.LBB0_0:   - clang branch label (but only in clang mode)
         ^\tjnz foo  - conditional branches
     fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
              R(MAP_SIZE));

这里outf为待插桩的汇编文件,R(MAP_SIZE)生成一个随机数,用来表示当前的插桩的位置(分支)的标识符。虽然是随机生成,但是插桩后就是个确定值了,后面进项fuzz时对代码路径的追踪就是通过记录代码执行过程中的遇到的每个标识符来实现的。

对所有分支插桩完后,后面还会插入一段代码main_payload_64或者main_payload_32,前面插入的trampoline_fmt_64或trampoline_fmt_32会调用到这段代码。

  if (ins_lines)
    fputs(use_64bit ? main_payload_64 : main_payload_32, outf);

插桩完成后,最后,启动一个子进程,调用as将插桩后的汇编代码翻译成机器码,完成整个插桩编译流程。

  as_params[0] = afl_as ? afl_as : (u8*)"as";
  if (!(pid = fork())) {

    execvp(as_params[0], (char**)as_params);
    FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]);

  }

后面的篇章,我们以64位为例,分析trampoline_fmt_64和main_payload_64的内容。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值