static void wrap_pre_SSL_write(void *wrapcxt, OUT void **user_data) { /* int SSL_write(SSL *ssl, const void *buf, int num); * * ssize_t gnutls_record_send(gnutls_session_t session, * const void * data, size_t sizeofdata); */ void *ssl = (void *)drwrap_get_arg(wrapcxt, 0); unsigned char *buf = (unsigned char *)drwrap_get_arg(wrapcxt, 1); size_t sz = (size_t)drwrap_get_arg(wrapcxt, 2); /* By generating unique filenames (per SSL context), we are able to * simplify logging of SSL traffic (no file locking is required). */ char filename[MAXIMUM_PATH] = { 0 }; dr_snprintf(filename, BUFFER_SIZE_ELEMENTS(filename), "trace-%x.write", ssl); NULL_TERMINATE_BUFFER(filename); FILE *fp = fopen(filename, "ab+"); /* Error handling of logging operations isn't critical - in fact, we don't * even know what to do in such error conditions, so we simply return! */ if (!fp) { dr_fprintf(STDERR, "Couldn’t open the output file %s\n", filename); return; } /* We assume that SSL_write always succeeds and writes the whole buffer. */ fwrite(buf, 1, sz, fp); fclose(fp); }
static void wrap_realloc_pre(void *wrapctx, void **user_data) { logging_paused++; void *ptr = drwrap_get_arg(wrapctx, 0); freed(ptr); *user_data = drwrap_get_arg(wrapctx, 1); }
static void wrap_pre_SSL_read(void *wrapcxt, OUT void **user_data) { struct SSL_read_data *sd; /* int SSL_read(SSL *ssl, void *buf, int num); * * ssize_t gnutls_record_recv(gnutls_session_t session, * void * data, size_t sizeofdata); */ sd = dr_global_alloc(sizeof(struct SSL_read_data)); sd->read_buffer = (unsigned char *)drwrap_get_arg(wrapcxt, 1); sd->ssl = (void *)drwrap_get_arg(wrapcxt, 0); *user_data = (void *)sd; }
static void wrap_pre(void *wrapcxt, OUT void **user_data) { bool ok; CHECK(wrapcxt != NULL && user_data != NULL, "invalid arg"); if (drwrap_get_func(wrapcxt) == addr_skip_flags) { CHECK(drwrap_get_arg(wrapcxt, 0) == (void *) 1, "get_arg wrong"); CHECK(drwrap_get_arg(wrapcxt, 1) == (void *) 2, "get_arg wrong"); } else if (drwrap_get_func(wrapcxt) == addr_level0) { dr_fprintf(STDERR, " <pre-level0>\n"); CHECK(drwrap_get_arg(wrapcxt, 0) == (void *) 37, "get_arg wrong"); ok = drwrap_set_arg(wrapcxt, 0, (void *) 42); CHECK(ok, "set_arg error"); *user_data = (void *) 99; } else if (drwrap_get_func(wrapcxt) == addr_level1) { dr_fprintf(STDERR, " <pre-level1>\n"); ok = drwrap_set_arg(wrapcxt, 1, (void *) 1111); CHECK(ok, "set_arg error"); } else if (drwrap_get_func(wrapcxt) == addr_tailcall) { dr_fprintf(STDERR, " <pre-makes_tailcall>\n"); } else if (drwrap_get_func(wrapcxt) == addr_level2) { dr_fprintf(STDERR, " <pre-level2>\n"); } else if (drwrap_get_func(wrapcxt) == addr_skipme) { dr_fprintf(STDERR, " <pre-skipme>\n"); drwrap_skip_call(wrapcxt, (void *) 7, 0); } else if (drwrap_get_func(wrapcxt) == addr_repeat) { dr_mcontext_t *mc = drwrap_get_mcontext(wrapcxt); dr_fprintf(STDERR, " <pre-repeat#%d>\n", repeated ? 2 : 1); repeat_xsp = mc->xsp; #ifdef ARM repeat_link = mc->lr; #endif if (repeated) /* test changing the arg value on the second pass */ drwrap_set_arg(wrapcxt, 0, (void *)2); CHECK(drwrap_redirect_execution(NULL) != DREXT_SUCCESS, "allowed redirect with NULL wrapcxt"); CHECK(drwrap_redirect_execution(wrapcxt) != DREXT_SUCCESS, "allowed redirect in pre-wrap"); } else if (drwrap_get_func(wrapcxt) == addr_preonly) { dr_fprintf(STDERR, " <pre-preonly>\n"); } else CHECK(false, "invalid wrap"); }
/* * We wrap the C library function memset, because I've noticed that at * least one optimised implementation of it diverges control flow * internally based on what appears to be the _alignment_ of the input * pointer - and that alignment check can vary depending on the * addresses of allocated blocks. So I can't guarantee no divergence * of control flow inside memset if malloc doesn't return the same * values, and instead I just have to trust that memset isn't reading * the contents of the block and basing control flow decisions on that. */ static void wrap_memset_pre(void *wrapctx, void **user_data) { uint was_already_paused = logging_paused++; if (outfile == INVALID_FILE || was_already_paused) return; const void *addr = drwrap_get_arg(wrapctx, 0); size_t size = (size_t)drwrap_get_arg(wrapctx, 2); struct allocation *alloc = find_allocation(addr); if (!alloc) { dr_fprintf(outfile, "memset %"PRIuMAX" @ %"PRIxMAX"\n", (uintmax_t)size, (uintmax_t)addr); } else { dr_fprintf(outfile, "memset %"PRIuMAX" @ allocations[%"PRIuPTR"]" " + %"PRIxMAX"\n", (uintmax_t)size, alloc->index, (uintmax_t)(addr - alloc->start)); } }
void pre_calloc(void *wrapctx, OUT void **user_data) { void *drc; drc = drwrap_get_drcontext(wrapctx); dr_mutex_lock(lock); if (!module_is_wrapped(drc)) { dr_mutex_unlock(lock); return; } *user_data = add_block((size_t)drwrap_get_arg(wrapctx, 1) * (size_t)drwrap_get_arg(wrapctx, 0), get_prev_instr_pc(drwrap_get_retaddr(wrapctx), drc), drc); dr_mutex_unlock(lock); }
/** Wrapper pre-callback. */ static void wrapperPre( void* ctx, void** data ) { dr_mutex_lock( outMutex ); uint64 currentTimeMillis = dr_get_milliseconds(); if( currentTimeMillis > nextTimestampMillis ) { dr_fprintf( outFile, "T 0x%.16llx\n", currentTimeMillis ); nextTimestampMillis = currentTimeMillis + timestampIntervalMillis; } dr_fprintf( outFile, "X %p %p\n", (void*)drwrap_get_func( ctx ), drwrap_get_arg( ctx, 0 ) ); dr_mutex_unlock( outMutex ); }
static void wrap_pre(void *wrapcxt, OUT void **user_data) { /* malloc(size) or HeapAlloc(heap, flags, size) */ size_t sz = (size_t) drwrap_get_arg(wrapcxt, IF_WINDOWS_ELSE(2,0)); /* find the maximum malloc request */ if (sz > max_malloc) { dr_mutex_lock(max_lock); if (sz > max_malloc) max_malloc = sz; dr_mutex_unlock(max_lock); } *user_data = (void *) sz; }
/* * Wrap the log_set_file() function in testsc.c, and respond to it by * opening or closing log files. */ static void wrap_logsetfile(void *wrapctx, void **user_data) { if (outfile) { dr_close_file(outfile); outfile = INVALID_FILE; } const char *outfilename = drwrap_get_arg(wrapctx, 0); if (outfilename) { outfile = dr_open_file(outfilename, DR_FILE_WRITE_OVERWRITE); DR_ASSERT(outfile != INVALID_FILE); } /* * Reset the allocation list to empty, whenever we open or close a * log file. */ while (alloc_ends->next != alloc_ends) free_allocation(alloc_ends->next); next_alloc_index = 0; }
void pre_malloc(void *wrapctx, OUT void **user_data) { void *drc; drc = drwrap_get_drcontext(wrapctx); dr_mutex_lock(lock); /* // the first call on 64 bit and the second in 32bit */ /* // are init call, so we have to do nothing */ /* #ifdef BUILD_64 */ /* if (!malloc_init++ && !realloc_init) */ /* { */ /* dr_mutex_unlock(lock); */ /* return; */ /* } */ /* #else */ /* if (malloc_init++ == 1 && realloc_init != 1) */ /* { */ /* dr_mutex_unlock(lock); */ /* return; */ /* } */ /* #endif */ if (!module_is_wrapped(drc)) { dr_mutex_unlock(lock); return; } *user_data = add_block((size_t)drwrap_get_arg(wrapctx, 0), get_prev_instr_pc(drwrap_get_retaddr(wrapctx), drc), drc); dr_mutex_unlock(lock); }
static void pre_fuzz_handler(void *wrapcxt, INOUT void **user_data) { void *dcontext = drwrap_get_drcontext(wrapcxt); app_pc target_to_fuzz = drwrap_get_func(wrapcxt); fuzz_target_t *target = hashtable_lookup(&fuzz_target_htable, target_to_fuzz); fuzz_pass_context_t *fp = (fuzz_pass_context_t *) drmgr_get_tls_field(dcontext, tls_idx_fuzzer); bool is_target_entry = false; pass_target_t *live = NULL; dr_mcontext_t *mc; uint i; ASSERT(target != NULL, "pre_fuzz must be associated with a fuzz target"); DRFUZZ_LOG(3, "pre_fuzz() for target "PFX" with %d args\n", target_to_fuzz, target->arg_count); /* XXX i#1734: this heuristic may be incorrect when a handled fault occurs during * the very last iteration of the last fuzz pass on any thread. */ clear_thread_state(fp); /* Stop the target iterator that was captured at the last critical fault, because * the fact that we are in pre-fuzz implies the fault was handled and doesn't matter. */ if (fp->thread_state->targets != NULL) drfuzz_target_iterator_stop(fp->thread_state->targets); /* XXX: assumes the fuzz target is never called recursively */ if (fp->live_targets != NULL && fp->live_targets->target->func_pc == target_to_fuzz) { live = fp->live_targets; /* this is a repetition of the last live target */ } else { is_target_entry = true; /* this is a new invocation of a target */ live = activate_cached_target(fp, target_to_fuzz); /* check the cache */ if (live == NULL) live = create_pass_target(dcontext, wrapcxt); live->next = fp->live_targets; /* push to live stack */ fp->live_targets = live; } /* required by dr_redirect_execution() (avoids having to merge the mcontext) */ mc = drwrap_get_mcontext_ex(wrapcxt, DR_MC_ALL); /* XXX: can we relax this? */ if (is_target_entry) { live->xsp = mc->xsp; #ifdef X86 live->unclobber.retaddr_loc = (reg_t *) mc->xsp; /* see retaddr_unclobber_t */ #endif live->unclobber.retaddr = (reg_t) drwrap_get_retaddr(wrapcxt); DRFUZZ_LOG(4, "fuzz target "PFX": saving stack pointer "PFX"\n", target_to_fuzz, mc->xsp); for (i = 0; i < target->arg_count; i++) { /* store the original arg values */ live->original_args[i] = (reg_t) drwrap_get_arg(wrapcxt, i); /* copy original args to current args for the first iteration of the fuzz */ live->current_args[i] = live->original_args[i]; DRFUZZ_LOG(4, "fuzz target "PFX": saving original arg #%d: "PFX"\n", target_to_fuzz, i, live->original_args[i]); } } /* restore the original arg values before calling the client */ for (i = 0; i < target->arg_count; i++) { DRFUZZ_LOG(4, "fuzz target "PFX": restoring original arg #%d: "PFX"\n", target_to_fuzz, i, live->original_args[i]); drwrap_set_arg(wrapcxt, i, (void *) live->original_args[i]); } #ifdef ARM mc->lr = live->unclobber.retaddr; /* restore retaddr to link register */ #else /* X86 */ *live->unclobber.retaddr_loc = live->unclobber.retaddr; /* restore retaddr to stack */ #endif target->pre_fuzz_cb(fp, (generic_func_t) target_to_fuzz, mc); drwrap_set_mcontext(wrapcxt); for (i = 0; i < target->arg_count; i++) live->current_args[i] = (reg_t) drwrap_get_arg(wrapcxt, i); *user_data = fp; }
/* * The actual wrapper functions for malloc, realloc and free. */ static void wrap_malloc_pre(void *wrapctx, void **user_data) { logging_paused++; *user_data = drwrap_get_arg(wrapctx, 0); }
void pre_realloc(void *wrapctx, OUT void **user_data) { malloc_t *block; malloc_t *new_block; realloc_tmp_t *tmp = NULL; void *start = drwrap_get_arg(wrapctx, 0); size_t size = (size_t)drwrap_get_arg(wrapctx, 1); void *drc = drwrap_get_drcontext(wrapctx); dr_mutex_lock(lock); /* // the first call on 64 bit and the second in 32bit */ /* // are init call, so we have to do nothing */ /* #ifdef BUILD_64 */ /* if (!realloc_init) */ /* { */ /* realloc_init++; */ /* dr_mutex_unlock(lock); */ /* return; */ /* } */ /* #else */ /* if (realloc_init++ == 1) */ /* { */ /* dr_mutex_unlock(lock); */ /* return; */ /* } */ /* #endif */ // if size == 0, realloc call free if (!size) { // this can happen if a block is alloc by a non wrap module and realloc // on a wrapped one if (!(block = search_on_tree(active_blocks, start))) { dr_mutex_unlock(lock); return; } block->free_pc = get_prev_instr_pc(drwrap_get_retaddr(wrapctx), drc); get_caller_data(&(block->free_func_pc), &(block->free_func_sym), &(block->free_module_name), drc, 1); dr_mutex_unlock(lock); return; } if (!(tmp = dr_global_alloc(sizeof(realloc_tmp_t)))) { dr_printf("dr_malloc fail\n"); dr_mutex_unlock(lock); return; } // if realloc is use like malloc save the size to set it on the post wrapping tmp->size = size; *user_data = tmp; // if start == 0, realloc call malloc if (!start) { // if a block is alloc by a wrapped function and realloc by // an unwrapped one we have to take the realloc // so when realloc is called to do a malloc is the only case // when we have to check if the module is wrapped if(!module_is_wrapped(drc)) { *user_data = NULL; dr_global_free(tmp, sizeof(*tmp)); dr_mutex_unlock(lock); return; } tmp->block = NULL; dr_mutex_unlock(lock); return; } // this can happen if the block is alloc by a non wrapped module if (!(block = search_on_tree(active_blocks, start))) { *user_data = NULL; dr_global_free(tmp, sizeof(*tmp)); dr_mutex_unlock(lock); return; } else { del_from_tree(&active_blocks, start, NULL, false); if ((new_block = dr_custom_alloc(NULL, 0, sizeof(*new_block), DR_MEMPROT_WRITE | DR_MEMPROT_READ, NULL))) { block->flag |= FREE_BY_REALLOC; block->free_pc = get_prev_instr_pc(drwrap_get_retaddr(wrapctx), drc); get_caller_data(&(block->free_func_pc), &(block->free_func_sym), &(block->free_module_name), drc, 1); block->next = old_blocks; old_blocks = block; old_blocks_count++; if (!args->console && old_blocks_count == MAX_OLD_BLOCKS) { flush_old_block(); old_blocks_count = 0; } ds_memset(new_block, 0, sizeof(*new_block)); new_block->flag |= ALLOC_BY_REALLOC; block = new_block; } else dr_printf("fail alloc\n"); block->size = size; } tmp->block = block; dr_mutex_unlock(lock); }