diff options
-rw-r--r-- | frida_mode/GNUmakefile | 1 | ||||
-rw-r--r-- | frida_mode/README.md | 6 | ||||
-rw-r--r-- | frida_mode/src/instrument/instrument.c | 111 | ||||
-rw-r--r-- | frida_mode/test/unstable/GNUmakefile | 90 | ||||
-rw-r--r-- | frida_mode/test/unstable/Makefile | 19 | ||||
-rwxr-xr-x | frida_mode/test/unstable/get_symbol_addr.py | 36 | ||||
-rw-r--r-- | frida_mode/test/unstable/unstable.c | 67 | ||||
-rw-r--r-- | include/envs.h | 1 |
8 files changed, 302 insertions, 29 deletions
diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile index a0387cac..329d9f7f 100644 --- a/frida_mode/GNUmakefile +++ b/frida_mode/GNUmakefile @@ -20,6 +20,7 @@ RT_CFLAGS:=-Wno-unused-parameter \ -Wno-unused-function \ -Wno-unused-result \ -Wno-int-to-pointer-cast \ + -Wno-pointer-sign \ LDFLAGS+=-shared \ -lpthread \ diff --git a/frida_mode/README.md b/frida_mode/README.md index 9b316cb9..296e6405 100644 --- a/frida_mode/README.md +++ b/frida_mode/README.md @@ -155,8 +155,10 @@ instrumentation (the default where available). Required to use * `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_TRACE` - Log to stdout the address of executed blocks -`AFL_FRIDA_INST_NO_OPTIMIZE`. +* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks, +requires `AFL_FRIDA_INST_NO_OPTIMIZE`. +* `AFL_FRIDA_INST_TRACE_UNIQUE` - As per `AFL_FRIDA_INST_TRACE`, but each edge +is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`. * `AFL_FRIDA_OUTPUT_STDOUT` - Redirect the standard output of the target application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`) * `AFL_FRIDA_OUTPUT_STDERR` - Redirect the standard error of the target diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c index f261e79a..ba82b89f 100644 --- a/frida_mode/src/instrument/instrument.c +++ b/frida_mode/src/instrument/instrument.c @@ -1,4 +1,6 @@ #include <unistd.h> +#include <sys/shm.h> +#include <sys/mman.h> #include "frida-gum.h" @@ -18,44 +20,50 @@ static gboolean tracing = false; static gboolean optimize = false; +static gboolean unique = false; static GumStalkerTransformer *transformer = NULL; __thread uint64_t previous_pc = 0; +static GumAddress previous_rip = 0; +static u8 * edges_notified = NULL; + +static void trace_debug(char *format, ...) { + + va_list ap; + char buffer[4096] = {0}; + int ret; + int len; + + va_start(ap, format); + ret = vsnprintf(buffer, sizeof(buffer) - 1, format, ap); + va_end(ap); + + if (ret < 0) { return; } + + len = strnlen(buffer, sizeof(buffer)); + + IGNORED_RETURN(write(STDOUT_FILENO, buffer, len)); + +} + __attribute__((hot)) static void on_basic_block(GumCpuContext *context, gpointer user_data) { UNUSED_PARAMETER(context); - /* - * This function is performance critical as it is called to instrument every - * basic block. By moving our print buffer to a global, we avoid it affecting - * the critical path with additional stack adjustments if tracing is not - * enabled. If tracing is enabled, then we're printing a load of diagnostic - * information so this overhead is unlikely to be noticeable. - */ - static char buffer[200]; - int len; - GumAddress current_pc = GUM_ADDRESS(user_data); - uint8_t * cursor; - uint64_t value; - if (unlikely(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); - IGNORED_RETURN(write(STDOUT_FILENO, buffer, len + 1)); + GumAddress current_rip = GUM_ADDRESS(user_data); + GumAddress current_pc; + GumAddress edge; + uint8_t * cursor; + uint64_t value; - } - - current_pc = (current_pc >> 4) ^ (current_pc << 8); + current_pc = (current_rip >> 4) ^ (current_rip << 8); current_pc &= MAP_SIZE - 1; - cursor = &__afl_area_ptr[current_pc ^ previous_pc]; + edge = current_pc ^ previous_pc; + + cursor = &__afl_area_ptr[edge]; value = *cursor; if (value == 0xff) { @@ -71,6 +79,23 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context, *cursor = value; previous_pc = current_pc >> 1; + if (unlikely(tracing)) { + + if (!unique || edges_notified[edge] == 0) { + + trace_debug("TRACE: edge: %10" G_GINT64_MODIFIER + "d, current_rip: 0x%016" G_GINT64_MODIFIER + "x, previous_rip: 0x%016" G_GINT64_MODIFIER "x\n", + edge, current_rip, previous_rip); + + } + + if (unique) { edges_notified[edge] = 1; } + + previous_rip = current_rip; + + } + } static void instr_basic_block(GumStalkerIterator *iterator, @@ -164,18 +189,28 @@ void instrument_init(void) { optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL); tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL); + unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL); if (!instrument_is_coverage_optimize_supported()) optimize = false; OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' '); OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' '); + OKF("Instrumentation - unique [%c]", unique ? 'X' : ' '); if (tracing && optimize) { - FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible"); + FATAL("AFL_FRIDA_INST_TRACE requires AFL_FRIDA_INST_NO_OPTIMIZE"); + + } + + if (unique && optimize) { + + FATAL("AFL_FRIDA_INST_TRACE_UNIQUE requires AFL_FRIDA_INST_NO_OPTIMIZE"); } + if (unique) { tracing = TRUE; } + if (__afl_map_size != 0x10000) { FATAL("Bad map size: 0x%08x", __afl_map_size); @@ -185,6 +220,28 @@ void instrument_init(void) { transformer = gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL); + if (unique) { + + int shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); + if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); } + + edges_notified = shmat(shm_id, NULL, 0); + g_assert(edges_notified != MAP_FAILED); + + /* + * Configure the shared memory region to be removed once the process dies. + */ + if (shmctl(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(edges_notified, '\0', MAP_SIZE); + + } + instrument_debug_init(); asan_init(); cmplog_init(); diff --git a/frida_mode/test/unstable/GNUmakefile b/frida_mode/test/unstable/GNUmakefile new file mode 100644 index 00000000..fed417a3 --- /dev/null +++ b/frida_mode/test/unstable/GNUmakefile @@ -0,0 +1,90 @@ +PWD:=$(shell pwd)/ +ROOT:=$(shell realpath $(PWD)../../..)/ +BUILD_DIR:=$(PWD)build/ +UNSTABLE_DATA_DIR:=$(BUILD_DIR)in/ +UNSTABLE_DATA_FILE:=$(UNSTABLE_DATA_DIR)in + +UNSTABLE_BIN:=$(BUILD_DIR)unstable +UNSTABLE_SRC:=$(PWD)unstable.c + +QEMU_OUT:=$(BUILD_DIR)qemu-out +FRIDA_OUT:=$(BUILD_DIR)frida-out + +ifndef ARCH + +ARCH=$(shell uname -m) +ifeq "$(ARCH)" "aarch64" + ARCH:=arm64 +endif + +ifeq "$(ARCH)" "i686" + ARCH:=x86 +endif +endif + +AFL_QEMU_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x4000000000) + +ifeq "$(ARCH)" "aarch64" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x0000aaaaaaaaa000) +endif + +ifeq "$(ARCH)" "x86_64" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x0000555555554000) +endif + +ifeq "$(ARCH)" "x86" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x56555000) +endif + +.PHONY: all 32 clean qemu frida + +all: $(UNSTABLE_BIN) + make -C $(ROOT)frida_mode/ + +32: + CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all + +$(BUILD_DIR): + mkdir -p $@ + +$(UNSTABLE_DATA_DIR): | $(BUILD_DIR) + mkdir -p $@ + +$(UNSTABLE_DATA_FILE): | $(UNSTABLE_DATA_DIR) + echo -n "000" > $@ + +$(UNSTABLE_BIN): $(UNSTABLE_SRC) | $(BUILD_DIR) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +clean: + rm -rf $(BUILD_DIR) + + +qemu: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) + AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \ + $(ROOT)afl-fuzz \ + -D \ + -Q \ + -i $(UNSTABLE_DATA_DIR) \ + -o $(QEMU_OUT) \ + -- \ + $(UNSTABLE_BIN) @@ + +frida: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) + AFL_DEBUG=1 \ + AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_FRIDA_INST_TRACE_UNIQUE=1 \ + AFL_FRIDA_INST_NO_OPTIMIZE=1 \ + $(ROOT)afl-fuzz \ + -D \ + -O \ + -i $(UNSTABLE_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -- \ + $(UNSTABLE_BIN) @@ + +debug: + gdb \ + --ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \ + --ex 'set disassembly-flavor intel' \ + --args $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) diff --git a/frida_mode/test/unstable/Makefile b/frida_mode/test/unstable/Makefile new file mode 100644 index 00000000..f843af19 --- /dev/null +++ b/frida_mode/test/unstable/Makefile @@ -0,0 +1,19 @@ +all: + @echo trying to use GNU make... + @gmake all || echo please install GNUmake + +32: + @echo trying to use GNU make... + @gmake 32 || echo please install GNUmake + +clean: + @gmake clean + +qemu: + @gmake qemu + +frida: + @gmake frida + +debug: + @gmake debug diff --git a/frida_mode/test/unstable/get_symbol_addr.py b/frida_mode/test/unstable/get_symbol_addr.py new file mode 100755 index 00000000..1c46e010 --- /dev/null +++ b/frida_mode/test/unstable/get_symbol_addr.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +import argparse +from elftools.elf.elffile import ELFFile + +def process_file(file, symbol, base): + with open(file, 'rb') as f: + elf = ELFFile(f) + symtab = elf.get_section_by_name('.symtab') + mains = symtab.get_symbol_by_name(symbol) + if len(mains) != 1: + print ("Failed to find main") + return 1 + + main_addr = mains[0]['st_value'] + main = base + main_addr + print ("0x%016x" % main) + return 0 + +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', '--symbol', dest='symbol', type=str, + help='symbol name', required=True) + parser.add_argument('-b', '--base', dest='base', type=hex_value, + help='elf base address', required=True) + + args = parser.parse_args() + return process_file (args.file, args.symbol, args.base) + +if __name__ == "__main__": + ret = main() + exit(ret) diff --git a/frida_mode/test/unstable/unstable.c b/frida_mode/test/unstable/unstable.c new file mode 100644 index 00000000..67d56b73 --- /dev/null +++ b/frida_mode/test/unstable/unstable.c @@ -0,0 +1,67 @@ +/* + 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 <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef __APPLE__ + #define TESTINSTR_SECTION +#else + #define TESTINSTR_SECTION __attribute__((section(".testinstr"))) +#endif + +void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + + if (size < 1) return; + + int r = rand(); + if ((r % 2) == 0) { + printf ("Hooray all even\n"); + } else { + printf ("Hmm that's odd\n"); + } + + // we support three input cases + if (data[0] == '0') + printf("Looks like a zero to me!\n"); + else if (data[0] == '1') + printf("Pretty sure that is a one!\n"); + else + printf("Neither one or zero? How quaint!\n"); + +} + +void run_test(char * file) { + fprintf(stderr, "Running: %s\n", file); + FILE *f = fopen(file, "r"); + assert(f); + fseek(f, 0, SEEK_END); + size_t len = ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char *buf = (unsigned char*)malloc(len); + size_t n_read = fread(buf, 1, len, f); + fclose(f); + assert(n_read == len); + LLVMFuzzerTestOneInput(buf, len); + free(buf); + fprintf(stderr, "Done: %s: (%zd bytes)\n", file, n_read); +} + +int main(int argc, char **argv) { + srand(1); + fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1); + for (int i = 1; i < argc; i++) { + run_test(argv[i]); + } +} diff --git a/include/envs.h b/include/envs.h index ea912a25..54bb6597 100644 --- a/include/envs.h +++ b/include/envs.h @@ -60,6 +60,7 @@ static char *afl_environment_variables[] = { "AFL_FRIDA_INST_NO_PREFETCH", "AFL_FRIDA_INST_RANGES", "AFL_FRIDA_INST_TRACE", + "AFL_FRIDA_INST_UNSTABLE", "AFL_FRIDA_OUTPUT_STDOUT", "AFL_FRIDA_OUTPUT_STDERR", "AFL_FRIDA_PERSISTENT_ADDR", |