sakuraのall fuzz : afl-unicorn
字数 1026 2025-08-24 16:48:16

AFL-Unicorn 模糊测试技术详解

1. Unicorn引擎基础

1.1 Unicorn简介

Unicorn是一个轻量级、多平台、多架构的CPU仿真框架,基于QEMU开发。它允许我们在不依赖真实硬件的情况下模拟执行二进制代码。

1.2 基本API使用

from unicorn import *
from unicorn.x86_const import *

# 初始化Unicorn实例
mu = Uc(UC_ARCH_X86, UC_MODE_64)  # 指定架构为x86-64

# 内存映射
BASE = 0x400000
STACK_ADDR = 0x0
STACK_SIZE = 1024*1024
mu.mem_map(BASE, 1024*1024)  # 映射代码段
mu.mem_map(STACK_ADDR, STACK_SIZE)  # 映射栈空间

# 写入程序到内存
mu.mem_write(BASE, read("./fibonacci"))

# 设置寄存器
mu.reg_write(UC_X86_REG_RSP, STACK_ADDR + STACK_SIZE - 1)

# 添加hook
def hook_code(mu, address, size, user_data):
    print('>>> Tracing instruction at 0x%x, size=0x%x' %(address, size))
mu.hook_add(UC_HOOK_CODE, hook_code)

# 开始仿真执行
mu.emu_start(0x00000000004004E0, 0x0000000000400582)

1.3 常见问题解决

问题1:BSS段未初始化

解决方案:跳过相关指令或手动初始化BSS段

instructions_skip_list = [0x00000000004004EF, 0x00000000004004F6]

def hook_code(mu, address, size, user_data):  
    if address in instructions_skip_list:
        mu.reg_write(UC_X86_REG_RIP, address+size)

问题2:系统调用/库函数缺失

解决方案:hook关键函数,模拟其行为

def hook_code(mu, address, size, user_data):
    if address == 0x400560:  # putchar调用点
        c = mu.reg_read(UC_X86_REG_RDI)  # 获取第一个参数
        print(chr(c))  # 模拟输出
        mu.reg_write(UC_X86_REG_RIP, address+size)  # 跳过调用

2. AFL-Unicorn集成

2.1 基本原理

AFL-Unicorn结合了AFL的模糊测试能力和Unicorn的仿真能力,工作流程:

  1. AFL生成变异输入
  2. 输入被写入仿真内存的固定地址
  3. Harness从该地址读取数据并执行目标代码
  4. 出现崩溃时模拟崩溃行为通知AFL

2.2 典型Harness结构

import argparse
from unicorn import *
from unicorn.x86_const import *
import unicorn_loader 

# 配置参数
START_ADDRESS = 0x0804D106  # 起始地址
END_ADDRESS = 0x0804D120    # 结束地址

def main():
    # 初始化
    uc = unicorn_loader.AflUnicornEngine(args.context_dir)
    
    # 启动fork server
    uc.emu_start(START_ADDRESS, 0, 0, count=1)
    
    # 处理输入
    input_content = open(args.input_file, 'rb').read()
    buf_addr = unicorn_heap.malloc(len(input_content))
    uc.mem_write(buf_addr, input_content)
    
    # 设置输入参数
    uc.reg_write(UC_X86_REG_EAX, buf_addr)  # pData
    uc.reg_write(UC_X86_REG_DL, len(input_content))  # dataLen
    
    # 执行测试
    try:
        uc.emu_start(START_ADDRESS, END_ADDRESS)
    except UcError as e:
        uc.force_crash(e)  # 通知AFL崩溃

2.3 关键Hook点

1. 绕过校验

CHKSUM_CMP_ADDR = 0x0804DA45
CHKSUM_PASSED_ADDR = 0x0804DA52

def hook_code(uc, address, size, user_data):
    if address == CHKSUM_CMP_ADDR:
        uc.reg_write(UC_X86_REG_EIP, CHKSUM_PASSED_ADDR)

2. 模拟malloc

def hook_code(uc, address, size, user_data):
    if address == MALLOC_ENTRY:
        size = struct.unpack("<I", uc.mem_read(uc.reg_read(UC_X86_REG_ESP) + 4, 4))[0]
        retval = unicorn_heap.malloc(size)
        uc.reg_write(UC_X86_REG_EAX, retval)
        uc.reg_write(UC_X86_REG_EIP, struct.unpack("<I", uc.mem_read(uc.reg_read(UC_X86_REG_ESP), 4))[0])

3. 跳过复杂函数

def hook_code(uc, address, size, user_data):
    if address == PRINTF_ENTRY:
        uc.reg_write(UC_X86_REG_EIP, struct.unpack("<I", uc.mem_read(uc.reg_read(UC_X86_REG_ESP), 4))[0])
        uc.reg_write(UC_X86_REG_ESP, uc.reg_read(UC_X86_REG_ESP) + 4)

3. 实战案例:FSK_Messaging_Service

3.1 目标分析

目标函数:cgc_receive_packet

void cgc_receive_packet(uint8_t *pData, uint8_t dataLen, uint16_t packetCRC) {
    // 校验checksum
    uint16_t check_checksum = cgc_simple_checksum16(pData, dataLen);
    if (packetCRC != check_checksum) return;
    
    // 处理packet
    for(int i=0; i<MAX_PACKET_HANDLERS; i++) {
        if (g_packetHandlers[i].type == pData[0]) {
            tSinglePacketData *pNewPacket = cgc_add_new_packet();
            cgc_memcpy(pNewPacket->buf, pData+1, dataLen-1);  // 潜在溢出点
            break;
        }
    }
}

3.2 漏洞挖掘步骤

  1. Dump进程上下文
gdb ./FSK_Messaging_Service
(gdb) b *0x804D106
(gdb) source unicorn_dumper_gdb.py
  1. 构建Harness
# 配置关键地址
START_ADDRESS = 0x0804D106
END_ADDRESS = 0x0804D120
CHKSUM_CMP_ADDR = 0x0804DA45
CHKSUM_PASSED_ADDR = 0x0804DA52
MALLOC_ENTRY = 0x08049C40
PRINTF_ENTRY = 0x0804AA60

# 输入约束
if len(input_content) > 0xFF:  # 限制输入大小
    return
  1. 运行Fuzzing
afl-fuzz -U -m none -i testcase/ -o fuzz_out/ -- python harness.py context_dir @@

3.3 漏洞分析

通过跟踪执行流发现cgc_memcpy未对dataLen进行校验,导致可以越界写入pNewPacket->buf(大小仅64字节)。构造特殊输入可触发堆溢出。

4. 最佳实践

  1. 输入约束:合理限制输入大小和格式
  2. 关键Hook:精确hook校验点和危险函数
  3. 执行跟踪:选择性记录关键指令执行信息
  4. 崩溃分析:结合原始程序调试崩溃样本
  5. 性能优化:减少不必要的hook和日志输出

5. 局限性

  1. 复杂环境模拟困难(如多线程、系统调用)
  2. 性能开销较大
  3. 需要准确dump目标执行上下文
  4. 崩溃路径分析复杂

6. 扩展学习

参考资源:

  • Unicorn官方文档:http://www.unicorn-engine.org/
  • AFL-Unicorn项目:https://github.com/Battelle/afl-unicorn
  • 高级Hook技巧:http://eternal.red/2018/unicorn-engine-tutorial/
  • 实战案例:https://hackernoon.com/afl-unicorn-part-2-fuzzing-the-unfuzzable-bea8de3540a5
AFL-Unicorn 模糊测试技术详解 1. Unicorn引擎基础 1.1 Unicorn简介 Unicorn是一个轻量级、多平台、多架构的CPU仿真框架,基于QEMU开发。它允许我们在不依赖真实硬件的情况下模拟执行二进制代码。 1.2 基本API使用 1.3 常见问题解决 问题1:BSS段未初始化 解决方案:跳过相关指令或手动初始化BSS段 问题2:系统调用/库函数缺失 解决方案:hook关键函数,模拟其行为 2. AFL-Unicorn集成 2.1 基本原理 AFL-Unicorn结合了AFL的模糊测试能力和Unicorn的仿真能力,工作流程: AFL生成变异输入 输入被写入仿真内存的固定地址 Harness从该地址读取数据并执行目标代码 出现崩溃时模拟崩溃行为通知AFL 2.2 典型Harness结构 2.3 关键Hook点 1. 绕过校验 2. 模拟malloc 3. 跳过复杂函数 3. 实战案例:FSK_ Messaging_ Service 3.1 目标分析 目标函数: cgc_receive_packet 3.2 漏洞挖掘步骤 Dump进程上下文 构建Harness 运行Fuzzing 3.3 漏洞分析 通过跟踪执行流发现 cgc_memcpy 未对 dataLen 进行校验,导致可以越界写入 pNewPacket->buf (大小仅64字节)。构造特殊输入可触发堆溢出。 4. 最佳实践 输入约束 :合理限制输入大小和格式 关键Hook :精确hook校验点和危险函数 执行跟踪 :选择性记录关键指令执行信息 崩溃分析 :结合原始程序调试崩溃样本 性能优化 :减少不必要的hook和日志输出 5. 局限性 复杂环境模拟困难(如多线程、系统调用) 性能开销较大 需要准确dump目标执行上下文 崩溃路径分析复杂 6. 扩展学习 参考资源: Unicorn官方文档:http://www.unicorn-engine.org/ AFL-Unicorn项目:https://github.com/Battelle/afl-unicorn 高级Hook技巧:http://eternal.red/2018/unicorn-engine-tutorial/ 实战案例:https://hackernoon.com/afl-unicorn-part-2-fuzzing-the-unfuzzable-bea8de3540a5