aboutsummaryrefslogtreecommitdiff
path: root/frida_mode
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2021-03-25 19:42:27 +0100
committerGitHub <noreply@github.com>2021-03-25 19:42:27 +0100
commit00a53a870d7ccd24e13e6cb2bbbd7535964f7737 (patch)
tree18a123e6c48bb9d3ffd31e87c818c35da60cb562 /frida_mode
parent0029c1a83ef03825c2d19c73151189f159458496 (diff)
parent1725e6be316b57e89df2a077710b66b684b55242 (diff)
downloadafl++-00a53a870d7ccd24e13e6cb2bbbd7535964f7737.tar.gz
Merge pull request #833 from WorksButNotTested/frida
Frida
Diffstat (limited to 'frida_mode')
-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
14 files changed, 1595 insertions, 0 deletions
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