diff options
author | comex | 2015-02-22 22:03:10 -0500 |
---|---|---|
committer | comex | 2015-02-23 00:54:02 -0500 |
commit | b258239d9674ebae73868eeaeb4b674ca14270f0 (patch) | |
tree | 94bb0fec594cd851c8d3d592ee2a4e8f4f84b4e1 /lib/darwin/execmem.c | |
parent | various fixes (diff) | |
download | substitute-b258239d9674ebae73868eeaeb4b674ca14270f0.tar.gz |
Redo manual syscalls, and use them for hooking.. And fix mmap, which now makes the whole thing slightly broken, because vm_remap into the middle of the shared region apparently silently does nothing.
Diffstat (limited to 'lib/darwin/execmem.c')
-rw-r--r-- | lib/darwin/execmem.c | 108 |
1 files changed, 72 insertions, 36 deletions
diff --git a/lib/darwin/execmem.c b/lib/darwin/execmem.c index d99e947..13c9ac5 100644 --- a/lib/darwin/execmem.c +++ b/lib/darwin/execmem.c @@ -4,11 +4,15 @@ #define _DARWIN_C_SOURCE #include "cbit/htab.h" #include "execmem.h" -/* #include "darwin/manual-syscall.h" */ +#include "darwin/manual-syscall.h" #include "darwin/mach-decls.h" #include "substitute.h" #include "substitute-internal.h" #include <mach/mach.h> +#ifndef __MigPackStructs +#error wtf +#endif +#include <mach/mig.h> #include <sys/mman.h> #include <sys/syscall.h> #include <errno.h> @@ -18,6 +22,30 @@ #include <signal.h> #include <pthread.h> +int manual_sigreturn(void *, int); +GEN_SYSCALL(sigreturn, 184); +__typeof__(mmap) manual_mmap; +GEN_SYSCALL(mmap, 197); +__typeof__(mprotect) manual_mprotect; +GEN_SYSCALL(mprotect, 74); +__typeof__(mach_msg) manual_mach_msg; +GEN_SYSCALL(mach_msg, -31); + +extern int __sigaction(int, struct __sigaction * __restrict, struct sigaction * __restrict); + +static void manual_memcpy(void *restrict dest, const void *src, size_t len) { + /* volatile to avoid compiler transformation to call to memcpy */ + volatile uint8_t *d8 = dest; + const uint8_t *s8 = src; + while (len--) + *d8++ = *s8++; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#include "../generated/manual-mach.inc.h" +#pragma GCC diagnostic pop + #define port_hash(portp) (*(portp)) #define port_eq(port1p, port2p) (*(port1p) == *(port2p)) #define port_null(portp) (*(portp) == MACH_PORT_NULL) @@ -101,20 +129,22 @@ static bool apply_one_pcp_with_state(native_thread_state *state, } static int apply_one_pcp(mach_port_t thread, execmem_pc_patch_callback callback, - void *ctx) { + void *ctx, mach_port_t reply_port) { native_thread_state state; mach_msg_type_number_t real_cnt = sizeof(state) / sizeof(int); mach_msg_type_number_t cnt = real_cnt; - kern_return_t kr = thread_get_state(thread, NATIVE_THREAD_STATE_FLAVOR, - (thread_state_t) &state, &cnt); + kern_return_t kr = manual_thread_get_state(thread, NATIVE_THREAD_STATE_FLAVOR, + (thread_state_t) &state, &cnt, + reply_port); if (kr == KERN_TERMINATED) return SUBSTITUTE_OK; if (kr || cnt != real_cnt) return SUBSTITUTE_ERR_ADJUSTING_THREADS;; if (apply_one_pcp_with_state(&state, callback, ctx)) { - kr = thread_set_state(thread, NATIVE_THREAD_STATE_FLAVOR, - (thread_state_t) &state, real_cnt); + kr = manual_thread_set_state(thread, NATIVE_THREAD_STATE_FLAVOR, + (thread_state_t) &state, real_cnt, + reply_port); if (kr) return SUBSTITUTE_ERR_ADJUSTING_THREADS; } @@ -197,19 +227,23 @@ static void resume_other_threads() { htab_free_storage_mach_port_set(suspended_set); } -static void segfault_handler(UNUSED int sig, UNUSED siginfo_t *info, - void *uap_) { +/* note: unusual prototype since we are avoiding _sigtramp */ +static void segfault_handler(UNUSED void *func, int style, int sig, + UNUSED siginfo_t *sinfo, void *uap_) { + ucontext_t *uap = uap_; if (pthread_main_np()) { /* The patcher itself segfaulted. Oops. Reset the signal so the * process exits rather than going into an infinite loop. */ signal(sig, SIG_DFL); - return; + goto sigreturn; } /* We didn't catch it before it segfaulted so have to fix it up here. */ - ucontext_t *uap = uap_; apply_one_pcp_with_state(&uap->uc_mcontext->__ss, g_pc_patch_callback, g_pc_patch_callback_ctx); /* just let it continue, whatever */ +sigreturn: + if (manual_sigreturn(uap, style)) + abort(); } static int init_pc_patch(execmem_pc_patch_callback callback, void *ctx) { @@ -219,22 +253,23 @@ static int init_pc_patch(execmem_pc_patch_callback callback, void *ctx) { if ((ret = stop_other_threads())) return ret; - struct sigaction sa; + struct __sigaction sa; memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = segfault_handler; + sa.sa_sigaction = (void *) 0xdeadbeef; + sa.sa_tramp = segfault_handler; sigfillset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NODEFER | SA_SIGINFO; - if (sigaction(SIGSEGV, &sa, &old_segv)) + if (__sigaction(SIGSEGV, &sa, &old_segv)) return SUBSTITUTE_ERR_ADJUSTING_THREADS; - if (sigaction(SIGBUS, &sa, &old_bus)) { + if (__sigaction(SIGBUS, &sa, &old_bus)) { sigaction(SIGSEGV, &old_segv, NULL); return SUBSTITUTE_ERR_ADJUSTING_THREADS; } return SUBSTITUTE_OK; } -static int run_pc_patch() { +static int run_pc_patch(mach_port_t reply_port) { int ret; struct htab_mach_port_set *suspended_set = &g_suspended_ports.h; @@ -242,7 +277,7 @@ static int run_pc_patch() { UNUSED struct empty *_, mach_port_set) { if ((ret = apply_one_pcp(*threadp, g_pc_patch_callback, - g_pc_patch_callback_ctx))) + g_pc_patch_callback_ctx, reply_port))) return ret; } @@ -281,14 +316,6 @@ static kern_return_t get_page_prot(uintptr_t ptr, vm_prot_t *prot, return kr; } -static void manual_memcpy(void *restrict dest, const void *src, size_t len) { - /* volatile to avoid compiler transformation to call to memcpy */ - volatile uint8_t *d8 = dest; - const uint8_t *s8 = src; - while (len--) - *d8++ = *s8++; -} - int execmem_foreign_write_with_pc_patch(struct execmem_foreign_write *writes, size_t nwrites, execmem_pc_patch_callback callback, @@ -297,6 +324,9 @@ int execmem_foreign_write_with_pc_patch(struct execmem_foreign_write *writes, qsort(writes, nwrites, sizeof(*writes), compare_dsts); + mach_port_t task_self = mach_task_self(); + mach_port_t reply_port = mig_get_reply_port(); + if (callback) { /* Set the segfault handler - stopping all other threads before * doing so in case they were using it for something (this @@ -361,14 +391,18 @@ int execmem_foreign_write_with_pc_patch(struct execmem_foreign_write *writes, * new becomes the sole owner before actually writing. Though, for all * I know, these trips through the VM system could be slower than just * memcpying a page or two... */ - kr = vm_copy(mach_task_self(), page_start, len, (vm_address_t) new); + kr = vm_copy(task_self, page_start, len, (vm_address_t) new); if (kr) { ret = SUBSTITUTE_ERR_VM; goto fail_unmap; } - /* Disable access to the page so anyone trying to execute there - * will segfault. */ - if (mmap(NULL, len, PROT_NONE, MAP_ANON | MAP_SHARED, -1, 0) + /* Start of danger zone: between the mmap PROT_NONE and remap, we avoid + * using any standard library functions in case the user is trying to + * hook one of them. (This includes the mmap, since there's an epilog + * after the actual syscall instruction.) + * This includes the signal handler! */ + if (manual_mmap((void *) page_start, len, PROT_NONE, + MAP_ANON | MAP_SHARED | MAP_FIXED, -1, 0) == MAP_FAILED) { ret = SUBSTITUTE_ERR_VM; goto fail_unmap; @@ -389,25 +423,26 @@ int execmem_foreign_write_with_pc_patch(struct execmem_foreign_write *writes, * patched. (A call instruction within the affected region would * break this assumption, as then a thread could move to an * affected PC by returning. */ - if ((ret = run_pc_patch())) + if ((ret = run_pc_patch(reply_port))) goto fail_unmap; } /* Protect new like the original, and move it into place. */ vm_address_t target = page_start; - if (mprotect(new, len, prot)) { + if (manual_mprotect(new, len, prot)) { ret = SUBSTITUTE_ERR_VM; goto fail_unmap; } vm_prot_t c, m; - kr = vm_remap(mach_task_self(), &target, len, 0, VM_FLAGS_OVERWRITE, - mach_task_self(), (vm_address_t) new, /*copy*/ FALSE, - &c, &m, inherit); + printf("new=%p\n", new); + kr = manual_vm_remap(task_self, &target, len, 0, VM_FLAGS_OVERWRITE, + task_self, (vm_address_t) new, /*copy*/ FALSE, + &c, &m, inherit, reply_port); if (kr) { ret = SUBSTITUTE_ERR_VM; goto fail_unmap; } - /* ignore errors... */ + /* Danger zone over. Ignore errors when unmapping the temporary buffer. */ munmap(new, len); continue; @@ -426,8 +461,9 @@ fail: if (callback) { /* Other threads are no longer in danger of segfaulting, so put * back the old segfault handler. */ - if ((ret = finish_pc_patch())) - return ret; + int ret2; + if ((ret2 = finish_pc_patch())) + return ret2; } return ret; |