diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/darwin/execmem.c | 57 | ||||
-rw-r--r-- | lib/darwin/inject-asm-raw.c | 62 | ||||
-rw-r--r-- | lib/darwin/manual-syscall.h | 54 | ||||
-rw-r--r-- | lib/execmem.h | 3 | ||||
-rw-r--r-- | lib/hook-functions.c | 2 | ||||
-rw-r--r-- | lib/stop-other-threads.h | 9 | ||||
-rw-r--r-- | lib/substitute-internal.h | 7 | ||||
-rw-r--r-- | lib/substitute.h | 1 |
8 files changed, 130 insertions, 65 deletions
diff --git a/lib/darwin/execmem.c b/lib/darwin/execmem.c new file mode 100644 index 0000000..c707db5 --- /dev/null +++ b/lib/darwin/execmem.c @@ -0,0 +1,57 @@ +#include "execmem.h" +#include "darwin/manual-syscall.h" +#include "substitute.h" +#include <mach/mach.h> +#include <sys/mman.h> +#include <sys/syscall.h> +#include <errno.h> +#include <stdio.h> + +int execmem_write(void *dest, const void *src, size_t len) { + /* Use vm_region to determine the original protection, so we can mprotect + * it back afterwards. (Note: PROT_* are equal to VM_PROT_*.) */ + vm_address_t region = (vm_address_t) dest; + vm_size_t region_len = 0; + struct vm_region_submap_short_info_64 info; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + natural_t max_depth = 99999; + kern_return_t kr = vm_region_recurse_64(mach_task_self(), ®ion, ®ion_len, + &max_depth, + (vm_region_recurse_info_t) &info, + &info_count); + if (kr) { + /* Weird; this probably means the region doesn't exist, but we should + * have already read from the memory in order to generate the patch. */ + errno = 0; + return SUBSTITUTE_ERR_VM; + } + + uintptr_t lopage = (uintptr_t) dest & ~PAGE_MASK; + uintptr_t hipage = ((uintptr_t) dest + len + PAGE_MASK) & ~PAGE_MASK; + + /* We do the syscall manually just in case the user is trying to write to + * the mprotect syscall stub itself, or one of the functions it calls. + * (Obviously, it will still break if the user targets some libsubstitute + * function within the same page as this one, though.) */ + int ret = manual_syscall(SYS_mprotect, lopage, hipage - lopage, + PROT_READ | PROT_WRITE, 0, 0); + if (ret) { + errno = ret; + return SUBSTITUTE_ERR_VM; + } + + /* volatile to avoid compiler transformation to call to memcpy */ + volatile uint8_t *d8 = dest; + const uint8_t *s8 = src; + while (len--) + *d8++ = *s8++; + + int oldprot = info.protection & (PROT_READ | PROT_WRITE | PROT_EXEC); + ret = manual_syscall(SYS_mprotect, lopage, hipage - lopage, + oldprot, 0, 0); + if (ret) { + errno = ret; + return SUBSTITUTE_ERR_VM; + } + return SUBSTITUTE_OK; +} diff --git a/lib/darwin/inject-asm-raw.c b/lib/darwin/inject-asm-raw.c index 0519dc6..3b0f1ce 100644 --- a/lib/darwin/inject-asm-raw.c +++ b/lib/darwin/inject-asm-raw.c @@ -1,65 +1,11 @@ +#include "darwin/manual-syscall.h" + #ifdef __arm64__ #define _PAGE_SIZE 0x4000 #else #define _PAGE_SIZE 0x1000 #endif -#define REG(var, reg) register long _##var asm(#reg) = var -#define OREG(var, reg) register long var asm(#reg) - -__attribute__((always_inline)) -#if defined(__x86_64__) -static int syscall(long s, long a, long b, long c, long d, long e) { - 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("push %1; syscall; pop %1" - : "=r"(out) - : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); - return out; -} -#elif defined(__i386__) -static int syscall(long s, long a, long b, long c, long d, long e) { - REG(s, eax); - OREG(out, eax); - OREG(sp, ecx); - asm volatile("mov %%esp, %0" : "=r"(sp)); - asm volatile("push %0" :: "r"(e)); - asm volatile("push %0" :: "r"(d)); - asm volatile("push %0" :: "r"(c)); - asm volatile("push %0" :: "r"(b)); - asm volatile("push %0" :: "r"(a)); - asm volatile("call 1f; 1: pop %%edx; add $(2f-1b), %%edx;" - "sysenter; 2:" - : "=r"(out) : "r"(_s) : "edx"); - return out; -} -#elif defined(__arm__) -static int syscall(long s, long a, long b, long c, long d, long e) { - REG(s, r12); REG(a, r0); REG(b, r1); REG(c, r2); REG(d, r3); - OREG(out, r0); - asm volatile("push {%1}; svc #0x80; pop {%1}" - : "=r"(out) - : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); - return out; -} -#elif defined(__arm64__) -static int syscall(long s, long a, long b, long c, long d, long e) { - REG(s, x16); REG(a, x0); REG(b, x1); REG(c, x2); REG(d, x3); - OREG(out, x0); - asm volatile("str %1, [sp, #-0x10]!\n svc #0x80\n ldr %1, [sp], #0x10" - : "=r"(out) - : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); - return out; -} -#else -#error ? -#endif - - struct baton { int (*pthread_create)(int *, void *, void *(*)(void *), void *); void (*dlopen)(const char *, int); @@ -81,8 +27,8 @@ void entry(struct baton *baton) { baton->pthread_create(&pt, 0, bsd_thread_func, baton); unsigned long ptr = (unsigned long) baton & ~(_PAGE_SIZE - 1); while (!baton->done) - syscall(-62 /*clock_sleep_trap */, 0, 1, 0, 8000 /*ns*/, -1); - syscall(361 /*bsdthread_terminate*/, ptr, 0x2000, 0, 0, 0); + manual_syscall(-62 /*clock_sleep_trap */, 0, 1, 0, 8000 /*ns*/, -1); + manual_syscall(361 /*bsdthread_terminate*/, ptr, 0x2000, 0, 0, 0); ((void (*)()) 0xbad)(); } static void *bsd_thread_func(void *arg) { diff --git a/lib/darwin/manual-syscall.h b/lib/darwin/manual-syscall.h new file mode 100644 index 0000000..4d847ac --- /dev/null +++ b/lib/darwin/manual-syscall.h @@ -0,0 +1,54 @@ +#pragma once + +#define REG(var, reg) register long _##var __asm__(#reg) = var +#define OREG(var, reg) register long var __asm__(#reg) + +__attribute__((always_inline)) +#if defined(__x86_64__) +static int manual_syscall(long s, long a, long b, long c, long d, long e) { + 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("push %1; syscall; pop %1" + : "=r"(out) + : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); + return out; +} +#elif defined(__i386__) +static int manual_syscall(long s, long a, long b, long c, long d, long e) { + REG(s, eax); + OREG(out, eax); + __asm__ volatile("push %6; 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 $(6 * 4), %%esp" + : "=r"(out) + : "r"(_s), "g"(a), "g"(b), "g"(c), "g"(d), "g"(e) + : "edx", "ecx"); + return out; +} +#elif defined(__arm__) +static int manual_syscall(long s, long a, long b, long c, long d, long e) { + REG(s, r12); REG(a, r0); REG(b, r1); REG(c, r2); REG(d, r3); + OREG(out, r0); + __asm__ volatile("push {%1}; svc #0x80; pop {%1}" + : "=r"(out) + : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); + return out; +} +#elif defined(__arm64__) +static int manual_syscall(long s, long a, long b, long c, long d, long e) { + REG(s, x16); REG(a, x0); REG(b, x1); REG(c, x2); REG(d, x3); + OREG(out, x0); + __asm__ volatile("str %1, [sp, #-0x10]!\n svc #0x80\n ldr %1, [sp], #0x10" + : "=r"(out) + : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); + return out; +} +#else +#error ? +#endif diff --git a/lib/execmem.h b/lib/execmem.h new file mode 100644 index 0000000..f46b7d6 --- /dev/null +++ b/lib/execmem.h @@ -0,0 +1,3 @@ +#pragma once +#include <stdlib.h> +int execmem_write(void *dest, const void *src, size_t len); diff --git a/lib/hook-functions.c b/lib/hook-functions.c index 975520e..e20f069 100644 --- a/lib/hook-functions.c +++ b/lib/hook-functions.c @@ -1,5 +1,7 @@ #include "substitute.h" #include "substitute-internal.h" +#include "execmem.h" +#include "stop-other-threads.h" static uintptr_t patch_callback(UNUSED void *ctx, uintptr_t pc) { printf("patch_callback: pc=%llx\n", (long long) pc); diff --git a/lib/stop-other-threads.h b/lib/stop-other-threads.h new file mode 100644 index 0000000..1f6e639 --- /dev/null +++ b/lib/stop-other-threads.h @@ -0,0 +1,9 @@ +#pragma once +#include <stdint.h> + +/* Stop the world; return token to be used for applying PC patches and resuming. */ +int stop_other_threads(void **token_ptr); +int apply_pc_patch_callback(void *token, + uintptr_t (*pc_patch_callback)(void *ctx, uintptr_t pc), + void *ctx); +int resume_other_threads(void *token); diff --git a/lib/substitute-internal.h b/lib/substitute-internal.h index 80052f9..0bfff95 100644 --- a/lib/substitute-internal.h +++ b/lib/substitute-internal.h @@ -69,10 +69,3 @@ enum { int substitute_dlopen_in_pid(int pid, const char *filename, int options, char **error); #endif - -/* Stop the world; return token to be used for applying PC patches and resuming. */ -int stop_other_threads(void **token_ptr); -int apply_pc_patch_callback(void *token, - uintptr_t (*pc_patch_callback)(void *ctx, uintptr_t pc), - void *ctx); -int resume_other_threads(void *token); diff --git a/lib/substitute.h b/lib/substitute.h index ada57f2..95f8436 100644 --- a/lib/substitute.h +++ b/lib/substitute.h @@ -39,6 +39,7 @@ enum { /* substitute_hook_functions: mmap or mprotect failure other than ENOMEM * (preserved in errno on return) + * substitute_hook_functions: vm_region failure (errno = 0) * substitute_hook_objc_message: vm_remap failure * Most likely to come up with substitute_hook_functions if the kernel is * preventing pages from being marked executable. */ |