aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rwxr-xr-xafl-cmin11
-rwxr-xr-xafl-cmin.bash9
-rw-r--r--frida_mode/.gitignore5
-rw-r--r--frida_mode/Makefile350
-rw-r--r--frida_mode/README.md135
-rw-r--r--frida_mode/inc/instrument.h7
-rw-r--r--frida_mode/inc/interceptor.h4
-rw-r--r--frida_mode/inc/prefetch.h5
-rw-r--r--frida_mode/inc/ranges.h6
-rw-r--r--frida_mode/src/instrument.c265
-rw-r--r--frida_mode/src/interceptor.c16
-rw-r--r--frida_mode/src/main.c149
-rw-r--r--frida_mode/src/prefetch.c121
-rw-r--r--frida_mode/src/ranges.c395
-rw-r--r--frida_mode/test/testinstr.c105
-rwxr-xr-xfrida_mode/test/testinstr.py32
-rw-r--r--include/envs.h7
-rw-r--r--include/forkserver.h2
-rw-r--r--src/afl-analyze.c48
-rw-r--r--src/afl-fuzz-init.c7
-rw-r--r--src/afl-fuzz.c52
-rw-r--r--src/afl-showmap.c45
-rw-r--r--src/afl-tmin.c47
24 files changed, 1803 insertions, 21 deletions
diff --git a/.gitignore b/.gitignore
index 3f440730..2aaaf9ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,3 +82,4 @@ libAFLDriver.a
libAFLQemuDriver.a
test/.afl_performance
gmon.out
+afl-frida-trace.so
diff --git a/afl-cmin b/afl-cmin
index a1fc6f21..3f3a7517 100755
--- a/afl-cmin
+++ b/afl-cmin
@@ -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
diff --git a/afl-cmin.bash b/afl-cmin.bash
index 5b2c3894..f4bd269d 100755
--- a/afl-cmin.bash
+++ b/afl-cmin.bash
@@ -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
diff --git a/frida_mode/.gitignore b/frida_mode/.gitignore
new file mode 100644
index 00000000..956b9911
--- /dev/null
+++ b/frida_mode/.gitignore
@@ -0,0 +1,5 @@
+build/
+frida_test.dat
+qemu_test.dat
+frida_out/**
+qemu_out/**
diff --git a/frida_mode/Makefile b/frida_mode/Makefile
new file mode 100644
index 00000000..efae5ebf
--- /dev/null
+++ b/frida_mode/Makefile
@@ -0,0 +1,350 @@
+PWD:=$(shell pwd)/
+INC_DIR:=$(PWD)inc/
+SRC_DIR:=$(PWD)src/
+INCLUDES:=$(wildcard $(INC_DIR)*.h)
+SOURCES:=$(wildcard $(SRC_DIR)*.c)
+BUILD_DIR:=$(PWD)build/
+CFLAGS:= $(CFLAGS) \
+ -fPIC \
+ -D_GNU_SOURCE
+
+FRIDA_BUILD_DIR:=$(BUILD_DIR)frida/
+FRIDA_TRACE:=$(FRIDA_BUILD_DIR)afl-frida-trace.so
+
+ARCH=$(shell uname -m)
+ifeq "$(ARCH)" "aarch64"
+ARCH:=arm64
+TESTINSTR_BASE:=0x0000aaaaaaaaa000
+endif
+
+ifeq "$(ARCH)" "x86_64"
+TESTINSTR_BASE:=0x0000555555554000
+endif
+
+ifeq "$(shell uname)" "Darwin"
+OS:=macos
+AFL_FRIDA_INST_RANGES=0x0000000000001000-0xFFFFFFFFFFFFFFFF
+CFLAGS:=$(CFLAGS) -Wno-deprecated-declarations
+TEST_LDFLAGS:=-undefined dynamic_lookup
+endif
+ifeq "$(shell uname)" "Linux"
+OS:=linux
+AFL_FRIDA_INST_RANGES=$(shell $(PWD)test/testinstr.py -f $(BUILD_DIR)testinstr -s .testinstr -b $(TESTINSTR_BASE))
+CFLAGS:=$(CFLAGS) -Wno-prio-ctor-dtor
+TEST_LDFLAGS:=
+endif
+
+ifndef OS
+$(error "Operating system unsupported")
+endif
+
+VERSION=14.2.13
+GUM_DEVKIT_FILENAME=frida-gum-devkit-$(VERSION)-$(OS)-$(ARCH).tar.xz
+GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(VERSION)/$(GUM_DEVKIT_FILENAME)"
+GUM_DEVKIT_TARBALL:=$(FRIDA_BUILD_DIR)$(GUM_DEVKIT_FILENAME)
+GUM_DEVIT_LIBRARY=$(FRIDA_BUILD_DIR)libfrida-gum.a
+GUM_DEVIT_HEADER=$(FRIDA_BUILD_DIR)frida-gum.h
+
+TEST_BUILD_DIR:=$(BUILD_DIR)test/
+
+LIBPNG_FILE:=$(TEST_BUILD_DIR)libpng-1.2.56.tar.gz
+LIBPNG_URL:=https://downloads.sourceforge.net/project/libpng/libpng12/older-releases/1.2.56/libpng-1.2.56.tar.gz
+LIBPNG_DIR:=$(TEST_BUILD_DIR)libpng-1.2.56/
+LIBPNG_MAKEFILE:=$(LIBPNG_DIR)Makefile
+LIBPNG_LIB:=$(LIBPNG_DIR).libs/libpng12.a
+
+HARNESS_FILE:=$(TEST_BUILD_DIR)StandaloneFuzzTargetMain.c
+HARNESS_OBJ:=$(TEST_BUILD_DIR)StandaloneFuzzTargetMain.o
+HARNESS_URL:="https://raw.githubusercontent.com/llvm/llvm-project/main/compiler-rt/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c"
+
+PNGTEST_FILE:=$(TEST_BUILD_DIR)target.cc
+PNGTEST_OBJ:=$(TEST_BUILD_DIR)target.o
+PNGTEST_URL:="https://raw.githubusercontent.com/google/fuzzbench/master/benchmarks/libpng-1.2.56/target.cc"
+
+TEST_BIN:=$(TEST_BUILD_DIR)pngtest
+
+TESTINSTBIN:=$(BUILD_DIR)testinstr
+TESTINSTSRC:=$(PWD)test/testinstr.c
+
+TEST_DATA_DIR:=$(PWD)build/test/libpng-1.2.56/contrib/pngsuite/
+
+TESTINSTR_DATA_DIR:=$(BUILD_DIR)testinstr_in/
+TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)test.dat
+FRIDA_OUT:=$(PWD)frida_out
+QEMU_OUT:=$(PWD)qemu_out
+
+.PHONY: all frida test clean format test_frida test_qemu compare testinstr test_testinstr standalone
+
+all: $(FRIDA_TRACE)
+
+frida: $(FRIDA_TRACE)
+
+$(BUILD_DIR):
+ mkdir -p $(BUILD_DIR)
+
+############################# FRIDA ############################################
+$(FRIDA_BUILD_DIR): | $(BUILD_DIR)
+ mkdir -p $@
+
+$(GUM_DEVKIT_TARBALL): | $(FRIDA_BUILD_DIR)
+ wget -O $@ $(GUM_DEVKIT_URL)
+
+$(GUM_DEVIT_LIBRARY): | $(GUM_DEVKIT_TARBALL)
+ tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
+
+$(GUM_DEVIT_HEADER): | $(GUM_DEVKIT_TARBALL)
+ tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
+
+$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(SOURCES) Makefile | $(FRIDA_BUILD_DIR)
+ $(CC) -shared \
+ $(CFLAGS) \
+ -o $@ $(SOURCES) \
+ $(GUM_DEVIT_LIBRARY) \
+ -I $(FRIDA_BUILD_DIR) \
+ -I .. \
+ -I ../include \
+ -I $(INC_DIR) \
+ ../instrumentation/afl-compiler-rt.o.c \
+ -lpthread -ldl -lresolv
+
+ cp -v $(FRIDA_TRACE) ../
+
+############################# TEST #############################################
+
+test: $(TEST_BIN)
+
+$(TEST_BUILD_DIR): $(BUILD_DIR)
+ mkdir -p $@
+
+$(HARNESS_FILE): | $(TEST_BUILD_DIR)
+ wget -O $@ $(HARNESS_URL)
+
+$(HARNESS_OBJ): $(HARNESS_FILE)
+ $(CC) -o $@ -c $<
+
+$(PNGTEST_FILE): | $(TEST_BUILD_DIR)
+ wget -O $@ $(PNGTEST_URL)
+
+$(PNGTEST_OBJ): $(PNGTEST_FILE) | $(LIBPNG_DIR)
+ $(CXX) -std=c++11 -I $(LIBPNG_DIR) -o $@ -c $<
+
+$(LIBPNG_FILE): | $(TEST_BUILD_DIR)
+ wget -O $@ $(LIBPNG_URL)
+
+$(LIBPNG_DIR): $(LIBPNG_FILE)
+ tar zxvf $(LIBPNG_FILE) -C $(TEST_BUILD_DIR)
+
+$(LIBPNG_MAKEFILE): | $(LIBPNG_DIR)
+ cd $(LIBPNG_DIR) && ./configure
+
+$(LIBPNG_LIB): $(LIBPNG_MAKEFILE)
+ make -C $(LIBPNG_DIR)
+
+$(TEST_BIN): $(HARNESS_OBJ) $(PNGTEST_OBJ) $(LIBPNG_LIB)
+ $(CXX) \
+ -o $@ \
+ $(HARNESS_OBJ) $(PNGTEST_OBJ) $(LIBPNG_LIB) \
+ -lz \
+ $(TEST_LDFLAGS)
+
+############################# TESTINSR #########################################
+$(TESTINSTR_DATA_DIR): | $(BUILD_DIR)
+ mkdir -p $@
+
+$(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
+ echo -n "000" > $@
+
+$(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
+ $(CC) -o $@ $<
+
+testinstr: $(TESTINSTBIN)
+
+############################# CLEAN ############################################
+clean:
+ rm -rf $(BUILD_DIR)
+
+############################# FORMAT ###########################################
+format:
+ cd .. && echo $(SOURCES) | xargs -L1 ./.custom-format.py -i
+ cd .. && echo $(INCLUDES) | xargs -L1 ./.custom-format.py -i
+ cd .. && ./.custom-format.py -i $(TESTINSTSRC)
+
+############################# RUN #############################################
+
+# Add the environment variable AFL_DEBUG_CHILD=1 to show printf's from the target
+
+png_frida: $(FRIDA_TRACE) $(TEST_BIN)
+ make -C ..
+ cd .. && \
+ ./afl-fuzz \
+ -O \
+ -i $(TEST_DATA_DIR) \
+ -o $(FRIDA_OUT) \
+ -- \
+ $(TEST_BIN) @@
+
+png_qemu: $(TEST_BIN)
+ make -C ..
+ cd .. && \
+ ./afl-fuzz \
+ -Q \
+ -i $(TEST_DATA_DIR) \
+ -o $(QEMU_OUT) \
+ -- \
+ $(TEST_BIN) @@
+
+compare: $(FRIDA_TRACE) $(TEST_BIN)
+ cd .. && \
+ ./afl-fuzz \
+ -V30 \
+ -O \
+ -i $(TEST_DATA_DIR) \
+ -o $(FRIDA_OUT) \
+ -- \
+ $(TEST_BIN) @@
+ cd .. && \
+ ./afl-fuzz \
+ -V30 \
+ -Q \
+ -i $(TEST_DATA_DIR) \
+ -o $(QEMU_OUT) \
+ -- \
+ $(TEST_BIN) @@
+ cat frida_out/default/fuzzer_stats
+ cat qemu_out/default/fuzzer_stats
+
+testinstr_qemu: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+ make -C ..
+ cd .. && \
+ AFL_QEMU_INST_RANGES=$(AFL_FRIDA_INST_RANGES) \
+ ./afl-fuzz \
+ -Q \
+ -i $(TESTINSTR_DATA_DIR) \
+ -o $(QEMU_OUT) \
+ -- \
+ $(TESTINSTBIN) @@
+
+testinstr_frida: $(FRIDA_TRACE) $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+ make -C ..
+ cd .. && \
+ AFL_FRIDA_INST_RANGES=$(AFL_FRIDA_INST_RANGES) \
+ AFL_FRIDA_INST_NO_OPTIMIZE=1 \
+ AFL_FRIDA_INST_NO_PREFETCH=1 \
+ AFL_FRIDA_INST_STRICT=1 \
+ ./afl-fuzz \
+ -O \
+ -i $(TESTINSTR_DATA_DIR) \
+ -o $(FRIDA_OUT) \
+ -- \
+ $(TESTINSTBIN) @@
+
+standalone: $(FRIDA_TRACE) $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+ cd .. && \
+ AFL_FRIDA_INST_RANGES=$(AFL_FRIDA_INST_RANGES) \
+ AFL_DEBUG_CHILD=1 \
+ AFL_FRIDA_DEBUG_MAPS=1 \
+ AFL_FRIDA_INST_NO_OPTIMIZE=1 \
+ AFL_FRIDA_INST_NO_PREFETCH=1 \
+ AFL_FRIDA_INST_TRACE=1 \
+ AFL_FRIDA_INST_STRICT=1 \
+ LD_PRELOAD=$(FRIDA_TRACE) \
+ DYLD_INSERT_LIBRARIES=$(FRIDA_TRACE) \
+ $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+
+tmin_qemu: $(TEST_BIN)
+ make -C ..
+ cd .. && \
+ ./afl-tmin \
+ -Q \
+ -i $(TEST_DATA_DIR)basn0g01.png \
+ -o $(QEMU_OUT)/qemu-min-basn0g01.png \
+ -- \
+ $(TEST_BIN) @@
+
+tmin_frida: $(TEST_BIN)
+ make -C ..
+ cd .. && \
+ ./afl-tmin \
+ -O \
+ -i $(TEST_DATA_DIR)basn0g01.png \
+ -o $(FRIDA_OUT)/qemu-min-basn0g01.png \
+ -- \
+ $(TEST_BIN)
+
+showmap_qemu: $(TEST_BIN)
+ make -C ..
+ cd .. && \
+ ./afl-showmap \
+ -Q \
+ -i $(TEST_DATA_DIR) \
+ -o $(QEMU_OUT) \
+ -- \
+ $(TEST_BIN) @@
+
+showmap_frida: $(TEST_BIN)
+ make -C ..
+ cd .. && \
+ ./afl-showmap \
+ -O \
+ -i $(TEST_DATA_DIR) \
+ -o $(FRIDA_OUT) \
+ -- \
+ $(TEST_BIN) @@
+
+analyze_qemu: $(TEST_BIN)
+ make -C ..
+ cd .. && \
+ ./afl-analyze \
+ -Q \
+ -i $(TEST_DATA_DIR)basn0g01.png \
+ -- \
+ $(TEST_BIN) @@
+
+analyze_frida: $(TEST_BIN)
+ make -C ..
+ cd .. && \
+ ./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) @@ \ No newline at end of file
diff --git a/frida_mode/README.md b/frida_mode/README.md
new file mode 100644
index 00000000..bc260e3e
--- /dev/null
+++ b/frida_mode/README.md
@@ -0,0 +1,135 @@
+# FRIDA MODE
+The purpose of FRIDA mode is to provide an alternative binary only fuzzer for AFL
+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.
+
+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.
+
+# 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.
+
+ | 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 | |
+
+# 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.
+
+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 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.
+
+* `AFL_FRIDA_DEBUG_MAPS` - See `AFL_QEMU_DEBUG_MAPS`
+* `AFL_FRIDA_EXCLUDE_RANGES` - See `AFL_QEMU_EXCLUDE_RANGES`
+* `AFL_FRIDA_INST_RANGES` - See `AFL_QEMU_INST_RANGES`
+
+# 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
+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. \ No newline at end of file
diff --git a/frida_mode/inc/instrument.h b/frida_mode/inc/instrument.h
new file mode 100644
index 00000000..ff71bed4
--- /dev/null
+++ b/frida_mode/inc/instrument.h
@@ -0,0 +1,7 @@
+#include "frida-gum.h"
+
+void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output,
+ gpointer user_data);
+
+void instrument_init();
+
diff --git a/frida_mode/inc/interceptor.h b/frida_mode/inc/interceptor.h
new file mode 100644
index 00000000..5ed3cf49
--- /dev/null
+++ b/frida_mode/inc/interceptor.h
@@ -0,0 +1,4 @@
+#include "frida-gum.h"
+
+void intercept(void *address, gpointer replacement, gpointer user_data);
+
diff --git a/frida_mode/inc/prefetch.h b/frida_mode/inc/prefetch.h
new file mode 100644
index 00000000..b7f25a97
--- /dev/null
+++ b/frida_mode/inc/prefetch.h
@@ -0,0 +1,5 @@
+void prefetch_init();
+void prefetch_start(GumStalker *stalker);
+void prefetch_write(void *addr);
+void prefetch_read(GumStalker *stalker);
+
diff --git a/frida_mode/inc/ranges.h b/frida_mode/inc/ranges.h
new file mode 100644
index 00000000..b9394dbc
--- /dev/null
+++ b/frida_mode/inc/ranges.h
@@ -0,0 +1,6 @@
+#include "frida-gum.h"
+
+void ranges_init(GumStalker *stalker);
+
+gboolean range_is_excluded(gpointer address);
+
diff --git a/frida_mode/src/instrument.c b/frida_mode/src/instrument.c
new file mode 100644
index 00000000..042fdab8
--- /dev/null
+++ b/frida_mode/src/instrument.c
@@ -0,0 +1,265 @@
+#include "frida-gum.h"
+#include "config.h"
+#include "debug.h"
+#include "prefetch.h"
+#include "ranges.h"
+#include "unistd.h"
+
+extern uint8_t *__afl_area_ptr;
+extern u32 __afl_map_size;
+
+uint64_t __thread previous_pc = 0;
+GumAddress current_log_impl = GUM_ADDRESS(0);
+
+static gboolean tracing = false;
+static gboolean optimize = false;
+static gboolean strict = false;
+
+#if defined(__x86_64__)
+static const guint8 afl_log_code[] = {
+
+ 0x9c, /* pushfq */
+ 0x50, /* push rax */
+ 0x51, /* push rcx */
+ 0x52, /* push rdx */
+
+ 0x48, 0x8d, 0x05, 0x27,
+ 0x00, 0x00, 0x00, /* lea rax, sym._afl_area_ptr_ptr */
+ 0x48, 0x8b, 0x00, /* mov rax, qword [rax] */
+ 0x48, 0x8b, 0x00, /* mov rax, qword [rax] */
+ 0x48, 0x8d, 0x0d, 0x22,
+ 0x00, 0x00, 0x00, /* lea rcx, sym.previous_pc */
+ 0x48, 0x8b, 0x11, /* mov rdx, qword [rcx] */
+ 0x48, 0x8b, 0x12, /* mov rdx, qword [rdx] */
+ 0x48, 0x31, 0xfa, /* xor rdx, rdi */
+ 0xfe, 0x04, 0x10, /* inc byte [rax + rdx] */
+ 0x48, 0xd1, 0xef, /* shr rdi, 1 */
+ 0x48, 0x8b, 0x01, /* mov rax, qword [rcx] */
+ 0x48, 0x89, 0x38, /* mov qword [rax], rdi */
+
+ 0x5a, /* pop rdx */
+ 0x59, /* pop rcx */
+ 0x58, /* pop rax */
+ 0x9d, /* popfq */
+
+ 0xc3, /* ret */
+
+ /* Read-only data goes here: */
+ /* uint8_t** afl_area_ptr_ptr */
+ /* uint64_t* afl_prev_loc_ptr */
+
+};
+
+void instrument_coverage_optimize(const cs_insn * instr,
+ GumStalkerOutput *output) {
+
+ guint64 current_pc = instr->address;
+ guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
+ area_offset &= MAP_SIZE - 1;
+ GumX86Writer *cw = output->writer.x86;
+
+ if (current_log_impl == 0 ||
+ !gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) ||
+ !gum_x86_writer_can_branch_directly_between(cw->pc + 128,
+ current_log_impl)) {
+
+ gconstpointer after_log_impl = cw->code + 1;
+
+ gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
+
+ current_log_impl = cw->pc;
+ gum_x86_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
+
+ uint8_t **afl_area_ptr_ptr = &__afl_area_ptr;
+ uint64_t *afl_prev_loc_ptr = &previous_pc;
+ gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr,
+ sizeof(afl_area_ptr_ptr));
+ gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
+ sizeof(afl_prev_loc_ptr));
+
+ gum_x86_writer_put_label(cw, after_log_impl);
+
+ }
+
+ gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
+ -GUM_RED_ZONE_SIZE);
+ gum_x86_writer_put_push_reg(cw, GUM_REG_RDI);
+ gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDI, area_offset);
+ gum_x86_writer_put_call_address(cw, current_log_impl);
+ gum_x86_writer_put_pop_reg(cw, GUM_REG_RDI);
+ gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
+ GUM_RED_ZONE_SIZE);
+
+}
+
+#elif defined(__aarch64__)
+static const guint8 afl_log_code[] = {
+
+ // __afl_area_ptr[current_pc ^ previous_pc]++;
+ // previous_pc = current_pc >> 1;
+ 0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]!
+ 0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]!
+
+ // x0 = current_pc
+ 0xc1, 0x01, 0x00, 0x58, // ldr x1, #0x38, =&__afl_area_ptr
+ 0x21, 0x00, 0x40, 0xf9, // ldr x1, [x1] (=__afl_area_ptr)
+
+ 0xc2, 0x01, 0x00, 0x58, // ldr x2, #0x38, =&previous_pc
+ 0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2] (=previous_pc)
+
+ // __afl_area_ptr[current_pc ^ previous_pc]++;
+ 0x42, 0x00, 0x00, 0xca, // eor x2, x2, x0
+ 0x23, 0x68, 0x62, 0xf8, // ldr x3, [x1, x2]
+ 0x63, 0x04, 0x00, 0x91, // add x3, x3, #1
+ 0x23, 0x68, 0x22, 0xf8, // str x3, [x1, x2]
+
+ // previous_pc = current_pc >> 1;
+ 0xe0, 0x07, 0x40, 0x8b, // add x0, xzr, x0, LSR #1
+ 0xe2, 0x00, 0x00, 0x58, // ldr x2, #0x1c, =&previous_pc
+ 0x40, 0x00, 0x00, 0xf9, // str x0, [x2]
+
+ 0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10
+ 0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10
+ 0xC0, 0x03, 0x5F, 0xD6, // ret
+
+ // &afl_area_ptr_ptr
+ // &afl_prev_loc_ptr
+
+};
+
+void instrument_coverage_optimize(const cs_insn * instr,
+ GumStalkerOutput *output) {
+
+ guint64 current_pc = instr->address;
+ guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
+ area_offset &= MAP_SIZE - 1;
+ GumArm64Writer *cw = output->writer.arm64;
+
+ if (current_log_impl == 0 ||
+ !gum_arm64_writer_can_branch_directly_between(cw, cw->pc,
+ current_log_impl) ||
+ !gum_arm64_writer_can_branch_directly_between(cw, cw->pc + 128,
+ current_log_impl)) {
+
+ gconstpointer after_log_impl = cw->code + 1;
+
+ gum_arm64_writer_put_b_label(cw, after_log_impl);
+
+ current_log_impl = cw->pc;
+ gum_arm64_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
+
+ uint8_t **afl_area_ptr_ptr = &__afl_area_ptr;
+ uint64_t *afl_prev_loc_ptr = &previous_pc;
+ gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr,
+ sizeof(afl_area_ptr_ptr));
+ gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
+ sizeof(afl_prev_loc_ptr));
+
+ gum_arm64_writer_put_label(cw, after_log_impl);
+
+ }
+
+ gum_arm64_writer_put_stp_reg_reg_reg_offset(
+ cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, -(16 + GUM_RED_ZONE_SIZE),
+ GUM_INDEX_PRE_ADJUST);
+ gum_arm64_writer_put_ldr_reg_u64(cw, ARM64_REG_X0, area_offset);
+ gum_arm64_writer_put_bl_imm(cw, current_log_impl);
+ gum_arm64_writer_put_ldp_reg_reg_reg_offset(
+ cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE,
+ GUM_INDEX_POST_ADJUST);
+
+}
+
+#endif
+
+static void on_basic_block(GumCpuContext *context, gpointer user_data) {
+
+ /* Avoid stack operations in potentially performance critical code */
+ static char buffer[200];
+ int len;
+ guint64 current_pc = (guint64)user_data;
+ if (tracing) {
+
+ /* Avoid any functions which may cause an allocation since the target app
+ * may already be running inside malloc and it isn't designed to be
+ * re-entrant on a single thread */
+ len = snprintf(buffer, sizeof(buffer),
+ "current_pc: 0x%016" G_GINT64_MODIFIER
+ "x, previous_pc: 0x%016" G_GINT64_MODIFIER "x\n",
+ current_pc, previous_pc);
+
+ write(STDOUT_FILENO, buffer, len + 1);
+
+ }
+
+ current_pc = (current_pc >> 4) ^ (current_pc << 8);
+ current_pc &= MAP_SIZE - 1;
+
+ __afl_area_ptr[current_pc ^ previous_pc]++;
+ previous_pc = current_pc >> 1;
+
+}
+
+void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output,
+ gpointer user_data) {
+
+ const cs_insn *instr;
+ gboolean begin = TRUE;
+ while (gum_stalker_iterator_next(iterator, &instr)) {
+
+ if (begin) {
+
+ prefetch_write((void *)instr->address);
+ if (!strict || !range_is_excluded((void *)instr->address)) {
+
+ if (optimize) {
+
+ instrument_coverage_optimize(instr, output);
+
+ } else {
+
+ gum_stalker_iterator_put_callout(iterator, on_basic_block,
+ (gpointer)instr->address, NULL);
+
+ }
+
+ }
+
+ begin = FALSE;
+
+ }
+
+ gum_stalker_iterator_keep(iterator);
+
+ }
+
+}
+
+void instrument_init() {
+
+ optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
+ tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
+ strict = (getenv("AFL_FRIDA_INST_STRICT") != NULL);
+
+#if !defined(__x86_64__) && !defined(__aarch64__)
+ optimize = false;
+#endif
+
+ OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' ');
+ OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' ');
+ OKF("Instrumentation - strict [%c]", strict ? 'X' : ' ');
+
+ if (tracing && optimize) {
+
+ FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible");
+
+ }
+
+ if (__afl_map_size != 0x10000) {
+
+ FATAL("Bad map size: 0x%08x", __afl_map_size);
+
+ }
+
+}
+
diff --git a/frida_mode/src/interceptor.c b/frida_mode/src/interceptor.c
new file mode 100644
index 00000000..ba05a80a
--- /dev/null
+++ b/frida_mode/src/interceptor.c
@@ -0,0 +1,16 @@
+#include "frida-gum.h"
+#include "debug.h"
+
+#include "interceptor.h"
+
+void intercept(void *address, gpointer replacement, gpointer user_data) {
+
+ GumInterceptor *interceptor = gum_interceptor_obtain();
+ gum_interceptor_begin_transaction(interceptor);
+ GumReplaceReturn ret =
+ gum_interceptor_replace(interceptor, address, replacement, user_data);
+ if (ret != GUM_ATTACH_OK) { FATAL("gum_interceptor_attach: %d", ret); }
+ gum_interceptor_end_transaction(interceptor);
+
+}
+
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
new file mode 100644
index 00000000..444c9583
--- /dev/null
+++ b/frida_mode/src/main.c
@@ -0,0 +1,149 @@
+#include <unistd.h>
+#include <sys/types.h>
+
+#ifdef __APPLE__
+ #include <mach/mach.h>
+ #include <mach-o/dyld_images.h>
+#else
+ #include <sys/wait.h>
+ #include <sys/personality.h>
+#endif
+
+#include "frida-gum.h"
+#include "config.h"
+#include "debug.h"
+
+#include "interceptor.h"
+#include "instrument.h"
+#include "prefetch.h"
+#include "ranges.h"
+
+#ifdef __APPLE__
+extern mach_port_t mach_task_self();
+extern GumAddress gum_darwin_find_entrypoint(mach_port_t task);
+#else
+extern int __libc_start_main(int *(main)(int, char **, char **), int argc,
+ char **ubp_av, void (*init)(void),
+ void (*fini)(void), void (*rtld_fini)(void),
+ void(*stack_end));
+#endif
+
+typedef int *(*main_fn_t)(int argc, char **argv, char **envp);
+
+static main_fn_t main_fn = NULL;
+static GumStalker * stalker = NULL;
+static GumMemoryRange code_range = {0};
+
+extern void __afl_manual_init();
+extern __thread uint64_t previous_pc;
+
+static int on_fork() {
+
+ prefetch_read(stalker);
+ return fork();
+
+}
+
+#ifdef __APPLE__
+static void on_main_os(int argc, char **argv, char **envp) {
+
+}
+
+#else
+static void on_main_os(int argc, char **argv, char **envp) {
+
+ /* Personality doesn't affect the current process, it only takes effect on
+ * evec */
+ int persona = personality(ADDR_NO_RANDOMIZE);
+ if ((persona & ADDR_NO_RANDOMIZE) == 0) { execvpe(argv[0], argv, envp); }
+
+ GumInterceptor *interceptor = gum_interceptor_obtain();
+
+ gum_interceptor_begin_transaction(interceptor);
+ gum_interceptor_revert(interceptor, __libc_start_main);
+ gum_interceptor_end_transaction(interceptor);
+ gum_interceptor_flush(interceptor);
+
+}
+
+#endif
+
+static int *on_main(int argc, char **argv, char **envp) {
+
+ on_main_os(argc, argv, envp);
+
+ stalker = gum_stalker_new();
+ if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
+
+ gum_stalker_set_trust_threshold(stalker, 0);
+
+ GumStalkerTransformer *transformer =
+ gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL);
+
+ instrument_init();
+ prefetch_init();
+ ranges_init(stalker);
+
+ intercept(fork, on_fork, stalker);
+
+ gum_stalker_follow_me(stalker, transformer, NULL);
+ gum_stalker_deactivate(stalker);
+
+ __afl_manual_init();
+
+ /* Child here */
+ previous_pc = 0;
+ prefetch_start(stalker);
+ main_fn(argc, argv, envp);
+ _exit(0);
+
+}
+
+#ifdef __APPLE__
+static void intercept_main() {
+
+ mach_port_t task = mach_task_self();
+ OKF("Task Id: %u", task);
+ GumAddress entry = gum_darwin_find_entrypoint(task);
+ OKF("Entry Point: 0x%016" G_GINT64_MODIFIER "x", entry);
+ void *main = GSIZE_TO_POINTER(entry);
+ main_fn = main;
+ intercept(main, on_main, NULL);
+
+}
+
+#else
+static int on_libc_start_main(int *(main)(int, char **, char **), int argc,
+ char **ubp_av, void (*init)(void),
+ void (*fini)(void), void (*rtld_fini)(void),
+ void(*stack_end)) {
+
+ main_fn = main;
+ intercept(main, on_main, NULL);
+ return __libc_start_main(main, argc, ubp_av, init, fini, rtld_fini,
+ stack_end);
+
+}
+
+static void intercept_main() {
+
+ intercept(__libc_start_main, on_libc_start_main, NULL);
+
+}
+
+#endif
+
+__attribute__((constructor)) static void init() {
+
+ gum_init_embedded();
+ if (!gum_stalker_is_supported()) {
+
+ gum_deinit_embedded();
+ FATAL("Failed to initialize embedded");
+
+ }
+
+ intercept_main();
+
+}
+
diff --git a/frida_mode/src/prefetch.c b/frida_mode/src/prefetch.c
new file mode 100644
index 00000000..64633c1c
--- /dev/null
+++ b/frida_mode/src/prefetch.c
@@ -0,0 +1,121 @@
+#include <errno.h>
+#include <sys/shm.h>
+#include <sys/mman.h>
+
+#include "frida-gum.h"
+#include "prefetch.h"
+#include "debug.h"
+
+#define TRUST 0
+#define PREFETCH_SIZE 65536
+#define PREFETCH_ENTRIES ((PREFETCH_SIZE - sizeof(size_t)) / sizeof(void *))
+
+typedef struct {
+
+ size_t count;
+ void * entry[PREFETCH_ENTRIES];
+
+} prefetch_data_t;
+
+static prefetch_data_t *prefetch_data = NULL;
+
+static int prefetch_shm_id = -1;
+
+/*
+ * We do this from the transformer since we need one anyway for coverage, this
+ * saves the need to use an event sink.
+ */
+void prefetch_write(void *addr) {
+
+ /* Bail if we aren't initialized */
+ if (prefetch_data == NULL) return;
+
+ /*
+ * Our shared memory IPC is large enough for about 1000 entries, we can fine
+ * tune this if we need to. But if we have more new blocks that this in a
+ * single run then we ignore them and we'll pick them up next time.
+ */
+ if (prefetch_data->count >= PREFETCH_ENTRIES) return;
+
+ /*
+ * Write the block address to the SHM IPC and increment the number of entries.
+ */
+
+ prefetch_data->entry[prefetch_data->count] = addr;
+ prefetch_data->count++;
+
+}
+
+/*
+ * Read the IPC region one block at the time and prefetch it
+ */
+void prefetch_read(GumStalker *stalker) {
+
+ if (prefetch_data == NULL) return;
+
+ for (size_t i = 0; i < prefetch_data->count; i++) {
+
+ void *addr = prefetch_data->entry[i];
+ gum_stalker_prefetch(stalker, addr, 1);
+
+ }
+
+ /*
+ * Reset the entry count to indicate we have finished with it and it can be
+ * refilled by the child.
+ */
+ prefetch_data->count = 0;
+
+}
+
+void prefetch_init() {
+
+ g_assert_cmpint(sizeof(prefetch_data_t), ==, PREFETCH_SIZE);
+ gboolean prefetch = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL);
+
+ OKF("Instrumentation - prefetch [%c]", prefetch ? 'X' : ' ');
+
+ if (!prefetch) { return; }
+ /*
+ * Make our shared memory, we can attach before we fork, just like AFL does
+ * with the coverage bitmap region and fork will take care of ensuring both
+ * the parent and child see the same consistent memory region.
+ */
+ prefetch_shm_id =
+ shmget(IPC_PRIVATE, sizeof(prefetch_data_t), IPC_CREAT | IPC_EXCL | 0600);
+ if (prefetch_shm_id < 0) {
+
+ FATAL("prefetch_shm_id < 0 - errno: %d\n", errno);
+
+ }
+
+ prefetch_data = shmat(prefetch_shm_id, NULL, 0);
+ g_assert(prefetch_data != MAP_FAILED);
+
+ /*
+ * Configure the shared memory region to be removed once the process dies.
+ */
+ if (shmctl(prefetch_shm_id, IPC_RMID, NULL) < 0) {
+
+ FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
+
+ }
+
+ /* Clear it, not sure it's necessary, just seems like good practice */
+ memset(prefetch_data, '\0', sizeof(prefetch_data_t));
+
+}
+
+__attribute__((noinline)) static void prefetch_activation() {
+
+ asm volatile("");
+
+}
+
+void prefetch_start(GumStalker *stalker) {
+
+ gum_stalker_activate(stalker, prefetch_activation);
+ prefetch_activation();
+
+}
+
diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c
new file mode 100644
index 00000000..fc14710f
--- /dev/null
+++ b/frida_mode/src/ranges.c
@@ -0,0 +1,395 @@
+// 0x123-0x321
+// module.so
+
+#include "ranges.h"
+#include "debug.h"
+
+#define MAX_RANGES 20
+
+typedef struct {
+
+ gchar * suffix;
+ GumMemoryRange *range;
+ gboolean done;
+
+} convert_name_ctx_t;
+
+typedef struct {
+
+ GumStalker *stalker;
+ GArray * array;
+
+} include_range_ctx_t;
+
+GArray * ranges = NULL;
+gboolean exclude_ranges = false;
+
+static void convert_address_token(gchar *token, GumMemoryRange *range) {
+
+ gchar **tokens;
+ int token_count;
+ tokens = g_strsplit(token, "-", 2);
+ for (token_count = 0; tokens[token_count] != NULL; token_count++)
+ ;
+
+ if (token_count != 2) {
+
+ FATAL("Invalid range (should have two addresses seperated by a '-'): %s\n",
+ token);
+
+ }
+
+ gchar *from_str = tokens[0];
+ gchar *to_str = tokens[1];
+
+ if (!g_str_has_prefix(from_str, "0x")) {
+
+ FATAL("Invalid range: %s - Start address should have 0x prefix: %s\n",
+ token, from_str);
+
+ }
+
+ if (!g_str_has_prefix(to_str, "0x")) {
+
+ FATAL("Invalid range: %s - End address should have 0x prefix: %s\n", token,
+ to_str);
+
+ }
+
+ from_str = &from_str[2];
+ to_str = &to_str[2];
+
+ for (char *c = from_str; *c != '\0'; c++) {
+
+ if (!g_ascii_isxdigit(*c)) {
+
+ FATAL("Invalid range: %s - Start address not formed of hex digits: %s\n",
+ token, from_str);
+
+ }
+
+ }
+
+ for (char *c = to_str; *c != '\0'; c++) {
+
+ if (!g_ascii_isxdigit(*c)) {
+
+ FATAL("Invalid range: %s - End address not formed of hex digits: %s\n",
+ token, to_str);
+
+ }
+
+ }
+
+ guint64 from = g_ascii_strtoull(from_str, NULL, 16);
+ if (from == 0) {
+
+ FATAL("Invalid range: %s - Start failed hex conversion: %s\n", token,
+ from_str);
+
+ }
+
+ guint64 to = g_ascii_strtoull(to_str, NULL, 16);
+ if (to == 0) {
+
+ FATAL("Invalid range: %s - End failed hex conversion: %s\n", token, to_str);
+
+ }
+
+ if (from >= to) {
+
+ FATAL("Invalid range: %s - Start (0x%016" G_GINT64_MODIFIER
+ "x) must be less than end "
+ "(0x%016" G_GINT64_MODIFIER "x)\n",
+ token, from, to);
+
+ }
+
+ range->base_address = from;
+ range->size = to - from;
+
+ g_strfreev(tokens);
+
+}
+
+static gboolean convert_name_token_for_module(const GumModuleDetails *details,
+ gpointer user_data) {
+
+ convert_name_ctx_t *ctx = (convert_name_ctx_t *)user_data;
+ if (details->path == NULL) { return true; };
+
+ if (!g_str_has_suffix(details->path, ctx->suffix)) { return true; };
+
+ OKF("Found module - prefix: %s, 0x%016" G_GINT64_MODIFIER
+ "x-0x%016" G_GINT64_MODIFIER "x %s",
+ ctx->suffix, details->range->base_address,
+ details->range->base_address + details->range->size, details->path);
+
+ *ctx->range = *details->range;
+ ctx->done = true;
+ return false;
+
+}
+
+static void convert_name_token(gchar *token, GumMemoryRange *range) {
+
+ gchar * suffix = g_strconcat("/", token, NULL);
+ convert_name_ctx_t ctx = {.suffix = suffix, .range = range, .done = false};
+
+ gum_process_enumerate_modules(convert_name_token_for_module, &ctx);
+ if (!ctx.done) { FATAL("Failed to resolve module: %s\n", token); }
+ g_free(suffix);
+
+}
+
+static void convert_token(gchar *token, GumMemoryRange *range) {
+
+ if (g_strrstr(token, "-")) {
+
+ convert_address_token(token, range);
+
+ } else {
+
+ convert_name_token(token, range);
+
+ }
+
+ OKF("Converted token: %s -> 0x%016" G_GINT64_MODIFIER
+ "x-0x%016" G_GINT64_MODIFIER "x\n",
+ token, range->base_address, range->base_address + range->size);
+
+}
+
+static gboolean include_ranges(const GumRangeDetails *details,
+ gpointer user_data) {
+
+ include_range_ctx_t *ctx = (include_range_ctx_t *)user_data;
+ GArray * array = (GArray *)ctx->array;
+ GumAddress base = details->range->base_address;
+ GumAddress limit = details->range->base_address + details->range->size;
+
+ OKF("Range for inclusion 0x%016" G_GINT64_MODIFIER
+ "x-0x%016" G_GINT64_MODIFIER "x",
+ base, limit);
+
+ for (int i = 0; i < array->len; i++) {
+
+ GumMemoryRange *range = &g_array_index(array, GumMemoryRange, i);
+ GumAddress range_base = range->base_address;
+ GumAddress range_limit = range->base_address + range->size;
+
+ /* Before the region */
+ if (range_limit < base) { continue; }
+
+ /* After the region */
+ if (range_base > limit) {
+
+ GumMemoryRange exclude = {.base_address = base, .size = limit - base};
+ OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER
+ "x",
+ base, limit);
+ gum_stalker_exclude(ctx->stalker, &exclude);
+ return true;
+
+ }
+
+ /* Overlap the start of the region */
+ if (range_base < base) {
+
+ /* Range contains the region */
+ if (range_limit > limit) {
+
+ return true;
+
+ } else {
+
+ base = range_limit;
+ continue;
+
+ }
+
+ /* Overlap the end of the region */
+
+ } else {
+
+ GumMemoryRange exclude = {.base_address = base,
+ .size = range_base - base};
+ OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER
+ "x",
+ base, range_base);
+ gum_stalker_exclude(ctx->stalker, &exclude);
+ /* Extend past the end of the region */
+ if (range_limit >= limit) {
+
+ return true;
+
+ /* Contained within the region */
+
+ } else {
+
+ base = range_limit;
+ continue;
+
+ }
+
+ }
+
+ }
+
+ GumMemoryRange exclude = {.base_address = base, .size = limit - base};
+ OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x",
+ base, limit);
+ gum_stalker_exclude(ctx->stalker, &exclude);
+ return true;
+
+}
+
+gint range_sort(gconstpointer a, gconstpointer b) {
+
+ return ((GumMemoryRange *)a)->base_address -
+ ((GumMemoryRange *)b)->base_address;
+
+}
+
+static gboolean print_ranges(const GumRangeDetails *details,
+ gpointer user_data) {
+
+ if (details->file == NULL) {
+
+ OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER "X",
+ details->range->base_address,
+ details->range->base_address + details->range->size);
+
+ } else {
+
+ OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
+ "X %s(0x%016" G_GINT64_MODIFIER "x)",
+ details->range->base_address,
+ details->range->base_address + details->range->size,
+ details->file->path, details->file->offset);
+
+ }
+
+ return true;
+
+}
+
+void ranges_init(GumStalker *stalker) {
+
+ char * showmaps;
+ char * include;
+ char * exclude;
+ char * list;
+ gchar ** tokens;
+ int token_count;
+ GumMemoryRange range;
+
+ int i;
+
+ showmaps = getenv("AFL_FRIDA_DEBUG_MAPS");
+ include = getenv("AFL_FRIDA_INST_RANGES");
+ exclude = getenv("AFL_FRIDA_EXCLUDE_RANGES");
+
+ if (showmaps) {
+
+ gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges, NULL);
+
+ }
+
+ if (include != NULL && exclude != NULL) {
+
+ FATAL(
+ "Cannot specifify both AFL_FRIDA_INST_RANGES and "
+ "AFL_FRIDA_EXCLUDE_RANGES");
+
+ }
+
+ if (include == NULL && exclude == NULL) { return; }
+
+ list = include == NULL ? exclude : include;
+ exclude_ranges = include == NULL ? true : false;
+
+ tokens = g_strsplit(list, ",", MAX_RANGES);
+
+ for (token_count = 0; tokens[token_count] != NULL; token_count++)
+ ;
+
+ ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), token_count);
+
+ for (i = 0; i < token_count; i++) {
+
+ convert_token(tokens[i], &range);
+ g_array_append_val(ranges, range);
+
+ }
+
+ g_array_sort(ranges, range_sort);
+
+ /* Check for overlaps */
+ for (i = 1; i < token_count; i++) {
+
+ GumMemoryRange *prev = &g_array_index(ranges, GumMemoryRange, i - 1);
+ GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
+ GumAddress prev_limit = prev->base_address + prev->size;
+ GumAddress curr_limit = curr->base_address + curr->size;
+ if (prev_limit > curr->base_address) {
+
+ FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER
+ "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER
+ "x-0x%016" G_GINT64_MODIFIER "x",
+ prev->base_address, prev_limit, curr->base_address, curr_limit);
+
+ }
+
+ }
+
+ for (i = 0; i < token_count; i++) {
+
+ GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
+ GumAddress curr_limit = curr->base_address + curr->size;
+ OKF("Range %3d - 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x",
+ i, curr->base_address, curr_limit);
+
+ }
+
+ if (include == NULL) {
+
+ for (i = 0; i < token_count; i++) {
+
+ gum_stalker_exclude(stalker, &g_array_index(ranges, GumMemoryRange, i));
+
+ }
+
+ } else {
+
+ include_range_ctx_t ctx = {.stalker = stalker, .array = ranges};
+ gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, include_ranges, &ctx);
+
+ }
+
+ g_strfreev(tokens);
+
+}
+
+gboolean range_is_excluded(gpointer address) {
+
+ int i;
+ GumAddress test = GUM_ADDRESS(address);
+
+ if (ranges == NULL) { return false; }
+
+ for (i = 0; i < ranges->len; i++) {
+
+ GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
+ GumAddress curr_limit = curr->base_address + curr->size;
+
+ if (test < curr->base_address) { return !exclude_ranges; }
+
+ if (test < curr_limit) { return exclude_ranges; }
+
+ }
+
+ return !exclude_ranges;
+
+}
+
diff --git a/frida_mode/test/testinstr.c b/frida_mode/test/testinstr.c
new file mode 100644
index 00000000..2c3d5144
--- /dev/null
+++ b/frida_mode/test/testinstr.c
@@ -0,0 +1,105 @@
+/*
+ american fuzzy lop++ - a trivial program to test the build
+ --------------------------------------------------------
+ Originally written by Michal Zalewski
+ Copyright 2014 Google Inc. All rights reserved.
+ Copyright 2019-2020 AFLplusplus Project. All rights reserved.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+ http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef __APPLE__
+ #define TESTINSTR_SECTION
+#else
+ #define TESTINSTR_SECTION __attribute__((section(".testinstr")))
+#endif
+
+TESTINSTR_SECTION void testinstr(char *buf, int len) {
+
+ if (len < 1) return;
+ buf[len] = 0;
+
+ // we support three input cases
+ if (buf[0] == '0')
+ printf("Looks like a zero to me!\n");
+ else if (buf[0] == '1')
+ printf("Pretty sure that is a one!\n");
+ else
+ printf("Neither one or zero? How quaint!\n");
+
+}
+
+int main(int argc, char **argv) {
+
+ char * file;
+ int fd = -1;
+ off_t len;
+ char * buf = NULL;
+ size_t n_read;
+ int result = -1;
+
+ if (argc != 2) { return 1; }
+
+ do {
+
+ file = argv[1];
+
+ dprintf(STDERR_FILENO, "Running: %s\n", file);
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+
+ perror("open");
+ break;
+
+ }
+
+ len = lseek(fd, 0, SEEK_END);
+ if (len < 0) {
+
+ perror("lseek (SEEK_END)");
+ break;
+
+ }
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+
+ perror("lseek (SEEK_SET)");
+ break;
+
+ }
+
+ buf = malloc(len);
+ n_read = read(fd, buf, len);
+ if (n_read != len) {
+
+ perror("read");
+ break;
+
+ }
+
+ dprintf(STDERR_FILENO, "Running: %s: (%zd bytes)\n", file, n_read);
+
+ testinstr(buf, len);
+ dprintf(STDERR_FILENO, "Done: %s: (%zd bytes)\n", file, n_read);
+
+ result = 0;
+
+ } while (false);
+
+ if (buf != NULL) { free(buf); }
+
+ if (fd != -1) { close(fd); }
+
+ return result;
+
+}
+
diff --git a/frida_mode/test/testinstr.py b/frida_mode/test/testinstr.py
new file mode 100755
index 00000000..8f5fe886
--- /dev/null
+++ b/frida_mode/test/testinstr.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python3
+import argparse
+from elftools.elf.elffile import ELFFile
+
+def process_file(file, section, base):
+ with open(file, 'rb') as f:
+ for sect in ELFFile(f).iter_sections():
+ if (sect.name == section):
+ start = base + sect.header['sh_offset']
+ end = start + sect.header['sh_size']
+ print ("0x%016x-0x%016x" % (start, end))
+ return
+
+ print ("Section '%s' not found in '%s'" % (section, file))
+
+def hex_value(x):
+ return int(x, 16)
+
+def main():
+ parser = argparse.ArgumentParser(description='Process some integers.')
+ parser.add_argument('-f', '--file', dest='file', type=str,
+ help='elf file name', required=True)
+ parser.add_argument('-s', '--section', dest='section', type=str,
+ help='elf section name', required=True)
+ parser.add_argument('-b', '--base', dest='base', type=hex_value,
+ help='elf base address', required=True)
+
+ args = parser.parse_args()
+ process_file (args.file, args.section, args.base)
+
+if __name__ == "__main__":
+ main() \ No newline at end of file
diff --git a/include/envs.h b/include/envs.h
index 37adeff2..f7c8b460 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -50,6 +50,13 @@ static char *afl_environment_variables[] = {
"AFL_EXIT_WHEN_DONE",
"AFL_FAST_CAL",
"AFL_FORCE_UI",
+ "AFL_FRIDA_DEBUG_MAPS",
+ "AFL_FRIDA_EXCLUDE_RANGES",
+ "AFL_FRIDA_INST_NO_OPTIMIZE",
+ "AFL_FRIDA_INST_NO_PREFETCH",
+ "AFL_FRIDA_INST_RANGES",
+ "AFL_FRIDA_INST_STRICT",
+ "AFL_FRIDA_INST_TRACE",
"AFL_FUZZER_ARGS", // oss-fuzz
"AFL_GDB",
"AFL_GCC_ALLOWLIST",
diff --git a/include/forkserver.h b/include/forkserver.h
index 808f6bd2..cc759545 100644
--- a/include/forkserver.h
+++ b/include/forkserver.h
@@ -77,6 +77,8 @@ typedef struct afl_forkserver {
bool qemu_mode; /* if running in qemu mode or not */
+ bool frida_mode; /* if running in frida mode or not */
+
bool use_stdin; /* use stdin for sending data */
bool no_unlink; /* do not unlink cur_input */
diff --git a/src/afl-analyze.c b/src/afl-analyze.c
index e106cd31..8e5a1772 100644
--- a/src/afl-analyze.c
+++ b/src/afl-analyze.c
@@ -83,6 +83,7 @@ static volatile u8 stop_soon, /* Ctrl-C pressed? */
child_timed_out; /* Child timed out? */
static u8 *target_path;
+static u8 frida_mode;
static u8 qemu_mode;
static u32 map_size = MAP_SIZE;
@@ -717,9 +718,11 @@ static void handle_stop_sig(int sig) {
/* Do basic preparations - persistent fds, filenames, etc. */
-static void set_up_environment(void) {
+static void set_up_environment(char **argv) {
- u8 *x;
+ u8 * x;
+ char *afl_preload;
+ char *frida_afl_preload = NULL;
dev_null_fd = open("/dev/null", O_RDWR);
if (dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
@@ -824,6 +827,25 @@ static void set_up_environment(void) {
/* afl-qemu-trace takes care of converting AFL_PRELOAD. */
+ } else if (frida_mode) {
+
+ afl_preload = getenv("AFL_PRELOAD");
+ u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
+ if (afl_preload) {
+
+ frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
+
+ } else {
+
+ frida_afl_preload = alloc_printf("%s", frida_binary);
+
+ }
+
+ ck_free(frida_binary);
+
+ setenv("LD_PRELOAD", frida_afl_preload, 1);
+ setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
+
} else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
@@ -831,8 +853,17 @@ static void set_up_environment(void) {
}
+ } else if (frida_mode) {
+
+ 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);
+ ck_free(frida_binary);
+
}
+ if (frida_afl_preload) { ck_free(frida_afl_preload); }
+
}
/* Setup signal handlers, duh. */
@@ -872,6 +903,7 @@ static void usage(u8 *argv0) {
" -f file - input file read by the tested program (stdin)\n"
" -t msec - timeout for each run (%u ms)\n"
" -m megs - memory limit for child process (%u MB)\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"
" -W - use qemu-based instrumentation with Wine (Wine "
@@ -914,7 +946,7 @@ int main(int argc, char **argv_orig, char **envp) {
SAYF(cCYA "afl-analyze" VERSION cRST " by Michal Zalewski\n");
- while ((opt = getopt(argc, argv, "+i:f:m:t:eQUWh")) > 0) {
+ while ((opt = getopt(argc, argv, "+i:f:m:t:eOQUWh")) > 0) {
switch (opt) {
@@ -1008,6 +1040,14 @@ int main(int argc, char **argv_orig, char **envp) {
break;
+ case 'O': /* FRIDA mode */
+
+ if (frida_mode) { FATAL("Multiple -O options not supported"); }
+
+ frida_mode = 1;
+
+ break;
+
case 'Q':
if (qemu_mode) { FATAL("Multiple -Q options not supported"); }
@@ -1062,7 +1102,7 @@ int main(int argc, char **argv_orig, char **envp) {
atexit(at_exit_handler);
setup_signal_handlers();
- set_up_environment();
+ set_up_environment(argv);
target_path = find_binary(argv[optind]);
detect_file_args(argv + optind, prog_in, &use_stdin);
diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c
index 70a49a6b..cb0190a0 100644
--- a/src/afl-fuzz-init.c
+++ b/src/afl-fuzz-init.c
@@ -2692,7 +2692,7 @@ void check_binary(afl_state_t *afl, u8 *fname) {
#endif /* ^!__APPLE__ */
- if (!afl->fsrv.qemu_mode && !afl->unicorn_mode &&
+ if (!afl->fsrv.qemu_mode && !afl->fsrv.frida_mode && !afl->unicorn_mode &&
!afl->non_instrumented_mode &&
!memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) {
@@ -2720,7 +2720,7 @@ void check_binary(afl_state_t *afl, u8 *fname) {
}
- if ((afl->fsrv.qemu_mode) &&
+ if ((afl->fsrv.qemu_mode || afl->fsrv.frida_mode) &&
memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) {
SAYF("\n" cLRD "[-] " cRST
@@ -2757,7 +2757,8 @@ void check_binary(afl_state_t *afl, u8 *fname) {
}
- if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) {
+ if (afl->fsrv.frida_mode ||
+ memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) {
OKF(cPIN "Deferred forkserver binary detected.");
setenv(DEFER_ENV_VAR, "1", 1);
diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c
index 75f97719..a7edb924 100644
--- a/src/afl-fuzz.c
+++ b/src/afl-fuzz.c
@@ -109,6 +109,7 @@ static void usage(u8 *argv0, int more_help) {
"maximum.\n"
" -m megs - memory limit for child process (%u MB, 0 = no limit "
"[default])\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"
" -W - use qemu-based instrumentation with Wine (Wine "
@@ -329,6 +330,8 @@ int main(int argc, char **argv_orig, char **envp) {
u8 *extras_dir[4];
u8 mem_limit_given = 0, exit_1 = 0, debug = 0,
extras_dir_cnt = 0 /*, have_p = 0*/;
+ char * afl_preload;
+ char * frida_afl_preload = NULL;
char **use_argv;
struct timeval tv;
@@ -372,7 +375,7 @@ int main(int argc, char **argv_orig, char **envp) {
while ((opt = getopt(
argc, argv,
- "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:RQs:S:t:T:UV:Wx:Z")) > 0) {
+ "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNOo:p:RQs:S:t:T:UV:Wx:Z")) > 0) {
switch (opt) {
@@ -764,6 +767,18 @@ int main(int argc, char **argv_orig, char **envp) {
afl->use_banner = optarg;
break;
+ case 'O': /* FRIDA mode */
+
+ if (afl->fsrv.frida_mode) {
+
+ FATAL("Multiple -O options not supported");
+
+ }
+
+ afl->fsrv.frida_mode = 1;
+
+ break;
+
case 'Q': /* QEMU mode */
if (afl->fsrv.qemu_mode) { FATAL("Multiple -Q options not supported"); }
@@ -1118,6 +1133,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (afl->non_instrumented_mode) {
if (afl->crash_mode) { FATAL("-C and -n are mutually exclusive"); }
+ if (afl->fsrv.frida_mode) { FATAL("-O and -n are mutually exclusive"); }
if (afl->fsrv.qemu_mode) { FATAL("-Q and -n are mutually exclusive"); }
if (afl->unicorn_mode) { FATAL("-U and -n are mutually exclusive"); }
@@ -1322,6 +1338,25 @@ int main(int argc, char **argv_orig, char **envp) {
/* afl-qemu-trace takes care of converting AFL_PRELOAD. */
+ } else if (afl->fsrv.frida_mode) {
+
+ afl_preload = getenv("AFL_PRELOAD");
+ u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
+ if (afl_preload) {
+
+ frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
+
+ } else {
+
+ frida_afl_preload = alloc_printf("%s", frida_binary);
+
+ }
+
+ ck_free(frida_binary);
+
+ setenv("LD_PRELOAD", frida_afl_preload, 1);
+ setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
+
} else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
@@ -1329,6 +1364,13 @@ int main(int argc, char **argv_orig, char **envp) {
}
+ } else if (afl->fsrv.frida_mode) {
+
+ 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);
+ ck_free(frida_binary);
+
}
if (getenv("AFL_LD_PRELOAD")) {
@@ -1512,7 +1554,8 @@ int main(int argc, char **argv_orig, char **envp) {
}
- if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) {
+ if (!afl->fsrv.qemu_mode && !afl->fsrv.frida_mode &&
+ !afl->non_instrumented_mode) {
check_binary(afl, afl->cmplog_binary);
@@ -1563,7 +1606,8 @@ int main(int argc, char **argv_orig, char **envp) {
}
- if (afl->non_instrumented_mode || afl->fsrv.qemu_mode || afl->unicorn_mode) {
+ if (afl->non_instrumented_mode || afl->fsrv.qemu_mode ||
+ afl->fsrv.frida_mode || afl->unicorn_mode) {
map_size = afl->fsrv.map_size = MAP_SIZE;
afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size);
@@ -2124,6 +2168,8 @@ stop_fuzzing:
}
+ if (frida_afl_preload) { ck_free(frida_afl_preload); }
+
fclose(afl->fsrv.plot_file);
destroy_queue(afl);
destroy_extras(afl);
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index bedf7806..38d03d80 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -555,8 +555,10 @@ static void handle_stop_sig(int sig) {
/* Do basic preparations - persistent fds, filenames, etc. */
-static void set_up_environment(afl_forkserver_t *fsrv) {
+static void set_up_environment(afl_forkserver_t *fsrv, char **argv) {
+ char *afl_preload;
+ char *frida_afl_preload = NULL;
setenv("ASAN_OPTIONS",
"abort_on_error=1:"
"detect_leaks=0:"
@@ -600,6 +602,25 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
/* afl-qemu-trace takes care of converting AFL_PRELOAD. */
+ } else if (fsrv->frida_mode) {
+
+ afl_preload = getenv("AFL_PRELOAD");
+ u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
+ if (afl_preload) {
+
+ frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
+
+ } else {
+
+ frida_afl_preload = alloc_printf("%s", frida_binary);
+
+ }
+
+ ck_free(frida_binary);
+
+ setenv("LD_PRELOAD", frida_afl_preload, 1);
+ setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
+
} else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
@@ -607,8 +628,17 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
}
+ } else if (fsrv->frida_mode) {
+
+ 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);
+ ck_free(frida_binary);
+
}
+ if (frida_afl_preload) { ck_free(frida_afl_preload); }
+
}
/* Setup signal handlers, duh. */
@@ -655,6 +685,7 @@ static void usage(u8 *argv0) {
"Execution control settings:\n"
" -t msec - timeout for each run (none)\n"
" -m megs - memory limit for child process (%u MB)\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"
" -W - use qemu-based instrumentation with Wine (Wine mode)\n"
@@ -723,7 +754,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; }
- while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrsh")) > 0) {
+ while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZOQUWbcrsh")) > 0) {
switch (opt) {
@@ -857,6 +888,14 @@ int main(int argc, char **argv_orig, char **envp) {
at_file = optarg;
break;
+ case 'O': /* FRIDA mode */
+
+ if (fsrv->frida_mode) { FATAL("Multiple -O options not supported"); }
+
+ fsrv->frida_mode = 1;
+
+ break;
+
case 'Q':
if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); }
@@ -943,7 +982,7 @@ int main(int argc, char **argv_orig, char **envp) {
shm.cmplog_mode = 0;
setup_signal_handlers();
- set_up_environment(fsrv);
+ set_up_environment(fsrv, argv);
fsrv->target_path = find_binary(argv[optind]);
fsrv->trace_bits = afl_shm_init(&shm, map_size, 0);
diff --git a/src/afl-tmin.c b/src/afl-tmin.c
index fc974262..bad5d71b 100644
--- a/src/afl-tmin.c
+++ b/src/afl-tmin.c
@@ -640,9 +640,11 @@ static void handle_stop_sig(int sig) {
/* Do basic preparations - persistent fds, filenames, etc. */
-static void set_up_environment(afl_forkserver_t *fsrv) {
+static void set_up_environment(afl_forkserver_t *fsrv, char **argv) {
- u8 *x;
+ u8 * x;
+ char *afl_preload;
+ char *frida_afl_preload = NULL;
fsrv->dev_null_fd = open("/dev/null", O_RDWR);
if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
@@ -755,6 +757,25 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
/* afl-qemu-trace takes care of converting AFL_PRELOAD. */
+ } else if (fsrv->frida_mode) {
+
+ afl_preload = getenv("AFL_PRELOAD");
+ u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
+ if (afl_preload) {
+
+ frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
+
+ } else {
+
+ frida_afl_preload = alloc_printf("%s", frida_binary);
+
+ }
+
+ ck_free(frida_binary);
+
+ setenv("LD_PRELOAD", frida_afl_preload, 1);
+ setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
+
} else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
@@ -762,8 +783,17 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
}
+ } else if (fsrv->frida_mode) {
+
+ 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);
+ ck_free(frida_binary);
+
}
+ if (frida_afl_preload) { ck_free(frida_afl_preload); }
+
}
/* Setup signal handlers, duh. */
@@ -804,6 +834,7 @@ static void usage(u8 *argv0) {
" -f file - input file read by the tested program (stdin)\n"
" -t msec - timeout for each run (%u ms)\n"
" -m megs - memory limit for child process (%u MB)\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"
" -W - use qemu-based instrumentation with Wine (Wine "
@@ -859,7 +890,7 @@ int main(int argc, char **argv_orig, char **envp) {
SAYF(cCYA "afl-tmin" VERSION cRST " by Michal Zalewski\n");
- while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeQUWHh")) > 0) {
+ while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeOQUWHh")) > 0) {
switch (opt) {
@@ -971,6 +1002,14 @@ int main(int argc, char **argv_orig, char **envp) {
break;
+ case 'O': /* FRIDA mode */
+
+ if (fsrv->frida_mode) { FATAL("Multiple -O options not supported"); }
+
+ fsrv->frida_mode = 1;
+
+ break;
+
case 'Q':
if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); }
@@ -1054,7 +1093,7 @@ int main(int argc, char **argv_orig, char **envp) {
atexit(at_exit_handler);
setup_signal_handlers();
- set_up_environment(fsrv);
+ set_up_environment(fsrv, argv);
fsrv->target_path = find_binary(argv[optind]);
fsrv->trace_bits = afl_shm_init(&shm, map_size, 0);