Fuzz之初见端倪

模糊测试又称为fuzzing,是一种软件测试技术。其核心概念为自动产生随机输入到一个程序中,并监视程序异常,如崩溃、断言失败,以发现可能的程序错误。

简单来说在该技术下存在一种称之为模糊器的东西通过测试不同的输入来判断程序的输出是否为关键信息以保存对该程序的有效输入。

fuzzing三组件:种子选择、突变、覆盖范围。

例:

main.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
char input[8] = {0};
read(0, input, 8);

if (input[0] == 'A') {
puts("AAA");
if(input[1] == 'B') {
puts("BBB");
if(input[2] == 'C') {
*((unsigned int *)0) = 0xdeadbeef; //bug
}
}
}
}

check.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import subprocess
import random

target = './test'
inps = ['A', 'B'] #语料库
count = 1

while True:
inp = inps[0]
inp += random.choince['A', 'B', 'C']) #简单的变异
del inps[0]
count += 1

try:
comp = subprocess.run([target], input=inp.encode(), capture_output=Truem check=True)
if comp.stdout != b'';
inps.append(inp) #我为你有输出而感到喜悦
except subprocess.CalledProcessError:
print(f"bug found with input: '{inp}'")
break

if count % 100 == 0 or len(inps) == 0: #避免变异效果不好
inps = ['A', 'B']

以上只是原理展示,我们当然不会这么原始人。

我们作为智人当然需要使用工具:

  • American Fuzzy Lop(AFL):AFL是一个高效的模糊测试工具
  • libFuzzer:libFuzzer是LLVM/Clang提供的一个模糊测试引擎,它可以轻松地集成到现有的代码中
  • Syzkaller:Syzkaller是一个专注于系统调用接口的模糊测试工具,它可以自动生成各种系统调用序列,并对内核进行测试以发现漏洞和错误。
  • OSS-Fuzz:OSS-Fuzz旨在通过自动化模糊测试发现开源软件中的安全漏洞和错误。

Fuzz方式
AFL有两种fuzz途径:

  • 开源软件:AFL软件进行编译的同时进行插桩,以方便fuzz
  • 闭源软件:配合QEMU直接对闭源的二进制代码进行fuzz

安装afl++:

1
sudo apt install afl++

插桩(instrumentation)

在保证原程序逻辑的完整性下,在程序中插入一些程序码来采集运行期间的执行状态。(手动调试这一块)

简单来说就是简单的修改一下源代码使其输出一些内容方便afl检查输入是否有效。

特点:

  • 插桩的对象通常都具有相同的属性或类别涉及所有的功能、所有的基本块,比较少针对单一目标。
  • 插桩的程序代码通常只有几行汇编代码,并且不会做太复杂的操作。
  • 在模糊器中,插桩被用来进行覆盖,那么记录多少程序码被执行到。

测试:

main.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <unistd.h>

int main() {
int a, idx;
char buf[100];

scanf("%d", &idx);
buf[idx] = '\0';

read(0, &a, 0x2);
if(a == 0xdead) *(int *)0 = 0xdeadbeef;
return 0;
}

在使用前需要启用选项:

1
export AFL_USE_ASAN=1

使用afl-gcc编程程序:

1
afl-gcc -fsanitize=address -o test main.c

实际上这条命令是对原版gcc的一个封装,添加了许多afl需要的参数。

其中有一种操作用到了afl-as,在编译main.c时执行了插桩。

afl-fuzz使用:

1
afl-fuzz -i seed-dir -o out-dir -m none ./test
  • i 存放测试用例的资料夹
  • o 搁置执行结果资料夹
  • f 从指定文件读取输入
  • t timeout,执行时间超过的话就会被kill掉
  • m 内存限制,执行时所能使用的内存体上限
  • d 跳过确定性,突变阶段跳过最初的处理
  • n 对没有插桩的目标进行模糊测试

其中seed-dir是输入种子,没有也无所谓,搜索过程中会自动创建,这和密码库一样,没有就是暴力破解。

out-dir放的是有效种子,也就是让程序有特殊反应的种子。

如果命令运行有报错最下面可以看到提示,这是正常的。

产物最终会在out-dir/default/crashes下。

像这样:

1
./test < out-dir/default/crashes/id:000000,sig:06,src:000000,time:63,execs:17,op:havoc,rep:1

将产物输入给程序就能得到详细的调试结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
=================================================================
==23936==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x77022df000bf at pc 0x5eb2e9eef73e bp 0x7fff21f1a690 sp 0x7fff21f1a680
WRITE of size 1 at 0x77022df000bf thread T0
#0 0x5eb2e9eef73d in main /media/sf_fuzz/main.c:9
#1 0x77022fe2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#2 0x77022fe2a28a in __libc_start_main_impl ../csu/libc-start.c:360
#3 0x5eb2e9eef854 in _start (/home/zlsf/fuzz/test+0x1854) (BuildId: 0e27121e088773996fbdd361c828ca743ab26090)

Address 0x77022df000bf is located in stack of thread T0 at offset 191 in frame
#0 0x5eb2e9eef2ff in main /media/sf_fuzz/main.c:4

This frame has 3 object(s):
[48, 52) 'a' (line 5)
[64, 68) 'idx' (line 5)
[80, 180) 'buf' (line 6) <== Memory access at offset 191 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /media/sf_fuzz/main.c:9 in main
Shadow bytes around the buggy address:
0x77022deffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x77022deffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x77022defff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x77022defff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x77022df00000: f1 f1 f1 f1 f1 f1 04 f2 04 f2 00 00 00 00 00 00
=>0x77022df00080: 00 00 00 00 00 00 04[f3]f3 f3 f3 f3 00 00 00 00
0x77022df00100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x77022df00180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x77022df00200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x77022df00280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x77022df00300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==23936==ABORTING

即使程序存在漏洞,也不一定会在执行到有漏洞的程式码时触发异常。

为了解决这种问题,我们有Sanitizer。

常见的Sanitizer有:

  • AddressSanitizer (+LeakSanitizer)
  • ThreadSanitizer
  • UndefinedBehaviorSanitizer
  • MemorySanitizer

额外的开销

  • CPU减速通常在2倍到5倍之间正常情况下,CPU速度减慢2倍至3倍。在某些极端情况下,他们的速度下降了5倍。
  • 内存开销2x-3x
  • AddressSanitizer使用比本机运行更多的实际内存。确切的开销取决于分配大小。分配越小,开销就越大。
  • AddressSanitizer使用更多的堆栈内存。我们看到增长高达3倍。

如果你需要一些比较好的种子可以在直接拉取:

1
wget http://lcamtuf.coredump.cx/afl/demo/afl_testcases.tgz

Fuzz之初见端倪
https://zlsf-zl.github.io/2026/03/30/Fuzz之初见端倪/
作者
ZLSF
发布于
2026年3月30日
许可协议