Compare commits

..

8 Commits

@ -1,18 +0,0 @@
{
"configurations": [
{
"name": "windows-gcc-x86",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "C:/Program Files/MinGW/bin/gcc.exe",
"cStandard": "${default}",
"cppStandard": "${default}",
"intelliSenseMode": "windows-gcc-x86",
"compilerArgs": [
""
]
}
],
"version": 4
}

@ -13,89 +13,79 @@
# http://www.apache.org/licenses/LICENSE-2.0
#
# 定义程序名称和版本
PROGNAME = afl
VERSION = $(shell grep '^\#define VERSION ' config.h | cut -d '"' -f2)
# 安装路径
PREFIX ?= /usr/local
BIN_PATH = $(PREFIX)/bin
HELPER_PATH = $(PREFIX)/lib/afl
DOC_PATH = $(PREFIX)/share/doc/afl
MISC_PATH = $(PREFIX)/share/afl
# 程序和脚本的定义
# PROGS intentionally omit afl-as, which gets installed elsewhere.
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
SH_PROGS = afl-plot afl-cmin afl-whatsup
# 编译标志
CFLAGS ?= -O3 -funroll-loops
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
-DAFL_PATH=\"$(HELPER_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" \
-DBIN_PATH=\"$(BIN_PATH)\"
# Linux 平台的链接标志
ifneq "$(filter Linux GNU%,$(shell uname))" ""
LDFLAGS += -ldl
endif
# 判断是否使用 Clang 编译器
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
TEST_CC = afl-gcc
else
TEST_CC = afl-clang
endif
# 公共头文件
COMM_HDR = alloc-inl.h config.h debug.h types.h
# 默认目标:编译和测试程序
all: test_x86 $(PROGS) afl-as test_build all_done
# 测试 x86 编译能力
ifndef AFL_NO_X86
test_x86:
@echo "[*] Checking for the ability to compile x86 code..."
@echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) -w -x c - -o .test || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 )
@rm -f .test
@echo "[+] Everything seems to be working, ready to compile."
else
test_x86:
@echo "[!] Note: skipping x86 compilation checks (AFL_NO_X86 set)."
endif
# 编译 afl-gcc
afl-gcc: afl-gcc.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done
# 编译 afl-as
afl-as: afl-as.c afl-as.h $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
ln -sf afl-as as
# 编译 afl-fuzz
afl-fuzz: afl-fuzz.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
# 编译 afl-showmap
afl-showmap: afl-showmap.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
# 编译 afl-tmin
afl-tmin: afl-tmin.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
# 编译 afl-analyze
afl-analyze: afl-analyze.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
# 编译 afl-gotcpu
afl-gotcpu: afl-gotcpu.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
# 测试构建:检查插桩功能
ifndef AFL_NO_X86
test_build: afl-gcc afl-as afl-showmap
@echo "[*] Testing the CC wrapper and instrumentation output..."
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS)
@ -104,20 +94,22 @@ test_build: afl-gcc afl-as afl-showmap
@rm -f test-instr
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please ping <lcamtuf@google.com> to troubleshoot the issue."; echo; exit 1; fi
@echo "[+] All right, the instrumentation seems to be working!"
else
test_build: afl-gcc afl-as afl-showmap
@echo "[!] Note: skipping build tests (you may need to use LLVM or QEMU mode)."
endif
# 完成构建
all_done: test_build
@if [ ! "`which clang 2>/dev/null`" = "" ]; then echo "[+] LLVM users: see llvm_mode/README.llvm for a faster alternative to afl-gcc."; fi
@echo "[+] All done! Be sure to review README - it's pretty short and useful."
@if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi
@! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.txt for advice.\033[0m\n" 2>/dev/null
# 清理构建生成的文件
.NOTPARALLEL: clean
clean:
rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test test-instr .test-instr0 .test-instr1 qemu_mode/qemu-2.10.0.tar.bz2 afl-qemu-trace
rm -rf out_dir qemu_mode/qemu-2.10.0
@ -125,7 +117,6 @@ clean:
$(MAKE) -C libdislocator clean
$(MAKE) -C libtokencap clean
# 安装程序
install: all
mkdir -p -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh
@ -137,4 +128,26 @@ ifndef AFL_TRACE_PC
else
if [ -f afl-clang-fast -a -f afl-llvm-rt.o ]; then set -e; install -m 755 afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
endif
@echo "[+] All done!"
if [ -f afl-llvm-rt-32.o ]; then set -e; install -m 755 afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi
if [ -f afl-llvm-rt-64.o ]; then set -e; install -m 755 afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/$$i; done
install -m 755 afl-as $${DESTDIR}$(HELPER_PATH)
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
install -m 644 README.md docs/ChangeLog docs/*.txt $${DESTDIR}$(DOC_PATH)
cp -r testcases/ $${DESTDIR}$(MISC_PATH)
cp -r dictionaries/ $${DESTDIR}$(MISC_PATH)
publish: clean
test "`basename $$PWD`" = "AFL" || exit 1
test -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz; if [ "$$?" = "0" ]; then echo; echo "Change program version in config.h, mmkay?"; echo; exit 1; fi
cd ..; rm -rf $(PROGNAME)-$(VERSION); cp -pr $(PROGNAME) $(PROGNAME)-$(VERSION); \
tar -cvz -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz $(PROGNAME)-$(VERSION)
chmod 644 ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz
( cd ~/www/afl/releases/; ln -s -f $(PROGNAME)-$(VERSION).tgz $(PROGNAME)-latest.tgz )
cat docs/README >~/www/afl/README.txt
cat docs/status_screen.txt >~/www/afl/status_screen.txt
cat docs/historical_notes.txt >~/www/afl/historical_notes.txt
cat docs/technical_details.txt >~/www/afl/technical_details.txt
cat docs/ChangeLog >~/www/afl/ChangeLog.txt
cat docs/QuickStartGuide.txt >~/www/afl/QuickStartGuide.txt
echo -n "$(VERSION)" >~/www/afl/version.txt

@ -1,56 +1,56 @@
# american fuzzy lop
[![Build Status](https://travis-ci.org/google/AFL.svg?branch=master)](https://travis-ci.org/google/AFL) # code
[![Build Status](https://travis-ci.org/google/AFL.svg?branch=master)](https://travis-ci.org/google/AFL)
Originally developed by Michal Zalewski <lcamtuf@google.com>.
See [QuickStartGuide.txt](docs/QuickStartGuide.txt) if you don't have time to read
this file.
Originally developed by Michal Zalewski <lcamtuf@google.com>. # code
``````
See [QuickStartGuide.txt](docs/QuickStartGuide.txt) if you don't have time to read # code
this file. ``````
# code
## 1) Challenges of guided fuzzing
Fuzzing is one of the most powerful and proven strategies for identifying
security issues in real-world software; it is responsible for the vast
majority of remote code execution and privilege escalation bugs found to date # code
majority of remote code execution and privilege escalation bugs found to date
in security-critical software.
Unfortunately, fuzzing is also relatively shallow; blind, random mutations # code
Unfortunately, fuzzing is also relatively shallow; blind, random mutations
make it very unlikely to reach certain code paths in the tested code, leaving
some vulnerabilities firmly outside the reach of this technique.
# code
There have been numerous attempts to solve this problem. One of the early
approaches - pioneered by Tavis Ormandy - is corpus distillation. The method
relies on coverage signals to select a subset of interesting seeds from a ``````
relies on coverage signals to select a subset of interesting seeds from a
massive, high-quality corpus of candidate files, and then fuzz them by
traditional means. The approach works exceptionally well, but requires such
a corpus to be readily available. In addition, block coverage measurements
provide only a very simplistic understanding of program state, and are less
useful for guiding the fuzzing effort in the long haul. # code
# code
useful for guiding the fuzzing effort in the long haul.
Other, more sophisticated research has focused on techniques such as program
flow analysis ("concolic execution"), symbolic execution, or static analysis.
All these methods are extremely promising in experimental settings, but tend
to suffer from reliability and performance problems in practical uses - and
currently do not offer a viable alternative to "dumb" fuzzing techniques.
## 2) The afl-fuzz approach # code
## 2) The afl-fuzz approach
American Fuzzy Lop is a brute-force fuzzer coupled with an exceedingly simple
but rock-solid instrumentation-guided genetic algorithm. It uses a modified
form of edge coverage to effortlessly pick up subtle, local-scale changes to
program control flow. ``````
# code
program control flow.
Simplifying a bit, the overall algorithm can be summed up as:
1) Load user-supplied initial test cases into the queue,
2) Take next input file from the queue,
3) Attempt to trim the test case to the smallest size that doesn't alter # code
3) Attempt to trim the test case to the smallest size that doesn't alter
the measured behavior of the program,
4) Repeatedly mutate the file using a balanced and well-researched variety
of traditional fuzzing strategies, # code
of traditional fuzzing strategies,
5) If any of the generated mutations resulted in a new state transition
recorded by the instrumentation, add mutated output as a new entry in the
@ -58,9 +58,9 @@ Simplifying a bit, the overall algorithm can be summed up as:
6) Go to 2.
The discovered test cases are also periodically culled to eliminate ones that ``````
The discovered test cases are also periodically culled to eliminate ones that
have been obsoleted by newer, higher-coverage finds; and undergo several other
instrumentation-driven effort minimization steps. # code
instrumentation-driven effort minimization steps.
As a side result of the fuzzing process, the tool creates a small,
self-contained corpus of interesting test cases. These are extremely useful
@ -69,10 +69,10 @@ for stress-testing browsers, office applications, graphics suites, or
closed-source tools.
The fuzzer is thoroughly tested to deliver out-of-the-box performance far
superior to blind fuzzing or coverage-only tools. # code
superior to blind fuzzing or coverage-only tools.
## 3) Instrumenting programs for use with AFL
# code
When source code is available, instrumentation can be injected by a companion
tool that works as a drop-in replacement for gcc or clang in any standard build
process for third-party code.
@ -89,28 +89,28 @@ $ CC=/path/to/afl/afl-gcc ./configure
$ make clean all
```
For C++ programs, you'd would also want to set `CXX=/path/to/afl/afl-g++`. # code
For C++ programs, you'd would also want to set `CXX=/path/to/afl/afl-g++`.
The clang wrappers (afl-clang and afl-clang++) can be used in the same way;
clang users may also opt to leverage a higher-performance instrumentation mode, ``````
clang users may also opt to leverage a higher-performance instrumentation mode,
as described in llvm_mode/README.llvm.
``````
When testing libraries, you need to find or write a simple program that reads
data from stdin or from a file and passes it to the tested library. In such a # code
data from stdin or from a file and passes it to the tested library. In such a
case, it is essential to link this executable against a static version of the
instrumented library, or to make sure that the correct .so file is loaded at
runtime (usually by setting `LD_LIBRARY_PATH`). The simplest option is a static # code
runtime (usually by setting `LD_LIBRARY_PATH`). The simplest option is a static
build, usually possible via:
# code
```shell
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
```
Setting `AFL_HARDEN=1` when calling 'make' will cause the CC wrapper to
automatically enable code hardening options that make it easier to detect
simple memory bugs. Libdislocator, a helper library included with AFL (see # code
libdislocator/README.dislocator) can help uncover heap corruption issues, too. # code
# code
simple memory bugs. Libdislocator, a helper library included with AFL (see
libdislocator/README.dislocator) can help uncover heap corruption issues, too.
PS. ASAN users are advised to review [notes_for_asan.txt](docs/notes_for_asan.txt) file for important
caveats.
@ -118,36 +118,36 @@ caveats.
When source code is *NOT* available, the fuzzer offers experimental support for
fast, on-the-fly instrumentation of black-box binaries. This is accomplished
with a version of QEMU running in the lesser-known "user space emulation" mode. # code
with a version of QEMU running in the lesser-known "user space emulation" mode.
QEMU is a project separate from AFL, but you can conveniently build the
feature by doing: ``````
# code
feature by doing:
```shell
$ cd qemu_mode
$ ./build_qemu_support.sh
```
# code
For additional instructions and caveats, see qemu_mode/README.qemu.
The mode is approximately 2-5x slower than compile-time instrumentation, is
less conducive to parallelization, and may have some other quirks. # code
less conducive to parallelization, and may have some other quirks.
## 5) Choosing initial test cases ``````
## 5) Choosing initial test cases
To operate correctly, the fuzzer requires one or more starting file that
contains a good example of the input data normally expected by the targeted # code
contains a good example of the input data normally expected by the targeted
application. There are two basic rules:
- Keep the files small. Under 1 kB is ideal, although not strictly necessary. # code
- Keep the files small. Under 1 kB is ideal, although not strictly necessary.
For a discussion of why size matters, see [perf_tips.txt](docs/perf_tips.txt).
- Use multiple test cases only if they are functionally different from
each other. There is no point in using fifty different vacation photos
to fuzz an image library.
You can find many good examples of starting files in the testcases/ subdirectory # code
that comes with this tool. ``````
You can find many good examples of starting files in the testcases/ subdirectory
that comes with this tool.
PS. If a large corpus of data is available for screening, you may want to use
the afl-cmin utility to identify a subset of functionally distinct files that
@ -155,82 +155,82 @@ exercise different code paths in the target binary.
## 6) Fuzzing binaries
The fuzzing process itself is carried out by the afl-fuzz utility. This program ``````
requires a read-only directory with initial test cases, a separate place to # code
store its findings, plus a path to the binary to test. # code
The fuzzing process itself is carried out by the afl-fuzz utility. This program
requires a read-only directory with initial test cases, a separate place to
store its findings, plus a path to the binary to test.
For target binaries that accept input directly from stdin, the usual syntax is:
For target binaries that accept input directly from stdin, the usual syntax is: ``````
``````
```shell
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
``` ``````
# code
```
For programs that take input from a file, use '@@' to mark the location in
the target's command line where the input file name should be placed. The
fuzzer will substitute this for you:
```shell # code
```shell
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
```
You can also use the -f option to have the mutated data written to a specific # code
file. This is useful if the program expects a particular file extension or so. ``````
You can also use the -f option to have the mutated data written to a specific
file. This is useful if the program expects a particular file extension or so.
Non-instrumented binaries can be fuzzed in the QEMU mode (add -Q in the command
line) or in a traditional, blind-fuzzer mode (specify -n).
You can use -t and -m to override the default timeout and memory limit for the
executed process; rare examples of targets that may need these settings touched
include compilers and video decoders. # code
include compilers and video decoders.
Tips for optimizing fuzzing performance are discussed in [perf_tips.txt](docs/perf_tips.txt).
Note that afl-fuzz starts by performing an array of deterministic fuzzing ``````
Note that afl-fuzz starts by performing an array of deterministic fuzzing
steps, which can take several days, but tend to produce neat test cases. If you
want quick & dirty results right away - akin to zzuf and other traditional
fuzzers - add the -d option to the command line. # code
``````
fuzzers - add the -d option to the command line.
## 7) Interpreting output
See the [status_screen.txt](docs/status_screen.txt) file for information on
how to interpret the displayed stats and monitor the health of the process.
Be sure to consult this file especially if any UI elements are highlighted in
red.
# code
The fuzzing process will continue until you press Ctrl-C. At minimum, you want
to allow the fuzzer to complete one queue cycle, which may take anywhere from a
couple of hours to a week or so.
# code
There are three subdirectories created within the output directory and updated # code
There are three subdirectories created within the output directory and updated
in real time:
- queue/ - test cases for every distinctive execution path, plus all the
starting files given by the user. This is the synthesized corpus ``````
starting files given by the user. This is the synthesized corpus
mentioned in section 2.
Before using this corpus for any other purposes, you can shrink # code
Before using this corpus for any other purposes, you can shrink
it to a smaller size using the afl-cmin tool. The tool will find
a smaller subset of files offering equivalent edge coverage.
- crashes/ - unique test cases that cause the tested program to receive a
fatal signal (e.g., SIGSEGV, SIGILL, SIGABRT). The entries are # code
fatal signal (e.g., SIGSEGV, SIGILL, SIGABRT). The entries are
grouped by the received signal.
- hangs/ - unique test cases that cause the tested program to time out. The
default time limit before something is classified as a hang is
the larger of 1 second and the value of the -t parameter. # code
the larger of 1 second and the value of the -t parameter.
The value can be fine-tuned by setting AFL_HANG_TMOUT, but this
is rarely necessary. # code
is rarely necessary.
Crashes and hangs are considered "unique" if the associated execution paths
involve any state transitions not seen in previously-recorded faults. If a
single bug can be reached in multiple ways, there will be some count inflation
early in the process, but this should quickly taper off. # code
early in the process, but this should quickly taper off.
The file names for crashes and hangs are correlated with parent, non-faulting
queue entries. This should help with debugging.
When you can't reproduce a crash found by afl-fuzz, the most likely cause is # code
that you are not setting the same memory limit as used by the tool. Try: # code
When you can't reproduce a crash found by afl-fuzz, the most likely cause is
that you are not setting the same memory limit as used by the tool. Try:
```shell
$ LIMIT_MB=50
@ -243,28 +243,28 @@ also change -Sv to -Sd.
Any existing output directory can be also used to resume aborted jobs; try:
```shell
$ ./afl-fuzz -i- -o existing_output_dir [...etc...] # code
``` # code
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
```
If you have gnuplot installed, you can also generate some pretty graphs for any # code
If you have gnuplot installed, you can also generate some pretty graphs for any
active fuzzing task using afl-plot. For an example of how this looks like,
see [http://lcamtuf.coredump.cx/afl/plot/](http://lcamtuf.coredump.cx/afl/plot/).
## 8) Parallelized fuzzing
Every instance of afl-fuzz takes up roughly one core. This means that on
multi-core systems, parallelization is necessary to fully utilize the hardware. # code
multi-core systems, parallelization is necessary to fully utilize the hardware.
For tips on how to fuzz a common target on multiple cores or multiple networked
machines, please refer to [parallel_fuzzing.txt](docs/parallel_fuzzing.txt).
The parallel fuzzing mode also offers a simple way for interfacing AFL to other
fuzzers, to symbolic or concolic execution engines, and so forth; again, see the # code
fuzzers, to symbolic or concolic execution engines, and so forth; again, see the
last section of [parallel_fuzzing.txt](docs/parallel_fuzzing.txt) for tips.
## 9) Fuzzer dictionaries
By default, afl-fuzz mutation engine is optimized for compact data formats -
say, images, multimedia, compressed data, regular expression syntax, or shell # code
say, images, multimedia, compressed data, regular expression syntax, or shell
scripts. It is somewhat less suited for languages with particularly verbose and
redundant verbiage - notably including HTML, SQL, or JavaScript.
@ -277,48 +277,48 @@ magic headers, or other special tokens associated with the targeted data type
To use this feature, you first need to create a dictionary in one of the two
formats discussed in dictionaries/README.dictionaries; and then point the fuzzer
to it via the -x option in the command line. # code
``````
to it via the -x option in the command line.
(Several common dictionaries are already provided in that subdirectory, too.)
There is no way to provide more structured descriptions of the underlying
syntax, but the fuzzer will likely figure out some of this based on the
instrumentation feedback alone. This actually works in practice, say:
[http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html](http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html) ``````
``````
PS. Even when no explicit dictionary is given, afl-fuzz will try to extract ``````
[http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html](http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html)
PS. Even when no explicit dictionary is given, afl-fuzz will try to extract
existing syntax tokens in the input corpus by watching the instrumentation
very closely during deterministic byte flips. This works for some types of
parsers and grammars, but isn't nearly as good as the -x mode.
If a dictionary is really hard to come by, another option is to let AFL run
for a while, and then use the token capture library that comes as a companion
utility with AFL. For that, see libtokencap/README.tokencap. # code
# code
utility with AFL. For that, see libtokencap/README.tokencap.
## 10) Crash triage
The coverage-based grouping of crashes usually produces a small data set that
can be quickly triaged manually or with a very simple GDB or Valgrind script.
Every crash is also traceable to its parent non-crashing test case in the
queue, making it easier to diagnose faults. # code
queue, making it easier to diagnose faults.
Having said that, it's important to acknowledge that some fuzzing crashes can be # code
difficult to quickly evaluate for exploitability without a lot of debugging and # code
Having said that, it's important to acknowledge that some fuzzing crashes can be
difficult to quickly evaluate for exploitability without a lot of debugging and
code analysis work. To assist with this task, afl-fuzz supports a very unique
"crash exploration" mode enabled with the -C flag. # code
# code
"crash exploration" mode enabled with the -C flag.
In this mode, the fuzzer takes one or more crashing test cases as the input,
and uses its feedback-driven fuzzing strategies to very quickly enumerate all
code paths that can be reached in the program while keeping it in the
crashing state. ``````
crashing state.
Mutations that do not result in a crash are rejected; so are any changes that
do not affect the execution path. ``````
do not affect the execution path.
The output is a small corpus of files that can be very rapidly examined to see # code
what degree of control the attacker has over the faulting address, or whether # code
it is possible to get past an initial out-of-bounds read - and see what lies # code
The output is a small corpus of files that can be very rapidly examined to see
what degree of control the attacker has over the faulting address, or whether
it is possible to get past an initial out-of-bounds read - and see what lies
beneath.
Oh, one more thing: for test case minimization, give afl-tmin a try. The tool
@ -329,19 +329,19 @@ $ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
```
The tool works with crashing and non-crashing test cases alike. In the crash
mode, it will happily accept instrumented and non-instrumented binaries. In the ``````
mode, it will happily accept instrumented and non-instrumented binaries. In the
non-crashing mode, the minimizer relies on standard AFL instrumentation to make
the file simpler without altering the execution path. # code
the file simpler without altering the execution path.
The minimizer accepts the -m, -t, -f and @@ syntax in a manner compatible with
afl-fuzz.
# code
Another recent addition to AFL is the afl-analyze tool. It takes an input
file, attempts to sequentially flip bytes, and observes the behavior of the # code
file, attempts to sequentially flip bytes, and observes the behavior of the
tested program. It then color-codes the input based on which sections appear to
be critical, and which are not; while not bulletproof, it can often offer quick
insights into complex file formats. More info about its operation can be found
near the end of [technical_details.txt](docs/technical_details.txt). # code
near the end of [technical_details.txt](docs/technical_details.txt).
## 11) Going beyond crashes
@ -352,21 +352,21 @@ found by modifying the target programs to call abort() when, say:
- Two bignum libraries produce different outputs when given the same
fuzzer-generated input,
- An image library produces different outputs when asked to decode the same # code
- An image library produces different outputs when asked to decode the same
input image several times in a row,
- A serialization / deserialization library fails to produce stable outputs ``````
- A serialization / deserialization library fails to produce stable outputs
when iteratively serializing and deserializing fuzzer-supplied data,
- A compression library produces an output inconsistent with the input file
when asked to compress and then decompress a particular blob.
Implementing these or similar sanity checks usually takes very little time;
if you are the maintainer of a particular package, you can make this code # code
if you are the maintainer of a particular package, you can make this code
conditional with `#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` (a flag also
shared with libfuzzer) or `#ifdef __AFL_COMPILER` (this one is just for AFL).
## 12) Common-sense risks ``````
## 12) Common-sense risks
Please keep in mind that, similarly to many other computationally-intensive
tasks, fuzzing may put strain on your hardware and on the OS. In particular:
@ -379,19 +379,19 @@ tasks, fuzzing may put strain on your hardware and on the OS. In particular:
- Targeted programs may end up erratically grabbing gigabytes of memory or
filling up disk space with junk files. AFL tries to enforce basic memory
limits, but can't prevent each and every possible mishap. The bottom line # code
is that you shouldn't be fuzzing on systems where the prospect of data loss # code
is not an acceptable risk. # code
limits, but can't prevent each and every possible mishap. The bottom line
is that you shouldn't be fuzzing on systems where the prospect of data loss
is not an acceptable risk.
- Fuzzing involves billions of reads and writes to the filesystem. On modern
systems, this will be usually heavily cached, resulting in fairly modest # code
"physical" I/O - but there are many factors that may alter this equation. # code
systems, this will be usually heavily cached, resulting in fairly modest
"physical" I/O - but there are many factors that may alter this equation.
It is your responsibility to monitor for potential trouble; with very heavy
I/O, the lifespan of many HDDs and SSDs may be reduced.
A good way to monitor disk I/O on Linux is the 'iostat' command: # code
A good way to monitor disk I/O on Linux is the 'iostat' command:
```shell # code
```shell
$ iostat -d 3 -x -k [...optional disk ID...]
```
@ -403,34 +403,34 @@ Here are some of the most important caveats for AFL:
a signal (SIGSEGV, SIGABRT, etc). Programs that install custom handlers for
these signals may need to have the relevant code commented out. In the same
vein, faults in child processed spawned by the fuzzed target may evade
detection unless you manually add some code to catch that. # code
detection unless you manually add some code to catch that.
- As with any other brute-force tool, the fuzzer offers limited coverage if
encryption, checksums, cryptographic signatures, or compression are used to
wholly wrap the actual data format to be tested.
To work around this, you can comment out the relevant checks (see # code
To work around this, you can comment out the relevant checks (see
experimental/libpng_no_checksum/ for inspiration); if this is not possible,
you can also write a postprocessor, as explained in # code
you can also write a postprocessor, as explained in
experimental/post_library/.
- There are some unfortunate trade-offs with ASAN and 64-bit binaries. This
isn't due to any specific fault of afl-fuzz; see [notes_for_asan.txt](docs/notes_for_asan.txt)
for tips. # code
for tips.
- There is no direct support for fuzzing network services, background ``````
- There is no direct support for fuzzing network services, background
daemons, or interactive apps that require UI interaction to work. You may
need to make simple code changes to make them behave in a more traditional
way. Preeny may offer a relatively simple option, too - see: ``````
way. Preeny may offer a relatively simple option, too - see:
https://github.com/zardus/preeny
Some useful tips for modifying network-based services can be also found at: # code
Some useful tips for modifying network-based services can be also found at:
https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop
- AFL doesn't output human-readable coverage data. If you want to monitor
coverage, use afl-cov from Michael Rash: https://github.com/mrash/afl-cov
# code
- Occasionally, sentient machines rise against their creators. If this ``````
- Occasionally, sentient machines rise against their creators. If this
happens to you, please consult http://lcamtuf.coredump.cx/prep/.
Beyond this, see INSTALL for platform-specific tips.
@ -438,56 +438,56 @@ Beyond this, see INSTALL for platform-specific tips.
## 14) Special thanks
Many of the improvements to afl-fuzz wouldn't be possible without feedback,
bug reports, or patches from: # code
bug reports, or patches from:
```
Jann Horn Hanno Boeck
Felix Groebert Jakub Wilk # code
Felix Groebert Jakub Wilk
Richard W. M. Jones Alexander Cherepanov
Tom Ritter Hovik Manucharyan
Sebastian Roschke Eberhard Mattes ``````
Padraig Brady Ben Laurie # code
@dronesec Luca Barbato # code
Sebastian Roschke Eberhard Mattes
Padraig Brady Ben Laurie
@dronesec Luca Barbato
Tobias Ospelt Thomas Jarosch
Martin Carpenter Mudge Zatko
Joe Zbiciak Ryan Govostes
Michael Rash William Robinet # code
Michael Rash William Robinet
Jonathan Gray Filipe Cabecinhas
Nico Weber Jodie Cunningham
Andrew Griffiths Parker Thompson
Jonathan Neuschfer Tyler Nighswander
Ben Nagy Samir Aguiar ``````
Aidan Thornton Aleksandar Nikolich # code
Ben Nagy Samir Aguiar
Aidan Thornton Aleksandar Nikolich
Sam Hakim Laszlo Szekeres
David A. Wheeler Turo Lamminen
Andreas Stieger Richard Godbee
Louis Dassy teor2345 ``````
Louis Dassy teor2345
Alex Moneger Dmitry Vyukov
Keegan McAllister Kostya Serebryany # code
Keegan McAllister Kostya Serebryany
Richo Healey Martijn Bogaard
rc0r Jonathan Foote
Christian Holler Dominique Pelle
Jacek Wielemborek Leo Barnes # code
Jacek Wielemborek Leo Barnes
Jeremy Barnes Jeff Trull
Guillaume Endignoux ilovezfs
Daniel Godas-Lopez Franjo Ivancic # code
Austin Seipp Daniel Komaromy # code
Daniel Godas-Lopez Franjo Ivancic
Austin Seipp Daniel Komaromy
Daniel Binderman Jonathan Metzman
Vegard Nossum Jan Kneschke
Kurt Roeckx Marcel Bohme
Van-Thuan Pham Abhik Roychoudhury
Joshua J. Drake Toby Hutton ``````
Rene Freingruber Sergey Davidoff ``````
Joshua J. Drake Toby Hutton
Rene Freingruber Sergey Davidoff
Sami Liedes Craig Young
Andrzej Jackowski Daniel Hodson
```
Thank you! ``````
Thank you!
## 15) Contact
# code
Questions? Concerns? Bug reports? Please use GitHub. ``````
Questions? Concerns? Bug reports? Please use GitHub.
There is also a mailing list for the project; to join, send a mail to
<afl-users+subscribe@googlegroups.com>. Or, if you prefer to browse ``````
<afl-users+subscribe@googlegroups.com>. Or, if you prefer to browse
archives first, try: [https://groups.google.com/group/afl-users](https://groups.google.com/group/afl-users).

@ -14,8 +14,6 @@
limitations under the License.
*/
// 这部分是版权声明和许可协议,声明该文件受到 Apache 2.0 许可证保护。
/*
american fuzzy lop - wrapper for GNU as
---------------------------------------
@ -38,10 +36,6 @@
*/
// 该段注释解释了代码的功能
// 它是一个用于 GNU as 汇编器的包装器
// 目的是预处理由 GCC/Clang 生成的汇编文件并注入必要的仪器代码
#define AFL_MAIN
#include "config.h"
@ -62,43 +56,20 @@
#include <sys/wait.h>
#include <sys/time.h>
// 定义宏 AFL_MAIN通常用于标识主程序或相关模块。
// 包含内部的头文件,这些文件提供了项目的配置、类型定义、调试功能以及内存分配等支持。
// 包含标准的C库和POSIX库头文件用于文件操作、内存管理、时间处理等。
static u8** as_params; /* Parameters passed to the real 'as' */
static u8* input_file; /* Originally specified input file */
static u8* modified_file; /* Instrumented file for the real 'as' */
//声明静态变量:
// as_params保存传递给实际 as 汇编器的参数。
// input_file保存输入的文件路径。
// modified_file保存经过修改的文件路径即注入了仪器代码后的文件
static u8 be_quiet, /* Quiet mode (no stderr output) */
clang_mode, /* Running in clang mode? */
pass_thru, /* Just pass data through? */
just_version, /* Just show version? */
sanitizer; /* Using ASAN / MSAN */
// 声明其他控制程序行为的静态变量:
// be_quiet控制是否启用静默模式不输出标准错误。
// clang_mode是否处于 clang 模式。
// pass_thru是否跳过修改直接传递数据。
// just_version是否只显示版本信息。
// sanitizer是否启用了地址或内存错误检测工具如 ASAN 或 MSAN
static u32 inst_ratio = 100, /* Instrumentation probability (%) */
as_par_cnt = 1; /* Number of params to 'as' */
// 声明静态变量:
// inst_ratio仪器插入的概率百分比
// as_par_cnt传递给汇编器 as 的参数数量。
/* If we don't find --32 or --64 in the command line, default to
instrumentation for whichever mode we were compiled with. This is not
perfect, but should do the trick for almost all use cases. */
@ -117,32 +88,20 @@ static u8 use_64bit = 0;
#endif /* ^WORD_SIZE_64 */
// 根据编译时的定义判断是否使用 64 位模式。
// 如果没有定义 WORD_SIZE_64则默认使用 32 位模式。
// 如果是苹果平台,还会抛出一个错误,表明不支持 32 位 Apple 平台。
/* Examine and modify parameters to pass to 'as'. Note that the file name
is always the last parameter passed by GCC, so we exploit this property
to keep the code simple. */
// 这段注释解释了接下来要做的工作:分析和修改传递给汇编器 as 的参数,特别是文件名,它总是作为最后一个参数传递给 GCC。
static void edit_params(int argc, char** argv) {
// 定义函数 edit_params用于修改传递给 as 汇编器的参数。
u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS");
u32 i;
// 获取环境变量 TMPDIR 和 AFL_AS并声明变量 i。
#ifdef __APPLE__
u8 use_clang_as = 0;
// 如果是在 Apple 平台上,声明一个变量 use_clang_as 来标识是否使用 clang 作为汇编器。
/* On MacOS X, the Xcode cctool 'as' driver is a bit stale and does not work
with the code generated by newer versions of clang that are hand-built
by the user. See the thread here: http://goo.gl/HBWDtn.
@ -163,8 +122,6 @@ static void edit_params(int argc, char** argv) {
if (!afl_as) afl_as = getenv("AFL_CXX");
if (!afl_as) afl_as = "clang";
// 如果在 clang 模式下且没有指定 AFL_AS则尝试使用 clang 作为汇编器。
}
#endif /* __APPLE__ */
@ -177,17 +134,10 @@ static void edit_params(int argc, char** argv) {
if (!tmp_dir) tmp_dir = getenv("TMP");
if (!tmp_dir) tmp_dir = "/tmp";
//如果没有设置 TMPDIR则尝试使用其他环境变量如 TEMP 和 TMP如果都没有设置则默认使用 /tmp。
as_params = ck_alloc((argc + 32) * sizeof(u8*));
// 为汇编器参数分配内存,留出额外的空间。
as_params[0] = afl_as ? afl_as : (u8*)"as";
// 设置 as 汇编器命令,如果没有设置 AFL_AS则默认使用 "as"。
as_params[argc] = 0;
for (i = 1; i < argc - 1; i++) {
@ -219,17 +169,11 @@ static void edit_params(int argc, char** argv) {
}
// 遍历所有传递给程序的参数,如果参数为 --64 或 --32则根据平台设置 use_64bit
// 对于 Apple 平台,还会检查架构并设置为 64 位。
#ifdef __APPLE__
/* When calling clang as the upstream assembler, append -c -x assembler
and hope for the best. */
// 这段注释说明接下来的操作是调用真正的 as 汇编器,并传递适当的参数。
// 如果一切顺利as 会返回一个 0 的退出代码,程序会传播这个退出代码。
if (use_clang_as) {
as_params[as_par_cnt++] = "-c";
@ -238,23 +182,16 @@ static void edit_params(int argc, char** argv) {
}
// 在 exec_real_as 函数中:
// 如果处于静默模式be_quiet则关闭标准错误输出并将其重定向到 /dev/null避免打印调试信息。
// 使用 execvp 执行汇编器命令,传递修改后的参数。
// 如果执行失败,则打印错误信息并退出程序。
#endif /* __APPLE__ */
input_file = argv[argc - 1];
input_file = argv[argc - 1];
if (input_file[0] == '-') {
if (!strcmp(input_file + 1, "-version")) {
just_version = 1;
modified_file = input_file;
goto wrap_things_up; // 跳到 wrap_things_up 标签,处理版本信息
goto wrap_things_up;
}
if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)");
@ -262,118 +199,122 @@ static void edit_params(int argc, char** argv) {
} else {
/* 检查输入文件路径是否看起来像是编译程序的一部分,而不是使用 gcc 编译一个临时的 .s 文件 */
/* 这段代码是为了绕过某些特殊情况,比如编译 NSS 库时的情况 */
/* Check if this looks like a standard invocation as a part of an attempt
to compile a program, rather than using gcc on an ad-hoc .s file in
a format we may not understand. This works around an issue compiling
NSS. */
if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
strncmp(input_file, "/var/tmp/", 9) &&
strncmp(input_file, "/tmp/", 5)) pass_thru = 1; // 启用通过模式
strncmp(input_file, "/tmp/", 5)) pass_thru = 1;
}
modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),
(u32)time(NULL)); // 为生成的文件名分配内存并格式化
(u32)time(NULL));
wrap_things_up:
as_params[as_par_cnt++] = modified_file; // 将修改后的文件名添加到参数列表
as_params[as_par_cnt] = NULL; // 参数列表以 NULL 结尾
as_params[as_par_cnt++] = modified_file;
as_params[as_par_cnt] = NULL;
}
// 此段代码注释说明了程序如何检查和处理输入文件,包括:
// 如果文件名以 - 开头,可能是传递版本信息或者其他特殊的命令行参数。
// 如果文件路径看起来是临时目录之外的路径则启用“通过模式”pass_thru这表示不进行修改直接传递给汇编器。
/* Process input file, generate modified_file. Insert instrumentation in all
the appropriate places. */
/* 处理输入文件,生成修改后的文件,并在所有适当的位置插入仪器代码 */
static void add_instrumentation(void) {
static u8 line[MAX_LINE]; // 用于存储每一行的内容
static u8 line[MAX_LINE];
FILE* inf;
FILE* outf;
s32 outfd;
u32 ins_lines = 0; // 记录插入了多少行
u32 ins_lines = 0;
u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0,
skip_intel = 0, skip_app = 0, instrument_next = 0;
#ifdef __APPLE__
u8* colon_pos; // 用于查找冒号位置
u8* colon_pos;
#endif /* __APPLE__ */
if (input_file) {
inf = fopen(input_file, "r"); // 打开输入文件进行读取
if (!inf) PFATAL("Unable to read '%s'", input_file); // 如果打开失败,打印错误并退出
inf = fopen(input_file, "r");
if (!inf) PFATAL("Unable to read '%s'", input_file);
} else inf = stdin; // 如果没有指定输入文件,使用标准输入
} else inf = stdin;
outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600); // 创建输出文件
outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600);
if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file); // 如果创建失败,打印错误并退出
if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file);
outf = fdopen(outfd, "w"); // 获取文件流
outf = fdopen(outfd, "w");
if (!outf) PFATAL("fdopen() failed"); // 如果 fdopen 失败,打印错误并退出
if (!outf) PFATAL("fdopen() failed");
while (fgets(line, MAX_LINE, inf)) { // 逐行读取输入文件
while (fgets(line, MAX_LINE, inf)) {
/* 在某些情况下,我们希望在写入仪器跳板之前先跳过某些标签、宏或注释。
*/
/* In some cases, we want to defer writing the instrumentation trampoline
until after all the labels, macros, comments, etc. If we're in this
mode, and if the line starts with a tab followed by a character, dump
the trampoline now. */
if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
instrument_next && line[0] == '\t' && isalpha(line[1])) {
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
R(MAP_SIZE)); // 根据使用的位数选择合适的跳板格式
R(MAP_SIZE));
instrument_next = 0;
ins_lines++; // 统计插入的行数
ins_lines++;
}
/* 输出当前行,只有在通过模式下才跳过插入仪器 */
/* Output the actual line, call it a day in pass-thru mode. */
fputs(line, outf);
if (pass_thru) continue; // 如果启用了通过模式,则跳过剩下的处理
if (pass_thru) continue;
/* 以下代码处理实际的插桩逻辑,只在 .text 段插入仪器代码 */
/* All right, this is where the actual fun begins. For one, we only want to
instrument the .text section. So, let's keep track of that in processed
files - and let's set instr_ok accordingly. */
if (line[0] == '\t' && line[1] == '.') {
/* OpenBSD 将跳转表直接内联到代码中,这很麻烦。它们使用特定的 p2align 指令格式,
*/
/* OpenBSD puts jump tables directly inline with the code, which is
a bit annoying. They use a specific format of p2align directives
around them, so we use that as a signal. */
if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
isdigit(line[10]) && line[11] == '\n') skip_next_label = 1;
/* 如果检测到 .text 段或相关段,启用插桩 */
if (!strncmp(line + 2, "text\n", 5) ||
!strncmp(line + 2, "section\t.text", 13) ||
!strncmp(line + 2, "section\t__TEXT,__text", 21) ||
!strncmp(line + 2, "section __TEXT,__text", 21)) {
instr_ok = 1;
continue; // 继续处理下一行
continue;
}
/* 如果是其他段(如 bss、data禁用插桩 */
if (!strncmp(line + 2, "section\t", 8) ||
!strncmp(line + 2, "section ", 8) ||
!strncmp(line + 2, "bss\n", 4) ||
!strncmp(line + 2, "data\n", 5)) {
instr_ok = 0;
continue; // 继续处理下一行
continue;
}
}
/* 检测并跳过特定的汇编节(如 .code */
/* Detect off-flavor assembly (rare, happens in gdb). When this is
encountered, we set skip_csect until the opposite directive is
seen, and we do not instrument. */
if (strstr(line, ".code")) {
@ -382,11 +323,14 @@ static void add_instrumentation(void) {
}
/* 检测并跳过 Intel 语法块 */
/* Detect syntax changes, as could happen with hand-written assembly.
Skip Intel blocks, resume instrumentation when back to AT&T. */
if (strstr(line, ".intel_syntax")) skip_intel = 1;
if (strstr(line, ".att_syntax")) skip_intel = 0;
/* 跳过 ad-hoc 的 __asm__ 块 */
/* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */
if (line[0] == '#' || line[1] == '#') {
if (strstr(line, "#APP")) skip_app = 1;
@ -394,20 +338,45 @@ static void add_instrumentation(void) {
}
/* 检查并插入仪器代码:主要是函数标签、条件标签等 */
/* If we're in the right mood for instrumenting, check for function
names or conditional labels. This is a bit messy, but in essence,
we want to catch:
^main: - function entry point (always instrumented)
^.L0: - GCC branch label
^.LBB0_0: - clang branch label (but only in clang mode)
^\tjnz foo - conditional branches
...but not:
^# BB#0: - clang comments
^ # BB#0: - ditto
^.Ltmp0: - clang non-branch labels
^.LC0 - GCC non-branch labels
^.LBB0_0: - ditto (when in GCC mode)
^\tjmp foo - non-conditional jumps
Additionally, clang and GCC on MacOS X follow a different convention
with no leading dots on labels, hence the weird maze of #ifdefs
later on.
*/
if (skip_intel || skip_app || skip_csect || !instr_ok ||
line[0] == '#' || line[0] == ' ') continue; // 跳过不需要插入的行
line[0] == '#' || line[0] == ' ') continue;
/* 条件分支指令(如 jnz 等)。我们将插入仪器代码到分支指令后面(用于记录未采取的路径) */
/* Conditional branch instruction (jnz, etc). We append the instrumentation
right after the branch (to instrument the not-taken path) and at the
branch destination label (handled later on). */
if (line[0] == '\t') {
if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) {
fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
R(MAP_SIZE)); // 插入仪器代码
R(MAP_SIZE));
ins_lines++; // 统计插入的行数
ins_lines++;
}
@ -415,11 +384,13 @@ static void add_instrumentation(void) {
}
/* 标签的处理。标签可能是分支目标,我们需要根据格式区分处理 */
/* Label of some sort. This may be a branch destination, but we need to
tread carefully and account for several different formatting
conventions. */
#ifdef __APPLE__
/* 苹果系统标签格式:L<whatever><digit>: */
/* Apple: L<whatever><digit>: */
if ((colon_pos = strstr(line, ":"))) {
@ -427,7 +398,7 @@ static void add_instrumentation(void) {
#else
/* 其他平台的标签格式:.L<whatever>: */
/* Everybody else: .L<whatever>: */
if (strstr(line, ":")) {
@ -435,25 +406,34 @@ static void add_instrumentation(void) {
#endif /* __APPLE__ */
/* 处理跳转目标标签(如 .L0: 或 LBB0_0: */
/* .L0: or LBB0_0: style jump destination */
#ifdef __APPLE__
/* 苹果:L<num> / LBB<num> */
/* Apple: L<num> / LBB<num> */
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
&& R(100) < inst_ratio) {
#else
/* 其他平台:.L<num> / .LBB<num> */
/* Apple: .L<num> / .LBB<num> */
if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
&& R(100) < inst_ratio) {
#endif /* __APPLE__ */
/* 如果符合条件,则插入仪器代码 */
/* An optimization is possible here by adding the code only if the
label is mentioned in the code in contexts other than call / jmp.
That said, this complicates the code by requiring two-pass
processing (messy with stdin), and results in a speed gain
typically under 10%, because compilers are generally pretty good
about not generating spurious intra-function jumps.
We use deferred output chiefly to avoid disrupting
.Lfunc_begin0-style exception handling calculations (a problem on
MacOS X). */
if (!skip_next_label) instrument_next = 1; else skip_next_label = 0;
@ -461,38 +441,39 @@ static void add_instrumentation(void) {
} else {
/* 函数标签,插入仪器代码 */
/* Function label (always instrumented, deferred mode). */
instrument_next = 1;
}
}
}
fclose(inf); // 关闭输入文件
fclose(outf); // 关闭输出文件
if (ins_lines)
fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
if (input_file) fclose(inf);
fclose(outf);
if (!be_quiet) {
if (!ins_lines) WARNF("No instrumentation targets found%s.",
pass_thru ? " (pass-thru mode)" : "");
else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).",
ins_lines, use_64bit ? "64" : "32",
getenv("AFL_HARDEN") ? "hardened" :
(sanitizer ? "ASAN/MSAN" : "non-hardened"),
inst_ratio);
}
}
/* Main entry point */
// 在 main 函数中:
// 检查是否只需要显示版本信息,如果是,则调用 print_version 函数。
// 解析命令行参数:
// 解析 -q 启用静默模式。
// 解析 --clang 启用 clang 模式。
// 解析 --pass-through 启用数据通过模式。
// 解析 --sanitizer 启用 sanitizer。
// 解析 --inst-ratio 设置仪器插入的概率。
// 解析 --version 仅显示版本信息。
// 确保输入文件已经指定,如果没有指定,则退出并报错。
// 调用 edit_params 函数修改传递给汇编器的参数。
// 最后,调用 exec_real_as 函数执行实际的汇编器命令。
int main(int argc, char** argv) {
s32 pid;

File diff suppressed because it is too large Load Diff

@ -14,6 +14,8 @@
limitations under the License.
*/
// 这部分是版权声明和许可证信息说明这个文件是在Apache License 2.0下发布的。
/*
american fuzzy lop - wrapper for GCC and clang
----------------------------------------------
@ -38,7 +40,7 @@
specify its location via AFL_CC or AFL_CXX.
*/
// 这部分是注释,提供了关于这个程序的概述和使用说明。
#define AFL_MAIN
#include "config.h"
@ -51,6 +53,8 @@
#include <stdlib.h>
#include <string.h>
// 这些是包含的头文件其中一些是AFL自己的头文件其他的是C标准库的头文件
static u8* as_path; /* Path to the AFL 'as' wrapper */
static u8** cc_params; /* Parameters passed to the real CC */
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
@ -58,11 +62,14 @@ static u8 be_quiet, /* Quiet mode */
clang_mode; /* Invoked as afl-clang*? */
// 这些是全局变量声明。`as_path`存储AFL汇编器的路径`cc_params`存储传递给实际编译器的参数,`cc_par_cnt`是参数计数器,`be_quiet`用于控制
// 是否静默模式,`clang_mode`指示是否以`afl-clang`或`afl-clang++`模式调用。
/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
from argv[0]. If that fails, abort. */
static void find_as(u8* argv0) {
// 这个函数尝试在AFL_PATH环境变量指定的路径或从argv[0]派生的路径中找到AFL的“假”GNU汇编器。如果找不到程序将终止。
u8 *afl_path = getenv("AFL_PATH");
u8 *slash, *tmp;
@ -116,31 +123,32 @@ static void find_as(u8* argv0) {
/* Copy argv to cc_params, making the necessary edits. */
static void edit_params(u32 argc, char** argv) {
//定义了一个函数edit_params它接受两个参数argc是参数的数量argv是参数的数组。
u8 fortify_set = 0, asan_set = 0;
u8 *name;
u8 fortify_set = 0, asan_set = 0;//声明两个变量fortify_set和asan_set用于跟踪是否已经设置了FORTIFY_SOURCE和address sanitizerASan标志
u8 *name;//用于存储程序的名称
#if defined(__FreeBSD__) && defined(__x86_64__)
u8 m32_set = 0;
#endif
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
cc_params = ck_alloc((argc + 128) * sizeof(u8*));//分配内存以存储修改后的参数列表大小为argc + 128个u8*类型的指针。
name = strrchr(argv[0], '/');
if (!name) name = argv[0]; else name++;
name = strrchr(argv[0], '/');//找到argv[0](程序的路径)中最后一个'/'字符,这通常用于获取程序的名称。
if (!name) name = argv[0]; else name++;//如果name为NULL即argv[0]中没有'/'则name指向argv[0]的开始。否则name向前移动一个字符跳过'/'。
if (!strncmp(name, "afl-clang", 9)) {
clang_mode = 1;
clang_mode = 1;//检查程序名称是否以"afl-clang"开头如果是设置clang_mode标志为1
setenv(CLANG_ENV_VAR, "1", 1);
setenv(CLANG_ENV_VAR, "1", 1);//设置环境变量CLANG_ENV_VAR为"1"这可能用于通知其他部分的AFL工具链正在使用Clang。
if (!strcmp(name, "afl-clang++")) {
u8* alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";//如果AFL_CXX设置将其值作为第一个参数否则使用"clang++"。
} else {
u8* alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";//否则尝试获取环境变量AFL_CC的值。
}
} else {
@ -152,13 +160,13 @@ static void edit_params(u32 argc, char** argv) {
binaries. Meh. */
#ifdef __APPLE__
//在Apple系统上根据程序名称设置不同的编译器。如果AFL_CXX、AFL_GCJ或AFL_CC环境变量设置使用它们的值否则使用默认的编译器名称
if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX");
else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ");
else cc_params[0] = getenv("AFL_CC");
if (!cc_params[0]) {
//输出错误信息指出在MacOS X上需要设置AFL_CC或AFL_CXX环境变量。
SAYF("\n" cLRD "[-] " cRST
"On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
" 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
@ -169,7 +177,7 @@ static void edit_params(u32 argc, char** argv) {
}
#else
//对于非Apple系统根据程序名称设置不同的编译器。如果相应的环境变量设置使用它们的值否则使用默认的编译器名称。
if (!strcmp(name, "afl-g++")) {
u8* alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
@ -188,27 +196,27 @@ static void edit_params(u32 argc, char** argv) {
while (--argc) {
u8* cur = *(++argv);
if (!strncmp(cur, "-B", 2)) {
if (!strncmp(cur, "-B", 2)) {//如果当前参数以"-B"开头,输出警告信息,并跳过后续参数(如果当前参数后面紧跟着的是编译器的路径)。
if (!be_quiet) WARNF("-B is already set, overriding");
if (!be_quiet) WARNF("-B is already set, overriding");//如果程序不在静默模式,输出警告信息。
if (!cur[2] && argc > 1) { argc--; argv++; }
if (!cur[2] && argc > 1) { argc--; argv++; }//如果-B后面紧跟着的是编译器的路径跳过这个路径。
continue;
}
if (!strcmp(cur, "-integrated-as")) continue;
if (!strcmp(cur, "-integrated-as")) continue;//如果参数是"-integrated-as",跳过它。
if (!strcmp(cur, "-pipe")) continue;
if (!strcmp(cur, "-pipe")) continue;//如果参数是"-pipe",跳过它。
#if defined(__FreeBSD__) && defined(__x86_64__)
if (!strcmp(cur, "-m32")) m32_set = 1;
#endif
if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;//如果参数是"-fsanitize=address"或"-fsanitize=memory"设置asan_set标志。
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;//如果参数包含"FORTIFY_SOURCE"设置fortify_set标志。
cc_params[cc_par_cnt++] = cur;
@ -216,9 +224,11 @@ static void edit_params(u32 argc, char** argv) {
cc_params[cc_par_cnt++] = "-B";
cc_params[cc_par_cnt++] = as_path;
//向参数列表中添加"-B"和AFL汇编器的路径。
if (clang_mode)
cc_params[cc_par_cnt++] = "-no-integrated-as";
cc_params[cc_par_cnt++] = "-no-integrated-as";//如果clang_mode标志设置向参数列表中添加`"-no-integrated-as"
if (getenv("AFL_HARDEN")) {
@ -229,38 +239,38 @@ static void edit_params(u32 argc, char** argv) {
}
if (asan_set) {
if (asan_set) {//检查是否设置了asan_set标志。
/* Pass this on to afl-as to adjust map density. */
setenv("AFL_USE_ASAN", "1", 1);
setenv("AFL_USE_ASAN", "1", 1);//如果设置设置环境变量AFL_USE_ASAN为"1"
} else if (getenv("AFL_USE_ASAN")) {
} else if (getenv("AFL_USE_ASAN")) {//如果asan_set标志未设置但设置了环境变量AFL_USE_ASAN。
if (getenv("AFL_USE_MSAN"))
FATAL("ASAN and MSAN are mutually exclusive");
FATAL("ASAN and MSAN are mutually exclusive");//如果同时设置了AFL_USE_MSAN输出错误信息并终止程序。
if (getenv("AFL_HARDEN"))
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
FATAL("ASAN and AFL_HARDEN are mutually exclusive");//如果同时设置了AFL_HARDEN输出错误信息并终止程序。
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
cc_params[cc_par_cnt++] = "-fsanitize=address";
cc_params[cc_par_cnt++] = "-fsanitize=address";//向参数列表中添加"-U_FORTIFY_SOURCE"和"-fsanitize=address"。
} else if (getenv("AFL_USE_MSAN")) {
if (getenv("AFL_USE_ASAN"))
FATAL("ASAN and MSAN are mutually exclusive");
FATAL("ASAN and MSAN are mutually exclusive");//如果同时设置了AFL_USE_ASAN输出错误信息并终止程序。
if (getenv("AFL_HARDEN"))
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
FATAL("MSAN and AFL_HARDEN are mutually exclusive");//如果同时设置了AFL_HARDEN输出错误信息并终止程序。
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
cc_params[cc_par_cnt++] = "-fsanitize=memory";
cc_params[cc_par_cnt++] = "-fsanitize=memory";//向参数列表中添加"-U_FORTIFY_SOURCE"和"-fsanitize=memory"。
}
if (!getenv("AFL_DONT_OPTIMIZE")) {
if (!getenv("AFL_DONT_OPTIMIZE")) {//检查是否设置了环境变量AFL_DONT_OPTIMIZE。
#if defined(__FreeBSD__) && defined(__x86_64__)
@ -269,22 +279,23 @@ static void edit_params(u32 argc, char** argv) {
that bug. */
if (!clang_mode || !m32_set)
cc_params[cc_par_cnt++] = "-g";
cc_params[cc_par_cnt++] = "-g";//如果不是Clang模式或没有设置m32_set标志向参数列表中添加"-g"。
#else
cc_params[cc_par_cnt++] = "-g";
#endif
#endif//结束#if defined(__FreeBSD__) && defined(__x86_64__)条件编译块。
cc_params[cc_par_cnt++] = "-O3";
cc_params[cc_par_cnt++] = "-funroll-loops";
cc_params[cc_par_cnt++] = "-funroll-loops";//向参数列表中添加"-O3"和"-funroll-loops",这些是优化选项。
/* Two indicators that you're building for fuzzing; one of them is
AFL-specific, the other is shared with libfuzzer. */
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
//向参数列表中添加两个宏定义,这些宏定义指示编译器代码将用于模糊测试。
}
@ -297,7 +308,7 @@ static void edit_params(u32 argc, char** argv) {
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
//如果设置,向参数列表中添加一系列"-fno-builtin-*"选项,这些选项禁用编译器的内置函数。
}
cc_params[cc_par_cnt] = NULL;
@ -306,7 +317,7 @@ static void edit_params(u32 argc, char** argv) {
/* Main entry point */
//最后是函数结束语,结束函数定义。
int main(int argc, char** argv) {
if (isatty(2) && !getenv("AFL_QUIET")) {

@ -0,0 +1,274 @@
/*
2015 Google LLC
Apache License, 2.0
使
http://www.apache.org/licenses/LICENSE-2.0
*/
/*
- CPU
-----------------------------------
Michal Zalewski <lcamtuf@google.com>
CPU
afl-fuzz UIdocs/parallel_fuzzing.txt
CPUI/O100% CPU
getrusage()Jakub Wilk
*/
#define AFL_MAIN
#include "android-ashmem.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include "types.h"
#include "debug.h"
#ifdef __linux__
# define HAVE_AFFINITY 1
#endif /* __linux__ */
/*
Unix
*/
static u64 get_cur_time_us(void) {
// 定义一个timeval结构体变量tv用于存储当前时间
struct timeval tv;
// 定义一个timezone结构体变量tz用于存储时区信息
struct timezone tz;
// 使用gettimeofday函数获取当前时间并存储到tv和tz中
gettimeofday(&tv, &tz);
// 将秒转换为微秒,并加上微秒部分,返回当前时间的微秒值
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
}
/*
CPU使
*/
static u64 get_cpu_usage_us(void) {
// 定义一个rusage结构体变量u用于存储资源使用情况
struct rusage u;
// 使用getrusage函数获取当前进程的资源使用情况并存储到u中
getrusage(RUSAGE_SELF, &u);
// 计算用户态和核心态的总使用时间(微秒),并返回
return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec +
(u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec;
}
/*
*/
static u32 measure_preemption(u32 target_ms) {
// 定义两个易失性变量,用于循环测试
static volatile u32 v1, v2;
// 定义变量用于存储开始和结束的时间微秒以及CPU使用时间
u64 st_t, en_t, st_c, en_c, real_delta, slice_delta;
// 定义循环重复次数
s32 loop_repeats = 0;
// 获取当前时间(微秒)
st_t = get_cur_time_us();
// 获取当前CPU使用时间微秒
st_c = get_cpu_usage_us();
repeat_loop:
// 设置v1为一个忙循环的计数
v1 = CTEST_BUSY_CYCLES;
// 执行忙循环同时v2自增
while (v1--) v2++;
// 调用sched_yield()让出CPU
sched_yield();
// 再次获取当前时间(微秒)
en_t = get_cur_time_us();
// 如果当前时间与开始时间的差小于目标时间(毫秒转换为微秒),则增加循环次数并继续循环
if (en_t - st_t < target_ms * 1000) {
loop_repeats++;
goto repeat_loop;
}
/* 让我们看看这段时间里我们实际上有多少百分比的机会运行,
*/
// 获取当前CPU使用时间微秒
en_c = get_cpu_usage_us();
// 计算实际时间差毫秒和CPU使用时间差毫秒
real_delta = (en_t - st_t) / 1000;
slice_delta = (en_c - st_c) / 1000;
// 返回实际时间差占CPU使用时间差的百分比即抢占率
return real_delta * 100 / slice_delta;
}
/*
*/
int main(int argc, char** argv) {
#ifdef HAVE_AFFINITY
// 获取在线的CPU核心数
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN),
// 初始化空闲CPU和可能可用CPU的计数器
idle_cpus = 0, maybe_cpus = 0, i;
// 打印欢迎信息和版本号
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
// 打印测量每个核心抢占率所需的时间
ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...",
((double)CTEST_CORE_TRG_MS) / 1000);
// 遍历每个CPU核心
for (i = 0; i < cpu_cnt; i++) {
// 创建子进程
s32 fr = fork();
// 如果fork失败则打印错误信息并退出
if (fr < 0) PFATAL("fork failed");
// 如果是子进程,则执行以下操作
if (!fr) {
// 定义CPU集合
cpu_set_t c;
// 定义CPU使用率百分比
u32 util_perc;
// 初始化CPU集合
CPU_ZERO(&c);
// 将当前核心加入CPU集合
CPU_SET(i, &c);
// 如果设置CPU亲和性失败则打印错误信息并退出
if (sched_setaffinity(0, sizeof(c), &c))
PFATAL("sched_setaffinity failed for cpu %d", i);
// 测量抢占率
util_perc = measure_preemption(CTEST_CORE_TRG_MS);
// 如果使用率低于110%,则标记为核心可用,并退出
if (util_perc < 110) {
SAYF(" Core #%u: " cLGN "AVAILABLE" cRST "(%u%%)\n", i, util_perc);
exit(0);
} else if (util_perc < 250) {
// 如果使用率低于250%,则标记为核心需要谨慎,并退出
SAYF(" Core #%u: " cYEL "CAUTION" cRST "(%u%%)\n", i, util_perc);
exit(1);
}
// 如果使用率高于250%,则标记为核心过载,并退出
SAYF(" Core #%u: " cLRD "OVERBOOKED" cRST "(%u%%)\n" cRST, i,
util_perc);
exit(2);
}
}
// 等待子进程结束,并统计空闲和可能可用的核心数
for (i = 0; i < cpu_cnt; i++) {
int ret;
if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed");
if (WEXITSTATUS(ret) == 0) idle_cpus++;
if (WEXITSTATUS(ret) <= 1) maybe_cpus++;
}
// 根据空闲和可能可用的核心数打印结果
SAYF(cGRA "\n>>> ");
if (idle_cpus) {
if (maybe_cpus == idle_cpus) {
SAYF(cLGN "PASS: " cRST "You can run more processes on %u core%s.",
idle_cpus, idle_cpus > 1 ? "s" : "");
} else {
SAYF(cLGN "PASS: " cRST "You can run more processes on %u to %u core%s.",
idle_cpus, maybe_cpus, maybe_cpus > 1 ? "s" : "");
}
SAYF(cGRA " <<<" cRST "\n\n");
return 0;
}
if (maybe_cpus) {
SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.",
maybe_cpus, maybe_cpus > 1 ? "s" : "");
SAYF(cGRA " <<<" cRST "\n\n");
return 1;
}
SAYF(cLRD "FAIL: " cRST "All cores are overbooked.");
SAYF(cGRA " <<<" cRST "\n\n");
return 2;
#else
// 如果没有CPU亲和性支持则执行总体抢占率的测量
u32 util_perc;
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
/* 运行一个忙循环持续CTEST_TARGET_MS毫秒。 */
ACTF("Measuring gross preemption rate (this will take %0.02f sec)...",
((double)CTEST_TARGET_MS) / 1000);
// 测量抢占率
util_perc = measure_preemption(CTEST_TARGET_MS);
/* 输出最终结果。 */
SAYF(cGRA "\n>>> ");
if (util_perc < 105) {
SAYF(cLGN "PASS: " cRST "You can probably run additional processes.");
} else if (util_perc < 130) {
SAYF(cYEL "CAUTION: " cRST "Your CPU may be somewhat overbooked (%u%%).",
util_perc);
} else {
SAYF(cLRD "FAIL: " cRST "Your CPU is overbooked (%u%%).", util_perc);
}
SAYF(cGRA " <<<" cRST "\n\n");
// 返回结果代码
return (util_perc > 105) + (util_perc > 130);
#endif /* ^HAVE_AFFINITY */
}

Before

Width:  |  Height:  |  Size: 581 KiB

After

Width:  |  Height:  |  Size: 581 KiB

Before

Width:  |  Height:  |  Size: 892 B

After

Width:  |  Height:  |  Size: 892 B

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 38 B

After

Width:  |  Height:  |  Size: 38 B

Before

Width:  |  Height:  |  Size: 179 B

After

Width:  |  Height:  |  Size: 179 B

Before

Width:  |  Height:  |  Size: 642 B

After

Width:  |  Height:  |  Size: 642 B

Before

Width:  |  Height:  |  Size: 595 B

After

Width:  |  Height:  |  Size: 595 B

Before

Width:  |  Height:  |  Size: 876 B

After

Width:  |  Height:  |  Size: 876 B

Before

Width:  |  Height:  |  Size: 293 B

After

Width:  |  Height:  |  Size: 293 B

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 434 B

Before

Width:  |  Height:  |  Size: 996 B

After

Width:  |  Height:  |  Size: 996 B

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save