AFL源码审计-Fuzz学习
字数 2049 2025-09-01 11:26:17

AFL源码审计与Fuzz学习指南

1. AFL概述

American Fuzzy Lop (AFL)是一款基于覆盖制导的模糊测试工具,其核心设计原则是速度、可靠性和易用性。AFL通过轻量级插装技术获取程序执行路径信息,指导测试用例的变异和选择。

1.1 基本工作原理

AFL通过以下机制实现高效的模糊测试:

  • 覆盖率测量:捕获分支(边缘)覆盖率和粗略的分支执行次数
  • 路径反馈:通过共享内存记录执行路径的元组信息
  • 进化算法:基于反馈信息逐步优化测试用例

2. AFL插桩机制

2.1 插桩类型

AFL支持两种插桩方式:

  1. 汇编层插桩:在汇编代码中寻找条件跳转指令插入探测代码

    • 优点:直接操作底层代码
    • 缺点:需要针对不同架构实现,不够通用
  2. LLVM PASS插桩:通过Clang/LLVM在中间语言层面插入探测代码

    • 优点:跨平台,更通用
    • 实现方式:使用LLVM的Pass机制

2.2 插桩实现细节

插桩代码的核心逻辑:

cur_location = <随机生成的位置标识>;
shared_mem[cur_location ^ prev_location]++; 
prev_location = cur_location >> 1;
  • shared_mem[]:64KB共享内存区域,记录(branch_src, branch_dst)元组执行次数
  • 位移操作:保留元组方向性(A^B ≠ B^A)和循环身份(A^A ≠ B^B)

3. AFL核心组件分析

3.1 afl-as源码分析

主要功能:

  1. 初始化随机数种子
  2. 实现汇编指令插桩
  3. 修改as参数
  4. 生成可执行文件并清理临时文件

关键流程:

  • 确定as名称(GNU as)
  • 检查临时目录(TMPDIR → TEMP → TMP → /tmp)
  • 参数拷贝(原始命令行参数)
  • 识别和检查输入文件
  • 创建插桩后内容

3.2 ForkServer机制

优化性能的关键设计:

  • 避免重复的execve()、链接和libc初始化
  • 利用写时复制(copy-on-write)克隆已初始化的进程镜像
  • 性能提升:通常1.5-2倍

实现方式:

  1. 在第一个插桩函数处停止
  2. 等待afl-fuzz的命令
  3. 通过管道通信控制子进程执行

3.3 变异策略

AFL采用多种变异策略组合:

确定性变异阶段:

  1. 按位翻转(bitflip):1-4位翻转

    • 实现:FLIP_BIT逐位翻转 + common_fuzz_stuff测试
    • 辅助算法:检测变异数据是否可通过按位翻转得到
  2. 算术增减(arithmetic)

    • 8位、16位、32位算术运算
    • 包括加减操作
  3. 特殊值测试(interesting)

    • 插入预定义的边界值和特殊值
    • 例如:0, 1, INT_MAX等

非确定性变异阶段:

  1. 字典替换(DICTIONARY)

    • 使用用户提供的字典条目
    • 自动识别语法标记
  2. 随机变异(havoc)

    • 包括位翻转、设置有趣值、增删字节等10多种操作
    • 支持多种变异方法组合
  3. 拼接(SPLICING)

    • 将当前输入与其他输入文件拼接
    • 作为其他策略无效时的最后手段

4. AFL使用实践

4.1 libxml2模糊测试流程

  1. 编译

    AFL_USE_ASAN=1 CC=afl-clang-lto CXX=afl-clang-lto++ \
    CXXFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" \
    ./configure --disable-shared
    make
    
  2. 准备输入文件

    • 使用简单有效的XML样本作为种子
    • 例如:<root></root>
  3. 执行模糊测试

    afl-fuzz -i seeds -o output -x xml.dict -M master -- ./xmllint @@
    afl-fuzz -i seeds -o output -x xml.dict -S slave1 -- ./xmllint @@
    

4.2 关键参数说明

  • -M master:主节点,负责探索新路径
  • -S slave:从节点,挖掘已有路径的深层bug
  • -x xml.dict:指定领域特定字典
  • @@:表示输入文件占位符

5. AFL高级特性

5.1 覆盖率引导优化

AFL通过以下方式优化测试效率:

  1. 效应器映射:识别输入文件中影响执行路径的关键区域

  2. virgin_bits追踪

    • 记录尚未探索到的路径(01)
    • 新路径会使virgin_bits变为01
    • 已探索路径标记为10
  3. 队列精简

    • 选择覆盖所有元组的最小测试用例集
    • 基于执行延迟和文件大小评分

5.2 崩溃处理

  1. 去重机制

    • 崩溃跟踪包含新元组
    • 崩溃跟踪缺少之前所有故障共有的元组
  2. 崩溃调查模式

    • 仅保留导致崩溃的变异
    • 探索崩溃程序的状态

6. AFL扩展开发

6.1 测试用例统计扩展

通过afl_postprocess函数实现:

  1. 编译为动态库:

    gcc -fPIC -shared work.c -o mypost.so
    
  2. 运行时加载:

    AFL_POST_LIBRARY=./mypost.so afl-fuzz -i input -o output -- ./target
    

6.2 自定义变异算子

添加步骤:

  1. 修改havoc函数中的变异算子
  2. 将定制算子编号为15-16
  3. 原有词典相关算子移至17-18
  4. 重新编译AFL

7. 性能优化技巧

  1. 持久模式:单个进程中测试多个输入,减少fork()开销

  2. 延迟初始化:跳过不必要的初始化代码

  3. 输入裁剪

    • afl-tmin:最小化崩溃样本
    • 内置修剪器:移除不影响执行路径的数据块
  4. 并行化

    • 多实例协同工作
    • 主从架构分工

8. 白皮书核心要点

  1. 设计哲学:实践验证的简单可靠方案组合
  2. 覆盖率测量:分支覆盖率+粗略执行计数
  3. 新行为检测:基于元组映射的快速比较
  4. 输入队列演化:保留所有发现新行为的测试用例
  5. 精简策略:选择最优测试用例子集
  6. 二进制插桩:QEMU模式实现黑盒测试

通过深入理解AFL的这些核心机制和技术细节,安全研究人员可以更有效地使用和定制AFL进行漏洞挖掘工作。

AFL源码审计与Fuzz学习指南 1. AFL概述 American Fuzzy Lop (AFL)是一款基于覆盖制导的模糊测试工具,其核心设计原则是速度、可靠性和易用性。AFL通过轻量级插装技术获取程序执行路径信息,指导测试用例的变异和选择。 1.1 基本工作原理 AFL通过以下机制实现高效的模糊测试: 覆盖率测量 :捕获分支(边缘)覆盖率和粗略的分支执行次数 路径反馈 :通过共享内存记录执行路径的元组信息 进化算法 :基于反馈信息逐步优化测试用例 2. AFL插桩机制 2.1 插桩类型 AFL支持两种插桩方式: 汇编层插桩 :在汇编代码中寻找条件跳转指令插入探测代码 优点:直接操作底层代码 缺点:需要针对不同架构实现,不够通用 LLVM PASS插桩 :通过Clang/LLVM在中间语言层面插入探测代码 优点:跨平台,更通用 实现方式:使用LLVM的Pass机制 2.2 插桩实现细节 插桩代码的核心逻辑: shared_mem[] :64KB共享内存区域,记录(branch_ src, branch_ dst)元组执行次数 位移操作:保留元组方向性(A^B ≠ B^A)和循环身份(A^A ≠ B^B) 3. AFL核心组件分析 3.1 afl-as源码分析 主要功能: 初始化随机数种子 实现汇编指令插桩 修改as参数 生成可执行文件并清理临时文件 关键流程: 确定as名称(GNU as) 检查临时目录(TMPDIR → TEMP → TMP → /tmp) 参数拷贝(原始命令行参数) 识别和检查输入文件 创建插桩后内容 3.2 ForkServer机制 优化性能的关键设计: 避免重复的execve()、链接和libc初始化 利用写时复制(copy-on-write)克隆已初始化的进程镜像 性能提升:通常1.5-2倍 实现方式: 在第一个插桩函数处停止 等待afl-fuzz的命令 通过管道通信控制子进程执行 3.3 变异策略 AFL采用多种变异策略组合: 确定性变异阶段: 按位翻转(bitflip) :1-4位翻转 实现: FLIP_BIT 逐位翻转 + common_fuzz_stuff 测试 辅助算法:检测变异数据是否可通过按位翻转得到 算术增减(arithmetic) : 8位、16位、32位算术运算 包括加减操作 特殊值测试(interesting) : 插入预定义的边界值和特殊值 例如:0, 1, INT_ MAX等 非确定性变异阶段: 字典替换(DICTIONARY) : 使用用户提供的字典条目 自动识别语法标记 随机变异(havoc) : 包括位翻转、设置有趣值、增删字节等10多种操作 支持多种变异方法组合 拼接(SPLICING) : 将当前输入与其他输入文件拼接 作为其他策略无效时的最后手段 4. AFL使用实践 4.1 libxml2模糊测试流程 编译 : 准备输入文件 : 使用简单有效的XML样本作为种子 例如: <root></root> 执行模糊测试 : 4.2 关键参数说明 -M master :主节点,负责探索新路径 -S slave :从节点,挖掘已有路径的深层bug -x xml.dict :指定领域特定字典 @@ :表示输入文件占位符 5. AFL高级特性 5.1 覆盖率引导优化 AFL通过以下方式优化测试效率: 效应器映射 :识别输入文件中影响执行路径的关键区域 virgin_ bits追踪 : 记录尚未探索到的路径(01) 新路径会使virgin_ bits变为01 已探索路径标记为10 队列精简 : 选择覆盖所有元组的最小测试用例集 基于执行延迟和文件大小评分 5.2 崩溃处理 去重机制 : 崩溃跟踪包含新元组 崩溃跟踪缺少之前所有故障共有的元组 崩溃调查模式 : 仅保留导致崩溃的变异 探索崩溃程序的状态 6. AFL扩展开发 6.1 测试用例统计扩展 通过 afl_postprocess 函数实现: 编译为动态库: 运行时加载: 6.2 自定义变异算子 添加步骤: 修改 havoc 函数中的变异算子 将定制算子编号为15-16 原有词典相关算子移至17-18 重新编译AFL 7. 性能优化技巧 持久模式 :单个进程中测试多个输入,减少fork()开销 延迟初始化 :跳过不必要的初始化代码 输入裁剪 : afl-tmin :最小化崩溃样本 内置修剪器:移除不影响执行路径的数据块 并行化 : 多实例协同工作 主从架构分工 8. 白皮书核心要点 设计哲学 :实践验证的简单可靠方案组合 覆盖率测量 :分支覆盖率+粗略执行计数 新行为检测 :基于元组映射的快速比较 输入队列演化 :保留所有发现新行为的测试用例 精简策略 :选择最优测试用例子集 二进制插桩 :QEMU模式实现黑盒测试 通过深入理解AFL的这些核心机制和技术细节,安全研究人员可以更有效地使用和定制AFL进行漏洞挖掘工作。