diff options
Diffstat (limited to 'utils/afl_frida')
-rw-r--r-- | utils/afl_frida/GNUmakefile | 23 | ||||
-rw-r--r-- | utils/afl_frida/Makefile | 2 | ||||
-rw-r--r-- | utils/afl_frida/README.md | 34 | ||||
-rw-r--r-- | utils/afl_frida/afl-frida.c | 397 | ||||
-rw-r--r-- | utils/afl_frida/afl-frida.h | 53 | ||||
-rw-r--r-- | utils/afl_frida/android/README.md | 1 | ||||
-rw-r--r-- | utils/afl_frida/android/frida-gum-example.c | 130 | ||||
-rw-r--r-- | utils/afl_frida/libtestinstr.c | 35 |
8 files changed, 675 insertions, 0 deletions
diff --git a/utils/afl_frida/GNUmakefile b/utils/afl_frida/GNUmakefile new file mode 100644 index 00000000..8b56415b --- /dev/null +++ b/utils/afl_frida/GNUmakefile @@ -0,0 +1,23 @@ +ifdef DEBUG + OPT=-O0 -D_DEBUG=\"1\" +else + OPT=-O3 -funroll-loops +endif + +all: afl-frida libtestinstr.so + +libfrida-gum.a: + @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest + @exit 1 + +afl-frida: afl-frida.c libfrida-gum.a + $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-compiler-rt.o libfrida-gum.a -ldl -lresolv -pthread + +libtestinstr.so: libtestinstr.c + $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c + +clean: + rm -f afl-frida *~ core *.o libtestinstr.so + +deepclean: clean + rm -f libfrida-gum.a frida-gum* diff --git a/utils/afl_frida/Makefile b/utils/afl_frida/Makefile new file mode 100644 index 00000000..0b306dde --- /dev/null +++ b/utils/afl_frida/Makefile @@ -0,0 +1,2 @@ +all: + @echo please use GNU make, thanks! diff --git a/utils/afl_frida/README.md b/utils/afl_frida/README.md new file mode 100644 index 00000000..68b62009 --- /dev/null +++ b/utils/afl_frida/README.md @@ -0,0 +1,34 @@ +# afl-frida - faster fuzzing of binary-only libraries + +## Introduction + +afl-frida is an example skeleton file which can easily be used to fuzz +a closed source library. + +It requires less memory and is x5-10 faster than qemu_mode but does not +provide interesting features like compcov or cmplog. + +## How-to + +### Modify afl-frida.c + +Read and modify afl-frida.c then `make`. +To adapt afl-frida.c to your needs, read the header of the file and then +search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. + +### Fuzzing + +Example (after modifying afl-frida.c to your needs and compile it): +``` +LD_LIBRARY_PATH=/path/to/the/target/library/ afl-fuzz -i in -o out -- ./afl-frida +``` +(or even remote via afl-network-proxy). + +# Speed and stability + +The speed is very good, about x12 of fork() qemu_mode. +However the stability is low. Reason is currently unknown. + +# Background + +This code is copied for a larger part from https://github.com/meme/hotwax diff --git a/utils/afl_frida/afl-frida.c b/utils/afl_frida/afl-frida.c new file mode 100644 index 00000000..711d8f33 --- /dev/null +++ b/utils/afl_frida/afl-frida.c @@ -0,0 +1,397 @@ +/* + american fuzzy lop++ - afl-frida skeleton example + ------------------------------------------------- + + Copyright 2020 AFLplusplus Project. All rights reserved. + + Written mostly by meme -> https://github.com/meme/hotwax + + Modifications by Marc Heuse <mh@mh-sec.de> + + 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 + + HOW-TO + ====== + + You only need to change the following: + + 1. set the defines and function call parameters. + 2. dl load the library you want to fuzz, lookup the functions you need + and setup the calls to these. + 3. in the while loop you call the functions in the necessary order - + incl the cleanup. the cleanup is important! + + Just look these steps up in the code, look for "// STEP x:" + +*/ + +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <stdint.h> +#include <stddef.h> +#include <sys/shm.h> +#include <dlfcn.h> + +#ifdef __APPLE__ + #include <mach/mach.h> + #include <mach-o/dyld_images.h> +#else + #include <sys/wait.h> + #include <sys/personality.h> +#endif + +int debug = 0; + +// STEP 1: + +// The presets are for the example libtestinstr.so: + +/* What is the name of the library to fuzz */ +#define TARGET_LIBRARY "libtestinstr.so" + +/* What is the name of the function to fuzz */ +#define TARGET_FUNCTION "testinstr" + +/* here you need to specify the parameter for the target function */ +static void *(*o_function)(uint8_t *, int); + +// END STEP 1 + +#include "frida-gum.h" + +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data); +void afl_setup(void); +void afl_start_forkserver(void); +int __afl_persistent_loop(unsigned int max_cnt); + +#include "../../config.h" + +// Shared memory fuzzing. +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char *__afl_fuzz_ptr; + +// Notify AFL about persistent mode. +static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##\0"; +int __afl_persistent_loop(unsigned int); + +// Notify AFL about deferred forkserver. +static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##\0"; +void __afl_manual_init(); + +// Because we do our own logging. +extern uint8_t * __afl_area_ptr; +static __thread guint64 previous_pc; + +// Frida stuff below. +typedef struct { + + GumAddress base_address; + guint64 code_start, code_end; + GumAddress current_log_impl; + uint64_t afl_prev_loc; + +} range_t; + +inline static void afl_maybe_log(guint64 current_pc) { + + // fprintf(stderr, "PC: %p ^ %p\n", current_pc, previous_pc); + + 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; + +} + +#if GUM_NATIVE_CPU == GUM_CPU_AMD64 + +static const guint8 afl_maybe_log_code[] = { + + 0x9c, // pushfq + 0x50, // push rax + 0x51, // push rcx + 0x52, // push rdx + 0x56, // push rsi + + 0x89, 0xf8, // mov eax, edi + 0xc1, 0xe0, 0x08, // shl eax, 8 + 0xc1, 0xef, 0x04, // shr edi, 4 + 0x31, 0xc7, // xor edi, eax + 0x0f, 0xb7, 0xc7, // movzx eax, di + 0x48, 0x8d, 0x0d, 0x30, 0x00, 0x00, 0x00, // lea rcx, sym._afl_area_ptr_ptr + 0x48, 0x8b, 0x09, // mov rcx, qword [rcx] + 0x48, 0x8b, 0x09, // mov rcx, qword [rcx] + 0x48, 0x8d, 0x15, 0x1b, 0x00, 0x00, 0x00, // lea rdx, sym._afl_prev_loc_ptr + 0x48, 0x8b, 0x32, // mov rsi, qword [rdx] + 0x48, 0x8b, 0x36, // mov rsi, qword [rsi] + 0x48, 0x31, 0xc6, // xor rsi, rax + 0xfe, 0x04, 0x31, // inc byte [rcx + rsi] + + 0x48, 0xd1, 0xe8, // shr rax, 1 + 0x48, 0x8b, 0x0a, // mov rcx, qword [rdx] + 0x48, 0x89, 0x01, // mov qword [rcx], rax + + 0x5e, // pop rsi + 0x5a, // pop rdx + 0x59, // pop rcx + 0x58, // pop rax + 0x9d, // popfq + + 0xc3, // ret + // Read-only data goes here: + // uint64_t* afl_prev_loc_ptr + // uint8_t** afl_area_ptr_ptr + // unsigned int afl_instr_rms + +}; + +#else + +static void on_basic_block(GumCpuContext *context, gpointer user_data) { + + afl_maybe_log((guint64)user_data); + +} + +#endif + +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data) { + + range_t *range = (range_t *)user_data; + + const cs_insn *instr; + gboolean begin = TRUE; + while (gum_stalker_iterator_next(iterator, &instr)) { + + if (begin) { + + if (instr->address >= range->code_start && + instr->address <= range->code_end) { + +#if GUM_NATIVE_CPU == GUM_CPU_AMD64 + GumX86Writer *cw = output->writer.x86; + if (range->current_log_impl == 0 || + !gum_x86_writer_can_branch_directly_between( + cw->pc, range->current_log_impl) || + !gum_x86_writer_can_branch_directly_between( + cw->pc + 128, range->current_log_impl)) { + + gconstpointer after_log_impl = cw->code + 1; + + gum_x86_writer_put_jmp_near_label(cw, after_log_impl); + + range->current_log_impl = cw->pc; + gum_x86_writer_put_bytes(cw, afl_maybe_log_code, + sizeof(afl_maybe_log_code)); + + uint64_t *afl_prev_loc_ptr = &range->afl_prev_loc; + uint8_t **afl_area_ptr_ptr = &__afl_area_ptr; + gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr, + sizeof(afl_prev_loc_ptr)); + gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr, + sizeof(afl_area_ptr_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, + GUM_ADDRESS(instr->address)); + gum_x86_writer_put_call_address(cw, range->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); +#else + gum_stalker_iterator_put_callout(iterator, on_basic_block, + (gpointer)instr->address, NULL); +#endif + begin = FALSE; + + } + + } + + gum_stalker_iterator_keep(iterator); + + } + +} + +/* Because this CAN be called more than once, it will return the LAST range */ +static int enumerate_ranges(const GumRangeDetails *details, + gpointer user_data) { + + GumMemoryRange *code_range = (GumMemoryRange *)user_data; + memcpy(code_range, details->range, sizeof(*code_range)); + return 0; + +} + +int main(int argc, char **argv) { + +#ifndef __APPLE__ + (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR +#endif + + // STEP 2: load the library you want to fuzz and lookup the functions, + // inclusive of the cleanup functions. + // If there is just one function, then there is nothing to change + // or add here. + + void *dl = NULL; + if (argc > 2) { + + dl = dlopen(argv[1], RTLD_LAZY); + + } else { + + dl = dlopen(TARGET_LIBRARY, RTLD_LAZY); + + } + + if (!dl) { + + if (argc > 2) + fprintf(stderr, "Could not load %s\n", argv[1]); + else + fprintf(stderr, "Could not load %s\n", TARGET_LIBRARY); + exit(-1); + + } + + if (argc > 2) + o_function = dlsym(dl, argv[2]); + else + o_function = dlsym(dl, TARGET_FUNCTION); + if (!o_function) { + + if (argc > 2) + fprintf(stderr, "Could not find function %s\n", argv[2]); + else + fprintf(stderr, "Could not find function %s\n", TARGET_FUNCTION); + exit(-1); + + } + + // END STEP 2 + + if (!getenv("AFL_FRIDA_TEST_INPUT")) { + + gum_init_embedded(); + if (!gum_stalker_is_supported()) { + + gum_deinit_embedded(); + return 1; + + } + + GumStalker *stalker = gum_stalker_new(); + + GumAddress base_address; + if (argc > 2) + base_address = gum_module_find_base_address(argv[1]); + else + base_address = gum_module_find_base_address(TARGET_LIBRARY); + GumMemoryRange code_range; + if (argc > 2) + gum_module_enumerate_ranges(argv[1], GUM_PAGE_RX, enumerate_ranges, + &code_range); + else + gum_module_enumerate_ranges(TARGET_LIBRARY, GUM_PAGE_RX, enumerate_ranges, + &code_range); + + guint64 code_start = code_range.base_address; + guint64 code_end = code_range.base_address + code_range.size; + range_t instr_range = {0, code_start, code_end, 0, 0}; + + printf("Frida instrumentation: base=0x%lx instrumenting=0x%lx-%lx\n", + base_address, code_start, code_end); + if (!code_start || !code_end) { + + if (argc > 2) + fprintf(stderr, "Error: no valid memory address found for %s\n", + argv[1]); + else + fprintf(stderr, "Error: no valid memory address found for %s\n", + TARGET_LIBRARY); + exit(-1); + + } + + GumStalkerTransformer *transformer = + gum_stalker_transformer_make_from_callback(instr_basic_block, + &instr_range, NULL); + + // to ensure that the signatures are not optimized out + memcpy(__afl_area_ptr, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT)); + memcpy(__afl_area_ptr + 32, (void *)AFL_DEFER_FORKSVR, + sizeof(AFL_DEFER_FORKSVR)); + __afl_manual_init(); + + // + // any expensive target library initialization that has to be done just once + // - put that here + // + + gum_stalker_follow_me(stalker, transformer, NULL); + + while (__afl_persistent_loop(UINT32_MAX) != 0) { + + previous_pc = 0; // Required! + +#ifdef _DEBUG + fprintf(stderr, "CLIENT crc: %016llx len: %u\n", + hash64(__afl_fuzz_ptr, *__afl_fuzz_len), *__afl_fuzz_len); + fprintf(stderr, "RECV:"); + for (int i = 0; i < *__afl_fuzz_len; i++) + fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); + fprintf(stderr, "\n"); +#endif + + // STEP 3: ensure the minimum length is present and setup the target + // function to fuzz. + + if (*__afl_fuzz_len > 0) { + + __afl_fuzz_ptr[*__afl_fuzz_len] = 0; // if you need to null terminate + (*o_function)(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + + // END STEP 3 + + } + + gum_stalker_unfollow_me(stalker); + + while (gum_stalker_garbage_collect(stalker)) + g_usleep(10000); + + g_object_unref(stalker); + g_object_unref(transformer); + gum_deinit_embedded(); + + } else { + + char buf[8 * 1024] = {0}; + int count = read(0, buf, sizeof(buf)); + buf[8 * 1024 - 1] = '\0'; + (*o_function)(buf, count); + + } + + return 0; + +} + diff --git a/utils/afl_frida/afl-frida.h b/utils/afl_frida/afl-frida.h new file mode 100644 index 00000000..efa3440f --- /dev/null +++ b/utils/afl_frida/afl-frida.h @@ -0,0 +1,53 @@ +extern int is_persistent; + +G_BEGIN_DECLS + +#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) + +G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, + FAKE_EVENT_SINK, GObject) + +struct _GumFakeEventSink { + + GObject parent; + GumEventType mask; + +}; + +GumEventSink *gum_fake_event_sink_new(void); +void gum_fake_event_sink_reset(GumFakeEventSink *self); + +G_END_DECLS + +typedef struct { + + GumAddress base_address; + guint64 code_start, code_end; + +} range_t; + +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data); +#pragma once + +void afl_setup(void); +void afl_start_forkserver(void); +int __afl_persistent_loop(unsigned int max_cnt); + +inline static inline void afl_maybe_log(guint64 current_pc) { + + extern unsigned int afl_instr_rms; + extern uint8_t * afl_area_ptr; + + static __thread guint64 previous_pc; + + current_pc = (current_pc >> 4) ^ (current_pc << 8); + current_pc &= MAP_SIZE - 1; + + if (current_pc >= afl_instr_rms) return; + + afl_area_ptr[current_pc ^ previous_pc]++; + previous_pc = current_pc >> 1; + +} + diff --git a/utils/afl_frida/android/README.md b/utils/afl_frida/android/README.md new file mode 100644 index 00000000..044b48a1 --- /dev/null +++ b/utils/afl_frida/android/README.md @@ -0,0 +1 @@ +For android, frida-gum package (ex. https://github.com/frida/frida/releases/download/14.2.6/frida-gum-devkit-14.2.6-android-arm64.tar.xz) is needed to be extracted in the directory. diff --git a/utils/afl_frida/android/frida-gum-example.c b/utils/afl_frida/android/frida-gum-example.c new file mode 100644 index 00000000..14d98248 --- /dev/null +++ b/utils/afl_frida/android/frida-gum-example.c @@ -0,0 +1,130 @@ +/* + * Compile with: + * + * clang -fPIC -DANDROID -ffunction-sections -fdata-sections -Os -pipe -g3 frida-gum-example.c -o frida-gum-example -L. -lfrida-gum -llog -ldl -lm -pthread -Wl,--gc-sections,-z,noexecstack,-z,relro,-z,now -fuse-ld=gold -fuse-ld=gold -Wl,--icf=all + * + * Visit https://frida.re to learn more about Frida. + */ + +#include "frida-gum.h" + +#include <fcntl.h> +#include <unistd.h> + +typedef struct _ExampleListener ExampleListener; +typedef enum _ExampleHookId ExampleHookId; + +struct _ExampleListener +{ + GObject parent; + + guint num_calls; +}; + +enum _ExampleHookId +{ + EXAMPLE_HOOK_OPEN, + EXAMPLE_HOOK_CLOSE +}; + +static void example_listener_iface_init (gpointer g_iface, gpointer iface_data); + +#define EXAMPLE_TYPE_LISTENER (example_listener_get_type ()) +G_DECLARE_FINAL_TYPE (ExampleListener, example_listener, EXAMPLE, LISTENER, GObject) +G_DEFINE_TYPE_EXTENDED (ExampleListener, + example_listener, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE (GUM_TYPE_INVOCATION_LISTENER, + example_listener_iface_init)) + +int +main (int argc, + char * argv[]) +{ + GumInterceptor * interceptor; + GumInvocationListener * listener; + + gum_init_embedded (); + + interceptor = gum_interceptor_obtain (); + listener = g_object_new (EXAMPLE_TYPE_LISTENER, NULL); + + gum_interceptor_begin_transaction (interceptor); + gum_interceptor_attach (interceptor, + GSIZE_TO_POINTER (gum_module_find_export_by_name (NULL, "open")), + listener, + GSIZE_TO_POINTER (EXAMPLE_HOOK_OPEN)); + gum_interceptor_attach (interceptor, + GSIZE_TO_POINTER (gum_module_find_export_by_name (NULL, "close")), + listener, + GSIZE_TO_POINTER (EXAMPLE_HOOK_CLOSE)); + gum_interceptor_end_transaction (interceptor); + + close (open ("/etc/hosts", O_RDONLY)); + close (open ("/etc/fstab", O_RDONLY)); + + g_print ("[*] listener got %u calls\n", EXAMPLE_LISTENER (listener)->num_calls); + + gum_interceptor_detach (interceptor, listener); + + close (open ("/etc/hosts", O_RDONLY)); + close (open ("/etc/fstab", O_RDONLY)); + + g_print ("[*] listener still has %u calls\n", EXAMPLE_LISTENER (listener)->num_calls); + + g_object_unref (listener); + g_object_unref (interceptor); + + gum_deinit_embedded (); + + return 0; +} + +static void +example_listener_on_enter (GumInvocationListener * listener, + GumInvocationContext * ic) +{ + ExampleListener * self = EXAMPLE_LISTENER (listener); + ExampleHookId hook_id = GUM_IC_GET_FUNC_DATA (ic, ExampleHookId); + + switch (hook_id) + { + case EXAMPLE_HOOK_OPEN: + g_print ("[*] open(\"%s\")\n", (const gchar *) gum_invocation_context_get_nth_argument (ic, 0)); + break; + case EXAMPLE_HOOK_CLOSE: + g_print ("[*] close(%d)\n", GPOINTER_TO_INT (gum_invocation_context_get_nth_argument (ic, 0))); + break; + } + + self->num_calls++; +} + +static void +example_listener_on_leave (GumInvocationListener * listener, + GumInvocationContext * ic) +{ +} + +static void +example_listener_class_init (ExampleListenerClass * klass) +{ + (void) EXAMPLE_IS_LISTENER; + (void) glib_autoptr_cleanup_ExampleListener; +} + +static void +example_listener_iface_init (gpointer g_iface, + gpointer iface_data) +{ + GumInvocationListenerInterface * iface = g_iface; + + iface->on_enter = example_listener_on_enter; + iface->on_leave = example_listener_on_leave; +} + +static void +example_listener_init (ExampleListener * self) +{ +} diff --git a/utils/afl_frida/libtestinstr.c b/utils/afl_frida/libtestinstr.c new file mode 100644 index 00000000..96b1cf21 --- /dev/null +++ b/utils/afl_frida/libtestinstr.c @@ -0,0 +1,35 @@ +/* + 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +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"); + +} + |