diff options
Diffstat (limited to 'lib/darwin')
-rw-r--r-- | lib/darwin/execmem.c | 4 | ||||
-rw-r--r-- | lib/darwin/inject-asm-raw.c | 41 | ||||
-rw-r--r-- | lib/darwin/inject.c | 285 | ||||
-rw-r--r-- | lib/darwin/manual-syscall.h | 26 |
4 files changed, 254 insertions, 102 deletions
diff --git a/lib/darwin/execmem.c b/lib/darwin/execmem.c index 22c1c2e..76f0643 100644 --- a/lib/darwin/execmem.c +++ b/lib/darwin/execmem.c @@ -34,7 +34,7 @@ int execmem_write(void *dest, const void *src, size_t len) { * (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); + PROT_READ | PROT_WRITE, 0); if (ret) { errno = ret; return SUBSTITUTE_ERR_VM; @@ -48,7 +48,7 @@ int execmem_write(void *dest, const void *src, size_t len) { int oldprot = info.protection & (PROT_READ | PROT_WRITE | PROT_EXEC); ret = manual_syscall(SYS_mprotect, lopage, hipage - lopage, - oldprot, 0, 0); + oldprot, 0); if (ret) { errno = ret; return SUBSTITUTE_ERR_VM; diff --git a/lib/darwin/inject-asm-raw.c b/lib/darwin/inject-asm-raw.c index 3b0f1ce..9b272e7 100644 --- a/lib/darwin/inject-asm-raw.c +++ b/lib/darwin/inject-asm-raw.c @@ -8,32 +8,37 @@ struct baton { int (*pthread_create)(int *, void *, void *(*)(void *), void *); - void (*dlopen)(const char *, int); + void *(*dlopen)(const char *, int); + void *(*dlsym)(void *, const char *); + int (*munmap)(void *, long); const char *path; - long done; + /* bsd_thread_func uses this to wait for entry to go away */ + long sem_port; + long nshuttle; + char shuttle[0]; }; -struct baton2 { - void (*dlopen)(const char *, int); - const char *path; - int port; -}; -static void *bsd_thread_func(void *); +static int bsd_thread_func(void *); #if defined(__i386__) __attribute__((fastcall)) #endif -/* xxx need to change this to have host allocate two pages - way easier */ void entry(struct baton *baton) { int pt; - baton->pthread_create(&pt, 0, bsd_thread_func, baton); - unsigned long ptr = (unsigned long) baton & ~(_PAGE_SIZE - 1); - while (!baton->done) - manual_syscall(-62 /*clock_sleep_trap */, 0, 1, 0, 8000 /*ns*/, -1); - manual_syscall(361 /*bsdthread_terminate*/, ptr, 0x2000, 0, 0, 0); + baton->pthread_create(&pt, 0, (void *) bsd_thread_func, baton); + manual_syscall(361 /* bsdthread_terminate */, 0, 0, 0, baton->sem_port); ((void (*)()) 0xbad)(); } -static void *bsd_thread_func(void *arg) { +static int bsd_thread_func(void *arg) { struct baton *baton = arg; - baton->dlopen(baton->path, 0); - baton->done = 1; - return 0; + void *r = baton->dlopen(baton->path, 0); + if (r) { + __attribute__((section("__TEXT,__text"))) + static char name[] = "substitute_init"; + void (*init)(void *, unsigned long) = baton->dlsym(r, name); + if (init) + init(baton->shuttle, baton->nshuttle); + + } + manual_syscall(-36 /* semaphore_wait_trap */, baton->sem_port, 0, 0, 0); + unsigned long ptr = (unsigned long) baton & ~(_PAGE_SIZE - 1); + return baton->munmap((void *) ptr, 0x2000); } diff --git a/lib/darwin/inject.c b/lib/darwin/inject.c index a515ada..4e715d5 100644 --- a/lib/darwin/inject.c +++ b/lib/darwin/inject.c @@ -8,6 +8,7 @@ #include <dlfcn.h> #include <pthread.h> #include <sys/param.h> +#include <sys/mman.h> #include <stdint.h> #include <stdio.h> #include <stdbool.h> @@ -57,10 +58,14 @@ struct dyld_all_image_infos_64 { #define FFI_SHORT_CIRCUIT -1 -static int find_foreign_images(mach_port_t task, uint64_t *libdyld_p, uint64_t *libpthread_p, char **error) { - *libdyld_p = 0; - *libpthread_p = 0; +struct foreign_image { + const char *name; + uint64_t address; +}; +static int find_foreign_images(mach_port_t task, + struct foreign_image *images, size_t nimages, + char **error) { struct task_dyld_info tdi; mach_msg_type_number_t cnt = TASK_DYLD_INFO_COUNT; @@ -139,6 +144,7 @@ static int find_foreign_images(mach_port_t task, uint64_t *libdyld_p, uint64_t * /* yay, slow file path reads! */ void *info_array_ptr = info_array; + size_t images_left = nimages; for (uint32_t i = 0; i < info_array_count; i++) { uint64_t load_address; uint64_t file_path; @@ -160,7 +166,7 @@ static int find_foreign_images(mach_port_t task, uint64_t *libdyld_p, uint64_t * kr = mach_vm_read_overwrite(task, file_path, toread, (mach_vm_address_t) path_buf, &size); if (kr) { - printf("kr=%d <%p %p>\n", kr, (void *) file_path, path_buf); + /* printf("kr=%d <%p %p>\n", kr, (void *) file_path, path_buf); */ continue; } if (strlen(path_buf) == toread && toread < MAXPATHLEN) { @@ -173,14 +179,15 @@ static int find_foreign_images(mach_port_t task, uint64_t *libdyld_p, uint64_t * path_buf[MAXPATHLEN] = '\0'; } - if (!strcmp(path_buf, "/usr/lib/system/libdyld.dylib")) - *libdyld_p = load_address; - else if (!strcmp(path_buf, "/usr/lib/system/libsystem_pthread.dylib")) - *libpthread_p = load_address; - - if (*libdyld_p && *libpthread_p) { - free(info_array); - return SUBSTITUTE_OK; + for (size_t i = 0; i < nimages; i++) { + if (!images[i].address && + !strcmp(path_buf, images[i].name)) { + images[i].address = load_address; + if (--images_left == 0) { + free(info_array); + return SUBSTITUTE_OK; + } + } } info_array_ptr += info_array_elm_size; @@ -380,26 +387,170 @@ got_symbol:; return true; } -int substitute_dlopen_in_pid(int pid, const char *filename, int options, char **error) { +static int do_baton(const char *filename, size_t filelen, bool is64, + mach_vm_address_t target_stackpage_end, + mach_vm_address_t *target_stack_top_p, + uint64_t sym_addrs[static 4], + const struct shuttle *shuttle, size_t nshuttle, + struct shuttle **target_shuttle_p, + semaphore_t *sem_port_p, + mach_port_t task, + char **error) { + int ret; + + size_t baton_len = 7 * (is64 ? 8 : 4); + size_t shuttles_len = nshuttle * sizeof(struct shuttle); + size_t filelen_rounded = (filelen + 7) & ~7; + size_t total_len = baton_len + shuttles_len + filelen_rounded; + mach_vm_address_t target_stack_top = target_stackpage_end - total_len; + *target_stack_top_p = target_stack_top; + char *stackbuf = calloc(total_len, 1); + if (!stackbuf) { + asprintf(error, "out of memory allocating stackbuf"); + ret = SUBSTITUTE_ERR_OOM; + goto fail; + } + strcpy(stackbuf + baton_len + shuttles_len, filename); + + struct shuttle *target_shuttle = calloc(nshuttle, sizeof(*target_shuttle)); + *target_shuttle_p = target_shuttle; + for (size_t i = 0; i < nshuttle; i++) { + const struct shuttle *in = &shuttle[i]; + struct shuttle *out = &target_shuttle[i]; + out->type = in->type; + switch (in->type) { + case SUBSTITUTE_SHUTTLE_MACH_PORT: + out->u.mach.right_type = in->u.mach.right_type; + while (1) { + mach_port_name_t name; + kern_return_t kr = mach_port_allocate(task, + MACH_PORT_RIGHT_DEAD_NAME, + &name); + if (kr) { + asprintf(error, "mach_port_allocate(temp dead name): kr=%d", + kr); + ret = SUBSTITUTE_ERR_MISC; + goto fail; + } + kr = mach_port_deallocate(task, name); + if (kr) { + asprintf(error, "mach_port_deallocate(temp dead name): kr=%d", + kr); + ret = SUBSTITUTE_ERR_MISC; + goto fail; + } + kr = mach_port_insert_right(task, name, in->u.mach.port, + in->u.mach.right_type); + if (kr == KERN_NAME_EXISTS) { + /* between the deallocate and the insert, someone must have + * grabbed this name - just try again */ + continue; + } else if (kr) { + asprintf(error, "mach_port_insert_right(shuttle %zu): kr=%d", + i, kr); + ret = SUBSTITUTE_ERR_MISC; + goto fail; + } + + /* ok */ + out->u.mach.port = name; + break; + } + break; + default: + asprintf(error, "bad shuttle type %d", in->type); + ret = SUBSTITUTE_ERR_MISC; + goto fail; + } + } + + memcpy(stackbuf + baton_len, target_shuttle, nshuttle * sizeof(*target_shuttle)); + + semaphore_t sem_port = MACH_PORT_NULL; + kern_return_t kr = semaphore_create(task, &sem_port, SYNC_POLICY_FIFO, 0); + if (kr) { + asprintf(error, "semaphore_create: kr=%d", kr); + ret = SUBSTITUTE_ERR_MISC; + goto fail; + } + *sem_port_p = sem_port; + + uint64_t baton_vals[] = { + sym_addrs[0], + sym_addrs[1], + sym_addrs[2], + sym_addrs[3], + target_stack_top + baton_len + shuttles_len, + sem_port, + nshuttle + }; + + if (is64) { + uint64_t *p = (void *) stackbuf; + for (size_t i = 0; i < sizeof(baton_vals)/sizeof(*baton_vals); i++) + p[i] = baton_vals[i]; + } else { + uint32_t *p = (void *) stackbuf; + for (size_t i = 0; i < sizeof(baton_vals)/sizeof(*baton_vals); i++) + p[i] = (uint32_t) baton_vals[i]; + } + + kr = mach_vm_write(task, target_stack_top, + (mach_vm_address_t) stackbuf, total_len); + if (kr) { + asprintf(error, "mach_vm_write(stack data): kr=%d", kr); + ret = SUBSTITUTE_ERR_MISC; + goto fail; + } + + ret = SUBSTITUTE_OK; + +fail: + free(stackbuf); + return ret; +} + +int substitute_dlopen_in_pid(int pid, const char *filename, int options, + const struct shuttle *shuttle, size_t nshuttle, + char **error) { + if (nshuttle > 10) { + asprintf(error, "nshuttle too high"); + return SUBSTITUTE_ERR_MISC; + } + size_t filelen = strlen(filename); + if (filelen >= 0x400) { + asprintf(error, "you gave me a terrible filename (%s)", filename); + return SUBSTITUTE_ERR_MISC; + } + mach_port_t task; mach_vm_address_t target_stack = 0; + struct shuttle *target_shuttle = NULL; + semaphore_t sem_port = MACH_PORT_NULL; + int ret; *error = NULL; + kern_return_t kr = task_for_pid(mach_task_self(), pid, &task); - int ret; if (kr) { asprintf(error, "task_for_pid: kr=%d", kr); return SUBSTITUTE_ERR_TASK_FOR_PID; } - uint64_t libdyld_addr, libpthread_addr; - if ((ret = find_foreign_images(task, &libdyld_addr, &libpthread_addr, error)) > 0) + struct foreign_image images[] = { + {"/usr/lib/system/libdyld.dylib", 0}, + {"/usr/lib/system/libsystem_pthread.dylib", 0}, + {"/usr/lib/system/libsystem_kernel.dylib", 0} + }; + if ((ret = find_foreign_images(task, images, 3, error)) > 0) goto fail; - uint64_t dlopen_addr, pthread_create_addr; + uint64_t pthread_create_addr, dlopen_addr, dlsym_addr, munmap_addr; cpu_type_t cputype; if (ret == FFI_SHORT_CIRCUIT) { - dlopen_addr = (uint64_t) dlopen; pthread_create_addr = (uint64_t) pthread_create; + dlopen_addr = (uint64_t) dlopen; + dlsym_addr = (uint64_t) dlsym; + munmap_addr = (uint64_t) munmap; #if defined(__x86_64__) cputype = CPU_TYPE_X86_64; #elif defined(__i386__) @@ -412,14 +563,18 @@ int substitute_dlopen_in_pid(int pid, const char *filename, int options, char ** } else { struct { uint64_t addr; - const char *symname; - uint64_t symaddr; - } libs[2] = { - {libdyld_addr, "_dlopen", 0}, - {libpthread_addr, "_pthread_create", 0} + int nsyms; + struct { + const char *symname; + uint64_t symaddr; + } syms[2]; + } libs[3] = { + {images[0].address, 2, {{"_dlopen", 0}, {"_dlsym", 0}}}, + {images[1].address, 1, {{"_pthread_create", 0}}}, + {images[2].address, 1, {{"_munmap", 0}}}, }; - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 3; i++) { void *linkedit, *export; size_t linkedit_size, export_size; if ((ret = get_foreign_image_export(task, libs[i].addr, @@ -427,18 +582,26 @@ int substitute_dlopen_in_pid(int pid, const char *filename, int options, char ** &export, &export_size, &cputype, error))) goto fail; - bool fesr = find_export_symbol(export, export_size, libs[i].symname, - libs[i].addr, &libs[i].symaddr); + const char *failed_symbol = NULL; + for (int j = 0; j < libs[i].nsyms; j++) { + if (!find_export_symbol(export, export_size, libs[i].syms[j].symname, + libs[i].addr, &libs[i].syms[j].symaddr)) { + failed_symbol = libs[i].syms[j].symname; + break; + } + } + vm_deallocate(mach_task_self(), (vm_offset_t) linkedit, (vm_size_t) linkedit_size); - if (!fesr) { - asprintf(error, "couldn't find _dlopen in libdyld"); + if (failed_symbol) { + asprintf(error, "couldn't find target symbol %s", failed_symbol); ret = SUBSTITUTE_ERR_MISC; goto fail; } } - dlopen_addr = libs[0].symaddr; - pthread_create_addr = libs[1].symaddr; + dlopen_addr = libs[0].syms[0].symaddr; + dlsym_addr = libs[0].syms[1].symaddr; + pthread_create_addr = libs[1].syms[0].symaddr; } UNUSED @@ -469,45 +632,13 @@ int substitute_dlopen_in_pid(int pid, const char *filename, int options, char ** goto fail; } - size_t filelen = strlen(filename); - if (filelen >= 0x400) { - asprintf(error, "you gave me a terrible filename (%s)", filename); - ret = SUBSTITUTE_ERR_MISC; - goto fail; - } - size_t filelen_rounded = (filelen + 7) & ~7; - - size_t baton_len = (cputype & CPU_ARCH_ABI64) ? 32 : 16; - mach_vm_address_t target_stack_top = target_stack + target_page_size - - baton_len - filelen_rounded; - char *stackbuf = calloc(baton_len + filelen_rounded, 1); - if (!stackbuf) { - ret = SUBSTITUTE_ERR_OOM; + uint64_t sym_addrs[] = {pthread_create_addr, dlopen_addr, dlsym_addr, munmap_addr}; + mach_vm_address_t target_stack_top; + if ((ret = do_baton(filename, filelen, cputype & CPU_ARCH_ABI64, + target_code_page, &target_stack_top, + sym_addrs, shuttle, nshuttle, &target_shuttle, &sem_port, + task, error))) goto fail; - } - strcpy(stackbuf + baton_len, filename); - - uint64_t vals[3] = {pthread_create_addr, dlopen_addr, target_stack_top + baton_len}; - if (cputype & CPU_ARCH_ABI64) { - uint64_t *p = (void *) stackbuf; - p[0] = vals[0]; - p[1] = vals[1]; - p[2] = vals[2]; - } else { - uint32_t *p = (void *) stackbuf; - p[0] = (uint32_t) vals[0]; - p[1] = (uint32_t) vals[1]; - p[2] = (uint32_t) vals[2]; - } - - kr = mach_vm_write(task, target_stack_top, - (mach_vm_address_t) stackbuf, baton_len + filelen_rounded); - free(stackbuf); - if (kr) { - asprintf(error, "mach_vm_write(stack data): kr=%d", kr); - ret = SUBSTITUTE_ERR_MISC; - goto fail; - } union { struct _x86_thread_state_32 x32; @@ -558,7 +689,7 @@ int substitute_dlopen_in_pid(int pid, const char *filename, int options, char ** goto fail; } - mach_port_t thread; + mach_port_t thread = MACH_PORT_NULL; kr = thread_create_running(task, flavor, (thread_state_t) &u, state_size / sizeof(int), &thread); if (kr) { @@ -578,6 +709,22 @@ int substitute_dlopen_in_pid(int pid, const char *filename, int options, char ** fail: if (target_stack) mach_vm_deallocate(task, target_stack, 2 * target_page_size); + if (target_shuttle) { + if (ret) { + for (size_t i = 0; i < nshuttle; i++) { + const struct shuttle *out = &target_shuttle[i]; + switch (out->type) { + case SUBSTITUTE_SHUTTLE_MACH_PORT: + if (out->u.mach.port) + mach_port_deallocate(task, out->u.mach.port); + break; + } + } + } + free(target_shuttle); + } + if (sem_port && ret) + mach_port_deallocate(task, sem_port); mach_port_deallocate(mach_task_self(), task); return ret; } diff --git a/lib/darwin/manual-syscall.h b/lib/darwin/manual-syscall.h index 4d847ac..9f04d2b 100644 --- a/lib/darwin/manual-syscall.h +++ b/lib/darwin/manual-syscall.h @@ -5,48 +5,48 @@ __attribute__((always_inline)) #if defined(__x86_64__) -static int manual_syscall(long s, long a, long b, long c, long d, long e) { +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("push %1; syscall; pop %1" + __asm__ volatile("syscall" : "=r"(out) - : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); + : "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) { +static int manual_syscall(long s, long a, long b, long c, long d) { REG(s, eax); OREG(out, eax); - __asm__ volatile("push %6; push %5; push %4; push %3; push %2; push $0;" + __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 $(6 * 4), %%esp" + "2: add $(5 * 4), %%esp" : "=r"(out) - : "r"(_s), "g"(a), "g"(b), "g"(c), "g"(d), "g"(e) + : "r"(_s), "g"(a), "g"(b), "g"(c), "g"(d) : "edx", "ecx"); return out; } #elif defined(__arm__) -static int manual_syscall(long s, long a, long b, long c, long d, long e) { +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("push {%1}; svc #0x80; pop {%1}" + __asm__ volatile("svc #0x80" : "=r"(out) - : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); + : "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) { +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("str %1, [sp, #-0x10]!\n svc #0x80\n ldr %1, [sp], #0x10" + __asm__ volatile("svc #0x80" : "=r"(out) - : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); + : "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d)); return out; } #else |