static void event_thread_context_exit(void *drcontext, bool thread_exit) { if (!thread_exit && dr_get_thread_id(drcontext) != main_thread) { #if VERBOSE dr_fprintf(STDERR, " non-main thread exiting callback depth=%d cls=%d\n", cb_depth, (int)(ptr_int_t) drmgr_get_cls_field(drcontext, cls_idx)); #endif CHECK(drmgr_get_cls_field(drcontext, cls_idx) == (void *)(ptr_int_t)cb_depth, "cls not preserved"); cb_depth--; CHECK(drmgr_get_tls_field(drcontext, tls_idx) == (void *)(ptr_int_t)dr_get_thread_id(drcontext), "tls not preserved"); } }
static void check_cls_from_cache(void *cls_val) { CHECK(cls_val == drmgr_get_cls_field(dr_get_current_drcontext(), cls_idx), "cls read from cache incorrect"); checked_cls_from_cache = true; }
static void event_post_syscall(void *drcontext, int sysnum) { process_id_t child_pid = 0; per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, cls_idx); /* XXX i#752: should DR provide a child creation event that gives us the pid? */ #ifdef LINUX if (sysnum == SYS_fork || (sysnum == SYS_clone && !TEST(CLONE_VM, data->saved_param))) { child_pid = dr_syscall_get_result(drcontext); if (child_pid > 0) nudge_child(child_pid); } #else if (sysnum == sysnum_CreateProcess || sysnum == sysnum_CreateProcessEx || sysnum == sysnum_CreateUserProcess) { if (dr_syscall_get_result(drcontext) >= 0) { /* success */ HANDLE *hproc = (HANDLE) data->saved_param; HANDLE h; size_t read; if (dr_safe_read(hproc, sizeof(h), &h, &read) && read == sizeof(h)) data->child_pid = dr_convert_handle_to_pid(h); /* we can't nudge now b/c the child's initial thread is suspended */ } } else if (sysnum == sysnum_ResumeThread) { /* child should be alive now */ if (data->child_pid != INVALID_PROCESS_ID) nudge_child(data->child_pid); } #endif }
static bool event_pre_syscall(void *drcontext, int sysnum) { #ifdef LINUX if (sysnum == SYS_clone) { per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, cls_idx); data->saved_param = dr_syscall_get_param(drcontext, 0); } #else if (sysnum == sysnum_CreateProcess || sysnum == sysnum_CreateProcessEx || sysnum == sysnum_CreateUserProcess) { per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, cls_idx); data->saved_param = dr_syscall_get_param(drcontext, 0); } #endif return true; }
static void event_thread_context_exit(void *drcontext, bool thread_exit) { if (thread_exit) { per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, cls_idx); dr_thread_free(drcontext, data, sizeof(per_thread_t)); } /* else, nothing to do: we leave the struct for re-use on next context */ }
static void check_cls_write_from_cache(void) { CHECK(drmgr_get_cls_field(dr_get_current_drcontext(), cls_idx) == (void *) MAGIC_NUMBER_FROM_CACHE, "cls write from cache incorrect"); /* now restore */ drmgr_set_cls_field(dr_get_current_drcontext(), cls_idx, (void *)(ptr_int_t)cb_depth); checked_cls_write_from_cache = true; }
static void event_thread_context_exit(void *drcontext, bool thread_exit) { #ifdef SHOW_RESULTS dr_fprintf(STDERR, "resuming prior thread context id="TIDFMT"\n", dr_get_thread_id(drcontext)); #endif if (thread_exit) { per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx); dr_thread_free(drcontext, data, sizeof(per_thread_t)); } /* else, nothing to do: we leave the struct for re-use on next context */ }
static void event_post_syscall(void *drcontext, int sysnum) { #ifdef SHOW_RESULTS dr_syscall_result_info_t info = { sizeof(info), }; dr_syscall_get_result_ex(drcontext, &info); if (!info.succeeded) { /* XXX: we could use the "drsyscall" Extension from the Dr. Memory * Framework (DRMF) to obtain the name of the system call (as well as * the number of arguments and the type of each). Please see the strace * sample and drstrace tool within DRMF for further information. */ dr_fprintf(STDERR, "<---- syscall %d failed (returned "PFX" == "SZFMT") ---->\n", sysnum, info.value, (ptr_int_t)info.value); } #endif if (sysnum == write_sysnum) { per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx); /* we repeat a write originally to stdout that we redirected to * stderr: on the repeat we use stdout */ if (data->repeat) { /* repeat syscall with stdout */ int i; #ifdef SHOW_RESULTS dr_fprintf(STDERR, "<---- repeating write ---->\n"); #endif dr_syscall_set_sysnum(drcontext, write_sysnum); dr_syscall_set_param(drcontext, 0, (reg_t) STDOUT); for (i = 1; i < SYS_MAX_ARGS; i++) dr_syscall_set_param(drcontext, i, data->param[i]); #ifdef WINDOWS if (dr_is_wow64()) { /* Set the xcx emulation parameter for wow64: since * we're executing the same system call again we can * use that same parameter. For new system calls we'd * need to determine the parameter from the ntdll * wrapper. */ dr_mcontext_t mc = {sizeof(mc),DR_MC_INTEGER/*only need xcx*/}; dr_get_mcontext(drcontext, &mc); mc.xcx = data->xcx; dr_set_mcontext(drcontext, &mc); } #endif dr_syscall_invoke_another(drcontext); } } }
static void event_thread_context_init(void *drcontext, bool new_depth) { /* create an instance of our data structure for this thread context */ per_thread_t *data; #ifdef SHOW_RESULTS dr_fprintf(STDERR, "new thread context id="TIDFMT"%s\n", dr_get_thread_id(drcontext), new_depth ? " new depth" : ""); #endif if (new_depth) { data = (per_thread_t *) dr_thread_alloc(drcontext, sizeof(per_thread_t)); drmgr_set_cls_field(drcontext, tcls_idx, data); } else data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx); memset(data, 0, sizeof(*data)); }
static void event_thread_context_init(void *drcontext, bool new_depth) { if (dr_get_thread_id(drcontext) != main_thread) { cb_depth++; #if VERBOSE /* # cbs differs on xp vs win7 so not part of output */ dr_fprintf(STDERR, "non-main thread entering callback depth=%d\n", cb_depth); #endif CHECK(new_depth || drmgr_get_cls_field(drcontext, cls_idx) == (void *)(ptr_int_t)cb_depth, "not re-using prior callback value"); drmgr_set_cls_field(drcontext, cls_idx, (void *)(ptr_int_t)cb_depth); CHECK(drmgr_get_tls_field(drcontext, tls_idx) == (void *)(ptr_int_t)dr_get_thread_id(drcontext), "tls not preserved"); } }
static void event_post_syscall(void *drcontext, int sysnum) { #ifdef SHOW_RESULTS dr_fprintf(STDERR, " [%d] => "PFX" ("SZFMT")\n", sysnum, dr_syscall_get_result(drcontext), (ptr_int_t)dr_syscall_get_result(drcontext)); #endif if (sysnum == write_sysnum) { per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx); /* we repeat a write originally to stdout that we redirected to * stderr: on the repeat we use stdout */ if (data->repeat) { /* repeat syscall with stdout */ int i; #ifdef SHOW_RESULTS dr_fprintf(STDERR, " [%d] => repeating\n", sysnum); #endif dr_syscall_set_sysnum(drcontext, write_sysnum); dr_syscall_set_param(drcontext, 0, (reg_t) STDOUT); for (i = 1; i < SYS_MAX_ARGS; i++) dr_syscall_set_param(drcontext, i, data->param[i]); #ifdef WINDOWS if (dr_is_wow64()) { /* Set the xcx emulation parameter for wow64: since * we're executing the same system call again we can * use that same parameter. For new system calls we'd * need to determine the parameter from the ntdll * wrapper. */ dr_mcontext_t mc = {sizeof(mc),DR_MC_INTEGER/*only need xcx*/}; dr_get_mcontext(drcontext, &mc); mc.xcx = data->xcx; dr_set_mcontext(drcontext, &mc); } #endif dr_syscall_invoke_another(drcontext); } } }
static void event_thread_context_init(void *drcontext, bool new_depth) { /* create an instance of our data structure for this thread context */ per_thread_t *data; if (new_depth) { data = (per_thread_t *) dr_thread_alloc(drcontext, sizeof(per_thread_t)); drmgr_set_cls_field(drcontext, cls_idx, data); } else data = (per_thread_t *) drmgr_get_cls_field(drcontext, cls_idx); memset(data, 0, sizeof(*data)); /* test self-nudge to make up for lack of nudge_test on windows (waiting * for runall support (i#120) */ if (!sent_self) { sent_self = true; if (!dr_nudge_client(client_id, NUDGE_ARG_SELF)) dr_fprintf(STDERR, "self nudge failed"); } }
static bool event_pre_syscall(void *drcontext, int sysnum) { bool modify_write = (sysnum == write_sysnum); dr_atomic_add32_return_sum(&num_syscalls, 1); #ifdef UNIX if (sysnum == SYS_execve) { /* our stats will be re-set post-execve so display now */ show_results(); # ifdef SHOW_RESULTS dr_fprintf(STDERR, "<---- execve ---->\n"); # endif } #endif #ifndef SHOW_RESULTS /* for sanity tests that don't show results we don't change the app's output */ modify_write = false; #endif if (modify_write) { /* store params for access post-syscall */ int i; per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx); #ifdef WINDOWS /* stderr and stdout are identical in our cygwin rxvt shell so for * our example we suppress output starting with 'H' instead */ byte *output = (byte *) dr_syscall_get_param(drcontext, 5); byte first; size_t read; bool ok = dr_safe_read(output, 1, &first, &read); if (!ok || read != 1) return true; /* data unreadable: execute normally */ if (dr_is_wow64()) { /* store the xcx emulation parameter for wow64 */ dr_mcontext_t mc = {sizeof(mc),DR_MC_INTEGER/*only need xcx*/}; dr_get_mcontext(drcontext, &mc); data->xcx = mc.xcx; } #endif for (i = 0; i < SYS_MAX_ARGS; i++) data->param[i] = dr_syscall_get_param(drcontext, i); /* suppress stderr */ if (dr_syscall_get_param(drcontext, 0) == (reg_t) STDERR #ifdef WINDOWS && first == 'H' #endif ) { /* pretend it succeeded */ #ifdef UNIX /* return the #bytes == 3rd param */ dr_syscall_result_info_t info = { sizeof(info), }; info.succeeded = true; info.value = dr_syscall_get_param(drcontext, 2); dr_syscall_set_result_ex(drcontext, &info); #else /* XXX: we should also set the IO_STATUS_BLOCK.Information field */ dr_syscall_set_result(drcontext, 0); #endif #ifdef SHOW_RESULTS dr_fprintf(STDERR, "<---- skipping write to stderr ---->\n"); #endif return false; /* skip syscall */ } else if (dr_syscall_get_param(drcontext, 0) == (reg_t) STDOUT) { if (!data->repeat) { /* redirect stdout to stderr (unless it's our repeat) */ #ifdef SHOW_RESULTS dr_fprintf(STDERR, "<---- changing stdout to stderr ---->\n"); #endif dr_syscall_set_param(drcontext, 0, (reg_t) STDERR); } /* we're going to repeat this syscall once */ data->repeat = !data->repeat; } } return true; /* execute normally */ }
static bool event_pre_syscall(void *drcontext, int sysnum) { ATOMIC_INC(num_syscalls); #ifdef LINUX if (sysnum == SYS_execve) { /* our stats will be re-set post-execve so display now */ show_results(); # ifdef SHOW_RESULTS dr_fprintf(STDERR, "<---- execve ---->\n"); # endif } #endif #ifdef SHOW_RESULTS dr_fprintf(STDERR, "[%d] "PFX" "PFX" "PFX"\n", sysnum, dr_syscall_get_param(drcontext, 0), dr_syscall_get_param(drcontext, 1), dr_syscall_get_param(drcontext, 2)); #endif if (sysnum == write_sysnum) { /* store params for access post-syscall */ int i; per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx); #ifdef WINDOWS /* stderr and stdout are identical in our cygwin rxvt shell so for * our example we suppress output starting with 'H' instead */ byte *output = (byte *) dr_syscall_get_param(drcontext, 5); byte first; size_t read; bool ok = dr_safe_read(output, 1, &first, &read); if (!ok || read != 1) return true; /* data unreadable: execute normally */ if (dr_is_wow64()) { /* store the xcx emulation parameter for wow64 */ dr_mcontext_t mc = {sizeof(mc),DR_MC_INTEGER/*only need xcx*/}; dr_get_mcontext(drcontext, &mc); data->xcx = mc.xcx; } #endif for (i = 0; i < SYS_MAX_ARGS; i++) data->param[i] = dr_syscall_get_param(drcontext, i); /* suppress stderr */ if (dr_syscall_get_param(drcontext, 0) == (reg_t) STDERR #ifdef WINDOWS && first == 'H' #endif ) { /* pretend it succeeded */ #ifdef LINUX /* return the #bytes == 3rd param */ dr_syscall_set_result(drcontext, dr_syscall_get_param(drcontext, 2)); #else /* we should also set the IO_STATUS_BLOCK.Information field */ dr_syscall_set_result(drcontext, 0); #endif #ifdef SHOW_RESULTS dr_fprintf(STDERR, " [%d] => skipped\n", sysnum); #endif return false; /* skip syscall */ } else if (dr_syscall_get_param(drcontext, 0) == (reg_t) STDOUT) { if (!data->repeat) { /* redirect stdout to stderr (unless it's our repeat) */ #ifdef SHOW_RESULTS dr_fprintf(STDERR, " [%d] STDOUT => STDERR\n", sysnum); #endif dr_syscall_set_param(drcontext, 0, (reg_t) STDERR); } /* we're going to repeat this syscall once */ data->repeat = !data->repeat; } } return true; /* execute normally */ }