diff options
author | comex | 2015-02-18 02:22:36 -0500 |
---|---|---|
committer | comex | 2015-02-18 02:22:36 -0500 |
commit | 7c26a1964d2d2e54f87d9c42735f6c99b546abd4 (patch) | |
tree | f50838e492aa101a4d87e6ef01d1dc2611f32f97 /lib/hook-functions.c | |
parent | more fixes (diff) | |
download | substitute-7c26a1964d2d2e54f87d9c42735f6c99b546abd4.tar.gz |
Fix hook-function:
- Thread stoppage is now complemented by sigaction to catch injected
threads (sigaction is not used exclusively because the rest of the
program could be trying to use sigaction itself in the meantime - this
is a real thing, ask Dolphin)
- mprotect is no longer used due to max_protection possibly getting in
the way; instead, a copy is created and mapped onto the original.
Diffstat (limited to '')
-rw-r--r-- | lib/hook-functions.c | 75 |
1 files changed, 31 insertions, 44 deletions
diff --git a/lib/hook-functions.c b/lib/hook-functions.c index 7db06d4..5d1f1d5 100644 --- a/lib/hook-functions.c +++ b/lib/hook-functions.c @@ -4,8 +4,8 @@ #include "jump-dis.h" #include "transform-dis.h" #include "execmem.h" -#include "stop-other-threads.h" #include stringify(TARGET_DIR/jump-patch.h) +#include <pthread.h> struct hook_internal { int offset_by_pcdiff[MAX_JUMP_PATCH_SIZE + 1]; @@ -16,6 +16,7 @@ struct hook_internal { /* page allocated with execmem_alloc_unsealed - only if we had to allocate * one when processing this hook */ void *trampoline_page; + struct arch_dis_ctx arch_dis_ctx; }; struct pc_callback_info { @@ -125,22 +126,21 @@ skip_after:; EXPORT int substitute_hook_functions(const struct substitute_function_hook *hooks, size_t nhooks, int options) { - struct hook_internal *his = malloc(nhooks * sizeof(*his)); + bool thread_safe = !(options & SUBSTITUTE_NO_THREAD_SAFETY); + if (thread_safe && !pthread_main_np()) + return SUBSTITUTE_ERR_NOT_ON_MAIN_THREAD; + + struct execmem_foreign_write *fws; + struct hook_internal *his = malloc(nhooks * sizeof(*his) + + nhooks + sizeof(*fws)); if (!his) return SUBSTITUTE_ERR_OOM; + fws = (void *) (his + nhooks); for (size_t i = 0; i < nhooks; i++) his[i].trampoline_page = NULL; int ret = SUBSTITUTE_OK; - ssize_t emw_finished_i = -1; - bool stopped = false; - void *stop_token; - if (!(options & SUBSTITUTE_DONT_STOP_THREADS)) { - if ((ret = stop_other_threads(&stop_token))) - goto end; - stopped = true; - } void *trampoline_ptr = NULL; size_t trampoline_size_left = 0; @@ -160,6 +160,7 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, } #endif hi->code = code; + hi->arch_dis_ctx = arch; uintptr_t pc_patch_start = (uintptr_t) code; int patch_size; bool need_intro_trampoline; @@ -201,6 +202,7 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, } hi->outro_trampoline = trampoline_ptr; + *(void **) hook->old_ptr = hi->outro_trampoline; uintptr_t dpc = pc_patch_end; #ifdef __arm__ if (arch.pc_low_bit) { @@ -229,49 +231,34 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, /* Now commit. */ for (size_t i = 0; i < nhooks; i++) { - const struct substitute_function_hook *hook = &hooks[i]; struct hook_internal *hi = &his[i]; - emw_finished_i = (ssize_t) i; - if ((ret = execmem_write(hi->code, hi->jump_patch, hi->jump_patch_size))) { - /* User is probably screwed, since this probably means a failure to - * re-protect exec, thanks to code signing, so now the function is - * permanently inaccessible. */ - goto end; - } - if (hook->old_ptr) - *(void **) hook->old_ptr = hi->outro_trampoline; + void *page = hi->trampoline_page; + if (page) + execmem_seal(page); + fws[i].dst = hi->code; + fws[i].src = hi->jump_patch; + fws[i].len = hi->jump_patch_size; } - /* *sigh of relief* now we can rewrite the PCs. */ - if (stopped) { - struct pc_callback_info info = {his, nhooks, false}; - if ((ret = apply_pc_patch_callback(stop_token, pc_callback, &info))) - goto end; - if (info.encountered_bad_pc) { - ret = SUBSTITUTE_ERR_UNEXPECTED_PC_ON_OTHER_THREAD; - goto end; - } + struct pc_callback_info info = {his, nhooks, false}; + if ((ret = execmem_foreign_write_with_pc_patch( + fws, nhooks, thread_safe ? pc_callback : NULL, &info))) { + /* Too late to free the trampolines. Chances are this is fatal anyway. */ + goto end_dont_free; + } + if (info.encountered_bad_pc) { + ret = SUBSTITUTE_ERR_UNEXPECTED_PC_ON_OTHER_THREAD; + goto end_dont_free; } end: + /* if we failed, get rid of the trampolines. */ for (size_t i = 0; i < nhooks; i++) { void *page = his[i].trampoline_page; - if (page) { - /* if we failed, get rid of the trampolines. if we succeeded, make - * them executable */ - if (ret && (ssize_t) i >= emw_finished_i) { - execmem_free(page); - } else { - /* we already patched them all, too late to go back.. */ - ret = execmem_seal(page); - } - } - } - if (stopped) { - int r2 = resume_other_threads(stop_token); - if (!ret) - ret = r2; + if (page) + execmem_free(page); } +end_dont_free: free(his); return ret; } |