aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--lib/darwin/find-syms.c87
-rw-r--r--lib/darwin/find-syms.h14
-rw-r--r--lib/darwin/inject.c184
-rw-r--r--lib/darwin/substrate-compat.c6
-rw-r--r--lib/substitute-internal.h19
-rw-r--r--lib/substitute.h11
-rw-r--r--test/test-find-syms.c7
8 files changed, 290 insertions, 39 deletions
diff --git a/Makefile b/Makefile
index d8d5cdf..9b3aa10 100644
--- a/Makefile
+++ b/Makefile
@@ -38,6 +38,7 @@ out/transform-dis.o: $(GENERATED)
LIB_OBJS := \
out/darwin/find-syms.o \
+ out/darwin/inject.o \
out/darwin/interpose.o \
out/darwin/objc-asm.o \
out/darwin/objc.o \
diff --git a/lib/darwin/find-syms.c b/lib/darwin/find-syms.c
index 44adf78..f2261f0 100644
--- a/lib/darwin/find-syms.c
+++ b/lib/darwin/find-syms.c
@@ -6,6 +6,7 @@
#include "substitute.h"
#include "substitute-internal.h"
+#include "find-syms.h"
extern const struct dyld_all_image_infos *_dyld_get_all_image_infos();
@@ -22,11 +23,23 @@ static void *sym_to_ptr(substitute_sym *sym, intptr_t slide) {
return (void *) addr;
}
-static void find_syms_raw(const void *hdr, intptr_t *slide, const char **names, substitute_sym **syms, size_t count) {
- memset(syms, 0, sizeof(*syms) * count);
+static bool find_slide(const mach_header_x *mh, intptr_t *slidep) {
+ uint32_t ncmds = mh->ncmds;
+ struct load_command *lc = (void *) (mh + 1);
+ for (uint32_t i = 0; i < ncmds; i++) {
+ if (lc->cmd == LC_SEGMENT_X) {
+ segment_command_x *sc = (void *) lc;
+ if (sc->fileoff == 0) {
+ *slidep = (uintptr_t) mh - sc->vmaddr;
+ return true;
+ }
+ }
+ lc = (void *) lc + lc->cmdsize;
+ }
+ return false;
+}
- /* note: no verification at all */
- const mach_header_x *mh = hdr;
+bool find_symtab_data(const mach_header_x *mh, struct symtab_data *data) {
uint32_t ncmds = mh->ncmds;
struct load_command *lc = (void *) (mh + 1);
struct symtab_command syc;
@@ -37,38 +50,45 @@ static void find_syms_raw(const void *hdr, intptr_t *slide, const char **names,
}
lc = (void *) lc + lc->cmdsize;
}
- return; /* no symtab, no symbols */
+ return false; /* no symtab, no symbols */
ok: ;
- substitute_sym *symtab = NULL;
- const char *strtab = NULL;
lc = (void *) (mh + 1);
for (uint32_t i = 0; i < ncmds; i++) {
if (lc->cmd == LC_SEGMENT_X) {
segment_command_x *sc = (void *) lc;
- if (syc.symoff - sc->fileoff < sc->filesize)
- symtab = (void *) sc->vmaddr + syc.symoff - sc->fileoff;
- if (syc.stroff - sc->fileoff < sc->filesize)
- strtab = (void *) sc->vmaddr + syc.stroff - sc->fileoff;
- if (*slide == -1 && sc->fileoff == 0) {
- // used only for dyld
- *slide = (uintptr_t) hdr - sc->vmaddr;
+ uint32_t seg_symoff = syc.symoff - sc->fileoff;
+ uint32_t seg_stroff = syc.stroff - sc->fileoff;
+ if (seg_symoff < sc->filesize &&
+ syc.nsyms <= (sc->filesize - seg_symoff) / sizeof(substitute_sym) &&
+ seg_stroff < sc->filesize &&
+ syc.strsize <= sc->filesize - seg_stroff) {
+ data->linkedit_vmaddr = sc->vmaddr;
+ data->linkedit_symoff = seg_symoff;
+ data->nsyms = syc.nsyms;
+ data->linkedit_stroff = seg_stroff;
+ data->strsize = syc.strsize;
+ return true;
}
- if (symtab && strtab)
- goto ok2;
}
lc = (void *) lc + lc->cmdsize;
}
- return; /* uh... weird */
-ok2: ;
- symtab = (void *) symtab + *slide;
- strtab = (void *) strtab + *slide;
+ return false; /* not in any segment? */
+}
+
+void find_syms_raw(const struct symtab_data *restrict data, void *linkedit,
+ const char **restrict names, substitute_sym **restrict syms,
+ size_t count) {
+ memset(syms, 0, count * sizeof(*syms));
+ substitute_sym *symtab = linkedit + data->linkedit_symoff;
+ const char *strtab = linkedit + data->linkedit_stroff;
/* This could be optimized for efficiency with a large number of names... */
- for (uint32_t i = 0; i < syc.nsyms; i++) {
+ for (uint32_t i = 0; i < data->nsyms; i++) {
substitute_sym *sym = &symtab[i];
uint32_t strx = sym->n_un.n_strx;
- const char *name = strx == 0 ? "" : strtab + strx;
+ const char *name = (strx == 0 || strx >= data->strsize) ? "" : strtab + strx;
+ size_t maxlen = data->strsize - strx;
for (size_t j = 0; j < count; j++) {
- if (!strcmp(name, names[j])) {
+ if (!strncmp(name, names[j], maxlen)) {
syms[j] = sym;
break;
}
@@ -93,14 +113,18 @@ static void inspect_dyld() {
const char *names[2] = { "__ZNK16ImageLoaderMachO8getSlideEv", "__ZNK16ImageLoaderMachO10machHeaderEv" };
substitute_sym *syms[2];
intptr_t dyld_slide = -1;
- find_syms_raw(dyld_hdr, &dyld_slide, names, syms, 2);
+ struct symtab_data symtab_data;
+ if (!find_slide(dyld_hdr, &dyld_slide) ||
+ !find_symtab_data(dyld_hdr, &symtab_data))
+ substitute_panic("couldn't find ImageLoader methods\n");
+ void *linkedit = (void *) (symtab_data.linkedit_vmaddr + dyld_slide);
+ find_syms_raw(&symtab_data, linkedit, names, syms, 2);
if (!syms[0] || !syms[1])
substitute_panic("couldn't find ImageLoader methods\n");
ImageLoaderMachO_getSlide = sym_to_ptr(syms[0], dyld_slide);
ImageLoaderMachO_machHeader = sym_to_ptr(syms[1], dyld_slide);
}
-/* 'dlopen_header' keeps the image alive */
EXPORT
struct substitute_image *substitute_open_image(const char *filename) {
pthread_once(&dyld_inspect_once, inspect_dyld);
@@ -129,8 +153,17 @@ void substitute_close_image(struct substitute_image *im) {
EXPORT
int substitute_find_private_syms(struct substitute_image *im, const char **names,
- substitute_sym **syms, size_t nsyms) {
- find_syms_raw(im->image_header, &im->slide, names, syms, nsyms);
+ void **syms, size_t nsyms) {
+ struct symtab_data symtab_data;
+ if (!find_symtab_data(im->image_header, &symtab_data))
+ return SUBSTITUTE_OK;
+ void *linkedit = (void *) (symtab_data.linkedit_vmaddr + im->slide);
+ substitute_sym **ssyms = (void *) syms;
+ find_syms_raw(&symtab_data, linkedit, names, ssyms, nsyms);
+ for (size_t i = 0; i < nsyms; i++) {
+ if (ssyms[i])
+ syms[i] = sym_to_ptr(ssyms[i], im->slide);
+ }
return SUBSTITUTE_OK;
}
diff --git a/lib/darwin/find-syms.h b/lib/darwin/find-syms.h
new file mode 100644
index 0000000..c017d2a
--- /dev/null
+++ b/lib/darwin/find-syms.h
@@ -0,0 +1,14 @@
+#pragma once
+#include "substitute.h"
+#include "substitute-internal.h"
+
+struct symtab_data {
+ uint64_t linkedit_vmaddr;
+ uint32_t linkedit_symoff, nsyms;
+ uint32_t linkedit_stroff, strsize;
+};
+
+bool find_symtab_data(const mach_header_x *mh, struct symtab_data *data);
+void find_syms_raw(const struct symtab_data *restrict data, void *linkedit,
+ const char **restrict names, substitute_sym **restrict syms,
+ size_t count);
diff --git a/lib/darwin/inject.c b/lib/darwin/inject.c
new file mode 100644
index 0000000..a73c9aa
--- /dev/null
+++ b/lib/darwin/inject.c
@@ -0,0 +1,184 @@
+#ifdef __APPLE__
+#include "substitute.h"
+#include "substitute-internal.h"
+#include <mach/mach.h>
+#include <sys/param.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+kern_return_t mach_vm_read_overwrite(vm_map_t, mach_vm_address_t, mach_vm_size_t, mach_vm_address_t, mach_vm_size_t *);
+
+#define DEFINE_STRUCTS
+
+#define dyld_image_infos_fields(ptr) \
+ uint32_t version; \
+ uint32_t infoArrayCount; \
+ ptr infoArray; \
+ ptr notification; \
+ bool processDetachedFromSharedRegion; \
+ bool libSystemInitialized; \
+ ptr dyldImageLoadAddress; \
+ ptr jitInfo; \
+ ptr dyldVersion; \
+ ptr errorMessage; \
+ ptr terminationFlags; \
+ ptr coreSymbolicationShmPage; \
+ ptr systemOrderFlag; \
+ ptr uuidArrayCount; \
+ ptr uuidArray; \
+ ptr dyldAllImageInfosAddress; \
+ ptr initialImageCount; \
+ ptr errorKind; \
+ ptr errorClientOfDylibPath; \
+ ptr errorTargetDylibPath; \
+ ptr errorSymbol; \
+ ptr sharedCacheSlide; \
+ uint8_t sharedCacheUUID[16]; \
+ ptr reserved[16];
+
+struct dyld_all_image_infos_32 {
+ dyld_image_infos_fields(uint32_t)
+};
+struct dyld_all_image_infos_64 {
+ dyld_image_infos_fields(uint64_t)
+};
+
+static int find_libs_in_task(mach_port_t task, uint64_t *dyld_addr_p,
+ uint64_t *libpthread_p, char **error) {
+ struct task_dyld_info tdi;
+ mach_msg_type_number_t cnt = TASK_DYLD_INFO_COUNT;
+
+ kern_return_t kr = task_info(task, TASK_DYLD_INFO, (void *) &tdi, &cnt);
+ if (kr || cnt != TASK_DYLD_INFO_COUNT) {
+ asprintf(error, "task_info(TASK_DYLD_INFO): kr=%d", kr);
+ return SUBSTITUTE_ERR_MISC;
+ }
+
+ if (!tdi.all_image_info_addr || !tdi.all_image_info_size ||
+ tdi.all_image_info_size > 1024 ||
+ tdi.all_image_info_format > TASK_DYLD_ALL_IMAGE_INFO_64) {
+ asprintf(error, "TASK_DYLD_INFO obviously malformed");
+ return SUBSTITUTE_ERR_MISC;
+ }
+ char all_image_infos_buf[1024];
+
+ cnt = tdi.all_image_info_size;
+ mach_vm_size_t size = tdi.all_image_info_size;
+ kr = mach_vm_read_overwrite(task, tdi.all_image_info_addr, tdi.all_image_info_size,
+ (mach_vm_address_t) all_image_infos_buf, &size);
+ if (kr || size != tdi.all_image_info_size) {
+ asprintf(error, "mach_vm_read_overwrite(all_image_info): kr=%d", kr);
+ return SUBSTITUTE_ERR_MISC;
+ }
+
+ bool is64 = tdi.all_image_info_format = TASK_DYLD_ALL_IMAGE_INFO_64;
+ const struct dyld_all_image_infos_32 *aii32 = (void *) all_image_infos_buf;
+ const struct dyld_all_image_infos_64 *aii64 = (void *) all_image_infos_buf;
+
+ #define FIELD(f) (is64 ? aii64->f : aii32->f)
+
+ if (FIELD(version) < 2) {
+ /* apparently we're on Leopard or something */
+ asprintf(error, "dyld_all_image_infos version too low");
+ return SUBSTITUTE_ERR_MISC;
+ }
+
+ *dyld_addr_p = FIELD(dyldImageLoadAddress);
+
+ uint64_t info_array_addr = FIELD(infoArray);
+ uint32_t info_array_count = FIELD(infoArrayCount);
+ size_t info_array_elm_size = (is64 ? sizeof(uint64_t) : sizeof(uint32_t)) * 3;
+
+ #undef FIELD
+
+ if (info_array_count > 2000) {
+ asprintf(error, "unreasonable number of loaded libraries: %u", info_array_count);
+ return SUBSTITUTE_ERR_MISC;
+ }
+
+ size_t info_array_size = info_array_count * info_array_elm_size;
+ void *info_array = malloc(info_array_count * info_array_elm_size);
+ if (!info_array)
+ return SUBSTITUTE_ERR_OOM;
+
+ kr = mach_vm_read_overwrite(task, info_array_addr, info_array_size,
+ (mach_vm_address_t) info_array, &size);
+ if (kr || size != info_array_size) {
+ asprintf(error, "mach_vm_read_overwrite(info_array): kr=%d", kr);
+ return SUBSTITUTE_ERR_MISC;
+ }
+
+ /* yay, slow file path reads! */
+
+ void *info_array_ptr = info_array;
+ for (uint32_t i = 0; i < info_array_count; i++) {
+ uint64_t load_address;
+ uint64_t file_path;
+ if (is64) {
+ uint64_t *e = info_array_ptr;
+ load_address = e[0];
+ file_path = e[1];
+ } else {
+ uint32_t *e = info_array_ptr;
+ load_address = e[0];
+ file_path = e[1];
+ }
+
+ /* mach_vm_read_overwrite won't do partial copies, so... */
+
+ char path_buf[MAXPATHLEN+1];
+ size_t toread = MIN(MAXPATHLEN, -file_path & 0xfff);
+ path_buf[toread] = '\0';
+ kr = mach_vm_read_overwrite(task, (mach_vm_address_t) path_buf, toread,
+ load_address, &size);
+ if (kr) {
+ continue;
+ }
+ if (strlen(path_buf) == toread && toread < MAXPATHLEN) {
+ /* get the rest... */
+ kr = mach_vm_read_overwrite(task, (mach_vm_address_t) path_buf + toread,
+ MAXPATHLEN - toread, load_address + toread,
+ &size);
+ if (kr) {
+ continue;
+ }
+ path_buf[MAXPATHLEN] = '\0';
+ }
+
+ if (!strcmp(path_buf, "/usr/lib/libpthread.dylib")) {
+ *libpthread_p = load_address;
+ return 0;
+ }
+
+ info_array_ptr += info_array_elm_size;
+ }
+
+ asprintf(error, "couldn't find libpthread");
+ return SUBSTITUTE_ERR_MISC;
+}
+
+EXPORT
+int substitute_dlopen_in_pid(int pid, const char *filename, int options, char **error) {
+ mach_port_t task;
+ *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 dyld_addr, libpthread_addr;
+ if ((ret = find_libs_in_task(task, &dyld_addr, &libpthread_addr, error)))
+ goto fail;
+ (void) filename;
+ (void) options;
+ printf("%p %p\n", (void *) dyld_addr, (void *) libpthread_addr);
+
+ ret = 0;
+fail:
+ mach_port_deallocate(mach_task_self(), task);
+ return ret;
+}
+#endif /* __APPLE__ */
diff --git a/lib/darwin/substrate-compat.c b/lib/darwin/substrate-compat.c
index 3658ac7..cf44b19 100644
--- a/lib/darwin/substrate-compat.c
+++ b/lib/darwin/substrate-compat.c
@@ -32,10 +32,10 @@ void *SubFindSymbol(void *image, const char *name) {
return NULL;
}
- substitute_sym *sym;
- if (substitute_find_private_syms(image, &name, &sym, 1) || !sym)
+ void *ptr;
+ if (substitute_find_private_syms(image, &name, &ptr, 1))
return NULL;
- return substitute_sym_to_ptr(image, sym);
+ return ptr;
}
/*
diff --git a/lib/substitute-internal.h b/lib/substitute-internal.h
index 0f3e0ad..b21a190 100644
--- a/lib/substitute-internal.h
+++ b/lib/substitute-internal.h
@@ -53,3 +53,22 @@ typedef struct section section_x;
#elif defined(TARGET_arm64)
#include "arm64/misc.h"
#endif
+
+#ifdef __APPLE__
+/* This could graduate to a public API but is not yet. */
+enum {
+ SUBSTITUTE_DIP_INJECT_MAIN_THREAD, /* not yet */
+};
+
+enum {
+ /* substitute_dlopen_in_pid: task_for_pid failed; on OS X the reasons this
+ * can happen are really complicated and dumb, but generally one solution
+ * is to be root */
+ SUBSTITUTE_ERR_TASK_FOR_PID = 1000,
+
+ /* substitute_dlopen_in_pid: something didn't work */
+ SUBSTITUTE_ERR_MISC,
+};
+
+int substitute_dlopen_in_pid(int pid, const char *filename, int options, char **error);
+#endif
diff --git a/lib/substitute.h b/lib/substitute.h
index 398b7fd..00cc1b8 100644
--- a/lib/substitute.h
+++ b/lib/substitute.h
@@ -103,16 +103,17 @@ void substitute_close_image(struct substitute_image *handle);
*
* @handle handle opened with substitute_open_image
* @names an array of symbol names to search for
- * @syms an array of substitute_sym *, one per name; on return, each entry
- * will be a pointer into the symbol table for that image, or NULL if
- * the symbol wasn't found
+ * @syms an array of void *, one per name; on return, each entry will be
+ * filled in with the corresponding symbol address, or NULL if the
+ * symbol wasn't found
+ * (on ARM, this will be | 1 for Thumb functions)
* @nsyms number of names
*
* @return SUBSTITUTE_OK (maybe errors in the future)
*/
int substitute_find_private_syms(struct substitute_image *handle,
- const char **names,
- substitute_sym **syms,
+ const char **__restrict names,
+ void **__restrict syms,
size_t nsyms);
/* Get a pointer corresponding to a loaded symbol table entry.
diff --git a/test/test-find-syms.c b/test/test-find-syms.c
index acd31ba..5dc1514 100644
--- a/test/test-find-syms.c
+++ b/test/test-find-syms.c
@@ -9,11 +9,10 @@ int main() {
struct substitute_image *im = substitute_open_image(foundation);
assert(im);
const char *names[] = { "_absolute_from_gregorian" };
- substitute_sym *syms[1];
- assert(!substitute_find_private_syms(im, names, syms, 1));
- assert(syms[0]);
+ int (*f)(int);
+ assert(!substitute_find_private_syms(im, names, (void **) &f, 1));
+ assert(f);
- int (*f)(int) = (int (*)(int)) substitute_sym_to_ptr(im, syms[0]);
assert(f(12345) < 0);
substitute_close_image(im);