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 | |
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')
-rw-r--r-- | lib/darwin/execmem.c | 108 | ||||
-rw-r--r-- | lib/darwin/inject-asm-raw.c | 14 | ||||
-rw-r--r-- | lib/darwin/inject-asm-raw.order | 1 | ||||
-rw-r--r-- | lib/darwin/manual-syscall.h | 75 |
4 files changed, 114 insertions, 84 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; diff --git a/lib/darwin/inject-asm-raw.c b/lib/darwin/inject-asm-raw.c index ca316f1..55dab5f 100644 --- a/lib/darwin/inject-asm-raw.c +++ b/lib/darwin/inject-asm-raw.c @@ -1,3 +1,5 @@ +#define WANT_BSDTHREAD_TERMINATE +#define WANT_SEMAPHORE_WAIT_TRAP #include "darwin/manual-syscall.h" #ifdef __arm64__ @@ -6,6 +8,11 @@ #define _PAGE_SIZE 0x1000 #endif +int manual_bsdthread_terminate(void *, unsigned long, unsigned, unsigned); +GEN_SYSCALL(bsdthread_terminate, 361); +int manual_semaphore_wait_trap(int); +GEN_SYSCALL(semaphore_wait_trap, -36); + /* This is somewhat more complicated than it has to be because it does not use * pthread_join, which depends on pthread_self, which would need to be * initialized manually; the format of this has changed in the past, and could @@ -31,7 +38,7 @@ void entry(struct baton *baton) { int pt = 0; baton->pthread_create(&pt, 0, (void *) bsd_thread_func, baton); baton->pthread_detach(pt); - manual_syscall(361 /* bsdthread_terminate */, 0, 0, 0, baton->sem_port); + manual_bsdthread_terminate(0, 0, 0, baton->sem_port); ((void (*)()) 0xbad)(); } static int bsd_thread_func(void *arg) { @@ -45,7 +52,7 @@ static int bsd_thread_func(void *arg) { init(baton->shuttle, baton->nshuttle); } - manual_syscall(-36 /* semaphore_wait_trap */, baton->sem_port, 0, 0, 0); + manual_semaphore_wait_trap(baton->sem_port); #ifndef __i386__ /* since we're munmapping our own code, this must be optimized into a jump * (taill call elimination) */ @@ -61,8 +68,7 @@ static int bsd_thread_func(void *arg) { } #ifdef __i386__ /* yuck */ -asm("jmp _entry;" - ".globl _jump_to_munmap;" +asm(".globl _jump_to_munmap;" "_jump_to_munmap:" "push %ebp;" "mov %esp, %ebp;" diff --git a/lib/darwin/inject-asm-raw.order b/lib/darwin/inject-asm-raw.order new file mode 100644 index 0000000..faafe03 --- /dev/null +++ b/lib/darwin/inject-asm-raw.order @@ -0,0 +1 @@ +_entry diff --git a/lib/darwin/manual-syscall.h b/lib/darwin/manual-syscall.h index 9f04d2b..b1bac7e 100644 --- a/lib/darwin/manual-syscall.h +++ b/lib/darwin/manual-syscall.h @@ -1,54 +1,41 @@ #pragma once -#define REG(var, reg) register long _##var __asm__(#reg) = var -#define OREG(var, reg) register long var __asm__(#reg) +#define GEN_SYSCALL(name, num) \ + __asm__(".globl _manual_" #name "\n" \ + ".pushsection __TEXT,__text,regular,pure_instructions\n" \ + "_manual_" #name ":\n" \ + ".set num, " #num "\n" \ + GEN_SYSCALL_INNER() \ + ".popsection\n") -__attribute__((always_inline)) #if defined(__x86_64__) -static int manual_syscall(long s, long a, long b, long c, long d) { - if (s < 0) - s = -s | 1 << 24; - else - s |= 2 << 24; - REG(s, rax); REG(a, rdi); REG(b, rsi); REG(c, rdx); REG(d, rcx); - OREG(out, rax); - __asm__ volatile("syscall" - : "=r"(out) - : "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); - return out; -} +/* Look at me, I'm different! */ +#define GEN_SYSCALL_INNER() \ + ".if num < 0\n" \ + "mov $(-num | 1 << 24), %eax\n" \ + ".else\n" \ + "mov $( num | 2 << 24), %eax\n" \ + ".endif\n" \ + "mov %rcx, %r10\n" \ + "syscall\n" \ + "ret\n" + #elif defined(__i386__) -static int manual_syscall(long s, long a, long b, long c, long d) { - REG(s, eax); - OREG(out, eax); - __asm__ volatile("push %5; push %4; push %3; push %2; push $0;" - "mov %%esp, %%ecx;" - "call 1f; 1: pop %%edx; add $(2f-1b), %%edx;" - "sysenter;" - "2: add $(5 * 4), %%esp" - : "=r"(out) - : "r"(_s), "g"(a), "g"(b), "g"(c), "g"(d) - : "edx", "ecx"); - return out; -} +#define GEN_SYSCALL_INNER() \ + "mov $num, %eax\n" \ + "pop %edx\n" \ + "mov %esp, %ecx\n" \ + "sysenter\n" #elif defined(__arm__) -static int manual_syscall(long s, long a, long b, long c, long d) { - REG(s, r12); REG(a, r0); REG(b, r1); REG(c, r2); REG(d, r3); - OREG(out, r0); - __asm__ volatile("svc #0x80" - : "=r"(out) - : "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); - return out; -} +#define GEN_SYSCALL_INNER() \ + "mov r12, #num\n" \ + "svc #0x80\n" \ + "bx lr\n" #elif defined(__arm64__) -static int manual_syscall(long s, long a, long b, long c, long d) { - REG(s, x16); REG(a, x0); REG(b, x1); REG(c, x2); REG(d, x3); - OREG(out, x0); - __asm__ volatile("svc #0x80" - : "=r"(out) - : "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); - return out; -} +#define GEN_SYSCALL_INNER() \ + "mov x12, #num\n" \ + "svc #0x80\n" \ + "ret\n" #else #error ? #endif |