快捷搜索:  test  英雄  申请  地球  创意文化园  透露  RNG  防守

AFL二三事——源码剖析

欧博APP下载www.aLLbetgame.us)是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

前言

AFL,全称“American Fuzzy Lop”,是由平安研究员Michal Zalewski开发的一款基于笼罩指导(Coverage-guided)的模糊测试工具,它通过纪录输入样本的代码笼罩率(代码执行路径的笼罩情形),以此举行反馈,对输入样本举行调整以提高笼罩率,从而提升发现破绽的可能性。AFL可以针对有源码和无源码的程序举行模糊测试,其设计头脑和实现方案在模糊测试领域具有十分主要的意义。

深入剖析AFL源码,对明晰AFL的设计理念和其中用到的技巧有着伟大的辅助,对于后期举行定制化Fuzzer开发也具有深刻的指导意义。以是,阅读AFL源码是学习AFL必不能少的一个要害步骤。

(注:需要强调的是,本文的主要目的是协助fuzz兴趣者阅读AFL的源码,以是需要在领会AFL基本事情流程和原理的条件下举行阅读,本文并不会在原理侧做过多说明。)

当别人都要快的时刻,你要慢下来。

宏观

首先在宏观上看一下AFL的源码结构:


主要的代码在 afl-fuzz.c 文件中,然后是几个自力模块的实现代码,llvm_mode 和 qemu_mode 的代码量大致相当,以是剖析的重点应该照样在AFL的根目录下的几个焦点功效的实现上,尤其是 afl-fuzz.c,属于焦点中的重点。

各个模块的主要功效和作用的简要说明:

插桩模块

1.afl-as.h, afl-as.c, afl-gcc.c:通俗插桩模式,针对源码插桩,编译器可以使用gcc, clang;

2.llvm_mode:llvm 插桩模式,针对源码插桩,编译器使用clang;

3.qemu_mode:qemu 插桩模式,针对二进制文件插桩。

fuzzer 模块

 afl-fuzz.c:fuzzer 实现的焦点代码,AFL 的主体。

其他辅助模块

1.afl-analyze:对测试用例举行剖析,通太过析给定的用例,确定是否可以发现用例中有意义的字段;

2.afl-plot:天生测试义务的状态图;

3.afl-tmin:对测试用例举行最小化;

4.afl-cmin:对语料库举行精简操作;

5.afl-showmap:对单个测试用例举行执行路径跟踪;

6.afl-whatsup:各并行例程fuzzing效果统计;

7.afl-gotcpu:查看当前CPU状态。

部门头文件说明

1.alloc-inl.h:界说带检测功效的内存分配和释放操作;

2.config.h:界说设置信息;

3.debug.h:与提醒信息相关的宏界说;

4.hash.h:哈希函数的实现界说;

5.types.h:部门类型及宏的界说。

一、AFL的插桩——通俗插桩

(一) 、AFL 的 gcc —— afl-gcc.c

1. 概述

afl-gcc 是GCC 或 clang 的一个wrapper(封装),通例的使用方式是在挪用 ./configure 时通过 CC 将路径通报给 afl-gcc 或 afl-clang。(对于 C 代码,则使用 CXX 并将其指向 afl-g / afl-clang 。)afl-clang, afl-clang , afl-g 均为指向 afl-gcc 的一个符号链接。

afl-gcc 的主要作用是实现对于要害节点的代码插桩,属于汇编级,从而纪录程序执行路径之类的要害信息,对程序的运行情形举行反馈。

2. 源码

1. 要害变量

在最先函数代码剖析前,首先要明确几个要害变量:

static u8*  as_path;                /* Path to the AFL 'as' wrapper,AFL的as的路径      */
static u8** cc_params;              /* Parameters passed to the real CC,CC现实使用的编译器参数 */
static u32  cc_par_cnt = 1;         /* Param count, including argv0 ,参数计数 */
static u8   be_quiet,               /* Quiet mode,静默模式      */
            clang_mode;             /* Invoked as afl-clang*? ,是否使用afl-clang*模式 */
, 数据类型说明
, typedef uint8_t  u8;
, typedef uint16_t u16;
, typedef uint32_t u32;

2. main函数

main 函数所有逻辑如下:


其中主要有如下三个函数的挪用:

find_as(argv[0]) :查找使用的汇编器

edit_params(argc, argv):处置传入的编译参数,将确定好的参数放入 cc_params[] 数组

挪用 execvp(cc_params[0], (cahr**)cc_params) 执行 afl-gcc


这里添加了部门代码打印出传入的参数 arg[0] - arg[7] ,其中一部门是我们指定的参数,另外一部门是自动添加的编译选项。

3. find_as 函数

函数的焦点作用:寻找 afl-as 

函数内部也许的流程如下(软件自动天生,控制流程图存在误差,但要害逻辑没有问题):


1.首先检查环境变量 AFL_PATH ,若是存在直接赋值给 afl_path ,然后检查 afl_path/as 文件是否可以接见,若是可以,as_path = afl_path。

2.若是不存在环境变量 AFL_PATH ,检查 argv[0] (如“/Users/v4ler1an/AFL/afl-gcc”)中是否存在 "/" ,若是存在则取最后“/” 前面的字符串作为 dir,然后检查 dir/afl-as 是否可以接见,若是可以,将 as_path = dir 。

3.以上两种方式都失败,抛出异常。

4. edit_params 函数

焦点作用:将 argv 拷贝到 u8 **cc_params,然后举行响应的处置。

函数内部的也许流程如下:


1.挪用 ch_alloc() 为 cc_params 分配巨细为 (argc 128) * 8 的内存(u8的类型为1byte无符号整数)

2.检查 argv[0] 中是否存在/,若是不存在则 name = argv[0],若是存在则一直找到最后一个/,并将厥后面的字符串赋值给 name

3.对比 name和牢固字符串afl-clang:

1.若相同,设置clang_mode = 1,设置环境变量CLANG_ENV_VAR为1

1.对比name和牢固字符串afl-clang ::

1.若相同,则获取环境变量AFL_CXX的值,若是存在,则将该值赋值给cc_params[0],否则将afl-clang 赋值给cc_params[0]。这里的cc_params为保留编译参数的数组;

2.若不相同,则获取环境变量AFL_CC的值,若是存在,则将该值赋值给cc_params[0],否则将afl-clang赋值给cc_params[0]。

2.若是不相同,而且是Apple平台,会进入 ,ifdef __APPLE__。在Apple平台下,最先对 name 举行对比,并通过 cc_params[0] = getenv("") 对cc_params[0]举行赋值;若是是非Apple平台,对比 name 和 牢固字符串afl-g (此处忽略对Java环境的处置历程):

1.若相同,则获取环境变量AFL_CXX的值,若是存在,则将该值赋值给cc_params[0],否则将g 赋值给cc_params[0];

2.若不相同,则获取环境变量AFL_CC的值,若是存在,则将该值赋值给cc_params[0],否则将gcc赋值给cc_params[0]。

4.进入 while 循环,遍历从argv[1]最先的argv参数:

若是扫描到 -B ,-B选项用于设置编译器的搜索路径,直接跳过。(由于在这之前已经处置过as_path了);

若是扫描到 -integrated-as,跳过;

若是扫描到 -pipe,跳过;

若是扫描到 -fsanitize=address 和 -fsanitize=memory 告诉 gcc 检查内存接见的错误,好比数组越界之类,设置 asan_set = 1;

若是扫描到 FORTIFY_SOURCE ,设置 fortify_set = 1 。FORTIFY_SOURCE 主要举行缓冲区溢出问题的检查,检查的常见函数有memcpy, mempcpy, memmove, memset, strcpy, stpcpy, strncpy, strcat, strncat, sprintf, vsprintf, snprintf, gets 等;

对 cc_params 举行赋值:cc_params[cc_par_cnt ] = cur;

5.跳出 while 循环,设置其他参数:

1.取出前面盘算出的 as_path ,设置 -B as_path ;

6.若是为 clang_mode ,则设置-no-integrated-as;

1.若是存在环境变量 AFL_HARDEN,则设置-fstack-protector-all。且若是没有设置 fortify_set ,追加 -D_FORTIFY_SOURCE=2 ;

7.sanitizer相关,通过多个if举行判断:

若是 asan_set 在前面被设置为1,则设置环境变量 AFL_USE_ASAN 为1;

若是 asan_set 不为1且,存在 AFL_USE_ASAN 环境变量,则设置-U_FORTIFY_SOURCE -fsanitize=address;

若是不存在 AFL_USE_ASAN 环境变量,但存在 AFL_USE_MSAN 环境变量,则设置-fsanitize=memory(不能同时指定AFL_USE_ASAN或者AFL_USE_MSAN,也不能同时指定 AFL_USE_MSAN 和 AFL_HARDEN,由于这样运行时速率过慢;

若是不存在 AFL_DONT_OPTIMIZE 环境变量,则设置-g -O3 -funroll-loops -D__AFL_COMPILER=1 -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1;

若是存在 AFL_NO_BUILTIN 环境变量,则示意允许举行优化,设置-fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp -fno-builtin-strncasecmp -fno-builtin-memcmp -fno-builtin-strstr -fno-builtin-strcasestr。

8.最后弥补cc_params[cc_par_cnt] = NULL;,cc_params 参数数组编辑完成。

(二)、AFL的插桩 —— afl-as.c

1. 概述

afl-gcc 是 GNU as 的一个wrapper(封装),唯一目的是预处置由 GCC/clang 天生的汇编文件,并注入包罗在 afl-as.h 中的插桩代码。 使用 afl-gcc / afl-clang 编译程序时,工具链会自动挪用它。该wapper的目的并不是为了实现向 .s 或 asm 代码块中插入手写的代码。

 experiment/clang_asm_normalize/ 中可以找到可能允许 clang 用户举行手动插入自界说代码的解决方案,GCC并不能实现该功效。

2. 源码

1. 要害变量

在最先函数代码剖析前,首先要明确几个要害变量:

static u8** as_params;          /* Parameters passed to the real 'as',通报给as的参数   */
static u8*  input_file;         /* Originally specified input file ,输入文件     */
static u8*  modified_file;      /* Instrumented file for the real 'as',as举行插桩处置的文件  */
static u8   be_quiet,           /* Quiet mode (no stderr output) ,静默模式,没有尺度输出       */
            clang_mode,         /* Running in clang mode?    是否运行在clang模式           */
            pass_thru,          /* Just pass data through?   只通过数据           */
            just_version,       /* Just show version?        只显示版本   */
            sanitizer;          /* Using ASAN / MSAN         是否使用ASAN/MSAN           */
static u32  inst_ratio = 100,   /* Instrumentation probability (%)  插桩笼罩率    */
            as_par_cnt = 1;     /* Number of params to 'as'    通报给as的参数数目初始值         */

注:若是在参数中没有指明 --m32 或 --m64 ,则默认使用在编译时使用的选项。

2. main函数

main 函数所有逻辑如下:


1.首先获取环境变量 AFL_INST_RATIO ,赋值给 inst_ratio_str,该环境变量主要控制检测每个分支的概率,取值为0到100%,设置为0时则只检测函数入口的跳转,而不会检测函数分支的跳转;

2.通过 gettimeofday(&tv,&tz);获取时区和时间,然后设置 srandom() 的随机种子 rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();

3.挪用 edit_params(argc, argv) 函数举行参数处置;

4.检测 inst_ratio_str 的值是否正当局限内,并设置环境变量 AFL_LOOP_ENV_VAR;

5.读取环境变量`AFL_USE_ASAN和AFL_USE_MSAN的值,若是其中有一个为1,则设置sanitizer为1,且将inst_ratio除3。这是由于在举行ASAN的编译时,AFL无法识别出ASAN特定的分支,导致插入许多无意义的桩代码,以是直接暴力地将插桩概率/3;

6.挪用 add_instrumentation() 函数,这是现实的插桩函数;

7.fork 一个子历程来执行 execvp(as_params[0], (char**)as_params);。这里接纳的是 fork 一个子历程的方式来执行插桩。这着实是由于我们的 execvp 执行的时刻,会用 as_params[0] 来完全替换掉当前历程空间中的程序,若是不通过子历程来执行现实的 as,那么后续就无法在执行完现实的as之后,还能unlink掉modified_file;

8.挪用 waitpid(pid, &status, 0) 守候子历程执行竣事;

9.读取环境变量 AFL_KEEP_ASSEMBLY 的值,若是没有设置这个环境变量,就unlink掉 modified_file(已插完桩的文件)。设置该环境变量主要是为了防止 afl-as 删掉插桩后的汇编文件,设置为1则会保留插桩后的汇编文件。

可以通过在main函数中添加如下代码来打印现实执行的参数:

print("n");
for (int i = 0; i <  sizeof(as_params); i  ){
  peinrf("as_params[%d]:%sn", i, as_params[i]);
  
}


在插桩完成后,会天生 .s 文件,内容如下(详细的文件位置与设置的环境变量相关):


3. add_instrumentation函数

add_instrumentation 函数卖力处置输入文件,天生 modified_file ,将 instrumentation 插入所有适当的位置。其整体控制流程如下:


整体逻辑看上去有点庞大,然则要害内容并不算许多。在main函数中挪用完 edit_params() 函数完成 as_params 参数数组的处置后,进入到该函数。

1.判断 input_file 是否为空,若是不为空则实验打开文件获取fd赋值给 inf,失败则抛出异常;input_file 为空则 inf 设置为尺度输入; 

2.打开 modified_file ,获取fd赋值给 outfd,失败返回异常;进一步验证该文件是否可写,不能写返回异常;

3.while 循环读取 inf 指向文件的每一行到 line 数组,每行最多 MAX_LINE = 8192个字节(含末尾的‘’),从line数组里将读取到的内容写入到 outf 指向的文件,然后进入到真正的插桩逻辑。这里需要注重的是,插桩只向 .text 段插入,:

1.首先跳过标签、宏、注释;

2.这里连系部门要害代码举行注释。需要注重的是,变量 instr_ok 本质上是一个flag,用于示意是否位于.text段。变量设置为1,示意位于 .text 中,若是不为1,则示意不再。于是,若是instr_ok 为1,就会在分支处执行插桩逻辑,否则就不插桩。

1.首先判断读入的行是否以‘t’ 开头,本质上是在匹配.s文件中声明的段,然后判断line[1]是否为.:

 if (line[0] == 't' && line[1] == '.') {
      /* OpenBSD puts jump tables directly inline with the code, which is
         a bit annoying. They use a specific format of p2align directives
         around them, so we use that as a signal. */
      if (!clang_mode && instr_ok && !strncmp(line   2, "p2align ", 8) &&
          isdigit(line[10]) && line[11] == 'n') skip_next_label = 1;
      if (!strncmp(line   2, "textn", 5) ||
          !strncmp(line   2, "sectiont.text", 13) ||
          !strncmp(line   2, "sectiont__TEXT,__text", 21) ||
          !strncmp(line   2, "section __TEXT,__text", 21)) {
        instr_ok = 1;
        continue; 
      }
      if (!strncmp(line   2, "sectiont", 8) ||
          !strncmp(line   2, "section ", 8) ||
          !strncmp(line   2, "bssn", 4) ||
          !strncmp(line   2, "datan", 5)) {
        instr_ok = 0;
        continue;
      }
    }

1.'t'开头,且line[1]=='.',检查是否为 p2align 指令,若是是,则设置 skip_next_label = 1;

2.实验匹配 "textn" "sectiont.text" "sectiont__TEXT,__text" "section __TEXT,__text" 其中随便一个,匹配乐成, 设置 instr_ok = 1, 示意位于 .text 段中,continue 跳出,举行下一次遍历;

3.实验匹配"sectiont" "section " "bssn" "datan" 其中随便一个,匹配乐成,设置 instr_ok = 0,表位于其他段中,continue 跳出,举行下一次遍历;

2.接下来通过几个 if 判断,来设置一些标志信息,包罗 off-flavor assembly,Intel/AT&T的块处置方式、ad-hoc __asm__块的处置方式等;

     /* Detect off-flavor assembly (rare, happens in gdb). When this is
       encountered, we set skip_csect until the opposite directive is
       seen, and we do not instrument. */
    if (strstr(line, ".code")) {
      if (strstr(line, ".code32")) skip_csect = use_64bit;
      if (strstr(line, ".code64")) skip_csect = !use_64bit;
    }
    /* Detect syntax changes, as could happen with hand-written assembly.
       Skip Intel blocks, resume instrumentation when back to AT&T. */
    if (strstr(line, ".intel_syntax")) skip_intel = 1;
    if (strstr(line, ".att_syntax")) skip_intel = 0;
    /* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */
    if (line[0] == ',' || line[1] == ',') {
      if (strstr(line, ",APP")) skip_app = 1;
      if (strstr(line, ",NO_APP")) skip_app = 0;
    }

3.AFL在插桩时重点关注的内容包罗:^main, ^.L0, ^.LBB0_0, ^tjnz foo (_main函数, gcc和clang下的分支符号,条件跳转分支符号),这些内容通常标志了程序的流程转变,因此AFL会重点在这些位置举行插桩:

对于形如tj[^m].名堂的指令,即条件跳转指令,且R(100)发生的随机数小于插桩密度inst_ratio,直接使用fprintf将trampoline_fmt_64(插桩部门的指令)写入 outf 指向的文件,写入巨细为小于 MAP_SIZE的随机数——R(MAP_SIZE),然后插桩计数ins_lines加一,continue 跳出,举行下一次遍历;

     /* 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
       ...but not:
         ^, BB,0:    - clang comments
         ^ , BB,0:   - ditto
         ^.Ltmp0:    - clang non-branch labels
         ^.LC0       - GCC non-branch labels
         ^.LBB0_0:   - ditto (when in GCC mode)
         ^tjmp foo  - non-conditional jumps
       Additionally, clang and GCC on MacOS X follow a different convention
       with no leading dots on labels, hence the weird maze of ,ifdefs
       later on.
     */
    if (skip_intel || skip_app || skip_csect || !instr_ok ||
        line[0] == ',' || line[0] == ' ') continue;
    /* Conditional branch instruction (jnz, etc). We append the instrumentation
       right after the branch (to instrument the not-taken path) and at the
       branch destination label (handled later on). */
    if (line[0] == 't') {
      if (line[1] == 'j' && line[2] != 'm' && R(100) <  inst_ratio) {
        fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
                R(MAP_SIZE));
        ins_lines  ;
      }
      continue;
    }

4.对于label的相关评估,有一些label可能是一些分支的目的地,需要自己评判

 首先检查该行中是否存在:,然后检查是否以.最先

1.若是以.最先,则代表想要插桩^.L0:或者 ^.LBB0_0:这样的branch label,即 style jump destination

1.检查 line[2]是否为数字 或者 若是是在clang_mode下,对照从line[1]最先的三个字节是否为LBB.,前述所得效果和R(100) < inst_ratio)相与。若是效果为真,则设置instrument_next = 1;

2.否则代表这是一个function,插桩^func:,function entry point,直接设置instrument_next = 1(defer mode)。

     /* Label of some sort. This may be a branch destination, but we need to
       tread carefully and account for several different formatting
       conventions. */
,ifdef __APPLE__
    /* Apple: L< whatever>< digit>: */
    if ((colon_pos = strstr(line, ":"))) {
      if (line[0] == 'L' && isdigit(*(colon_pos - 1))) {
,else
    /* Everybody else: .L< whatever>: */
    if (strstr(line, ":")) {
      if (line[0] == '.') {
,endif /* __APPLE__ */
        /* .L0: or LBB0_0: style jump destination */
,ifdef __APPLE__
        /* Apple: L< num> / LBB< num> */
        if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
            && R(100) < inst_ratio) {
,else
        /* Apple: .L< num> / .LBB< num> */
        if ((isdigit(line[2]) || (clang_mode && !strncmp(line   1, "LBB", 3)))
            && R(100) <  inst_ratio) {
,endif /* __APPLE__ */
          /* An optimization is possible here by adding the code only if the
             label is mentioned in the code in contexts other than call / jmp.
             That said, this complicates the code by requiring two-pass
             processing (messy with stdin), and results in a speed gain
             typically under 10%, because compilers are generally pretty good
             about not generating spurious intra-function jumps.
             We use deferred output chiefly to avoid disrupting
             .Lfunc_begin0-style exception handling calculations (a problem on
             MacOS X). */
          if (!skip_next_label) instrument_next = 1; else skip_next_label = 0;
        }
      } else {
        /* Function label (always instrumented, deferred mode). */
        instrument_next = 1;
    
      }
    }
  }

5.上述历程完成后,来到 while 循环的下一个循环,在 while 的开头,可以看到对以 defered mode 举行插桩的位置举行了真正的插桩处置:

     if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
        instrument_next && line[0] == 't' && isalpha(line[1])) {
      fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
              R(MAP_SIZE));
      instrument_next = 0;
      ins_lines  ;
    }

这里对 instr_ok, instrument_next 变量举行了磨练是否为1,而且进一步校验是否位于 .text 段中,且设置了 defered mode 举行插桩,则就举行插桩操作,写入 trampoline_fmt_64/32 。

至此,插桩函数 add_instrumentation 的主要逻辑已梳理完成。

4. edit_params函数

edit_params,该函数主要是设置变量 as_params 的值,以及 use_64bit/modified_file 的值, 其整体控制流程如下:


1.获取环境变量 TMPDIR 和 AFL_AS;

2.对于 __APPLE_ 宏, 若是当前在 clang_mode 且没有设置 AFL_AS 环境变量,会设置 use_clang_mode = 1,并设置 afl-as 为 AFL_CC/AFL_CXX/clang中的一种;

3.设置 tmp_dir ,实验获取的环境变量依次为 TEMP, TMP,若是都失败,则直接设置为 /tmp;

4.挪用 ck_alloc() 函数为 as_params 参数数组分配内存,巨细为(argc 32) * 8;

5.设置 afl-as 路径:as_params[0] = afl_as ? afl_as : (u8*)"as";

6.设置 as_params[argc] = 0; ,as_par_cnt 初始值为1;

7.遍历从 argv[1] 到 argv[argc-1] 之前的每个 argv:

1.若是存在字符串 --64, 则设置 use_64bit = 1 ;若是存在字符串 --32 ,则设置 use_64bit = 0。对于__APPLE__ ,若是存在-arch x86_64,设置 use_64bit=1,并跳过-q和-Q选项;

2.as_params[as_par_cnt ] = argv[i],设置as_params的值为argv对应的参数值

8.最先设置其他参数:

1.对于 __APPLE__,若是设置了 use_clang_as,则追加 -c -x assembler;

2.设置 input_file 变量:input_file = argv[argc - 1];,把最后一个参数的值作为 input_file;

1.若是 input_file 的首字符为-:

1.若是后续为 -version,则 just_version = 1, modified_file = input_file,然后跳转到wrap_things_up。这里就只是做version的查询;

2.若是后续不为 -version,抛出异常;

2.若是 input_file 首字符不为-,对照 input_file 和 tmp_dir、/var/tmp 、/tmp/的前 strlen(tmp_dir)/9/5个字节是否相同,若是不相同,就设置 pass_thru 为1;

9.设置 modified_file:modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),

   (u32)time(NULL));,即为tmp_dir/afl-pid-tim.s 名堂的字符串

 

1.设置as_params[as_par_cnt ] = modified_file,as_params[as_par_cnt] = NULL;。

3. instrumentation trampoline 和 main_payload

trampoline 的寄义是“蹦床”,直译过来就是“插桩蹦床”。小我私人感受直接使用英文更能表达出其代表的真实寄义和作用,可以简朴明晰为桩代码。

1. trampoline_fmt_64/32

凭证前面内容知道,在64位环境下,AFL会插入 trampoline_fmt_64 到文件中,在32位环境下,AFL会插入trampoline_fmt_32 到文件中。trampoline_fmt_64/32界说在 afl-as.h 头文件中:

static const u8* trampoline_fmt_32 =
  "n"
  "/* --- AFL TRAMPOLINE (32-BIT) --- */n"
  "n"
  ".align 4n"
  "n"
  "leal -16(%%esp), %%espn"
  "movl %  
		          
发表评论
sunbet声明:该文看法仅代表作者自己,与本平台无关。请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片

您可能还会对下面的文章感兴趣: