static process_id_t fork_suspended_child(const char *exe, const char **argv, int fds[2]) { process_id_t pid = fork(); if (pid == 0) { /* child, suspend before exec */ char libdr_path[MAXIMUM_PATH]; ssize_t nread; size_t sofar = 0; char *real_exe; close(fds[1]); /* Close writer in child, keep reader. */ do { nread = read(fds[0], libdr_path + sofar, BUFFER_SIZE_BYTES(libdr_path) - sofar); sofar += nread; } while (nread > 0 && sofar < BUFFER_SIZE_BYTES(libdr_path)-1); libdr_path[sofar] = '\0'; close(fds[0]); /* Close reader before exec. */ if (libdr_path[0] == '\0') { /* If nothing was written to the pipe, let it run natively. */ real_exe = (char *) exe; } else { real_exe = libdr_path; } setenv(DYNAMORIO_VAR_EXE_PATH, exe, true/*overwrite*/); execv(real_exe, (char **) argv); /* If execv returns, there was an error. */ exit(-1); } return pid; }
static process_id_t fork_suspended_child(const char *exe, const char **argv, int fds[2]) { process_id_t pid = fork(); if (pid == 0) { /* child, suspend before exec */ char pipe_cmd[MAXIMUM_PATH]; ssize_t nread; size_t sofar = 0; const char *real_exe = NULL; const char *arg; close(fds[1]); /* Close writer in child, keep reader. */ do { nread = read(fds[0], pipe_cmd + sofar, BUFFER_SIZE_BYTES(pipe_cmd) - sofar); sofar += nread; } while (nread > 0 && sofar < BUFFER_SIZE_BYTES(pipe_cmd)-1); pipe_cmd[sofar] = '\0'; close(fds[0]); /* Close reader before exec. */ arg = pipe_cmd; /* The first token is the command and the rest is an argument. */ while (*arg != '\0' && !isspace(*arg)) arg++; while (*arg != '\0' && isspace(*arg)) arg++; if (pipe_cmd[0] == '\0') { /* If nothing was written to the pipe, let it run natively. */ real_exe = exe; } else if (strstr(pipe_cmd, "ld_preload ") == pipe_cmd) { pre_execve_ld_preload(arg); real_exe = exe; } else if (strcmp("ptrace", pipe_cmd) == 0) { /* If using ptrace, we're already attached and will walk across the * execv. */ real_exe = exe; } else if (strstr(pipe_cmd, "exec_dr ") == pipe_cmd) { pre_execve_early(exe); real_exe = arg; } #ifdef STATIC_LIBRARY setenv("DYNAMORIO_TAKEOVER_IN_INIT", "1", true/*overwrite*/); #endif execv(real_exe, (char **) argv); /* If execv returns, there was an error. */ exit(-1); } return pid; }
static bool drstrace_print_info_class_struct(buf_info_t *buf, drsys_arg_t *arg) { char buf_tmp[TYPE_OUTPUT_SIZE]; drsym_type_t *type; drsym_type_t *expand_type; drsym_error_t r; r = drsym_get_type_by_name(options.sympath, arg->enum_name, buf_tmp, BUFFER_SIZE_BYTES(buf_tmp), &type); if (r != DRSYM_SUCCESS) { NOTIFY("Value to symbol %s lookup failed", arg->enum_name); return false; } r = drsym_expand_type(options.sympath, type->id, UINT_MAX, buf_tmp, BUFFER_SIZE_BYTES(buf_tmp), &expand_type); if (r != DRSYM_SUCCESS) { NOTIFY("%s structure expanding failed", arg->enum_name); return false; } if (!type_has_unknown_components(expand_type)) { NOTIFY("%s structure has unknown types", arg->enum_name); return false; } if (arg->valid && !arg->pre) { if (arg->value64 == 0) { OUTPUT(buf, "NULL"); /* We return true since we already printed for this value */ return true; } /* We're expecting an address here. So we truncate int64 to void*. */ print_structure(buf, expand_type, arg, (void *)arg->value64); } else { return false; } return true; }
static void handle_pre_execve(void *drcontext) { #ifndef USE_DRSYMS /* PR 453867: tell postprocess.pl to watch for new logdir and * fork a new copy. * FIXME: what if syscall fails? Punting on that for now. * Note that if it fails and then a later one succeeds, postprocess.pl * will replace the first with the last. */ char logdir[MAXIMUM_PATH]; /* one reason we're not inside os_post_syscall() */ size_t bytes_read = 0; /* Not using safe_read() since we want a partial read if hits page boundary */ dr_safe_read((void *) dr_syscall_get_param(drcontext, 0), BUFFER_SIZE_BYTES(logdir), logdir, &bytes_read); if (bytes_read < BUFFER_SIZE_BYTES(logdir)) logdir[bytes_read] = '\0'; NULL_TERMINATE_BUFFER(logdir); ELOGF(0, f_fork, "EXEC path=%s\n", logdir); #endif }
int main(int argc, const char *argv[]) { /* We put the pid into the title so that tools/closewnd can target it * uniquely when run in a parallel test suite. * runall.cmake assumes this precise title. */ char title[64]; _snprintf_s(title, BUFFER_SIZE_BYTES(title), BUFFER_SIZE_ELEMENTS(title), "Infloop pid=%d", GetProcessId(GetCurrentProcess())); SetTimer(NULL, NULL, 180 * 1000 /*3 mins*/, TimerProc); MessageBoxA(NULL, "DynamoRIO test: will be auto-closed", title, MB_OK); print("MessageBox closed\n"); return 0; }
/* pass non-NULL for thandle if you want this routine to use * Get/SetThreadContext to get the context -- you must still pass * in a pointer to a cxt */ BOOL inject_into_thread(HANDLE phandle, CONTEXT *cxt, HANDLE thandle, char *dynamo_path) { size_t nbytes; BOOL success = FALSE; ptr_uint_t dynamo_entry_esp; ptr_uint_t dynamo_path_esp; LPVOID load_dynamo_code = NULL; /* = base of code allocation */ ptr_uint_t addr; reg_t *bufptr; char buf[MAX_PATH]; uint old_prot; ASSERT(cxt != NULL); #ifndef NOT_DYNAMORIO_CORE_PROPER /* FIXME - if we were early injected we couldn't call inject_init during * startup because kernel32 wasn't loaded yet, so we call it here which * isn't safe because it uses app locks. If we want to support a mix * of early and late follow children injection we should change load_dynamo * to use Nt functions (which we can link) rather then kernel32 functions * (which we have to look up). We could also use module.c code to safely * walk the exports of kernel32.dll (we can cache its mod handle when it * is loaded). */ if (!inject_initialized) { SYSLOG_INTERNAL_WARNING("Using late inject follow children from early injected process, unsafe LdrLock usage"); SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT); inject_init(); SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); } #else ASSERT(inject_initialized); #endif /* soon we'll start using alternative injection with case 102 - leaving block */ { reg_t app_xsp; if (thandle != NULL) { /* grab the context of the app's main thread */ cxt->ContextFlags = CONTEXT_DR_STATE; if (!NT_SUCCESS(nt_get_context(thandle, cxt))) { display_error("GetThreadContext failed"); goto error; } } app_xsp = cxt->CXT_XSP; /* copy load_dynamo() into the address space of the new process */ ASSERT(BUFFER_SIZE_BYTES(buf) > SIZE_OF_LOAD_DYNAMO); memcpy(buf, (char*)load_dynamo, SIZE_OF_LOAD_DYNAMO); /* R-X protection is adequate for our non-self modifying code, * and we'll update that after we're done with * nt_write_virtual_memory() calls */ /* get allocation, this will be freed by os_heap_free, so make sure * is compatible allocation method */ if (!NT_SUCCESS(nt_remote_allocate_virtual_memory(phandle, &load_dynamo_code, SIZE_OF_LOAD_DYNAMO, PAGE_EXECUTE_READWRITE, MEMORY_COMMIT))) { display_error("Failed to allocate memory for injection code"); goto error; } if (!nt_write_virtual_memory(phandle, load_dynamo_code, buf, SIZE_OF_LOAD_DYNAMO, &nbytes)) { display_error("WriteMemory failed"); goto error; } /* Xref PR 252745 & PR 252008 - we can use the app's stack to hold our data * even on WOW64 and 64-bit since we're using set context to set xsp. */ /* copy the DYNAMORIO_ENTRY string to the app's stack */ _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s", DYNAMORIO_ENTRY); NULL_TERMINATE_BUFFER(buf); nbytes = strlen(buf) + 1; // include the trailing '\0' /* keep esp at pointer-sized alignment */ cxt->CXT_XSP -= ALIGN_FORWARD(nbytes, XSP_SZ); dynamo_entry_esp = cxt->CXT_XSP; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, buf, nbytes, &nbytes)) { display_error("WriteMemory failed"); goto error; } /* copy the dynamorio_path string to the app's stack */ _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s", dynamo_path); NULL_TERMINATE_BUFFER(buf); nbytes = strlen(buf) + 1; // include the trailing '\0' /* keep esp at pointer-sized byte alignment */ cxt->CXT_XSP -= ALIGN_FORWARD(nbytes, XSP_SZ); dynamo_path_esp = cxt->CXT_XSP; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, buf, nbytes, &nbytes)) { display_error("WriteMemory failed"); goto error; } /* copy the current context to the app's stack. Only need the * control registers, so we use a dr_mcontext_t layout. */ bufptr = (reg_t*) buf; *bufptr++ = cxt->CXT_XDI; *bufptr++ = cxt->CXT_XSI; *bufptr++ = cxt->CXT_XBP; *bufptr++ = app_xsp; *bufptr++ = cxt->CXT_XBX; *bufptr++ = cxt->CXT_XDX; *bufptr++ = cxt->CXT_XCX; *bufptr++ = cxt->CXT_XAX; #ifdef X64 *bufptr++ = cxt->R8; *bufptr++ = cxt->R9; *bufptr++ = cxt->R10; *bufptr++ = cxt->R11; *bufptr++ = cxt->R12; *bufptr++ = cxt->R13; *bufptr++ = cxt->R14; *bufptr++ = cxt->R15; #endif /* It would be nice to use preserve_xmm_caller_saved(), but we'd need to * link proc.c and deal w/ messy dependencies to get it into arch_exports.h, * so we do our own check. We go ahead and put in the xmm slots even * if the underlying processor has no xmm support: no harm done. */ if (IF_X64_ELSE(true, is_wow64_process(NT_CURRENT_PROCESS))) { /* PR 264138: preserve xmm0-5. We fill in all slots even though * for 32-bit we don't use them (PR 306394). */ int i, j; for (i = 0; i < NUM_XMM_SLOTS; i++) { for (j = 0; j < IF_X64_ELSE(2,4); j++) { *bufptr++ = CXT_XMM(cxt, i)->reg[j]; } } } else { /* skip xmm slots */ bufptr += XMM_SLOTS_SIZE/sizeof(*bufptr); } *bufptr++ = cxt->CXT_XFLAGS; *bufptr++ = cxt->CXT_XIP; ASSERT((char *)bufptr - (char *)buf == sizeof(dr_mcontext_t)); *bufptr++ = (ptr_uint_t)load_dynamo_code; *bufptr++ = SIZE_OF_LOAD_DYNAMO; nbytes = sizeof(dr_mcontext_t) + 2*sizeof(reg_t); cxt->CXT_XSP -= nbytes; #ifdef X64 /* We need xsp to be aligned prior to each call, but we can only pad * before the context as all later users assume the info they need is * at TOS. */ cxt->CXT_XSP = ALIGN_BACKWARD(cxt->CXT_XSP, XMM_ALIGN); #endif if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, buf, nbytes, &nbytes)) { display_error("WriteMemory failed"); goto error; } /* push the address of the DYNAMORIO_ENTRY string on the app's stack */ cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &dynamo_entry_esp, sizeof(dynamo_entry_esp), &nbytes)) { display_error("WriteMemory failed"); goto error; } /* push the address of GetProcAddress on the app's stack */ ASSERT(addr_getprocaddr); addr = addr_getprocaddr; cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &addr, sizeof(addr), &nbytes)) { display_error("WriteMemory failed"); goto error; } /* push the address of the dynamorio_path string on the app's stack */ cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &dynamo_path_esp, sizeof(dynamo_path_esp), &nbytes)) { display_error("WriteMemory failed"); goto error; } /* push the address of LoadLibraryA on the app's stack */ ASSERT(addr_loadlibrarya); addr = addr_loadlibrarya; cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &addr, sizeof(addr), &nbytes)) { display_error("WriteMemory failed"); goto error; } #ifdef LOAD_DYNAMO_DEBUGBREAK /* push the address of DebugBreak on the app's stack */ ASSERT(addr_debugbreak); addr = addr_debugbreak; cxt->CXT_XSP -= XSP_SZ; if (!nt_write_virtual_memory(phandle, (LPVOID)cxt->CXT_XSP, &addr, sizeof(addr), &nbytes)) { display_error("WriteMemory failed"); goto error; } #endif /* make the code R-X now */ if (!nt_remote_protect_virtual_memory(phandle, load_dynamo_code, SIZE_OF_LOAD_DYNAMO, PAGE_EXECUTE_READ, &old_prot)) { display_error("Failed to make injection code R-X"); goto error; } ASSERT(old_prot == PAGE_EXECUTE_READWRITE); /* now change Eip to point to the entry point of load_dynamo(), so that when we resume, load_dynamo is invoked automatically */ cxt->CXT_XIP = (ptr_uint_t)load_dynamo_code; cxt->CXT_XFLAGS = 0; if (thandle != NULL) { if (!NT_SUCCESS(nt_set_context(thandle, cxt))) { display_error("SetThreadContext failed"); goto error; } } success = TRUE; } error: /* we do not recover any changes in the child's address space */ return success; }