使用 afl-unicorn: Fuzzing 任意二进制代码
字数 1259 2025-08-03 16:44:58
AFL-Unicorn 模糊测试任意二进制代码教学文档
1. AFL-Unicorn 概述
AFL-Unicorn 是 American Fuzzy Lop (AFL) 的一个扩展模式,它结合了 Unicorn 引擎的能力,允许对任意二进制代码进行模糊测试,即使这些代码无法通过传统命令行访问。
1.1 主要特点
- 能够模糊测试嵌入式系统中的解析函数
- 可以测试深藏在复杂、缓慢程序中的特定代码段
- 提供 AFL 的所有基于覆盖率(coverage-based)的优点
- 支持通过模拟(emulation)而非实际执行来测试代码
1.2 适用场景
- 嵌入式系统通过RF接收输入且不易调试的情况
- 复杂程序中难以通过传统工具访问的深层代码
- 需要测试特定函数而非整个程序的情况
2. 安装与配置
2.1 系统要求
- Linux 系统(推荐 Ubuntu 16.04 LTS)
- 已卸载任何现有的 Unicorn 二进制文件
2.2 安装步骤
# 克隆 afl-unicorn 仓库
git clone https://github.com/[afl-unicorn-repo]
# 构建和安装 AFL
cd /path/to/afl-unicorn
make
sudo make install
# 构建 Unicorn 支持
cd unicorn_mode
sudo ./build_unicorn_support.sh
注意:构建过程会编译并安装修补版的 Unicorn Engine v1.0.1
3. 工作原理
AFL-Unicorn 通过实现 AFL 的 QEMU 模式用于 Unicorn 引擎的块边缘检测来工作:
- AFL 使用来自模拟代码段的块覆盖信息驱动输入生成
- 测试工具加载目标代码,设置初始状态,并加载 AFL 变异的数据
- 模拟目标二进制代码
- 如果检测到崩溃或错误,抛出信号通知 AFL
4. 使用示例
4.1 目标代码示例
#define DATA_ADDRESS 0x00300000
int main(void) {
unsigned char* data_buf = (unsigned char*)DATA_ADDRESS;
if(data_buf[20] != 0) {
// 崩溃条件1
unsigned char invalid_read = *(unsigned char*)0x00000000;
}
else if(data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) {
// 崩溃条件2
unsigned char invalid_read = *(unsigned char*)0x00000000;
}
else if(data_buf[9] == 0x00 && data_buf[10] != 0x00 && data_buf[11] == 0x00) {
// 崩溃条件3
unsigned char invalid_read = *(unsigned char*)0x00000000;
}
return 0;
}
4.2 Python 测试工具框架
import argparse
import os
import signal
from unicorn import *
from unicorn.mips_const import *
# 内存映射配置
CODE_ADDRESS = 0x00100000 # 代码加载地址
CODE_SIZE_MAX = 0x00010000 # 最大代码大小(64KB)
STACK_ADDRESS = 0x00200000 # 栈地址
STACK_SIZE = 0x00010000 # 栈大小(64KB)
DATA_ADDRESS = 0x00300000 # 变异数据存放地址
DATA_SIZE_MAX = 0x00010000 # 最大数据大小(64KB)
def force_crash(uc_error):
"""强制崩溃以通知AFL"""
mem_errors = [UC_ERR_READ_UNMAPPED, UC_ERR_READ_PROT, ...]
if uc_error.errno in mem_errors:
os.kill(os.getpid(), signal.SIGSEGV)
elif uc_error.errno == UC_ERR_INSN_INVALID:
os.kill(os.getpid(), signal.SIGILL)
else:
os.kill(os.getpid(), signal.SIGABRT)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('input_file', type=str, help="变异输入文件路径")
parser.add_argument('-d', '--debug', default=False, action="store_true")
args = parser.parse_args()
# 初始化Unicorn引擎(MIPS32大端)
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)
# 加载并映射二进制代码
with open(BINARY_FILE, 'rb') as f:
binary_code = f.read()
uc.mem_map(CODE_ADDRESS, CODE_SIZE_MAX)
uc.mem_write(CODE_ADDRESS, binary_code)
# 设置程序计数器
start_address = CODE_ADDRESS
end_address = CODE_ADDRESS + 0xf4 # main()结束地址
uc.reg_write(UC_MIPS_REG_PC, start_address)
# 设置栈
uc.mem_map(STACK_ADDRESS, STACK_SIZE)
uc.reg_write(UC_MIPS_REG_SP, STACK_ADDRESS + STACK_SIZE)
# 启动AFL fork服务器(必须执行至少1条指令)
try:
uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), 0, 0, count=1)
except UcError as e:
print(f"启动fork服务器失败: {e}")
return
# 加载变异输入
with open(args.input_file, 'rb') as f:
input_data = f.read()
uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX)
uc.mem_write(DATA_ADDRESS, input_data)
# 执行模拟
try:
uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), end_address, timeout=0, count=0)
except UcError as e:
force_crash(e)
if __name__ == "__main__":
main()
4.3 运行模糊测试
afl-fuzz -U -m none -i ./sample_inputs -o ./output -- python simple_test_harness.py @@
参数说明:
-U: 启用Unicorn模式-m none: 禁用内存限制(推荐用于Unicorn)-i: 输入样本目录-o: 输出目录@@: AFL将替换为输入文件路径
5. 高级用法
5.1 性能优化
- 基于C的测试工具比Python版本快5-10倍
- 尽量减少测试工具中的不必要操作
- 合理设置内存映射大小
5.2 路径探索
可以通过在特定条件下强制"崩溃"来探索代码路径:
if pc == TARGET_ADDRESS: # 当执行到目标地址时
os.kill(os.getpid(), signal.SIGUSR1) # 使用自定义信号
AFL会保存导致到达该点的输入,可用于符号执行替代方案。
5.3 调试技巧
-
启用调试钩子:
uc.hook_add(UC_HOOK_CODE, unicorn_debug_instruction) uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, unicorn_debug_mem_access) -
使用Capstone反汇编:
from capstone import * cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN) for i in cs.disasm(code, size): print(f"0x{i.address:x}:\t{i.mnemonic}\t{i.op_str}") -
寄存器转储:
print(f"PC: 0x{uc.reg_read(UC_MIPS_REG_PC):x}")
6. 注意事项
- 必须执行至少一条指令才能正确启动AFL的fork服务器
- 内存映射应合理设置,避免冲突
- 测试工具必须正确处理各种错误条件
- 实际系统验证:仿真中发现的崩溃需要在真实系统上验证
- 性能考虑:复杂测试工具会显著降低模糊测试速度
7. 实际应用案例
- 嵌入式系统漏洞挖掘(如Broadcom WiFi芯片组漏洞)
- Windows/Linux/Android进程功能模拟测试
- 封闭系统(如IoT设备)的安全分析
- 协议解析器测试
8. 参考资料
通过本教学文档,您应该能够理解AFL-Unicorn的核心概念、安装配置方法、基本和高级使用技巧,以及在实际安全研究中的应用方式。