static bool load_dynamorio_lib(IF_NOT_X64(bool x64_in_wow64)) { HMODULE dll = NULL; char path[MAX_PATH]; #ifdef DEBUG char msg[3 * MAX_PATH]; #endif int retval = -1; /* failure */ #ifndef X64 bool wow64 = is_wow64_process(NT_CURRENT_PROCESS); if (x64_in_wow64) { ASSERT(wow64); retval = get_parameter_64(PARAM_STR(DYNAMORIO_VAR_AUTOINJECT), path, MAX_PATH); } else #endif retval = get_parameter(PARAM_STR(DYNAMORIO_VAR_AUTOINJECT), path, MAX_PATH); if (IS_GET_PARAMETER_SUCCESS(retval)) { dr_marker_t mark; VERBOSE_MESSAGE("Loading \"%hs\"", path); /* The read_and_verify_dr_marker is the canonical check for dr in a * process, we double check against GetModuleHandle here just to be * extra safe (in case dr failed to initialize before). Note that * GetModuleHandle won't find dr's dll if we implement certian -hide * or early_injection proposals. */ if (read_and_verify_dr_marker(GetCurrentProcess(), &mark) != DR_MARKER_FOUND && GetModuleHandle(DYNAMORIO_LIBRARY_NAME) == NULL #ifndef X64 /* these ifdefs are rather ugly: just export all routines in x64 builds? */ && /* check for 64-bit as well */ (!wow64 || read_and_verify_dr_marker_64(GetCurrentProcess(), &mark) != DR_MARKER_FOUND) /* FIXME PR 251677: need 64-bit early injection to fully test * read_and_verify_dr_marker_64 */ #endif ) { /* OK really going to load dr now, verify that we are injecting * early enough (i.e. user32.dll is statically linked). This * presumes preinject is only used with app_init injection which is * currently the case. FIXME - should we also check_sole_thread * here? We can't really handle more then one thread when dr is * loading, but this can happen with early remote injected threads * many of which (CTRL) are relatively harmless. */ LDR_MODULE *mod = get_ldr_module_by_name(L"user32.dll"); ASSERT(mod != NULL); if (ldr_module_statically_linked(mod)) { #ifndef X64 if (x64_in_wow64) dll = load_library_64(path); else #endif dll = LoadLibrary(path); } else { /* FIXME - would be really nice to communicate this back to * the controller. */ #ifdef DEBUG _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " Error: improper injection - " PRODUCT_NAME " (%s) can't inject into process %s (%s) (user32.dll " "not statically linked)\n", path, get_application_name(), get_application_pid()); NULL_TERMINATE_BUFFER(msg); display_error(msg); #endif } } else { /* notify failure only in debug builds, otherwise just return */ #ifdef DEBUG /* with early injection this becomes even more likely */ if (read_and_verify_dr_marker(GetCurrentProcess(), &mark) == DR_MARKER_FOUND # ifndef X64 || (wow64 && read_and_verify_dr_marker_64(GetCurrentProcess(), &mark) == DR_MARKER_FOUND) # endif ) { /* ok, early injection should always beat this */ # if VERBOSE /* can't readily tell what was expected */ _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " ok if early injection, otherwise ERROR: " "double injection, " PRODUCT_NAME " (%s) is already loaded " "in process %s (%s), continuing\n", path, get_application_name(), get_application_pid()); NULL_TERMINATE_BUFFER(msg); display_error(msg); # endif /* VERBOSE */ } else { /* if GetModuleHandle finds us but we don't have a marker * we may have failed somehow */ _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " Error: failed injection, " PRODUCT_NAME " (%s) is " "loaded but not initialized in process %s (%s), continuing\n", path, get_application_name(), get_application_pid()); NULL_TERMINATE_BUFFER(msg); display_error(msg); } #endif /* DEBUG */ return false; } } else path[0] = 0; if (dll == NULL) { #ifdef DEBUG int err = GetLastError(); _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " Error %d loading %s\n", err, path); NULL_TERMINATE_BUFFER(msg); display_error(msg); #endif return false; } else { int_func_t init_func; void_func_t take_over_func; int res; #ifndef X64 if (x64_in_wow64) { init_func = (int_func_t)(ptr_uint_t) /*we know <4GB*/ get_proc_address_64((uint64)dll, "dynamorio_app_init"); take_over_func = (void_func_t)(ptr_uint_t) /*we know <4GB*/ get_proc_address_64((uint64)dll, "dynamorio_app_take_over"); VERBOSE_MESSAGE("dynamorio_app_init: 0x%08x; dynamorio_app_take_over: " "0x%08x\n", init_func, take_over_func); } else { #endif init_func = (int_func_t)GetProcAddress(dll, "dynamorio_app_init"); take_over_func = (void_func_t)GetProcAddress(dll, "dynamorio_app_take_over"); #ifndef X64 } #endif if (init_func == NULL || take_over_func == NULL) { /* unload the library so that it's clear DR is not in control * (o/w the DR library is in the process and it's not clear * what's going on) */ #ifndef X64 if (x64_in_wow64) { # ifdef DEBUG bool ok = # endif free_library_64(dll); ASSERT(ok); } else #endif FreeLibrary(dll); #ifdef DEBUG display_error("Error getting " PRODUCT_NAME " functions\n"); #endif return false; } VERBOSE_MESSAGE("about to inject dynamorio"); #ifndef X64 if (x64_in_wow64) res = switch_modes_and_call(init_func, NULL, NULL, NULL); else #endif res = (*init_func)(); VERBOSE_MESSAGE("dynamorio_app_init() returned %d\n", res); #ifndef X64 if (x64_in_wow64) switch_modes_and_call(take_over_func, NULL, NULL, NULL); else #endif (*take_over_func)(); VERBOSE_MESSAGE("inside " PRODUCT_NAME " now\n"); } return true; }
/* For 32-bit build, supports looking for x64 marker (in WOW64 process). * For 64-bit build, only supports looking for x64 marker. */ static int read_and_verify_dr_marker_common(HANDLE process, dr_marker_t *marker, bool x64) { byte buf[8]; /* only needs to be 5, but dword pad just in case */ size_t res; void *target = NULL; #if !defined(NOT_DYNAMORIO_CORE) && !defined(NOT_DYNAMORIO_CORE_PROPER) GET_NTDLL(DR_MARKER_HOOKED_FUNCTION, DR_MARKER_HOOKED_FUNCTION_ARGS); void *hook_func = (void *)DR_MARKER_HOOKED_FUNCTION; #else if (IF_X64_ELSE(!x64, x64 && !is_wow64_process(NT_CURRENT_PROCESS))) return DR_MARKER_ERROR; if (x64) { # ifndef X64 uint64 hook_func = get_proc_address_64 (get_module_handle_64(L_DR_MARKER_HOOKED_DLL), DR_MARKER_HOOKED_FUNCTION_STRING); uint64 landing_pad = 0; if (hook_func == 0) return DR_MARKER_ERROR; if (!NT_SUCCESS(nt_wow64_read_virtual_memory64(process, hook_func, buf, 5, &res)) || res != 5) { return DR_MARKER_ERROR; } if (buf[0] != OP_jmp_byte) return DR_MARKER_NOT_FOUND; /* jmp offset + EIP (after jmp = hook_func + size of jmp (5 bytes)) */ /* for 64-bit, the target is stored in front of the trampoline */ landing_pad = *(int *)&buf[1] + hook_func + 5 - 8; if (!NT_SUCCESS(nt_wow64_read_virtual_memory64(process, landing_pad, buf, 8, &res)) || res != 8U) return DR_MARKER_ERROR; /* trampoline address is stored at the top of the landing pad for 64-bit */ target = (void *)PAGE_START(*(ptr_int_t *)buf); } else { # endif /* !X64 */ void *hook_func = (void *)GetProcAddress(GetModuleHandle(DR_MARKER_HOOKED_DLL), DR_MARKER_HOOKED_FUNCTION_STRING); #endif void *landing_pad; if (hook_func == NULL) return DR_MARKER_ERROR; if (!READ_FUNC(process, hook_func, buf, 5, &res) || res != 5) return DR_MARKER_ERROR; if (buf[0] != OP_jmp_byte) return DR_MARKER_NOT_FOUND; /* jmp offset + EIP (after jmp = hook_func + size of jmp (5 bytes)) */ landing_pad = (void *)(*(int *)&buf[1] + (ptr_int_t)hook_func + 5); /* for 64-bit, the target is stored in front of the trampoline */ if (x64) landing_pad = (byte *)landing_pad - 8; /* see emit_landing_pad_code() for layout of landing pad */ if (!READ_FUNC(process, landing_pad, buf, (x64 ? 8 : 5), &res) || res != (x64 ? 8U : 5U)) return DR_MARKER_ERROR; if (x64) { /* trampoline address is stored at the top of the landing pad for 64-bit */ target = (void *)PAGE_START(*(ptr_int_t *)buf); } else { /* jmp offset + EIP (after jmp = landing_pad + size of jmp (5 bytes)) */ target = (void *)PAGE_START(*(int *)&buf[1] + (ptr_int_t)landing_pad + 5); } #if defined(NOT_DYNAMORIO_CORE) || defined(NOT_DYNAMORIO_CORE_PROPER) } #endif if (target == NULL) return DR_MARKER_ERROR; if (!READ_FUNC(process, target, marker, sizeof(dr_marker_t), &res) || res != sizeof(dr_marker_t)) { return DR_MARKER_NOT_FOUND; } if (dr_marker_verify(process, marker)) { return DR_MARKER_FOUND; } return DR_MARKER_NOT_FOUND; /* probably some other hooker */ }