DynamoRIO based syscall result fuzzer

From Practical Software Verification

Jump to: navigation, search
Compile:
gcc fuzz.c -DLINUX -DSHOW_RESULTS -DX86_32 -fPIC -I/path/to/DR/build/include/ /path/to/DR/build/lib/libdynamorio.so -shared -o fuzz_client 
Run:
./drdeploy -v -client /path/to/fuzz_client 0x1 "" netstat 

/*
 * This is a demo fuzzer to show some of the syscall and signal hooking 
 * features of DynamoRIO. The code is based on strace.c and signal.c from the DynamoRIO
 * distribution. It intercepts read() syscalls and fuzzes the result.
 *
 * A real fuzzer based off this would probably want to intercept open() calls to 
 * track what input file the fuzz data came from and probably note the address
 * of the instruction to call the read() for debugging purposes. Also allowing the 
 * user to specify the srand() seed would be useful
 *
 * To modify this to run on Windows see strace.c/signal.c (and also sort out the
 * various glibc calls to whatever Windows alternative you want)
 *
 * nnp@unprotectedhex.com
 * 14 April 2009
 */

#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <signal.h>
#include "dr_api.h"

#define READ_SYSCALL 3

/* Use these to store the paramaters passed to read() */
static int read_count = 0;
static void *read_buf = 0x0;
static int read_fd = 0;

static FILE *fp;

static void event_exit(void);
static bool event_filter_syscall(void *drcontext, int sysnum);
static bool event_pre_syscall(void *drcontext, int sysnum);
static void event_post_syscall(void *drcontext, int sysnum);
static dr_signal_action_t event_signal(void *drcontext, dr_siginfo_t *info);

DR_EXPORT void 
dr_init(client_id_t id)
{
    fp = fopen("/dev/urandom", "r");

    srand(time(0));

    /* register events */
    dr_register_exit_event(event_exit);
    dr_register_filter_syscall_event(event_filter_syscall);
    dr_register_pre_syscall_event(event_pre_syscall);
    dr_register_post_syscall_event(event_post_syscall);

    /* make it easy to tell, by looking at log file, which client executed */
    dr_log(NULL, LOG_ALL, 1, "Client 'taint' initializing\n");
#ifdef SHOW_RESULTS
    /* also give notification to stderr */
    if (dr_is_notify_on())
        dr_fprintf(STDERR, "Client 'taint' running\n");
#endif
}

static dr_signal_action_t 
event_signal(void *drcontext, dr_siginfo_t *info)
{
    if (info->sig == SIGTERM) {
        dr_printf("[!] Program received SIGTERM\n");
    } else if (info->sig == SIGSEGV) {
        dr_printf("[!] Program received SIGSEGV\n");
    }

    return DR_SIGNAL_DELIVER;
}

static void 
event_exit(void)
{
    dr_fprintf(STDERR, "Client 'taint' exiting\n");
    fclose(fp);
}

static bool
event_filter_syscall(void *drcontext, int sysnum)
{
    if (sysnum == READ_SYSCALL)
        return true;
    else
        return false;
}

static bool
event_pre_syscall(void *drcontext, int sysnum)
{
    /* Need to check this because the filter may not be accurate */
    if (sysnum == READ_SYSCALL) {
        read_fd = dr_syscall_get_param(drcontext, 0);
        read_buf = (void*)dr_syscall_get_param(drcontext, 1);
        read_count = dr_syscall_get_param(drcontext, 2);
        dr_printf("Read syscall (%d); fd : %d, buf : %p, count : %d\n",
                sysnum, read_fd, read_buf, read_count); 

    }

    /* returning false means the syscall isn't run */
    return true;
}

static void 
event_post_syscall(void *drcontext, int sysnum)
{
    /* Need to check this because the filter may not be accurate */
    if (sysnum == READ_SYSCALL) {
        int i, x, buf_len, fuzzed_count, mod_op;
        i = x = buf_len = fuzzed_count = mod_op = 0;
        /*
         * Decide whether to fuzz or not
         * Decide whether to max the buffer or not
         * Decide what bits to work the flip
         * Profit... or fail miserably
         */

        buf_len = dr_syscall_get_result(drcontext);
        dr_printf("Syscall result %d\n", buf_len);
        if (buf_len <= 0)
            return;
        /* Fuzz approx .5 of the reads, maybe decrease this for programs with many*/
        if (!(rand() % 2)) {
            dr_printf("[+] Fuzzing...\n");
            /* Max out the read buffer */
            if(!(rand() % 2) && buf_len < read_count) {
                dr_printf("[+] Maxing out the read buffer to %d bytes\n", read_count);
                dr_syscall_set_result(drcontext, read_count);
                /* Fill the unused space with junk */
                if(!(rand() % 2)) {
                    dr_printf("[+] Filling the remaining %d bytes with junk\n",
                            read_count - buf_len);
                    for (i = buf_len; i < read_count; i++) {
                        ((char*)read_buf)[i] = fgetc(fp);
                    }
                }
                buf_len = read_count;
            } 

            /* Flip bytes */
            for (i = 0; i < buf_len; i++) {
                /* Flip 10% */
                x = buf_len/10;
                mod_op = buf_len/x;
                if(!(rand() % mod_op)) {
                    fuzzed_count++;
                    ((char*)read_buf)[i] = fgetc(fp);
                }
            }
            dr_printf("[+] Fuzzed %d/%d bytes\n", fuzzed_count, buf_len);
        }
    }
}
Personal tools