diff --git a/doorstop.c b/doorstop.c index 0244219..55d591c 100755 --- a/doorstop.c +++ b/doorstop.c @@ -59,6 +59,8 @@ void (*r_mono_set_assemblies_path)(const char *path); void *(*r_mono_image_open_from_data_with_name)(void *data, uint32_t data_len, int need_copy, void *status, int refonly, const char *name); +void *(*r_mono_object_to_string)(void *obj, void **exc); +char *(*r_mono_string_to_utf8)(void *s); void doorstop_init_mono_functions(void *handle) { @@ -85,32 +87,63 @@ void doorstop_init_mono_functions(void *handle) LOAD_METHOD(mono_set_assemblies_path); LOAD_METHOD(mono_image_open_from_data_with_name); + LOAD_METHOD(mono_object_to_string); + LOAD_METHOD(mono_string_to_utf8); + #undef LOAD_METHOD } +#define LOG(message) \ + { \ + fprintf(stdout, "[Doorstop] "); \ + fprintf(stdout, message); \ + \ + fprintf(log_handle, "[Doorstop] "); \ + fprintf(log_handle, message); \ + \ + fflush(log_handle); \ + fflush(stdout); \ + } + +#define LOGF(message, ...) \ + { \ + fprintf(log_handle, "[Doorstop] "); \ + fprintf(log_handle, message, __VA_ARGS__); \ + \ + fprintf(stdout, "[Doorstop] "); \ + fprintf(stdout, message, __VA_ARGS__); \ + \ + fflush(log_handle); \ + fflush(stdout); \ + } + +static FILE *log_handle; + void *jit_init_hook(const char *root_domain_name, const char *runtime_version) { + LOG("jit_init_hook invoked\n"); + char *override = getenv("DOORSTOP_CORLIB_OVERRIDE_PATH"); DIR *override_dir = override ? opendir(override) : NULL; char *assembly_dir = r_mono_assembly_getrootdir(); if (override && override_dir) { closedir(override_dir); - printf("Got override: %s\n", override); - printf("Current root dir: %s\n", assembly_dir); + LOGF("Got override: %s\n", override); + LOGF("Current root dir: %s\n", assembly_dir); char bcl_root_full[PATH_MAX] = "\0"; realpath(override, bcl_root_full); - printf("New root path: %s\n", bcl_root_full); + LOGF("New root path: %s\n", bcl_root_full); char *search_path; asprintf(&search_path, "%s:%s", bcl_root_full, assembly_dir); - printf("Search path: %s\n", search_path); + LOGF("Search path: %s\n", search_path); r_mono_set_assemblies_path(search_path); setenv("DOORSTOP_DLL_SEARCH_DIRS", search_path, 1); free(search_path); } else { - printf("No override (or failed to find), unsetting.\n"); + LOG("No override (or failed to find), unsetting.\n"); unsetenv("DOORSTOP_CORLIB_OVERRIDE_PATH"); setenv("DOORSTOP_DLL_SEARCH_DIRS", assembly_dir, 1); } @@ -122,7 +155,7 @@ void *jit_init_hook(const char *root_domain_name, const char *runtime_version) if (getenv("DOORSTOP_INITIALIZED")) { - printf("DOORSTOP_INITIALIZED is set! Skipping!\n"); + LOG("DOORSTOP_INITIALIZED is set! Skipping!\n"); return domain; } setenv("DOORSTOP_INITIALIZED", "TRUE", TRUE); @@ -137,13 +170,14 @@ void *jit_init_hook(const char *root_domain_name, const char *runtime_version) strcat(config_path, ".config"); char *folder_path = dirname(app_path); - printf("Setting config paths; basedir: %s; config: %s\n", folder_path, config_path); + LOGF("Setting config paths; basedir: %s; config: %s\n", folder_path, config_path); r_mono_domain_set_config(domain, folder_path, config_path); } char *dll_path = getenv("DOORSTOP_INVOKE_DLL_PATH"); + LOGF("DOORSTOP_INVOKE_DLL_PATH: %s\n", dll_path); - printf("Managed dir: %s\n", assembly_dir); + LOGF("Managed dir: %s\n", assembly_dir); setenv("DOORSTOP_MANAGED_FOLDER_DIR", assembly_dir, TRUE); free(assembly_dir); @@ -156,18 +190,18 @@ void *jit_init_hook(const char *root_domain_name, const char *runtime_version) if (assembly == NULL) { - printf("Failed to load assembly\n"); + LOGF("Failed to load assembly '%s'\n", dll_path); return domain; } // Get assembly's image that contains CIL code void *image = r_mono_assembly_get_image(assembly); - printf("Got image: %p \n", image); + LOGF("Got image: %p \n", image); if (image == NULL) { - printf("Failed to locate the image!\n"); + LOG("Failed to locate the image!\n"); return domain; } @@ -181,9 +215,13 @@ void *jit_init_hook(const char *root_domain_name, const char *runtime_version) if (method == NULL) { - printf("Failed to locate any entrypoints!\n"); + LOGF("Failed to locate any entrypoints in '%s'!\n", dll_path); return domain; } + else + { + LOGF("Found entrypoints in '%s'!\n", dll_path); + } void *signature = r_mono_method_signature(method); @@ -200,7 +238,34 @@ void *jit_init_hook(const char *root_domain_name, const char *runtime_version) args[0] = args_array; } - r_mono_runtime_invoke(method, NULL, args, NULL); + LOG("Invoking entrypoint\n"); + + void *exc; + + // invoke the main method of the target assembly + r_mono_runtime_invoke(method, NULL, args, &exc); + + LOG("Invoking done!\n"); + + if(exc) + { + // some managed exception occured in the target assembly. + // try to print the exception. + LOG("Exception thrown while invoking assembly entrypoint!\n"); + + void *exc_str = r_mono_object_to_string(exc, NULL); + if (exc_str) + { + char *s = r_mono_string_to_utf8(exc_str); + LOGF("Exception: %s\n", s); + } + else + { + LOG("Unable to obtain exception text"); + } + + exit(1); + } if (args != NULL) { @@ -212,7 +277,8 @@ void *jit_init_hook(const char *root_domain_name, const char *runtime_version) } void *hook_mono_image_open_from_data_with_name(void *data, uint32_t data_len, int need_copy, void *status, int refonly, char *name) { - printf("Load DLL: %s\n", name); + LOGF("Load DLL: %s\n", name); + void *result = NULL; char *override = getenv("DOORSTOP_CORLIB_OVERRIDE_PATH"); if (override) { @@ -220,13 +286,13 @@ void *hook_mono_image_open_from_data_with_name(void *data, uint32_t data_len, in realpath(override, override_full); char *filename = basename(name); - printf("Base: %s\n", filename); + LOGF("Base: %s\n", filename); char *new_path; asprintf(&new_path, "%s/%s", override_full, filename); if (access(new_path, F_OK) == 0) { - printf("Redirecting to %s\n", new_path); + LOGF("Redirecting to %s\n", new_path); FILE *f = fopen(new_path, "rb"); fseek(f, 0, SEEK_END); long size = ftell(f); @@ -245,17 +311,21 @@ void *hook_mono_image_open_from_data_with_name(void *data, uint32_t data_len, in static char inited = 0; void *dlsym_hook(void *handle, const char *name) { - + if (!strcmp(name, "mono_jit_init_version")) { if (!inited) { + LOG("intercepting mono_jit_init_version\n"); doorstop_init_mono_functions(handle); inited = 1; } return (void *)jit_init_hook; } - if (!strcmp(name, "mono_image_open_from_data_with_name")) { - if (!inited) { + else if (!strcmp(name, "mono_image_open_from_data_with_name")) + { + if (!inited) + { + LOG("intercepting mono_image_open_from_data_with_name\n"); doorstop_init_mono_functions(handle); inited = 1; } @@ -279,6 +349,17 @@ __attribute__ ((constructor)) void doorstop_setup() { return; } + log_handle = fopen("doorstop.log", "w"); + + if (log_handle == NULL) + { + printf("[Doorstop] unable to create logfile\n"); + } + else + { + printf("[Doorstop] creating logfile 'doorstop.log'\n"); + } + plthook_t *hook; // Some versions of Unity (especially macOS) ship with UnityPlayer shared lib @@ -292,18 +373,18 @@ __attribute__ ((constructor)) void doorstop_setup() { #endif if(unity_player && plthook_open_by_handle(&hook, unity_player) == 0) { - printf("Found UnityPlayer, hooking into it instead\n"); + LOG("Found UnityPlayer, hooking into it instead\n"); } else if(plthook_open(&hook, NULL) != 0) { - printf("Failed to open current process PLT! Cannot run Doorstop! Error: %s\n", plthook_error()); + LOGF("Failed to open current process PLT! Cannot run Doorstop! Error: %s\n", plthook_error()); return; } if(plthook_replace(hook, "dlsym", &dlsym_hook, NULL) != 0) - printf("Failed to hook dlsym, ignoring it. Error: %s\n", plthook_error()); + LOGF("Failed to hook dlsym, ignoring it. Error: %s\n", plthook_error()); if(plthook_replace(hook, "fclose", &fclose_hook, NULL) != 0) - printf("Failed to hook fclose, ignoring it. Error: %s\n", plthook_error()); + LOGF("Failed to hook fclose, ignoring it. Error: %s\n", plthook_error()); #if __APPLE__ /*