aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcomex2015-01-17 14:40:29 -0500
committercomex2015-01-17 14:40:29 -0500
commitcaa2dd77749dc91d621f01187e20327aa413a3d7 (patch)
tree7d033f9b653fa37f34aa35e36947a7bac5f3e91f
parentadd an assert (diff)
downloadsubstitute-caa2dd77749dc91d621f01187e20327aa413a3d7.tar.gz
interpose works; add test
-rw-r--r--Makefile1
-rw-r--r--lib/interpose.c69
-rw-r--r--test/test-interpose.c35
3 files changed, 82 insertions, 23 deletions
diff --git a/Makefile b/Makefile
index fbc04af..2318f7d 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
+}