| /* |
| american fuzzy lop++ - afl-untracer skeleton example |
| --------------------------------------------------- |
| |
| Written by Marc Heuse <mh@mh-sec.de> |
| |
| Copyright 2019-2022 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 |
| |
| |
| HOW-TO |
| ====== |
| |
| You only need to change the following: |
| |
| 1. decide if you want to receive data from stdin [DEFAULT] or file(name) |
| -> use_stdin = 0 if via file, and what the maximum input size is |
| 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:" |
| |
| |
| */ |
| |
| #define __USE_GNU |
| #define _GNU_SOURCE |
| |
| #ifdef __ANDROID__ |
| #include "android-ashmem.h" |
| #endif |
| #include "config.h" |
| #include "types.h" |
| #include "debug.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <stdint.h> |
| #include <errno.h> |
| #include <dlfcn.h> |
| #include <fcntl.h> |
| #include <pthread.h> |
| |
| #include <sys/mman.h> |
| #include <sys/shm.h> |
| #include <sys/wait.h> |
| #include <sys/types.h> |
| |
| #if defined(__linux__) |
| #include <sys/personality.h> |
| #include <sys/ucontext.h> |
| #elif defined(__APPLE__) && defined(__LP64__) |
| #include <mach-o/dyld_images.h> |
| #elif defined(__FreeBSD__) |
| #include <sys/sysctl.h> |
| #include <sys/user.h> |
| #include <sys/procctl.h> |
| #else |
| #error "Unsupported platform" |
| #endif |
| |
| #define MEMORY_MAP_DECREMENT 0x200000000000 |
| #define MAX_LIB_COUNT 128 |
| |
| // STEP 1: |
| |
| /* here you need to specify the parameter for the target function */ |
| static void *(*o_function)(u8 *buf, int len); |
| |
| /* use stdin (1) or a file on the commandline (0) */ |
| static u32 use_stdin = 1; |
| |
| /* This is were the testcase data is written into */ |
| static u8 buf[10000]; // this is the maximum size for a test case! set it! |
| |
| /* If you want to have debug output set this to 1, can also be set with |
| AFL_DEBUG */ |
| static u32 debug = 0; |
| |
| // END STEP 1 |
| |
| typedef struct library_list { |
| |
| u8 *name; |
| u64 addr_start, addr_end; |
| |
| } library_list_t; |
| |
| #ifdef __ANDROID__ |
| u32 __afl_map_size = MAP_SIZE; |
| u32 do_exit; |
| #else |
| __thread u32 __afl_map_size = MAP_SIZE; |
| __thread u32 do_exit; |
| #endif |
| |
| static pid_t pid = 65537; |
| static pthread_t __afl_thread; |
| static u8 __afl_dummy[MAP_SIZE]; |
| static u8 * __afl_area_ptr = __afl_dummy; |
| static u8 * inputfile; // this will point to argv[1] |
| static u32 len; |
| |
| static library_list_t liblist[MAX_LIB_COUNT]; |
| static u32 liblist_cnt; |
| |
| static void sigtrap_handler(int signum, siginfo_t *si, void *context); |
| static void fuzz(void); |
| |
| /* read the library information */ |
| void read_library_information(void) { |
| |
| #if defined(__linux__) |
| FILE *f; |
| u8 buf[1024], *b, *m, *e, *n; |
| |
| if ((f = fopen("/proc/self/maps", "r")) == NULL) |
| FATAL("cannot open /proc/self/maps"); |
| |
| if (debug) fprintf(stderr, "Library list:\n"); |
| while (fgets(buf, sizeof(buf), f)) { |
| |
| if (strstr(buf, " r-x")) { |
| |
| if (liblist_cnt >= MAX_LIB_COUNT) { |
| |
| WARNF("too many libraries to old, maximum count of %d reached", |
| liblist_cnt); |
| return; |
| |
| } |
| |
| b = buf; |
| m = index(buf, '-'); |
| e = index(buf, ' '); |
| if ((n = strrchr(buf, '/')) == NULL) n = strrchr(buf, ' '); |
| if (n && |
| ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) |
| n = NULL; |
| else |
| n++; |
| if (b && m && e && n && *n) { |
| |
| *m++ = 0; |
| *e = 0; |
| if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; |
| |
| liblist[liblist_cnt].name = strdup(n); |
| liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); |
| liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); |
| if (debug) |
| fprintf( |
| stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, |
| liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, |
| liblist[liblist_cnt].addr_start, |
| liblist[liblist_cnt].addr_end - 1); |
| liblist_cnt++; |
| |
| } |
| |
| } |
| |
| } |
| |
| if (debug) fprintf(stderr, "\n"); |
| |
| #elif defined(__FreeBSD__) |
| int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; |
| char * buf, *start, *end; |
| size_t miblen = sizeof(mib) / sizeof(mib[0]); |
| size_t len; |
| |
| if (debug) fprintf(stderr, "Library list:\n"); |
| if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } |
| |
| len = len * 4 / 3; |
| |
| buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); |
| if (buf == MAP_FAILED) { return; } |
| |
| if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { |
| |
| munmap(buf, len); |
| return; |
| |
| } |
| |
| start = buf; |
| end = buf + len; |
| |
| while (start < end) { |
| |
| struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; |
| size_t size = region->kve_structsize; |
| |
| if (size == 0) { break; } |
| |
| if ((region->kve_protection & KVME_PROT_READ) && |
| !(region->kve_protection & KVME_PROT_EXEC)) { |
| |
| liblist[liblist_cnt].name = |
| region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; |
| liblist[liblist_cnt].addr_start = region->kve_start; |
| liblist[liblist_cnt].addr_end = region->kve_end; |
| |
| if (debug) { |
| |
| fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, |
| liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, |
| liblist[liblist_cnt].addr_start, |
| liblist[liblist_cnt].addr_end - 1); |
| |
| } |
| |
| liblist_cnt++; |
| |
| } |
| |
| start += size; |
| |
| } |
| |
| #endif |
| |
| } |
| |
| library_list_t *find_library(char *name) { |
| |
| #if defined(__linux__) |
| u32 i; |
| |
| for (i = 0; i < liblist_cnt; i++) |
| if (strncmp(liblist[i].name, name, strlen(name)) == 0) return &liblist[i]; |
| #elif defined(__APPLE__) && defined(__LP64__) |
| kern_return_t err; |
| static library_list_t lib; |
| |
| // get the list of all loaded modules from dyld |
| // the task_info mach API will get the address of the dyld all_image_info |
| // struct for the given task from which we can get the names and load |
| // addresses of all modules |
| task_dyld_info_data_t task_dyld_info; |
| mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; |
| err = task_info(mach_task_self(), TASK_DYLD_INFO, |
| (task_info_t)&task_dyld_info, &count); |
| |
| const struct dyld_all_image_infos *all_image_infos = |
| (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; |
| const struct dyld_image_info *image_infos = all_image_infos->infoArray; |
| |
| for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { |
| |
| const char * image_name = image_infos[i].imageFilePath; |
| mach_vm_address_t image_load_address = |
| (mach_vm_address_t)image_infos[i].imageLoadAddress; |
| if (strstr(image_name, name)) { |
| |
| lib.name = name; |
| lib.addr_start = (u64)image_load_address; |
| lib.addr_end = 0; |
| return &lib; |
| |
| } |
| |
| } |
| |
| #endif |
| |
| return NULL; |
| |
| } |
| |
| /* for having an easy breakpoint location after loading the shared library */ |
| // this seems to work for clang too. nice :) requires gcc 4.4+ |
| #pragma GCC push_options |
| #pragma GCC optimize("O0") |
| void breakpoint(void) { |
| |
| if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); |
| |
| } |
| |
| #pragma GCC pop_options |
| |
| /* Error reporting to forkserver controller */ |
| |
| void send_forkserver_error(int error) { |
| |
| u32 status; |
| if (!error || error > 0xffff) return; |
| status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); |
| if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; |
| |
| } |
| |
| /* SHM setup. */ |
| |
| static void __afl_map_shm(void) { |
| |
| char *id_str = getenv(SHM_ENV_VAR); |
| char *ptr; |
| |
| if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { |
| |
| u32 val = atoi(ptr); |
| if (val > 0) __afl_map_size = val; |
| |
| } |
| |
| if (__afl_map_size > MAP_SIZE) { |
| |
| if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { |
| |
| fprintf(stderr, |
| "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " |
| "be able to run this instrumented program!\n", |
| __afl_map_size); |
| if (id_str) { |
| |
| send_forkserver_error(FS_ERROR_MAP_SIZE); |
| exit(-1); |
| |
| } |
| |
| } else { |
| |
| fprintf(stderr, |
| "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " |
| "be able to run this instrumented program!\n", |
| __afl_map_size); |
| |
| } |
| |
| } |
| |
| if (id_str) { |
| |
| #ifdef USEMMAP |
| const char * shm_file_path = id_str; |
| int shm_fd = -1; |
| unsigned char *shm_base = NULL; |
| |
| /* create the shared memory segment as if it was a file */ |
| shm_fd = shm_open(shm_file_path, O_RDWR, 0600); |
| if (shm_fd == -1) { |
| |
| fprintf(stderr, "shm_open() failed\n"); |
| send_forkserver_error(FS_ERROR_SHM_OPEN); |
| exit(1); |
| |
| } |
| |
| /* map the shared memory segment to the address space of the process */ |
| shm_base = |
| mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); |
| |
| if (shm_base == MAP_FAILED) { |
| |
| close(shm_fd); |
| shm_fd = -1; |
| |
| fprintf(stderr, "mmap() failed\n"); |
| send_forkserver_error(FS_ERROR_MMAP); |
| exit(2); |
| |
| } |
| |
| __afl_area_ptr = shm_base; |
| #else |
| u32 shm_id = atoi(id_str); |
| |
| __afl_area_ptr = shmat(shm_id, 0, 0); |
| |
| #endif |
| |
| if (__afl_area_ptr == (void *)-1) { |
| |
| send_forkserver_error(FS_ERROR_SHMAT); |
| exit(1); |
| |
| } |
| |
| /* Write something into the bitmap so that the parent doesn't give up */ |
| |
| __afl_area_ptr[0] = 1; |
| |
| } |
| |
| } |
| |
| /* Fork server logic. */ |
| inline static void __afl_start_forkserver(void) { |
| |
| u8 tmp[4] = {0, 0, 0, 0}; |
| u32 status = 0; |
| |
| if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) |
| status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); |
| if (status) status |= (FS_OPT_ENABLED); |
| memcpy(tmp, &status, 4); |
| |
| /* Phone home and tell the parent that we're OK. */ |
| if (write(FORKSRV_FD + 1, tmp, 4) != 4) do_exit = 1; |
| // fprintf(stderr, "write0 %d\n", do_exit); |
| |
| } |
| |
| inline static u32 __afl_next_testcase(u8 *buf, u32 max_len) { |
| |
| s32 status; |
| |
| /* Wait for parent by reading from the pipe. Abort if read fails. */ |
| if (read(FORKSRV_FD, &status, 4) != 4) do_exit = 1; |
| // fprintf(stderr, "read %d\n", do_exit); |
| |
| /* we have a testcase - read it if we read from stdin */ |
| if (use_stdin) { |
| |
| if ((status = read(0, buf, max_len)) <= 0) exit(-1); |
| |
| } else |
| |
| status = 1; |
| // fprintf(stderr, "stdin: %d %d\n", use_stdin, status); |
| |
| /* report that we are starting the target */ |
| if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1; |
| // fprintf(stderr, "write1 %d\n", do_exit); |
| |
| __afl_area_ptr[0] = 1; // put something in the map |
| |
| return status; |
| |
| } |
| |
| inline static void __afl_end_testcase(int status) { |
| |
| if (write(FORKSRV_FD + 1, &status, 4) != 4) do_exit = 1; |
| // fprintf(stderr, "write2 %d\n", do_exit); |
| if (do_exit) exit(0); |
| |
| } |
| |
| #ifdef __aarch64__ |
| #define SHADOW(addr) \ |
| ((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \ |
| MEMORY_MAP_DECREMENT - \ |
| ((uintptr_t)addr & 0x7) * 0x10000000000)) |
| #else |
| #define SHADOW(addr) \ |
| ((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \ |
| MEMORY_MAP_DECREMENT - \ |
| ((uintptr_t)addr & 0x3) * 0x10000000000)) |
| #endif |
| |
| void setup_trap_instrumentation(void) { |
| |
| library_list_t *lib_base = NULL; |
| size_t lib_size = 0; |
| u8 * lib_addr; |
| char * line = NULL; |
| size_t nread, len = 0; |
| char * filename = getenv("AFL_UNTRACER_FILE"); |
| if (!filename) filename = getenv("TRAPFUZZ_FILE"); |
| if (!filename) FATAL("AFL_UNTRACER_FILE environment variable not set"); |
| |
| FILE *patches = fopen(filename, "r"); |
| if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename); |
| |
| // Index into the coverage bitmap for the current trap instruction. |
| #ifdef __aarch64__ |
| uint64_t bitmap_index = 0; |
| #ifdef __APPLE__ |
| pthread_jit_write_protect_np(0); |
| #endif |
| #else |
| uint32_t bitmap_index = 0; |
| #endif |
| |
| while ((nread = getline(&line, &len, patches)) != -1) { |
| |
| char *end = line + len; |
| |
| char *col = strchr(line, ':'); |
| if (col) { |
| |
| // It's a library:size pair |
| *col++ = 0; |
| |
| lib_base = find_library(line); |
| if (!lib_base) FATAL("Library %s does not appear to be loaded", line); |
| |
| // we ignore the defined lib_size |
| lib_size = strtoul(col, NULL, 16); |
| #if (__linux__) |
| if (lib_size < lib_base->addr_end - lib_base->addr_start) |
| lib_size = lib_base->addr_end - lib_base->addr_start; |
| #endif |
| if (lib_size % 0x1000 != 0) |
| WARNF("Invalid library size 0x%zx. Must be multiple of 0x1000", |
| lib_size); |
| |
| lib_addr = (u8 *)lib_base->addr_start; |
| // Make library code writable. |
| if (mprotect((void *)lib_addr, lib_size, |
| PROT_READ | PROT_WRITE | PROT_EXEC) != 0) |
| FATAL("Failed to mprotect library %s writable", line); |
| |
| // Create shadow memory. |
| #ifdef __aarch64__ |
| for (int i = 0; i < 8; i++) { |
| |
| #else |
| for (int i = 0; i < 4; i++) { |
| |
| #endif |
| |
| void *shadow_addr = SHADOW(lib_addr + i); |
| void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE, |
| MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0); |
| if (debug) |
| fprintf(stderr, "Shadow: %s %d = %p-%p for %p\n", line, i, shadow, |
| shadow + lib_size - 1, lib_addr); |
| if (shadow == MAP_FAILED) FATAL("Failed to mmap shadow memory"); |
| |
| } |
| |
| // Done, continue with next line. |
| continue; |
| |
| } |
| |
| // It's an offset, parse it and do the patching. |
| unsigned long offset = strtoul(line, NULL, 16); |
| |
| if (offset > lib_size) |
| FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large", |
| offset, lib_size); |
| |
| if (bitmap_index >= __afl_map_size) |
| FATAL("Too many basic blocks to instrument"); |
| |
| #ifdef __arch64__ |
| uint64_t |
| #else |
| uint32_t |
| #endif |
| *shadow = SHADOW(lib_addr + offset); |
| if (*shadow != 0) continue; // skip duplicates |
| |
| // Make lookup entry in shadow memory. |
| |
| #if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \ |
| defined(__i386__)) |
| |
| // this is for Intel x64 |
| |
| uint8_t orig_byte = lib_addr[offset]; |
| *shadow = (bitmap_index << 8) | orig_byte; |
| lib_addr[offset] = 0xcc; // replace instruction with debug trap |
| if (debug) |
| fprintf(stderr, |
| "Patch entry: %p[%lx] = %p = %02x -> SHADOW(%p) #%d -> %08x\n", |
| lib_addr, offset, lib_addr + offset, orig_byte, shadow, |
| bitmap_index, *shadow); |
| |
| #elif defined(__aarch64__) |
| |
| // this is for aarch64 |
| |
| uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset); |
| uint32_t orig_bytes = *patch_bytes; |
| *shadow = (bitmap_index << 32) | orig_bytes; |
| *patch_bytes = 0xd4200000; // replace instruction with debug trap |
| if (debug) |
| fprintf(stderr, |
| "Patch entry: %p[%lx] = %p = %02x -> SHADOW(%p) #%d -> %016x\n", |
| lib_addr, offset, lib_addr + offset, orig_bytes, shadow, |
| bitmap_index, *shadow); |
| |
| #else |
| // this will be ARM and AARCH64 |
| // for ARM we will need to identify if the code is in thumb or ARM |
| #error "non x86_64/aarch64 not supported yet" |
| //__arm__: |
| // linux thumb: 0xde01 |
| // linux arm: 0xe7f001f0 |
| //__aarch64__: |
| // linux aarch64: 0xd4200000 |
| #endif |
| |
| bitmap_index++; |
| |
| } |
| |
| free(line); |
| fclose(patches); |
| |
| // Install signal handler for SIGTRAP. |
| struct sigaction s; |
| s.sa_flags = SA_SIGINFO; |
| s.sa_sigaction = sigtrap_handler; |
| sigemptyset(&s.sa_mask); |
| sigaction(SIGTRAP, &s, 0); |
| |
| if (debug) fprintf(stderr, "Patched %u locations.\n", bitmap_index); |
| __afl_map_size = bitmap_index; |
| if (__afl_map_size % 8) __afl_map_size = (((__afl_map_size + 7) >> 3) << 3); |
| |
| } |
| |
| /* the signal handler for the traps / debugging interrupts |
| No debug output here because this would cost speed */ |
| static void sigtrap_handler(int signum, siginfo_t *si, void *context) { |
| |
| uint64_t addr; |
| // Must re-execute the instruction, so decrement PC by one instruction. |
| ucontext_t *ctx = (ucontext_t *)context; |
| #if defined(__APPLE__) && defined(__LP64__) |
| #if defined(__x86_64__) |
| ctx->uc_mcontext->__ss.__rip -= 1; |
| addr = ctx->uc_mcontext->__ss.__rip; |
| #else |
| ctx->uc_mcontext->__ss.__pc -= 4; |
| addr = ctx->uc_mcontext->__ss.__pc; |
| #endif |
| #elif defined(__linux__) |
| #if defined(__x86_64__) || defined(__i386__) |
| ctx->uc_mcontext.gregs[REG_RIP] -= 1; |
| addr = ctx->uc_mcontext.gregs[REG_RIP]; |
| #elif defined(__aarch64__) |
| ctx->uc_mcontext.pc -= 4; |
| addr = ctx->uc_mcontext.pc; |
| #else |
| #error "Unsupported processor" |
| #endif |
| #elif defined(__FreeBSD__) && defined(__LP64__) |
| ctx->uc_mcontext.mc_rip -= 1; |
| addr = ctx->uc_mcontext.mc_rip; |
| #else |
| #error "Unsupported platform" |
| #endif |
| |
| // fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr, |
| // si->si_addr); |
| |
| // If the trap didn't come from our instrumentation, then we probably will |
| // just segfault here |
| uint8_t *faultaddr; |
| if (unlikely(si->si_addr)) |
| faultaddr = (u8 *)si->si_addr - 1; |
| else |
| faultaddr = (u8 *)addr; |
| // if (debug) fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr)); |
| uint32_t shadow = *SHADOW(faultaddr); |
| uint8_t orig_byte = shadow & 0xff; |
| uint32_t index = shadow >> 8; |
| |
| // if (debug) fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", |
| // shadow, orig_byte, index); |
| |
| // Index zero is invalid so that it is still possible to catch actual trap |
| // instructions in instrumented libraries. |
| if (unlikely(index == 0)) abort(); |
| |
| // Restore original instruction |
| *faultaddr = orig_byte; |
| |
| __afl_area_ptr[index] = 128; |
| |
| } |
| |
| /* the MAIN function */ |
| int main(int argc, char *argv[]) { |
| |
| #if defined(__linux__) |
| (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR |
| #elif defined(__FreeBSD__) && __FreeBSD_version >= 1200000 |
| int no_randomize = PROC_ASLR_FORCE_DISABLE; |
| (void)procctl(P_PID, 0, PROC_ASLR_CTL, &no_randomize); |
| #endif |
| |
| pid = getpid(); |
| if (getenv("AFL_DEBUG")) debug = 1; |
| |
| /* by default we use stdin, but also a filename can be passed, in this |
| case the input is argv[1] and we have to disable stdin */ |
| if (argc > 1) { |
| |
| use_stdin = 0; |
| inputfile = argv[1]; |
| |
| } |
| |
| // STEP 2: load the library you want to fuzz and lookup the functions, |
| // inclusive of the cleanup functions |
| // NOTE: above the main() you have to define the functions! |
| |
| void *dl = dlopen("./libtestinstr.so", RTLD_LAZY); |
| if (!dl) FATAL("could not find target library"); |
| o_function = dlsym(dl, "testinstr"); |
| if (!o_function) FATAL("could not resolve target function from library"); |
| if (debug) fprintf(stderr, "Function address: %p\n", o_function); |
| |
| // END STEP 2 |
| |
| /* setup instrumentation, shared memory and forkserver */ |
| breakpoint(); |
| read_library_information(); |
| setup_trap_instrumentation(); |
| __afl_map_shm(); |
| __afl_start_forkserver(); |
| |
| while (1) { |
| |
| // instead of fork() we could also use the snapshot lkm or do our own mini |
| // snapshot feature like in https://github.com/marcinguy/fuzzer |
| // -> snapshot.c |
| if ((pid = fork()) == -1) PFATAL("fork failed"); |
| |
| if (pid) { |
| |
| u32 status; |
| if (waitpid(pid, &status, 0) < 0) exit(1); |
| /* report the test case is done and wait for the next */ |
| __afl_end_testcase(status); |
| |
| } else { |
| |
| pid = getpid(); |
| while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { |
| |
| // in this function the fuzz magic happens, this is STEP 3 |
| fuzz(); |
| |
| // we can use _exit which is faster because our target library |
| // was loaded via dlopen and therefore cannot have deconstructors |
| // registered. |
| _exit(0); |
| |
| } |
| |
| } |
| |
| } |
| |
| return 0; |
| |
| } |
| |
| #ifndef _DEBUG |
| inline |
| #endif |
| static void |
| fuzz(void) { |
| |
| // STEP 3: call the function to fuzz, also the functions you might |
| // need to call to prepare the function and - important! - |
| // to clean everything up |
| |
| // in this example we use the input file, not stdin! |
| (*o_function)(buf, len); |
| |
| // normally you also need to cleanup |
| //(*o_LibFree)(foo); |
| |
| // END STEP 3 |
| |
| } |
| |