Fix support for afl-cmin and updated README

This commit is contained in:
Your Name
2021-03-25 17:41:14 +00:00
parent e1384b5086
commit 1725e6be31
8 changed files with 172 additions and 42 deletions

View File

@ -106,6 +106,7 @@ function usage() {
" -f file - location read by the fuzzed program (stdin)\n" \
" -m megs - memory limit for child process ("mem_limit" MB)\n" \
" -t msec - run time limit for child process (none)\n" \
" -O - use binary-only instrumentation (FRIDA mode)\n" \
" -Q - use binary-only instrumentation (QEMU mode)\n" \
" -U - use unicorn-based instrumentation (unicorn mode)\n" \
"\n" \
@ -140,7 +141,7 @@ BEGIN {
# process options
Opterr = 1 # default is to diagnose
Optind = 1 # skip ARGV[0]
while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eCQU?")) != -1) {
while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eCOQU?")) != -1) {
if (_go_c == "i") {
if (!Optarg) usage()
if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
@ -180,6 +181,12 @@ BEGIN {
extra_par = extra_par " -e"
continue
} else
if (_go_c == "O") {
if (frida_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
extra_par = extra_par " -O"
frida_mode = 1
continue
} else
if (_go_c == "Q") {
if (qemu_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
extra_par = extra_par " -Q"
@ -275,7 +282,7 @@ BEGIN {
target_bin = tnew
}
if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !unicorn_mode) {
if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !frida_mode && !unicorn_mode) {
if (0 != system( "grep -q __AFL_SHM_ID "target_bin )) {
print "[-] Error: binary '"target_bin"' doesn't appear to be instrumented." > "/dev/stderr"
exit 1

View File

@ -53,7 +53,7 @@ unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
export AFL_QUIET=1
while getopts "+i:o:f:m:t:eQUCh" opt; do
while getopts "+i:o:f:m:t:eOQUCh" opt; do
case "$opt" in
@ -83,6 +83,10 @@ while getopts "+i:o:f:m:t:eQUCh" opt; do
"C")
export AFL_CMIN_CRASHES_ONLY=1
;;
"O")
EXTRA_PAR="$EXTRA_PAR -O"
FRIDA_MODE=1
;;
"Q")
EXTRA_PAR="$EXTRA_PAR -Q"
QEMU_MODE=1
@ -118,6 +122,7 @@ Execution control settings:
-f file - location read by the fuzzed program (stdin)
-m megs - memory limit for child process ($MEM_LIMIT MB)
-t msec - run time limit for child process (none)
-O - use binary-only instrumentation (FRIDA mode)
-Q - use binary-only instrumentation (QEMU mode)
-U - use unicorn-based instrumentation (Unicorn mode)
@ -209,7 +214,7 @@ if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then
fi
if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$UNICORN_MODE" = "" ]; then
if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$FRIDA_MODE" = "" -a "$UNICORN_MODE" = "" ]; then
if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then
echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2

View File

@ -306,5 +306,45 @@ analyze_frida: $(TEST_BIN)
./afl-analyze \
-O \
-i $(TEST_DATA_DIR)basn0g01.png \
-- \
$(TEST_BIN) @@
cmin_qemu: $(TEST_BIN)
make -C ..
cd .. && \
./afl-cmin \
-Q \
-i $(TEST_DATA_DIR) \
-o $(QEMU_OUT) \
-- \
$(TEST_BIN) @@
cmin_frida: $(TEST_BIN)
make -C ..
cd .. && \
./afl-cmin \
-O \
-i $(TEST_DATA_DIR) \
-o $(FRIDA_OUT) \
-- \
$(TEST_BIN) @@
cmin_bash_qemu: $(TEST_BIN)
make -C ..
cd .. && \
./afl-cmin.bash \
-Q \
-i $(TEST_DATA_DIR) \
-o $(QEMU_OUT) \
-- \
$(TEST_BIN) @@
cmin_bash_frida: $(TEST_BIN)
make -C ..
cd .. && \
./afl-cmin.bash \
-O \
-i $(TEST_DATA_DIR) \
-o $(FRIDA_OUT) \
-- \
$(TEST_BIN) @@

View File

@ -3,46 +3,133 @@ The purpose of FRIDA mode is to provide an alternative binary only fuzzer for AF
just like that provided by QEMU mode. The intention is to provide a very similar
user experience, right down to the options provided through environment variables.
Additionally, the intention is to be able to make a direct performance comparison
between the two approaches. Hopefully, we should also be able to leverage the same
approaches for adding features which QEMU uses, possibly even sharing code.
Whilst AFLplusplus already has some support for running on FRIDA [here](https://github.com/AFLplusplus/AFLplusplus/tree/stable/utils/afl_frida)
this requires the code to be fuzzed to be provided as a shared library, it
cannot be used to fuzz executables. Additionally, it requires the user to write
a small harness around their target code of interest, FRIDA mode instead takes a
different approach to avoid these limitations.
## Limitations
The current focus is on x64 support for Intel. Although parts may be architecturally
dependent, the approach itself should remain architecture agnostic.
# Current Progress
As FRIDA mode is new, it is missing a lot of features. Most importantly,
persistent mode. The design is such that it should be possible to add these
features in a similar manner to QEMU mode and perhaps leverage some of its
design and implementation.
## Usage
FRIDA mode requires some small modifications to the afl-fuzz and similar tools in
AFLplusplus. The intention is that it behaves identically to QEMU, but uses the 'O'
switch rather than 'Q'.
| Feature/Instrumentation | frida-mode |
| -------------------------|:----------:|
| NeverZero | |
| Persistent Mode | |
| LAF-Intel / CompCov | |
| CmpLog | |
| Selective Instrumentation| x |
| Non-Colliding Coverage | |
| Ngram prev_loc Coverage | |
| Context Coverage | |
| Auto Dictionary | |
| Snapshot LKM Support | |
## Design
AFL Frida works by means of a shared library injected into a binary program using
LD_PRELOAD, similar to the way which other fuzzing features are injected into targets.
# Compatibility
Currently FRIDA mode supports Linux and macOS targets on both x86/x64
architecture and aarch64. Later releases may add support for aarch32 and Windows
targets as well as embedded linux environments.
## Testing
Alongside the FRIDA mode, we also include a test program for fuzzing. This test
program is built using the libpng benchmark from fuzz-bench and integrating the
StandaloneFuzzTargetMain from the llvm project. This is built and linked without
any special modifications to suit FRIDA or QEMU. However, at present we don't have
a representative corpus.
FRIDA has been used on various embedded targets using both uClibc and musl C
runtime libraries, so porting should be possible. However, the current build
system does not support cross compilation.
## Getting Started
To build everything run `make`.
To run the benchmark sample with qemu run `make test_qemu`.
To run the benchmark sample with frida run `make test_frida`.
To run the benchmark sample with qemu run `make png_qemu`.
To run the benchmark sample with frida run `make png_frida`.
## Usage
FRIDA mode requires some small modifications to the afl-fuzz and similar tools
in AFLplusplus. The intention is that it behaves identically to QEMU, but uses
the 'O' switch rather than 'Q'. Whilst the options 'f', 'F', 's' or 'S' may have
made more sense for a mode powered by FRIDA Stalker, they were all taken, so
instead we use 'O' in homage to the [author](https://github.com/oleavr) of
FRIDA.
Similarly, the intention is to mimic the use of environment variables used by
QEMU where possible (although replacing `s/QEMU/FRIDA/g`). Accodingly, the
following options are currently supported.
# Configuration options
* `AFL_FRIDA_DEBUG_MAPS` - See `AFL_QEMU_DEBUG_MAPS`
* `AFL_FRIDA_EXCLUDE_RANGES` - See `AFL_QEMU_EXCLUDE_RANGES`
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage instrumentation (the default where available). Required to use `AFL_FRIDA_INST_TRACE`.
* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default the child will report instrumented blocks back to the parent so that it can also instrument them and they be inherited by the next child on fork.
* `AFL_FRIDA_INST_RANGES` - See `AFL_QEMU_INST_RANGES`
* `AFL_FRIDA_INST_STRICT` - Under certain conditions, Stalker may encroach into excluded regions and generate both instrumented blocks and coverage data (e.g. indirect calls on x86). The excluded block is generally honoured as soon as another function is called within the excluded region. The overhead of generating, running and instrumenting these few additional blocks is likely to be fairly small, but it may hinder you when checking that the correct number of paths are found for testing purposes or similar. There is a performance penatly for this option during block compilation where we check the block isn't in a list of excluded ranges.
* `AFL_FRIDA_INST_TRACE` - Generate some logging when running instrumented code. Requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
# Performance
Additionally, the intention is to be able to make a direct performance
comparison between the two approaches. Accordingly, FRIDA mode includes a test
target based on the [libpng](https://libpng.sourceforge.io/) benchmark used by
[fuzzbench](https://google.github.io/fuzzbench/) and integrated with the
[StandaloneFuzzTargetMain](https://raw.githubusercontent.com/llvm/llvm-project/main/compiler-rt/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c)
from the llvm project. This is built and linked without any special
modifications to suit FRIDA or QEMU. We use the test data provided with libpng
as our corpus.
Whilst not much performance tuning has been completed to date, performance is
around 30-50% of that of QEMU mode, however, this gap may reduce with the
introduction of persistent mode. Performance can be tested by running
`make compare`, albeit a longer time measurement may be required for move
accurate results.
Whilst [afl_frida](https://github.com/AFLplusplus/AFLplusplus/tree/stable/utils/afl_frida)
claims a 5-10x performance increase over QEMU, it has not been possible to
reproduce these claims. However, the number of executions per second can vary
dramatically as a result of the randomization of the fuzzer input. Some inputs
may traverse relatively few paths before being rejected as invalid whilst others
may be valid inputs or be subject to much more processing before rejection.
Accordingly, it is recommended that testing be carried out over prolongued
periods to gather timings which are more than indicative.
# Design
FRIDA mode is supported by using `LD_PRELOAD` (`DYLD_INSERT_LIBRARIES` on macOS)
to inject a shared library (`afl-frida-trace.so`) into the target. This shared
library is built using the [frida-gum](https://github.com/frida/frida-gum)
devkit from the [FRIDA](https://github.com/frida/frida) project. One of the
components of frida-gum is [Stalker](https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8),
this allows the dynamic instrumentation of running code for AARCH32, AARCH64,
x86 and x64 architectutes. Implementation details can be found
[here](https://frida.re/docs/stalker/).
Dynamic instrumentation is used to augment the target application with similar
coverage information to that inserted by `afl-gcc` or `afl-clang`. The shared
library is also linked to the `compiler-rt` component of AFLplusplus to feedback
this coverage information to AFL and also provide a fork server. It also makes
use of the FRIDA [prefetch](https://github.com/frida/frida-gum/blob/56dd9ba3ee9a5511b4b0c629394bf122775f1ab7/gum/gumstalker.h#L115)
support to feedback instrumented blocks from the child to the parent using a
shared memory region to avoid the need to regenerate instrumented blocks on each
fork.
Whilst FRIDA allows for a normal C function to be used to augment instrumented
code, to minimize the costs of storing and restoring all of the registers, FRIDA
mode instead makes use of optimized assembly instead on AARCH64 and x86/64
targets.
# Advanced configuration options
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
instrumentation (the default where available). Required to use
`AFL_FRIDA_INST_TRACE`.
* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default the child will
report instrumented blocks back to the parent so that it can also instrument
them and they be inherited by the next child on fork.
* `AFL_FRIDA_INST_STRICT` - Under certain conditions, Stalker may encroach into
excluded regions and generate both instrumented blocks and coverage data (e.g.
indirect calls on x86). The excluded block is generally honoured as soon as
another function is called within the excluded region and so such encroachment
is usually of little consequence. This detail may however, hinder you when
checking that the correct number of paths are found for testing purposes or
similar. There is a performance penatly for this option during block compilation
where we check the block isn't in a list of excluded ranges.
* `AFL_FRIDA_INST_TRACE` - Generate some logging when running instrumented code.
Requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
# TODO
* Add AARCH64 inline assembly optimization from libFuzz
* Fix issues running on OSX
* Identify cause of erroneous additional paths
As can be seen from the progress section above, there are a number of features
which are missing in its currently form. Chief amongst which is persistent mode.
The intention is to achieve feature parity with QEMU mode in due course.
Contributions are welcome, but please get in touch to ensure that efforts are
deconflicted.

View File

@ -842,7 +842,6 @@ static void set_up_environment(char **argv) {
}
ck_free(frida_binary);
OKF("Frida Mode setting LD_PRELOAD %s", frida_afl_preload);
setenv("LD_PRELOAD", frida_afl_preload, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
@ -859,7 +858,6 @@ static void set_up_environment(char **argv) {
u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
setenv("LD_PRELOAD", frida_binary, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
OKF("Frida Mode setting LD_PRELOAD %s", frida_binary);
ck_free(frida_binary);
}

View File

@ -1320,7 +1320,6 @@ int main(int argc, char **argv_orig, char **envp) {
}
ck_free(frida_binary);
OKF("Frida Mode setting LD_PRELOAD %s", frida_afl_preload);
setenv("LD_PRELOAD", frida_afl_preload, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
@ -1337,7 +1336,6 @@ int main(int argc, char **argv_orig, char **envp) {
u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
setenv("LD_PRELOAD", frida_binary, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
OKF("Frida Mode setting LD_PRELOAD %s", frida_binary);
ck_free(frida_binary);
}

View File

@ -617,7 +617,6 @@ static void set_up_environment(afl_forkserver_t *fsrv, char **argv) {
}
ck_free(frida_binary);
OKF("Frida Mode setting LD_PRELOAD %s", frida_afl_preload);
setenv("LD_PRELOAD", frida_afl_preload, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
@ -634,7 +633,6 @@ static void set_up_environment(afl_forkserver_t *fsrv, char **argv) {
u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
setenv("LD_PRELOAD", frida_binary, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
OKF("Frida Mode setting LD_PRELOAD %s", frida_binary);
ck_free(frida_binary);
}
@ -996,7 +994,6 @@ int main(int argc, char **argv_orig, char **envp) {
}
if (in_dir) {
/* If we don't have a file name chosen yet, use a safe default. */

View File

@ -772,7 +772,6 @@ static void set_up_environment(afl_forkserver_t *fsrv, char **argv) {
}
ck_free(frida_binary);
OKF("Frida Mode setting LD_PRELOAD %s", frida_afl_preload);
setenv("LD_PRELOAD", frida_afl_preload, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
@ -789,7 +788,6 @@ static void set_up_environment(afl_forkserver_t *fsrv, char **argv) {
u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
setenv("LD_PRELOAD", frida_binary, 1);
setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
OKF("Frida Mode setting LD_PRELOAD %s", frida_binary);
ck_free(frida_binary);
}