diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | lib/interpose.c | 69 | ||||
-rw-r--r-- | test/test-interpose.c | 35 |
3 files changed, 82 insertions, 23 deletions
@@ -58,6 +58,7 @@ $(eval $(call define_test,substrate,substrate,$(CXX) -std=c++98)) $(eval $(call define_test,jump-dis,jump-dis,$(CC) -std=c11)) $(eval $(call define_test,imp-forwarding,imp-forwarding,$(CC) -std=c11 -framework Foundation -lobjc)) $(eval $(call define_test,objc-hook,objc-hook,$(CC) -std=c11 -framework Foundation -lsubstitute)) +$(eval $(call define_test,interpose,interpose,$(CC) -std=c11 -lsubstitute)) out/insns-arm.o: test/insns-arm.S Makefile clang -arch armv7 -c -o $@ $< diff --git a/lib/interpose.c b/lib/interpose.c index 4857648..06a357f 100644 --- a/lib/interpose.c +++ b/lib/interpose.c @@ -1,25 +1,19 @@ #ifdef __APPLE__ - #include <stdint.h> #include <stdbool.h> -//#include <stdlib.h> -//#include <stdio.h> -//#include <string.h> - #include "substitute.h" #include "substitute-internal.h" - -enum { MAX_SEGMENTS = 32 }; struct interpose_state { - int nsegments; + size_t nsegments; segment_command_x **segments; - segment_command_x *stack_segments[MAX_SEGMENTS]; + size_t max_segments; uintptr_t slide; const struct substitute_import_hook *hooks; size_t nhooks; + segment_command_x *stack_segments[32]; }; static uintptr_t read_leb128(void **ptr, void *end, bool is_signed) { @@ -48,8 +42,8 @@ static inline char *read_cstring(void **ptr, void *end) { return s; } - -static int try_bind_section(void *bind, size_t size, const struct interpose_state *st, bool lazy) { +static int try_bind_section(void *bind, size_t size, const struct interpose_state *st, + bool lazy) { void *ptr = bind, *end = bind + size; const char *sym = NULL; uint8_t type = lazy ? BIND_TYPE_POINTER : 0; @@ -112,21 +106,45 @@ static int try_bind_section(void *bind, size_t size, const struct interpose_stat size_t i; for (i = 0; i < st->nhooks; i++) { h = &st->hooks[i]; - // TODO abs/pcrel32? used on arm? - if (!strcmp(sym, h->name)) { - if (type != BIND_TYPE_POINTER) - return SUBSTITUTE_ERR_UNKNOWN_RELOCATION_TYPE; + if (!strcmp(sym, h->name)) break; - } } if (i != st->nhooks) { while (count--) { uintptr_t new = (uintptr_t) h->replacement + addend; - uintptr_t *p = (void *) (segment + offset); - uintptr_t old = __atomic_exchange_n(p, new, __ATOMIC_RELAXED); + uintptr_t old; + void *p = (void *) (segment + offset); + switch (type) { + case BIND_TYPE_POINTER: { + old = __atomic_exchange_n((uintptr_t *) p, new, __ATOMIC_RELAXED); + break; + } + case BIND_TYPE_TEXT_ABSOLUTE32: { + if ((uint32_t) new != new) { + /* text rels should only show up on i386, where + * this is impossible... */ + panic("bad TEXT_ABSOLUTE32 rel\n"); + } + old = __atomic_exchange_n((uint32_t *) p, (uint32_t) new, __ATOMIC_RELAXED); + break; + } + case BIND_TYPE_TEXT_PCREL32: { + uintptr_t pc = (uintptr_t) p + 4; + uintptr_t rel = new - pc; + if ((uint32_t) rel != rel) { + /* ditto */ + panic("bad TEXT_ABSOLUTE32 rel\n"); + } + old = __atomic_exchange_n((uint32_t *) p, (uint32_t) rel, __ATOMIC_RELAXED); + old += pc; + break; + } + default: + panic("unknown relocation type\n"); + break; + } if (h->old_ptr) - *(void **) h->old_ptr = (void *) (old - addend); - offset += stride; + *(uintptr_t *) h->old_ptr = old - addend; } break; } @@ -139,7 +157,7 @@ static int try_bind_section(void *bind, size_t size, const struct interpose_stat } static void *off_to_addr(const struct interpose_state *st, uint32_t off) { - for (int i = 0; i < st->nsegments; i++) { + for (size_t i = 0; i < st->nsegments; i++) { const segment_command_x *sc = st->segments[i]; if ((off - sc->fileoff) < sc->filesize) return (void *) (sc->vmaddr + st->slide + off - sc->fileoff); @@ -150,21 +168,26 @@ static void *off_to_addr(const struct interpose_state *st, uint32_t off) { EXPORT int substitute_interpose_imports(const struct substitute_image *image, const struct substitute_import_hook *hooks, - size_t nhooks, UNUSED int options) { + size_t nhooks, int options) { int ret = SUBSTITUTE_OK; + + if (options != 0) + panic("%s: unrecognized options\n", __func__); + struct interpose_state st; st.slide = image->slide; st.nsegments = 0; st.hooks = hooks; st.nhooks = nhooks; st.segments = st.stack_segments; + st.max_segments = sizeof(st.stack_segments) / sizeof(*st.stack_segments); const mach_header_x *mh = image->image_header; const struct load_command *lc = (void *) (mh + 1); for (uint32_t i = 0; i < mh->ncmds; i++) { if (lc->cmd == LC_SEGMENT_X) { segment_command_x *sc = (void *) lc; - if (st.nsegments == MAX_SEGMENTS) { + if (st.nsegments == st.max_segments) { segment_command_x **new = calloc(st.nsegments * 2, sizeof(*st.segments)); if (!new) substitute_panic("%s: out of memory\n", __func__); diff --git a/test/test-interpose.c b/test/test-interpose.c new file mode 100644 index 0000000..ea28abb --- /dev/null +++ b/test/test-interpose.c @@ -0,0 +1,35 @@ +#include "substitute.h" + +#include <unistd.h> +#include <stdio.h> +#include <mach-o/dyld.h> +#include <assert.h> + +static void *getpid_plus = (void *) getpid + 5; + +static pid_t (*old_getpid)(); +static pid_t my_getpid() { + return old_getpid() + 1; +} +static gid_t my_getgid() { + return 42; +} + +int main() { + const char *self = _dyld_get_image_name(0); + void *handle = substitute_open_image(self); + assert(handle); + struct substitute_import_hook hooks[] = { + {"_getpid", my_getpid, &old_getpid}, + {"_getgid", my_getgid, NULL}, + + }; + pid_t (*gp)() = getpid_plus - 5; + printf("original pid: %d\n", (int) gp()); + substitute_interpose_imports(handle, hooks, sizeof(hooks)/sizeof(*hooks), 0); + gp = getpid_plus - 5; + printf("new pid: %d\n", (int) gp()); + printf("new gid: %d\n", (int) getgid()); + + substitute_close_image(handle); +} |