The idea and much of the original implementation comes from Nathan Voss njvoss299@gmail.com.
The port to AFL++ is by Dominik Maier mail@dmnk.co.
The CompareCoverage and NeverZero counters features are by Andrea Fioraldi andreafioraldi@gmail.com.
The code in unicorn_mode/ allows you to build the Unicorn Engine with AFL++ support. This means, you can run anything that can be emulated in unicorn and obtain instrumentation output for black-box, closed-source binary code snippets. This mechanism can be then used by afl-fuzz to stress-test targets that couldn't be built with afl-cc or used in QEMU mode.
There is a significant performance penalty compared to native AFL, but at least we're able to use AFL++ on these binaries, right?
First, you will need a working harness for your target in unicorn, using Python, C, or Rust.
For some pointers for more advanced emulation, take a look at BaseSAFE and Qiling.
First, make AFL++ as usual. Once that completes successfully, you need to build and add in the Unicorn mode features:
cd unicorn_mode ./build_unicorn_support.sh
NOTE: This script checks out a Unicorn Engine fork as submodule that has been tested and is stable-ish, based on the unicorn engine next
branch.
Building Unicorn will take a little bit (~5-10 minutes). Once it completes, it automatically compiles a sample application and verifies that it works.
To use unicorn-mode effectively, you need to prepare the following:
Once you have all those things ready to go, you just need to run afl-fuzz in unicorn-mode
by passing in the -U
flag:
afl-fuzz -U -m none -i /path/to/inputs -o /path/to/results -- ./test_harness @@
The normal afl-fuzz command line format applies to everything here. Refer to AFL's main documentation for more info about how to use afl-fuzz effectively.
For a much clearer vision of what all of this looks like, refer to the sample provided in the samples/ directory. There is also a blog post that uses slightly older concepts, but describes the general ideas, at:
https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf
The helper_scripts/ directory also contains several helper scripts that allow you to dump context from a running process, load it, and hook heap allocations. For details on how to use this, check out the follow-up blog post to the one linked above:
https://hackernoon.com/afl-unicorn-part-2-fuzzing-the-unfuzzable-bea8de3540a5
An example use of AFL-Unicorn mode is discussed in the paper Unicorefuzz: https://www.usenix.org/conference/woot19/presentation/maier
As for the QEMU-based instrumentation, unicornafl comes with a sub-instruction based instrumentation similar in purpose to laf-intel.
The options that enable Unicorn CompareCoverage are the same used for QEMU. This will split up each multi-byte compare to give feedback for each correct byte:
AFL_COMPCOV_LEVEL=1
to instrument comparisons with only immediate values.AFL_COMPCOV_LEVEL=2
to instrument all comparison instructions.Comparison instructions are currently instrumented only for the x86, x86_64, and ARM targets.
Running the build script builds unicornafl and its Python bindings and installs them on your system. This installation will leave any existing Unicorn installations untouched.
If you want to use unicornafl instead of unicorn in a script, replace all unicorn
imports with unicornafl
inputs, everything else should “just work”. If you use 3rd party code depending on unicorn, you can use unicornafl monkeypatching. Before importing anything that depends on unicorn, do:
import unicornafl unicornafl.monkeypatch()
This will replace all unicorn imports with unicornafl inputs.
Apart from reading the documentation in afl.c
and the Python bindings of unicornafl, the best documentation are the samples/.
The following examples exist at the time of writing:
Usually, the place to look at is the harness
in each folder. The source code in each harness is pretty well documented. Most harnesses also have the afl-fuzz
commandline, or even offer a make fuzz
Makefile target. Targets in these folders, if x86, can usually be made using make target
in each folder or get shipped pre-built (plus their source).
Especially take a look at the speedtest documentation to see how the languages compare.