AFLGO的源码阅读(一)
字数 1678 2025-09-01 11:26:03

AFLGO源码分析与使用指南

1. AFLGO概述

AFLGO是一个基于AFL(American Fuzzy Lop)的定向模糊测试工具,它通过LLVM插桩和静态分析技术,将模糊测试引导至目标程序中的特定位置。本指南将详细解析AFLGO的核心实现原理和使用方法。

2. 距离计算机制

2.1 距离计算流程

AFLGO的核心是计算程序各基本块到目标位置的距离,主要流程如下:

  1. 生成控制流图(CFG)和调用图(CG)

    • 使用LLVM的opt工具生成每个模块的调用图
    • 通过merge_callgraphs.py合并所有模块的调用图
  2. 距离计算

    • 使用distance.py计算CG中的函数间距离
    • 对每个函数的CFG进行基本块间的距离计算
    • 合并所有距离信息到distance.cfg.txt文件

2.2 gen_distance_orig.sh脚本分析

该脚本负责生成目标项目中各块到目标位置的距离:

# 参数检查
if [ $# -lt 2 ]; then
    echo "Usage: $0 <binaries-directory> <temporary-directory> [<fuzzer-name>]"
    exit 1
fi

# 解析符号链接
BINARIES=$(readlink -f "$1")
TMPDIR=$(readlink -f "$2")

# 查找AFLGO路径
AFLGO=$(readlink -f "$(dirname "$(readlink -f "$0")")/../..")

关键步骤:

  1. 检查输入参数和路径有效性
  2. 查找目标项目中的.bc文件(LLVM bitcode文件)
  3. 构建CFG和CG
  4. 计算并生成距离信息文件

3. 编译与插桩

3.1 编译选项

AFLGO的第一次编译需要特殊选项:

export TMP_DIR="$TMP_DIR"              # 临时文件目录
export CC="$AFLGO/aflgo-clang"         # AFLGO包装的clang
export CXX="$AFLGO/aflgo-clang++"      # AFLGO包装的clang++
export CFLAGS="-g -fstack-protector-all -fno-omit-frame-pointer -Wno-error"

这些选项确保:

  • 保留调试信息(-g)
  • 启用栈保护(-fstack-protector-all)
  • 保留帧指针(-fno-omit-frame-pointer)
  • 将警告保持为警告(-Wno-error)

3.2 aflgo-clang分析

aflgo-clang是AFLGO对clang的包装,主要功能:

  1. 参数处理

    • 检查参数有效性
    • 处理用户传入的编译选项
  2. Pass加载

    -Xclang -load -Xclang $AFLGO/llvm-pass/aflgo-llvm-pass.so
    

    加载AFLGO的LLVM Pass进行插桩

  3. 特殊选项处理

    • -distance-targets-outdir等AFLGO特有选项
    • 架构位数设置
    • ASAN/MSAN消杀器选项处理
  4. 优化选项

    • 默认添加-g-funroll-loops
    • 可禁用内置函数优化(-fno-builtin-strcmp等)

3.3 运行时库(aflgo-runtime.o)

AFLGO的运行时库提供关键功能:

  1. 共享内存初始化

    __afl_map_shm() {
        __afl_area_ptr = (u8 *)shmat(__afl_global_mem_fd, NULL, 0);
    }
    
  2. forkserver实现

    __afl_start_forkserver() {
        // 与fuzzer通信
        // fork子进程
        // 处理竞态条件
    }
    
  3. 持久化模式

    __afl_persistent_loop(unsigned int max_cnt) {
        // 初始化循环
        // 执行目标代码
        // 挂起等待下一次执行
    }
    
  4. 延迟初始化

    __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) {
        if (!__afl_manual_control) __afl_start_forkserver();
    }
    

4. 插桩技术

4.1 SanitizerCoverage

LLVM提供的高级插桩方式:

// 每条边的插桩点
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    __afl_area_ptr[*guard]++;
}

// 初始化
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
    // 设置插桩率
    // 初始化边ID
}

4.2 AFLGO特有插桩

  1. 基本块记录

    void writeBB(char *bb_name) {
        // 拼接路径上下文
        // 哈希去重
        // 记录到文件
    }
    
  2. 哈希集合实现

    hashset_t *hashset_create(size_t size);
    size_t hashset_num_items(hashset_t *set);
    void hashset_destroy(hashset_t *set);
    bool hashset_add_member(hashset_t *set, const char *key);
    

5. 特殊模式实现

5.1 持久化模式(Persistent Mode)

实现原理:

#define __AFL_LOOP(_A) \
  ({ static volatile char *_B __attribute__((used)); \
     _B = (char*)"##SIG_AFL_PERSIST##"; \
     __afl_persistent_loop(_A); })

使用方法:

while (__AFL_LOOP(1000)) {
    // 测试代码
}

优点:

  • 减少fork开销
  • 单次fork执行多次测试

5.2 延迟模式(Deferred Mode)

实现原理:

#define __AFL_INIT() \
  do { __afl_manual_init(); } while (0)

使用方法:

// 初始化代码
__AFL_INIT();
// 测试代码

优点:

  • 控制forkserver启动时机
  • 减少不必要的初始化开销

6. 使用流程总结

  1. 第一次编译

    • 使用特殊编译选项生成bitcode文件
    • 保留调试信息和必要的保护选项
  2. 距离计算

    • 运行gen_distance_orig.sh生成距离信息
    • 得到distance.cfg.txtdistance.callgraph.txt
  3. 定向模糊测试

    • 使用距离信息引导fuzzer
    • 可选择持久化或延迟模式优化性能
  4. 结果分析

    • 检查覆盖率信息
    • 分析发现的漏洞

7. 关键文件说明

文件 用途
aflgo-clang 包装的编译器前端
aflgo-llvm-pass.so LLVM插桩Pass
aflgo-runtime.o 运行时库
gen_distance_orig.sh 距离计算脚本
distance.py 距离计算核心
merge_callgraphs.py 调用图合并工具

通过本指南,您应该能够深入理解AFLGO的工作原理并有效使用它进行定向模糊测试。如需进一步优化,可以调整距离计算算法或插桩策略。

AFLGO源码分析与使用指南 1. AFLGO概述 AFLGO是一个基于AFL(American Fuzzy Lop)的定向模糊测试工具,它通过LLVM插桩和静态分析技术,将模糊测试引导至目标程序中的特定位置。本指南将详细解析AFLGO的核心实现原理和使用方法。 2. 距离计算机制 2.1 距离计算流程 AFLGO的核心是计算程序各基本块到目标位置的距离,主要流程如下: 生成控制流图(CFG)和调用图(CG) : 使用LLVM的 opt 工具生成每个模块的调用图 通过 merge_callgraphs.py 合并所有模块的调用图 距离计算 : 使用 distance.py 计算CG中的函数间距离 对每个函数的CFG进行基本块间的距离计算 合并所有距离信息到 distance.cfg.txt 文件 2.2 gen_ distance_ orig.sh脚本分析 该脚本负责生成目标项目中各块到目标位置的距离: 关键步骤: 检查输入参数和路径有效性 查找目标项目中的 .bc 文件(LLVM bitcode文件) 构建CFG和CG 计算并生成距离信息文件 3. 编译与插桩 3.1 编译选项 AFLGO的第一次编译需要特殊选项: 这些选项确保: 保留调试信息( -g ) 启用栈保护( -fstack-protector-all ) 保留帧指针( -fno-omit-frame-pointer ) 将警告保持为警告( -Wno-error ) 3.2 aflgo-clang分析 aflgo-clang 是AFLGO对clang的包装,主要功能: 参数处理 : 检查参数有效性 处理用户传入的编译选项 Pass加载 : 加载AFLGO的LLVM Pass进行插桩 特殊选项处理 : -distance 、 -targets 、 -outdir 等AFLGO特有选项 架构位数设置 ASAN/MSAN消杀器选项处理 优化选项 : 默认添加 -g 和 -funroll-loops 可禁用内置函数优化( -fno-builtin-strcmp 等) 3.3 运行时库(aflgo-runtime.o) AFLGO的运行时库提供关键功能: 共享内存初始化 : forkserver实现 : 持久化模式 : 延迟初始化 : 4. 插桩技术 4.1 SanitizerCoverage LLVM提供的高级插桩方式: 4.2 AFLGO特有插桩 基本块记录 : 哈希集合实现 : 5. 特殊模式实现 5.1 持久化模式(Persistent Mode) 实现原理: 使用方法: 优点: 减少fork开销 单次fork执行多次测试 5.2 延迟模式(Deferred Mode) 实现原理: 使用方法: 优点: 控制forkserver启动时机 减少不必要的初始化开销 6. 使用流程总结 第一次编译 : 使用特殊编译选项生成bitcode文件 保留调试信息和必要的保护选项 距离计算 : 运行 gen_distance_orig.sh 生成距离信息 得到 distance.cfg.txt 和 distance.callgraph.txt 定向模糊测试 : 使用距离信息引导fuzzer 可选择持久化或延迟模式优化性能 结果分析 : 检查覆盖率信息 分析发现的漏洞 7. 关键文件说明 | 文件 | 用途 | |------|------| | aflgo-clang | 包装的编译器前端 | | aflgo-llvm-pass.so | LLVM插桩Pass | | aflgo-runtime.o | 运行时库 | | gen_distance_orig.sh | 距离计算脚本 | | distance.py | 距离计算核心 | | merge_callgraphs.py | 调用图合并工具 | 通过本指南,您应该能够深入理解AFLGO的工作原理并有效使用它进行定向模糊测试。如需进一步优化,可以调整距离计算算法或插桩策略。