AFL源码学习(二)
字数 1798 2025-08-22 12:23:24

AFL源码学习与工具分析教学文档

一、AFL基础架构回顾

1.1 AFL整体工作流程

  1. 编译插桩阶段

    • AFL作为源编译器的封装
    • 在编译成汇编代码后进行插桩
    • 插桩后的程序会在第一个桩处停下并初始化共享内存
  2. 运行阶段

    • 形成fork server模式
    • 主进程不断fork子进程进行探索
    • 子进程通过管道向afl-fuzz汇报结果

1.2 关键数据结构

  • 共享内存(shm):65536字节大小,用于存储覆盖率信息
  • 分桶机制:8个hit count桶(1,2,3,4-7,8-15,16-31,32-127,128+)

二、afl-tmin分析

2.1 功能概述

  • 测试用例最小化器
  • 两种模式:
    • non-crash模式:保持覆盖率不变(需插桩)
    • crash模式:保持程序崩溃状态(可不插桩)

2.2 核心工作流程

  1. 初始化阶段

    • 参数解析与设置
    • 共享内存初始化(setup_shm)
    • 信号处理设置
    • 环境变量处理
  2. dry run

    • 检查程序是否超时或崩溃
    • 通过run_target函数执行
  3. 最小化阶段

    • 调用minimize函数进行优化

2.3 run_target函数详解

  1. 执行流程

    • 清空共享内存区域
    • 写入input文件
    • fork子进程
      • 重定向流
      • 创建新进程组
      • 设置内存限制
      • 禁用core dump
      • 执行目标程序
    • 主进程:
      • 设置定时器
      • 等待子进程结束
      • 对shm进行分桶(classify_counts)
      • 处理崩溃情况
      • 计算hash决定是否保留input
  2. 分桶机制实现

static const u8 count_class_lookup8[256] = {
  [0]           = 0,
  [1]           = 1,
  [2]           = 2,
  [3]           = 3,
  [4 ... 7]     = 4,
  [8 ... 15]    = 5,
  [16 ... 31]   = 6,
  [32 ... 127]  = 7,
  [128 ... 255] = 8
};

2.4 minimize函数优化策略

  1. BLOCK NORMALIZATION

    • 将数据分为128个块(2的幂次)
    • 尝试将整个块替换为'0'
    • 若路径相同则保留更改
  2. BLOCK DELETION

    • 将数据分为16个块
    • 从前向后尝试删除每个块
    • 减半删除长度重复操作
    • 加速逻辑:跳过与前一块相同的块
  3. ALPHABET MINIMIZATION

    • 字符集最小化
    • 将相同字符替换为'0'并尝试删除
  4. CHARACTER MINIMIZATION

    • 字符最小化
    • 直接替换当前字符为'0'并尝试删除

三、afl-showmap分析

3.1 功能概述

  • 分析和显示程序运行时访问的代码路径
  • 显示共享内存中的覆盖率信息

3.2 核心实现

  1. 运行流程

    • 参数解析
    • 共享内存初始化
    • 环境设置
    • 执行run_target
    • 通过write_results输出结果
  2. 输出模式

    • 用户模式:输出0-8的自然数
    • 二进制模式:使用标准分桶机制

四、afl-analyze分析

4.1 功能概述

  • 分析输入文件结构
  • 通过字节翻转识别关键字段
  • 可识别magic number、checksum、length等

4.2 核心实现

  1. analyze函数

    • 对每个字节进行4种变异:
      • xor 0xff
      • xor 0x1
      • sub 0x10
      • add 0x10
    • 分类响应类型:
      • 完全无响应(不重要)
      • 部分无响应(不关键)
      • 固定响应(固定)
      • 可变响应(敏感)
  2. dump_hex函数

    • 字段类型识别:
      • 2字节且值<=输入长度:RESP_LEN
      • 2字节且差值>32:RESP_CKSUM
      • 4字节且值<=输入长度:RESP_LEN
      • 4字节且最高位不同:RESP_CKSUM
      • [1,32)字节(非2/4):保持原类型
      • ≥32字节:数据区域

五、关键问题解析

5.1 分桶机制的意义

  • 避免corpus爆炸:不区分100次和101次循环
  • 保持搜索能力:区分0-4次循环,7-8次循环
  • 平衡策略:在循环次数增加时给予适当奖励

5.2 共享内存初始化

void setup_shm() {
    shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
    setenv("__AFL_SHM_ID", shm_str, 1);
    trace_bits = shmat(shm_id, NULL, 0);
}

5.3 覆盖率反馈实现

  • 插桩代码计算基本块ID:
    movl $0xdeadbeef, %edi
    xorl %eax, %eax
    call __afl_maybe_log
    
  • 共享内存更新:
    cur_location = <COMPILE_TIME_RANDOM>;
    shared_mem[cur_location ^ prev_location]++; 
    prev_location = cur_location >> 1;
    

六、实践应用

6.1 编译注意事项

  • 使用AFL_DONT_OPTIMIZE=1防止优化移除插桩
    AFL_DONT_OPTIMIZE=1 afl-gcc test.c -o test -g
    

6.2 典型用例

  1. afl-tmin

    afl-tmin -i input -o minimized_output -- ./target
    
  2. afl-showmap

    afl-showmap -o coverage.txt -- ./target
    
  3. afl-analyze

    afl-analyze -i test_case -- ./target
    

七、总结与深入方向

7.1 核心要点

  • AFL通过插桩和共享内存实现高效反馈
  • 分桶机制平衡了路径敏感性和性能
  • 工具链协同工作形成完整模糊测试生态

7.2 深入方向

  1. afl-fuzz核心算法
  2. 并行化fuzzing实现
  3. 自定义变异策略
  4. 与符号执行结合

通过深入理解这些工具的实现原理,可以更好地定制和优化模糊测试流程,提高漏洞挖掘效率。

AFL源码学习与工具分析教学文档 一、AFL基础架构回顾 1.1 AFL整体工作流程 编译插桩阶段 : AFL作为源编译器的封装 在编译成汇编代码后进行插桩 插桩后的程序会在第一个桩处停下并初始化共享内存 运行阶段 : 形成fork server模式 主进程不断fork子进程进行探索 子进程通过管道向afl-fuzz汇报结果 1.2 关键数据结构 共享内存(shm) :65536字节大小,用于存储覆盖率信息 分桶机制 :8个hit count桶(1,2,3,4-7,8-15,16-31,32-127,128+) 二、afl-tmin分析 2.1 功能概述 测试用例最小化器 两种模式: non-crash模式:保持覆盖率不变(需插桩) crash模式:保持程序崩溃状态(可不插桩) 2.2 核心工作流程 初始化阶段 : 参数解析与设置 共享内存初始化( setup_shm ) 信号处理设置 环境变量处理 dry run : 检查程序是否超时或崩溃 通过 run_target 函数执行 最小化阶段 : 调用 minimize 函数进行优化 2.3 run_ target函数详解 执行流程 : 清空共享内存区域 写入input文件 fork子进程 重定向流 创建新进程组 设置内存限制 禁用core dump 执行目标程序 主进程: 设置定时器 等待子进程结束 对shm进行分桶( classify_counts ) 处理崩溃情况 计算hash决定是否保留input 分桶机制实现 : 2.4 minimize函数优化策略 BLOCK NORMALIZATION : 将数据分为128个块(2的幂次) 尝试将整个块替换为'0' 若路径相同则保留更改 BLOCK DELETION : 将数据分为16个块 从前向后尝试删除每个块 减半删除长度重复操作 加速逻辑:跳过与前一块相同的块 ALPHABET MINIMIZATION : 字符集最小化 将相同字符替换为'0'并尝试删除 CHARACTER MINIMIZATION : 字符最小化 直接替换当前字符为'0'并尝试删除 三、afl-showmap分析 3.1 功能概述 分析和显示程序运行时访问的代码路径 显示共享内存中的覆盖率信息 3.2 核心实现 运行流程 : 参数解析 共享内存初始化 环境设置 执行 run_target 通过 write_results 输出结果 输出模式 : 用户模式:输出0-8的自然数 二进制模式:使用标准分桶机制 四、afl-analyze分析 4.1 功能概述 分析输入文件结构 通过字节翻转识别关键字段 可识别magic number、checksum、length等 4.2 核心实现 analyze函数 : 对每个字节进行4种变异: xor 0xff xor 0x1 sub 0x10 add 0x10 分类响应类型: 完全无响应(不重要) 部分无响应(不关键) 固定响应(固定) 可变响应(敏感) dump_ hex函数 : 字段类型识别: 2字节且值<=输入长度:RESP_ LEN 2字节且差值>32:RESP_ CKSUM 4字节且值<=输入长度:RESP_ LEN 4字节且最高位不同:RESP_ CKSUM [ 1,32)字节(非2/4):保持原类型 ≥32字节:数据区域 五、关键问题解析 5.1 分桶机制的意义 避免corpus爆炸 :不区分100次和101次循环 保持搜索能力 :区分0-4次循环,7-8次循环 平衡策略 :在循环次数增加时给予适当奖励 5.2 共享内存初始化 5.3 覆盖率反馈实现 插桩代码计算基本块ID: 共享内存更新: 六、实践应用 6.1 编译注意事项 使用 AFL_DONT_OPTIMIZE=1 防止优化移除插桩 6.2 典型用例 afl-tmin : afl-showmap : afl-analyze : 七、总结与深入方向 7.1 核心要点 AFL通过插桩和共享内存实现高效反馈 分桶机制平衡了路径敏感性和性能 工具链协同工作形成完整模糊测试生态 7.2 深入方向 afl-fuzz核心算法 并行化fuzzing实现 自定义变异策略 与符号执行结合 通过深入理解这些工具的实现原理,可以更好地定制和优化模糊测试流程,提高漏洞挖掘效率。