Merge pull request #833 from WorksButNotTested/frida

Frida
This commit is contained in:
van Hauser 2021-03-25 19:42:27 +01:00 committed by GitHub
commit 00a53a870d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1803 additions and 21 deletions

1
.gitignore vendored
View File

@ -82,3 +82,4 @@ libAFLDriver.a
libAFLQemuDriver.a libAFLQemuDriver.a
test/.afl_performance test/.afl_performance
gmon.out gmon.out
afl-frida-trace.so

View File

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

View File

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

5
frida_mode/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
build/
frida_test.dat
qemu_test.dat
frida_out/**
qemu_out/**

350
frida_mode/Makefile Normal file
View File

@ -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) @@

135
frida_mode/README.md Normal file
View File

@ -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.

View File

@ -0,0 +1,7 @@
#include "frida-gum.h"
void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output,
gpointer user_data);
void instrument_init();

View File

@ -0,0 +1,4 @@
#include "frida-gum.h"
void intercept(void *address, gpointer replacement, gpointer user_data);

View File

@ -0,0 +1,5 @@
void prefetch_init();
void prefetch_start(GumStalker *stalker);
void prefetch_write(void *addr);
void prefetch_read(GumStalker *stalker);

6
frida_mode/inc/ranges.h Normal file
View File

@ -0,0 +1,6 @@
#include "frida-gum.h"
void ranges_init(GumStalker *stalker);
gboolean range_is_excluded(gpointer address);

265
frida_mode/src/instrument.c Normal file
View File

@ -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);
}
}

View File

@ -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);
}

149
frida_mode/src/main.c Normal file
View File

@ -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();
}

121
frida_mode/src/prefetch.c Normal file
View File

@ -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();
}

395
frida_mode/src/ranges.c Normal file
View File

@ -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;
}

105
frida_mode/test/testinstr.c Normal file
View File

@ -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;
}

32
frida_mode/test/testinstr.py Executable file
View File

@ -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()

View File

@ -50,6 +50,13 @@ static char *afl_environment_variables[] = {
"AFL_EXIT_WHEN_DONE", "AFL_EXIT_WHEN_DONE",
"AFL_FAST_CAL", "AFL_FAST_CAL",
"AFL_FORCE_UI", "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_FUZZER_ARGS", // oss-fuzz
"AFL_GDB", "AFL_GDB",
"AFL_GCC_ALLOWLIST", "AFL_GCC_ALLOWLIST",

View File

@ -77,6 +77,8 @@ typedef struct afl_forkserver {
bool qemu_mode; /* if running in qemu mode or not */ 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 use_stdin; /* use stdin for sending data */
bool no_unlink; /* do not unlink cur_input */ bool no_unlink; /* do not unlink cur_input */

View File

@ -83,6 +83,7 @@ static volatile u8 stop_soon, /* Ctrl-C pressed? */
child_timed_out; /* Child timed out? */ child_timed_out; /* Child timed out? */
static u8 *target_path; static u8 *target_path;
static u8 frida_mode;
static u8 qemu_mode; static u8 qemu_mode;
static u32 map_size = MAP_SIZE; static u32 map_size = MAP_SIZE;
@ -717,9 +718,11 @@ static void handle_stop_sig(int sig) {
/* Do basic preparations - persistent fds, filenames, etc. */ /* 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); dev_null_fd = open("/dev/null", O_RDWR);
if (dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } 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. */ /* 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 { } else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); 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. */ /* Setup signal handlers, duh. */
@ -872,6 +903,7 @@ static void usage(u8 *argv0) {
" -f file - input file read by the tested program (stdin)\n" " -f file - input file read by the tested program (stdin)\n"
" -t msec - timeout for each run (%u ms)\n" " -t msec - timeout for each run (%u ms)\n"
" -m megs - memory limit for child process (%u MB)\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" " -Q - use binary-only instrumentation (QEMU mode)\n"
" -U - use unicorn-based instrumentation (Unicorn mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n"
" -W - use qemu-based instrumentation with Wine (Wine " " -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"); 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) { switch (opt) {
@ -1008,6 +1040,14 @@ int main(int argc, char **argv_orig, char **envp) {
break; break;
case 'O': /* FRIDA mode */
if (frida_mode) { FATAL("Multiple -O options not supported"); }
frida_mode = 1;
break;
case 'Q': case 'Q':
if (qemu_mode) { FATAL("Multiple -Q options not supported"); } 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); atexit(at_exit_handler);
setup_signal_handlers(); setup_signal_handlers();
set_up_environment(); set_up_environment(argv);
target_path = find_binary(argv[optind]); target_path = find_binary(argv[optind]);
detect_file_args(argv + optind, prog_in, &use_stdin); detect_file_args(argv + optind, prog_in, &use_stdin);

View File

@ -2692,7 +2692,7 @@ void check_binary(afl_state_t *afl, u8 *fname) {
#endif /* ^!__APPLE__ */ #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 && !afl->non_instrumented_mode &&
!memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { !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)) { memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) {
SAYF("\n" cLRD "[-] " cRST 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."); OKF(cPIN "Deferred forkserver binary detected.");
setenv(DEFER_ENV_VAR, "1", 1); setenv(DEFER_ENV_VAR, "1", 1);

View File

@ -109,6 +109,7 @@ static void usage(u8 *argv0, int more_help) {
"maximum.\n" "maximum.\n"
" -m megs - memory limit for child process (%u MB, 0 = no limit " " -m megs - memory limit for child process (%u MB, 0 = no limit "
"[default])\n" "[default])\n"
" -O - use binary-only instrumentation (FRIDA mode)\n"
" -Q - use binary-only instrumentation (QEMU mode)\n" " -Q - use binary-only instrumentation (QEMU mode)\n"
" -U - use unicorn-based instrumentation (Unicorn mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n"
" -W - use qemu-based instrumentation with Wine (Wine " " -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 *extras_dir[4];
u8 mem_limit_given = 0, exit_1 = 0, debug = 0, u8 mem_limit_given = 0, exit_1 = 0, debug = 0,
extras_dir_cnt = 0 /*, have_p = 0*/; extras_dir_cnt = 0 /*, have_p = 0*/;
char * afl_preload;
char * frida_afl_preload = NULL;
char **use_argv; char **use_argv;
struct timeval tv; struct timeval tv;
@ -372,7 +375,7 @@ int main(int argc, char **argv_orig, char **envp) {
while ((opt = getopt( while ((opt = getopt(
argc, argv, 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) { switch (opt) {
@ -764,6 +767,18 @@ int main(int argc, char **argv_orig, char **envp) {
afl->use_banner = optarg; afl->use_banner = optarg;
break; 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 */ case 'Q': /* QEMU mode */
if (afl->fsrv.qemu_mode) { FATAL("Multiple -Q options not supported"); } 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->non_instrumented_mode) {
if (afl->crash_mode) { FATAL("-C and -n are mutually exclusive"); } 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->fsrv.qemu_mode) { FATAL("-Q and -n are mutually exclusive"); }
if (afl->unicorn_mode) { FATAL("-U 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. */ /* 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 { } else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); 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")) { 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); 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; map_size = afl->fsrv.map_size = MAP_SIZE;
afl->virgin_bits = ck_realloc(afl->virgin_bits, 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); fclose(afl->fsrv.plot_file);
destroy_queue(afl); destroy_queue(afl);
destroy_extras(afl); destroy_extras(afl);

View File

@ -555,8 +555,10 @@ static void handle_stop_sig(int sig) {
/* Do basic preparations - persistent fds, filenames, etc. */ /* 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", setenv("ASAN_OPTIONS",
"abort_on_error=1:" "abort_on_error=1:"
"detect_leaks=0:" "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. */ /* 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 { } else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); 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. */ /* Setup signal handlers, duh. */
@ -655,6 +685,7 @@ static void usage(u8 *argv0) {
"Execution control settings:\n" "Execution control settings:\n"
" -t msec - timeout for each run (none)\n" " -t msec - timeout for each run (none)\n"
" -m megs - memory limit for child process (%u MB)\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" " -Q - use binary-only instrumentation (QEMU mode)\n"
" -U - use Unicorn-based instrumentation (Unicorn mode)\n" " -U - use Unicorn-based instrumentation (Unicorn mode)\n"
" -W - use qemu-based instrumentation with Wine (Wine 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; } 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) { switch (opt) {
@ -857,6 +888,14 @@ int main(int argc, char **argv_orig, char **envp) {
at_file = optarg; at_file = optarg;
break; break;
case 'O': /* FRIDA mode */
if (fsrv->frida_mode) { FATAL("Multiple -O options not supported"); }
fsrv->frida_mode = 1;
break;
case 'Q': case 'Q':
if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); } 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; shm.cmplog_mode = 0;
setup_signal_handlers(); setup_signal_handlers();
set_up_environment(fsrv); set_up_environment(fsrv, argv);
fsrv->target_path = find_binary(argv[optind]); fsrv->target_path = find_binary(argv[optind]);
fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); fsrv->trace_bits = afl_shm_init(&shm, map_size, 0);

View File

@ -640,9 +640,11 @@ static void handle_stop_sig(int sig) {
/* Do basic preparations - persistent fds, filenames, etc. */ /* 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); fsrv->dev_null_fd = open("/dev/null", O_RDWR);
if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } 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. */ /* 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 { } else {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); 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. */ /* Setup signal handlers, duh. */
@ -804,6 +834,7 @@ static void usage(u8 *argv0) {
" -f file - input file read by the tested program (stdin)\n" " -f file - input file read by the tested program (stdin)\n"
" -t msec - timeout for each run (%u ms)\n" " -t msec - timeout for each run (%u ms)\n"
" -m megs - memory limit for child process (%u MB)\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" " -Q - use binary-only instrumentation (QEMU mode)\n"
" -U - use unicorn-based instrumentation (Unicorn mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n"
" -W - use qemu-based instrumentation with Wine (Wine " " -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"); 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) { switch (opt) {
@ -971,6 +1002,14 @@ int main(int argc, char **argv_orig, char **envp) {
break; break;
case 'O': /* FRIDA mode */
if (fsrv->frida_mode) { FATAL("Multiple -O options not supported"); }
fsrv->frida_mode = 1;
break;
case 'Q': case 'Q':
if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); } 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); atexit(at_exit_handler);
setup_signal_handlers(); setup_signal_handlers();
set_up_environment(fsrv); set_up_environment(fsrv, argv);
fsrv->target_path = find_binary(argv[optind]); fsrv->target_path = find_binary(argv[optind]);
fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); fsrv->trace_bits = afl_shm_init(&shm, map_size, 0);