aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile33
-rw-r--r--ent.plist3
-rw-r--r--lib/darwin/inject-asm-raw.S113
-rw-r--r--lib/darwin/inject-asm-raw.c87
-rw-r--r--lib/darwin/inject-asm.S19
-rw-r--r--lib/darwin/inject.c182
-rw-r--r--test/test-inject.c6
7 files changed, 298 insertions, 145 deletions
diff --git a/Makefile b/Makefile
index 38e846e..0c88a9d 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ ARCH := -arch x86_64
XCFLAGS := -O3 -Wall -Wextra -Werror -Ilib $(ARCH)
override CC := $(CC) $(XCFLAGS) $(CFLAGS)
override CXX := $(CXX) $(XCFLAGS) $(CFLAGS) -fno-exceptions -fno-asynchronous-unwind-tables
-LIB_LDFLAGS := -lobjc -dynamiclib -fvisibility=hidden
+LIB_LDFLAGS := -lobjc -dynamiclib -fvisibility=hidden -install_name /usr/lib/libsubstitute.dylib
IMAON2 := /Users/comex/c/imaon2
GEN_JS := node --harmony --harmony_arrow_functions $(IMAON2)/tables/gen.js
@@ -54,28 +54,35 @@ out/libsubstitute.dylib: $(LIB_OBJS)
# this doesn't need to be done on the building machine, just in case someone is
# trying to build with some Linux compiler that doesn't support all the
# architectures or something - meh
-ASCLANG := clang -dynamiclib -nostartfiles -nodefaultlibs
-out/inject-asm-raw-x86_64.o: lib/darwin/inject-asm-raw.S Makefile
- $(ASCLANG) -arch x86_64 -o $@ $<
-out/inject-asm-raw-i386.o: lib/darwin/inject-asm-raw.S Makefile
- $(ASCLANG) -arch i386 -o $@ $<
-out/inject-asm-raw-arm.o: lib/darwin/inject-asm-raw.S Makefile
- $(ASCLANG) -arch armv7 -o $@ $<
-out/inject-asm-raw-arm64.o: lib/darwin/inject-asm-raw.S Makefile
- $(ASCLANG) -arch arm64 -o $@ $<
+# Did you know? With -Oz + -marm, Apple clang-600.0.56 actually generated
+# wrong code for the ARM version. It works with -Os and with newer clang.
+IACLANG := clang -Os -dynamiclib -nostartfiles -nodefaultlibs -isysroot /dev/null -fPIC
+out/inject-asm-raw-x86_64.o: lib/darwin/inject-asm-raw.c Makefile
+ $(IACLANG) -arch x86_64 -o $@ $<
+out/inject-asm-raw-i386.o: lib/darwin/inject-asm-raw.c Makefile
+ $(IACLANG) -arch i386 -o $@ $<
+out/inject-asm-raw-arm.o: lib/darwin/inject-asm-raw.c Makefile
+ $(IACLANG) -arch armv7 -marm -o $@ $<
+out/inject-asm-raw-arm64.o: lib/darwin/inject-asm-raw.c Makefile
+ $(IACLANG) -arch arm64 -o $@ $<
IAR_BINS := out/inject-asm-raw-x86_64.bin out/inject-asm-raw-i386.bin out/inject-asm-raw-arm.bin out/inject-asm-raw-arm64.bin
out/inject-asm.S: $(IAR_BINS) Makefile
+ (echo ".align 12"; \
+ echo ".globl _inject_page_start"; \
+ echo "_inject_page_start:"; \
for i in x86_64 i386 arm arm64; do \
- echo ".globl inject_start_$$i"; \
- echo "inject_start_$$i:"; \
echo ".align 2"; \
+ echo ".globl _inject_start_$$i"; \
+ echo "_inject_start_$$i:"; \
printf ".byte "; \
xxd -i < out/inject-asm-raw-$$i.bin | xargs echo; \
- done > $@
+ done) > $@ || rm -f $@
define define_test
out/test-$(1): test/test-$(2).[cm]* $(HEADERS) $(GENERATED) Makefile out/libsubstitute.dylib
$(3) -g -o $$@ $$< -Ilib -Isubstrate -Lout -lsubstitute
+ ldid -Sent.plist $$@
+ install_name_tool -change /usr/lib/libsubstitute.dylib '@executable_path/libsubstitute.dylib' $$@
all: out/test-$(1)
endef
$(eval $(call define_test,tdarm-simple,td-simple,$(CC) -std=c11 -DHDR='"arm/dis-arm.inc.h"' -Dxdis=dis_arm -DFORCE_TARGET_arm))
diff --git a/ent.plist b/ent.plist
new file mode 100644
index 0000000..fc75011
--- /dev/null
+++ b/ent.plist
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0"><dict><key>get-task-allow</key><true/><key>run-unsigned-code</key><true/><key>task_for_pid-allow</key><true/></dict></plist>
diff --git a/lib/darwin/inject-asm-raw.S b/lib/darwin/inject-asm-raw.S
deleted file mode 100644
index a8980c3..0000000
--- a/lib/darwin/inject-asm-raw.S
+++ /dev/null
@@ -1,113 +0,0 @@
-.text
-.align 2
-/* sp -> {pthread_create, dlopen, dylib} */
-#if defined(__x86_64__)
- lea -8(%rsp), %rdi /* thread */
- xor %rsi, %rsi /* attr */
- lea thread_func(%rip), %rdx /* start_routine */
- mov %rsp, %rcx /* arg */
- mov %rdi, %rsp
- call *(%rcx)
-/* suicide */
- mov $361, %rax /* bsdthread_terminate */
- xor %rdi, %rdi /* stackaddr */
- xor %rsi, %rsi /* freesize */
- xor %rdx, %rdx /* port */
- xor %rcx, %rcx /* sem */
- syscall
-/* still here? */
- mov $0xbad, %rax
- jmp *%rax
-
-thread_func:
- mov 0x8(%rdi), %rax /* dlopen */
- mov 0x10(%rdi), %rdi /* dylib */
- xor %rsi, %rsi
- jmp *%rax
-
-#elif defined(__i386__)
-
- mov %esp, %ecx
- push %ecx /* arg */
- call 1f
-1:
- pop %eax
- add $(thread_func - 1b), %eax
- push %eax /* start_routine */
- xor %eax, %eax
- push %eax /* attr */
- push %esp /* thread */
- call *(%ecx)
-/* suicide */
- mov $361, %eax /* bsdthread_terminate */
- xor %edx, %edx
- push %edx /* sem */
- push %edx /* port */
- push %edx /* freesize */
- push %edx /* stackaddr */
- syscall
-/* still here? */
- mov $0xbad, %eax
- jmp *%eax
-
-thread_func:
- xor %edx, %edx
- push %edx
- mov 0x4(%esp), %ecx /* arg */
- mov 0x8(%ecx), %edx /* dylib */
- push %edx
- mov 0x4(%ecx), %edx /* dlopen */
- push %edx
- call *%eax
- add $8, %esp
- ret
-
-#elif defined(__arm__)
-
- sub sp, #4
- mov r0, sp
- mov r1, #0
- adr r2, thread_func
- add r3, sp, #4
- ldr r9, [r3]
- blx r9
-/* suicide */
- mov r0, #0
- mov r1, #0
- mov r2, #0
- mov r3, #0
- mov r12, #361
- svc #0x80
-/* still here? */
- mov r0, #0xbad
- bx r0
-thread_func:
- ldr r2, [r0]
- ldr r0, [r0, #4]
- mov r1, #0
- bx r2
-
-#elif defined(__arm64__)
- sub sp, sp, #8
- mov x0, sp
- mov x1, #0
- adr x2, 1f
- add x3, sp, #4
- ldr x9, [x3]
- blr x9
-/* suicide */
- mov x0, #0
- mov x1, #0
- mov x2, #0
- mov x3, #0
- mov x12, #361 /* ??? */
- svc #0x80
-/* still here? */
- mov x0, #0xbad
- br x0
-1:
- ldr x2, [x0]
- ldr x0, [x0, #8]
- mov x1, #0
- br x2
-#endif
diff --git a/lib/darwin/inject-asm-raw.c b/lib/darwin/inject-asm-raw.c
new file mode 100644
index 0000000..bb92828
--- /dev/null
+++ b/lib/darwin/inject-asm-raw.c
@@ -0,0 +1,87 @@
+#ifdef __arm64__
+#define _PAGE_SIZE 0x4000
+#else
+#define _PAGE_SIZE 0x1000
+#endif
+
+#define REG(var, reg) register long _##var asm(#reg) = var
+#define OREG(var, reg) register long var asm(#reg)
+
+__attribute__((always_inline))
+#if defined(__x86_64__)
+static int syscall(long s, long a, long b, long c, long d, long _) {
+ 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("syscall" : "=r"(out) : "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d));
+ return out;
+}
+#elif defined(__i386__)
+static int syscall(long s, long a, long b, long c, long d, long e) {
+ REG(s, eax);
+ OREG(out, eax);
+ OREG(sp, ecx);
+ asm volatile("mov %%esp, %0" : "=r"(sp));
+ asm volatile("push %0" :: "r"(e));
+ asm volatile("push %0" :: "r"(d));
+ asm volatile("push %0" :: "r"(c));
+ asm volatile("push %0" :: "r"(b));
+ asm volatile("push %0" :: "r"(a));
+ asm volatile("call 1f; 1: pop %%edx; add $(2f-1b), %%edx;"
+ "sysenter; 2:"
+ : "=r"(out) : "r"(_s) : "edx");
+ return out;
+}
+#elif defined(__arm__)
+static int syscall(long s, long a, long b, long c, long d, long _) {
+ REG(s, r12); REG(a, r0); REG(b, r1); REG(c, r2); REG(d, r3);
+ OREG(out, r0);
+ asm volatile("svc #0x80" : "=r"(out) : "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d));
+ return out;
+}
+#elif defined(__arm64__)
+static int syscall(long s, long a, long b, long c, long d, long _) {
+ REG(s, x16); REG(a, x0); REG(b, x1); REG(c, x2); REG(d, x3);
+ OREG(out, x0);
+ asm volatile("svc #0x80" : "=r"(out) : "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d));
+ return out;
+}
+#else
+#error ?
+#endif
+
+
+struct baton {
+ int (*pthread_create)(int *, void *, void *(*)(void *), void *);
+ void (*dlopen)(const char *, int);
+ const char *path;
+ long done;
+};
+struct baton2 {
+ void (*dlopen)(const char *, int);
+ const char *path;
+ int port;
+};
+static void *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)
+ syscall(-62 /*clock_sleep_trap */, 0, 1, 0, 8000 /*ns*/, -1);
+ syscall(361 /*bsdthread_terminate*/, ptr, 0x2000, 0, 0, 0);
+ ((void (*)()) 0xbad)();
+}
+static void *bsd_thread_func(void *arg) {
+ struct baton *baton = arg;
+ baton->dlopen(baton->path, 0);
+ baton->done = 1;
+ return 0;
+}
diff --git a/lib/darwin/inject-asm.S b/lib/darwin/inject-asm.S
new file mode 100644
index 0000000..a909879
--- /dev/null
+++ b/lib/darwin/inject-asm.S
@@ -0,0 +1,19 @@
+.align 12
+.globl _inject_page_start
+_inject_page_start:
+.align 2
+.globl _inject_start_x86_64
+_inject_start_x86_64:
+.byte 0x55, 0x48, 0x89, 0xe5, 0x53, 0x50, 0x48, 0x89, 0xfb, 0x48, 0x8d, 0x15, 0x53, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x7d, 0xf4, 0x31, 0xf6, 0x48, 0x89, 0xd9, 0xff, 0x13, 0xeb, 0x15, 0xb8, 0x3e, 0x00, 0x00, 0x01, 0x31, 0xff, 0xbe, 0x01, 0x00, 0x00, 0x00, 0x31, 0xd2, 0xb9, 0x40, 0x1f, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x83, 0x7b, 0x18, 0x00, 0x74, 0xe4, 0x48, 0x81, 0xe3, 0x00, 0xf0, 0xff, 0xff, 0xb8, 0x69, 0x01, 0x00, 0x02, 0xbe, 0x00, 0x20, 0x00, 0x00, 0x31, 0xd2, 0x31, 0xc9, 0x48, 0x89, 0xdf, 0x0f, 0x05, 0xb9, 0xad, 0x0b, 0x00, 0x00, 0x31, 0xc0, 0xff, 0xd1, 0x48, 0x83, 0xc4, 0x08, 0x5b, 0x5d, 0xc3, 0x55, 0x48, 0x89, 0xe5, 0x53, 0x50, 0x48, 0x89, 0xfb, 0x48, 0x8b, 0x7b, 0x10, 0x31, 0xf6, 0xff, 0x53, 0x08, 0x48, 0xc7, 0x43, 0x18, 0x01, 0x00, 0x00, 0x00, 0x31, 0xc0, 0x48, 0x83, 0xc4, 0x08, 0x5b, 0x5d, 0xc3
+.align 2
+.globl _inject_start_i386
+_inject_start_i386:
+.byte 0x55, 0x89, 0xe5, 0x53, 0x57, 0x56, 0x83, 0xec, 0x1c, 0x89, 0xce, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x58, 0x89, 0x74, 0x24, 0x0c, 0x8d, 0x80, 0x90, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x08, 0x8d, 0x45, 0xf0, 0x89, 0x04, 0x24, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0xff, 0x16, 0x8b, 0x46, 0x0c, 0x89, 0xe1, 0x83, 0xf8, 0x00, 0x75, 0x33, 0x31, 0xff, 0xbb, 0x01, 0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0xff, 0x50, 0xb8, 0x40, 0x1f, 0x00, 0x00, 0x50, 0x57, 0x53, 0x57, 0xb8, 0xc2, 0xff, 0xff, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x81, 0xc2, 0x09, 0x00, 0x00, 0x00, 0x0f, 0x34, 0x8b, 0x46, 0x0c, 0x89, 0xe1, 0x83, 0xf8, 0x00, 0x74, 0xd4, 0x81, 0xe6, 0x00, 0xf0, 0xff, 0xff, 0x31, 0xc0, 0x50, 0x50, 0x50, 0xb8, 0x00, 0x20, 0x00, 0x00, 0x50, 0x56, 0xb8, 0x69, 0x01, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x81, 0xc2, 0x09, 0x00, 0x00, 0x00, 0x0f, 0x34, 0xb8, 0xad, 0x0b, 0x00, 0x00, 0xff, 0xd0, 0x83, 0xc4, 0x1c, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x55, 0x89, 0xe5, 0x56, 0x83, 0xec, 0x14, 0x8b, 0x75, 0x08, 0x8b, 0x46, 0x08, 0x89, 0x04, 0x24, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0xff, 0x56, 0x04, 0xc7, 0x46, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x31, 0xc0, 0x83, 0xc4, 0x14, 0x5e, 0x5d, 0xc3
+.align 2
+.globl _inject_start_arm
+_inject_start_arm:
+.byte 0x90, 0x40, 0x2d, 0xe9, 0x04, 0x70, 0x8d, 0xe2, 0x04, 0xd0, 0x4d, 0xe2, 0x00, 0x40, 0xa0, 0xe1, 0x0d, 0x00, 0xa0, 0xe1, 0x00, 0x90, 0x94, 0xe5, 0x60, 0x20, 0x00, 0xe3, 0x00, 0x20, 0x40, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0x02, 0x20, 0x8f, 0xe0, 0x04, 0x30, 0xa0, 0xe1, 0x39, 0xff, 0x2f, 0xe1, 0x0c, 0x00, 0x94, 0xe5, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x1a, 0x3d, 0xc0, 0xe0, 0xe3, 0x01, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x7d, 0x3d, 0xa0, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x80, 0x00, 0x00, 0xef, 0x0c, 0x00, 0x94, 0xe5, 0x00, 0x00, 0x50, 0xe3, 0xfa, 0xff, 0xff, 0x0a, 0x1f, 0x40, 0xcb, 0xe7, 0x69, 0xc1, 0x00, 0xe3, 0x02, 0x1a, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x00, 0xa0, 0xe1, 0x00, 0x30, 0xa0, 0xe3, 0x80, 0x00, 0x00, 0xef, 0xad, 0x0b, 0x00, 0xe3, 0x30, 0xff, 0x2f, 0xe1, 0x04, 0xd0, 0x47, 0xe2, 0x90, 0x80, 0xbd, 0xe8, 0x90, 0x40, 0x2d, 0xe9, 0x00, 0x40, 0xa0, 0xe1, 0x00, 0x10, 0xa0, 0xe3, 0xd4, 0x20, 0xc4, 0xe1, 0x04, 0x70, 0x8d, 0xe2, 0x03, 0x00, 0xa0, 0xe1, 0x32, 0xff, 0x2f, 0xe1, 0x01, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x84, 0xe5, 0x00, 0x00, 0xa0, 0xe3, 0x90, 0x80, 0xbd, 0xe8
+.align 2
+.globl _inject_start_arm64
+_inject_start_arm64:
+.byte 0xf4, 0x4f, 0xbe, 0xa9, 0xfd, 0x7b, 0x01, 0xa9, 0xfd, 0x43, 0x00, 0x91, 0xff, 0x43, 0x00, 0xd1, 0xf3, 0x03, 0x00, 0xaa, 0x68, 0x02, 0x40, 0xf9, 0x01, 0x00, 0x80, 0xd2, 0x62, 0x03, 0x00, 0x10, 0x1f, 0x20, 0x03, 0xd5, 0xe0, 0x33, 0x00, 0x91, 0xe3, 0x03, 0x13, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0x68, 0x0e, 0x40, 0xf9, 0x28, 0x01, 0x00, 0xb5, 0xb0, 0x07, 0x80, 0x92, 0xe1, 0x03, 0x40, 0xb2, 0x03, 0xe8, 0x83, 0xd2, 0x02, 0x00, 0x80, 0xd2, 0x00, 0x00, 0x80, 0xd2, 0x01, 0x10, 0x00, 0xd4, 0x68, 0x0e, 0x40, 0xf9, 0xa8, 0xff, 0xff, 0xb4, 0x60, 0xc6, 0x72, 0x92, 0x30, 0x2d, 0x80, 0xd2, 0xe1, 0x03, 0x73, 0xb2, 0x02, 0x00, 0x80, 0xd2, 0x03, 0x00, 0x80, 0xd2, 0x01, 0x10, 0x00, 0xd4, 0xa8, 0x75, 0x81, 0xd2, 0x00, 0x01, 0x3f, 0xd6, 0xbf, 0x43, 0x00, 0xd1, 0xfd, 0x7b, 0x41, 0xa9, 0xf4, 0x4f, 0xc2, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0xf4, 0x4f, 0xbe, 0xa9, 0xfd, 0x7b, 0x01, 0xa9, 0xfd, 0x43, 0x00, 0x91, 0xf3, 0x03, 0x00, 0xaa, 0x68, 0x82, 0x40, 0xa9, 0x01, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0xe8, 0x03, 0x40, 0xb2, 0x68, 0x0e, 0x00, 0xf9, 0x00, 0x00, 0x80, 0xd2, 0xfd, 0x7b, 0x41, 0xa9, 0xf4, 0x4f, 0xc2, 0xa8, 0xc0, 0x03, 0x5f, 0xd6
diff --git a/lib/darwin/inject.c b/lib/darwin/inject.c
index b314df6..fb35a10 100644
--- a/lib/darwin/inject.c
+++ b/lib/darwin/inject.c
@@ -10,6 +10,10 @@
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 *);
kern_return_t mach_vm_remap(vm_map_t, mach_vm_address_t *, mach_vm_size_t, mach_vm_offset_t, int, vm_map_t, mach_vm_address_t, boolean_t, vm_prot_t *, vm_prot_t *, vm_inherit_t);
+kern_return_t mach_vm_write(vm_map_t, mach_vm_address_t, vm_offset_t, mach_msg_type_number_t);
+kern_return_t mach_vm_allocate(vm_map_t, mach_vm_address_t *, mach_vm_size_t, int);
+kern_return_t mach_vm_deallocate(vm_map_t, mach_vm_address_t, mach_vm_size_t);
+
#define DEFINE_STRUCTS
@@ -47,6 +51,9 @@ struct dyld_all_image_infos_64 {
};
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 task_dyld_info tdi;
mach_msg_type_number_t cnt = TASK_DYLD_INFO_COUNT;
@@ -73,7 +80,7 @@ static int find_foreign_images(mach_port_t task, uint64_t *libdyld_p, uint64_t *
return SUBSTITUTE_ERR_MISC;
}
- bool is64 = tdi.all_image_info_format = TASK_DYLD_ALL_IMAGE_INFO_64;
+ 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;
@@ -95,7 +102,6 @@ static int find_foreign_images(mach_port_t task, uint64_t *libdyld_p, uint64_t *
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)
@@ -105,6 +111,7 @@ static int find_foreign_images(mach_port_t task, uint64_t *libdyld_p, uint64_t *
(mach_vm_address_t) info_array, &size);
if (kr || size != info_array_size) {
asprintf(error, "mach_vm_read_overwrite(info_array): kr=%d", kr);
+ free(info_array);
return SUBSTITUTE_ERR_MISC;
}
@@ -147,30 +154,29 @@ static int find_foreign_images(mach_port_t task, uint64_t *libdyld_p, uint64_t *
if (!strcmp(path_buf, "/usr/lib/system/libdyld.dylib"))
*libdyld_p = load_address;
- else if (!strcmp(path_buf, "/usr/lib/system/libpthread.dylib"))
+ else if (!strcmp(path_buf, "/usr/lib/system/libsystem_pthread.dylib"))
*libpthread_p = load_address;
- if (*libdyld_p && *libpthread_p)
+ if (*libdyld_p && *libpthread_p) {
+ free(info_array);
return SUBSTITUTE_OK;
+ }
info_array_ptr += info_array_elm_size;
}
- asprintf(error, "couldn't find libpthread");
+ free(info_array);
+ asprintf(error, "couldn't find libdyld or libpthread");
return SUBSTITUTE_ERR_MISC;
}
static int get_foreign_image_export(mach_port_t task, uint64_t hdr_addr,
void **linkedit_p, size_t *linkedit_size_p,
void **export_p, size_t *export_size_p,
- char **error) {
- mach_vm_offset_t hdr_buf;
+ cpu_type_t *cputype_p, char **error) {
+ mach_vm_offset_t hdr_buf = 0;
mach_vm_size_t hdr_buf_size;
int ret;
- if (hdr_addr & (PAGE_SIZE - 1)) {
- asprintf(error, "unaligned mach_header");
- return SUBSTITUTE_ERR_MISC;
- }
vm_prot_t cur, max;
hdr_buf_size = PAGE_SIZE;
@@ -189,6 +195,8 @@ static int get_foreign_image_export(mach_port_t task, uint64_t hdr_addr,
goto fail;
}
+ *cputype_p = mh->cputype;
+
size_t mh_size = mh->magic == MH_MAGIC_64 ? sizeof(struct mach_header_64)
: sizeof(struct mach_header);
if (mh->sizeofcmds < mh_size || mh->sizeofcmds > 128*1024)
@@ -350,9 +358,28 @@ got_symbol:;
return true;
}
+struct _x86_thread_state_32 {
+ uint32_t eax, ebx, ecx, edx, edi, esi, ebp, esp;
+ uint32_t ss, eflags, eip, cs, ds, es, fs, gs;
+};
+struct _x86_thread_state_64 {
+ uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, rsp;
+ uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
+ uint64_t rip, rflags, cs, fs, gs;
+};
+struct _arm_thread_state_32 {
+ uint32_t r[13], sp, lr, pc, cpsr;
+};
+struct _arm_thread_state_64 {
+ uint64_t x[29], fp, lr, sp, pc;
+ uint32_t cpsr, pad;
+};
+
+
EXPORT
int substitute_dlopen_in_pid(int pid, const char *filename, int options, char **error) {
mach_port_t task;
+ mach_vm_address_t target_stack = 0;
*error = NULL;
kern_return_t kr = task_for_pid(mach_task_self(), pid, &task);
int ret;
@@ -374,15 +401,16 @@ int substitute_dlopen_in_pid(int pid, const char *filename, int options, char **
{libpthread_addr, "_pthread_create", 0}
};
+ cpu_type_t cputype;
+
for (int i = 0; i < 2; i++) {
void *linkedit, *export;
size_t linkedit_size, export_size;
if ((ret = get_foreign_image_export(task, libs[i].addr,
&linkedit, &linkedit_size,
&export, &export_size,
- error)))
+ &cputype, error)))
goto fail;
- printf("%p\n", (void *) libdyld_addr);
bool fesr = find_export_symbol(export, export_size, libs[i].symname,
libs[i].addr, &libs[i].symaddr);
vm_deallocate(mach_task_self(), (vm_offset_t) linkedit, (vm_size_t) linkedit_size);
@@ -392,14 +420,136 @@ int substitute_dlopen_in_pid(int pid, const char *filename, int options, char **
goto fail;
}
}
- printf("%p\n", (void *) libs[0].symaddr);
- printf("%p\n", (void *) libs[0].symaddr);
- (void) filename;
+ extern char inject_page_start[],
+ inject_start_x86_64[],
+ inject_start_i386[],
+ inject_start_arm[],
+ inject_start_arm64[];
+
+ int target_page_size = cputype == CPU_TYPE_ARM64 ? 0x4000 : 0x1000;
+ kr = mach_vm_allocate(task, &target_stack, 2 * target_page_size, VM_FLAGS_ANYWHERE);
+ if (kr) {
+ asprintf(error, "couldn't allocate target stack");
+ ret = SUBSTITUTE_ERR_OOM;
+ goto fail;
+ }
+
+ mach_vm_address_t target_code_page = target_stack + target_page_size;
+ vm_prot_t cur, max;
+ kr = mach_vm_remap(task, &target_code_page, target_page_size, 0,
+ VM_FLAGS_OVERWRITE, mach_task_self(),
+ (mach_vm_address_t) inject_page_start,
+ /*copy*/ false,
+ &cur, &max, VM_INHERIT_NONE);
+ if (kr) {
+ asprintf(error, "couldn't remap target code");
+ ret = SUBSTITUTE_ERR_VM;
+ 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;
+ goto fail;
+ }
+ strcpy(stackbuf + baton_len, filename);
+
+ uint64_t vals[3] = {libs[1].symaddr, libs[0].symaddr, 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];
+ }
+
+ printf("target_stack=%llx\n", target_stack_top);
+ 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;
+ struct _x86_thread_state_64 x64;
+ struct _arm_thread_state_32 a32;
+ struct _arm_thread_state_64 a64;
+ } u;
+ size_t state_size;
+ thread_state_flavor_t flavor;
+ memset(&u, 0, sizeof(u));
+
+ switch (cputype) {
+ case CPU_TYPE_X86_64:
+ u.x64.rsp = target_stack_top;
+ u.x64.rdi = target_stack_top;
+ u.x64.rip = target_code_page + (inject_start_x86_64 - inject_page_start);
+ state_size = sizeof(u.x64);
+ flavor = 4;
+ break;
+ case CPU_TYPE_I386:
+ u.x32.esp = target_stack_top;
+ u.x32.ecx = target_stack_top;
+ u.x32.eip = target_code_page + (inject_start_i386 - inject_page_start);
+ state_size = sizeof(u.x32);
+ flavor = 1;
+ break;
+ case CPU_TYPE_ARM:
+ u.a32.sp = target_stack_top;
+ u.a32.r[0] = target_stack_top;
+ u.a32.pc = target_code_page + (inject_start_arm - inject_page_start);
+ state_size = sizeof(u.a32);
+ flavor = 9;
+ break;
+ case CPU_TYPE_ARM64:
+ u.a64.sp = target_stack_top;
+ u.a64.x[0] = target_stack_top;
+ u.a64.pc = target_code_page + (inject_start_arm64 - inject_page_start);
+ state_size = sizeof(u.a64);
+ flavor = 6;
+ break;
+ }
+
+ mach_port_t thread;
+ kr = thread_create_running(task, flavor, (thread_state_t) &u,
+ state_size / sizeof(int), &thread);
+ if (kr) {
+ asprintf(error, "thread_create_running: kr=%d", kr);
+ ret = SUBSTITUTE_ERR_MISC;
+ goto fail;
+ }
+
+ target_stack = 0;
+
+ /* it will terminate itself */
+ mach_port_deallocate(mach_task_self(), thread);
+
(void) options;
ret = 0;
fail:
+ if (target_stack)
+ mach_vm_deallocate(task, target_stack, 2 * target_page_size);
mach_port_deallocate(mach_task_self(), task);
return ret;
}
diff --git a/test/test-inject.c b/test/test-inject.c
index 6cf0404..53918db 100644
--- a/test/test-inject.c
+++ b/test/test-inject.c
@@ -6,12 +6,12 @@
#include <stdlib.h>
int main(int argc, char **argv) {
- if (argc <= 1) {
- printf("usage: test-inject <pid>\n");
+ if (argc <= 2) {
+ printf("usage: test-inject <pid> <dylib>n");
return 1;
}
int pid = atoi(argv[1]);
char *error = NULL;
- int ret = substitute_dlopen_in_pid(pid, "/tmp/hello", 0, &error);
+ int ret = substitute_dlopen_in_pid(pid, argv[2], 0, &error);
printf("ret=%d err=%s\n", ret, error);
}