aboutsummaryrefslogtreecommitdiff
path: root/lib/darwin
diff options
context:
space:
mode:
Diffstat (limited to 'lib/darwin')
-rw-r--r--lib/darwin/execmem.c4
-rw-r--r--lib/darwin/inject-asm-raw.c41
-rw-r--r--lib/darwin/inject.c285
-rw-r--r--lib/darwin/manual-syscall.h26
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