aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--generated/darwin-inject-asm.S15
-rw-r--r--lib/darwin/execmem.c57
-rw-r--r--lib/darwin/inject-asm-raw.c62
-rw-r--r--lib/darwin/manual-syscall.h54
-rw-r--r--lib/execmem.h3
-rw-r--r--lib/hook-functions.c2
-rw-r--r--lib/stop-other-threads.h9
-rw-r--r--lib/substitute-internal.h7
-rw-r--r--lib/substitute.h1
-rwxr-xr-xscript/gen-inject-asm.sh4
-rw-r--r--test/test-execmem.c37
-rw-r--r--test/test-stop-threads.c1
13 files changed, 187 insertions, 72 deletions
diff --git a/Makefile b/Makefile
index beeae99..c66bce7 100644
--- a/Makefile
+++ b/Makefile
@@ -10,12 +10,11 @@ IMAON2 := /Users/comex/c/imaon2
GEN_JS := node --harmony --harmony_arrow_functions $(IMAON2)/tables/gen.js
all: \
- generateds \
out/libsubstitute.dylib
$(shell mkdir -p out generated)
-HEADERS := lib/*.h
+HEADERS := lib/*.h lib/darwin/*.h
GENERATED := generated/generic-dis-arm.inc.h generated/generic-dis-thumb.inc.h generated/generic-dis-thumb2.inc.h generated/generic-dis-arm64.inc.h
define do_prefix
generated/generic-dis-$(1).inc.h:
@@ -47,6 +46,7 @@ LIB_OBJS := \
out/darwin/read.o \
out/darwin/substrate-compat.o \
out/darwin/stop-other-threads.o \
+ out/darwin/execmem.o \
out/darwin-inject-asm.o \
out/jump-dis.o \
out/transform-dis.o \
@@ -61,7 +61,7 @@ out/libsubstitute.dylib: $(LIB_OBJS)
# architectures or something - meh
# 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
+IACLANG := clang -Os -dynamiclib -nostartfiles -nodefaultlibs -isysroot /dev/null -Ilib -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
@@ -101,6 +101,7 @@ $(eval $(call define_test,objc-hook,objc-hook,$(CC) -std=c11 -framework Foundati
$(eval $(call define_test,interpose,interpose,$(CC) -std=c11 -lsubstitute))
$(eval $(call define_test,inject,inject,$(CC) -std=c11 -lsubstitute out/darwin/inject.o out/darwin/read.o))
$(eval $(call define_test,stop-threads,stop-threads,$(CC) -std=c11 out/darwin/stop-other-threads.o -framework CoreFoundation))
+$(eval $(call define_test,execmem,execmem,$(CC) -std=c11 out/darwin/execmem.o -segprot __TEST rwx rx))
out/insns-arm.o: test/insns-arm.S Makefile
clang -arch armv7 -c -o $@ $<
diff --git a/generated/darwin-inject-asm.S b/generated/darwin-inject-asm.S
index 1d1d1b8..abcb721 100644
--- a/generated/darwin-inject-asm.S
+++ b/generated/darwin-inject-asm.S
@@ -1,4 +1,15 @@
-
+/* Generated by script/gen-inject-asm.sh. The relevant source is in-tree (make
+ * out/darwin-inject-asm.S), but this file has been checked in too, in case
+ * your C compiler doesn't support all of these architectures.
+ * This file contains code for 4 architectures in one text page; it's remapped
+ * into the target process and the appropriate thunk executed. Having ARM code
+ * here on x86 and whatnot is currently pointless (and use of that code is
+ * disabled in case any future Rosetta-like emulator breaks naive attempts to
+ * inject into foreign-architecture processes), but we need two architectures
+ * anyway, so the rest are included in case doing so is useful someday. */
+.align 12
+.globl _inject_page_start
+_inject_page_start:
.align 2
.globl _inject_start_x86_64
_inject_start_x86_64:
@@ -6,7 +17,7 @@ _inject_start_x86_64:
.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
+.byte 0x55, 0x89, 0xe5, 0x56, 0x83, 0xec, 0x24, 0x89, 0xce, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x58, 0x89, 0x74, 0x24, 0x0c, 0x8d, 0x80, 0x80, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x08, 0x8d, 0x45, 0xf8, 0x89, 0x04, 0x24, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0xff, 0x16, 0xeb, 0x21, 0xb8, 0xc2, 0xff, 0xff, 0xff, 0x89, 0xe1, 0x68, 0x40, 0x1f, 0x00, 0x00, 0x6a, 0x00, 0x6a, 0x01, 0x6a, 0x00, 0x50, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x81, 0xc2, 0x09, 0x00, 0x00, 0x00, 0x0f, 0x34, 0x83, 0x7e, 0x0c, 0x00, 0x74, 0xd9, 0x81, 0xe6, 0x00, 0xf0, 0xff, 0xff, 0x89, 0x75, 0xf4, 0xb8, 0x69, 0x01, 0x00, 0x00, 0x89, 0xe1, 0x6a, 0x00, 0x6a, 0x00, 0x68, 0x00, 0x20, 0x00, 0x00, 0xff, 0x75, 0xf4, 0x50, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x81, 0xc2, 0x09, 0x00, 0x00, 0x00, 0x0f, 0x34, 0xb8, 0xad, 0x0b, 0x00, 0x00, 0xff, 0xd0, 0x83, 0xc4, 0x24, 0x5e, 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:
diff --git a/lib/darwin/execmem.c b/lib/darwin/execmem.c
new file mode 100644
index 0000000..c707db5
--- /dev/null
+++ b/lib/darwin/execmem.c
@@ -0,0 +1,57 @@
+#include "execmem.h"
+#include "darwin/manual-syscall.h"
+#include "substitute.h"
+#include <mach/mach.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <errno.h>
+#include <stdio.h>
+
+int execmem_write(void *dest, const void *src, size_t len) {
+ /* Use vm_region to determine the original protection, so we can mprotect
+ * it back afterwards. (Note: PROT_* are equal to VM_PROT_*.) */
+ vm_address_t region = (vm_address_t) dest;
+ vm_size_t region_len = 0;
+ struct vm_region_submap_short_info_64 info;
+ mach_msg_type_number_t info_count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
+ natural_t max_depth = 99999;
+ kern_return_t kr = vm_region_recurse_64(mach_task_self(), &region, &region_len,
+ &max_depth,
+ (vm_region_recurse_info_t) &info,
+ &info_count);
+ if (kr) {
+ /* Weird; this probably means the region doesn't exist, but we should
+ * have already read from the memory in order to generate the patch. */
+ errno = 0;
+ return SUBSTITUTE_ERR_VM;
+ }
+
+ uintptr_t lopage = (uintptr_t) dest & ~PAGE_MASK;
+ uintptr_t hipage = ((uintptr_t) dest + len + PAGE_MASK) & ~PAGE_MASK;
+
+ /* We do the syscall manually just in case the user is trying to write to
+ * the mprotect syscall stub itself, or one of the functions it calls.
+ * (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);
+ if (ret) {
+ errno = ret;
+ return SUBSTITUTE_ERR_VM;
+ }
+
+ /* volatile to avoid compiler transformation to call to memcpy */
+ volatile uint8_t *d8 = dest;
+ const uint8_t *s8 = src;
+ while (len--)
+ *d8++ = *s8++;
+
+ int oldprot = info.protection & (PROT_READ | PROT_WRITE | PROT_EXEC);
+ ret = manual_syscall(SYS_mprotect, lopage, hipage - lopage,
+ oldprot, 0, 0);
+ if (ret) {
+ errno = ret;
+ return SUBSTITUTE_ERR_VM;
+ }
+ return SUBSTITUTE_OK;
+}
diff --git a/lib/darwin/inject-asm-raw.c b/lib/darwin/inject-asm-raw.c
index 0519dc6..3b0f1ce 100644
--- a/lib/darwin/inject-asm-raw.c
+++ b/lib/darwin/inject-asm-raw.c
@@ -1,65 +1,11 @@
+#include "darwin/manual-syscall.h"
+
#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 e) {
- 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"
- : "=r"(out)
- : "r"(e), "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 e) {
- 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}"
- : "=r"(out)
- : "r"(e), "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 e) {
- 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"
- : "=r"(out)
- : "r"(e), "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);
@@ -81,8 +27,8 @@ void entry(struct baton *baton) {
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);
+ manual_syscall(-62 /*clock_sleep_trap */, 0, 1, 0, 8000 /*ns*/, -1);
+ manual_syscall(361 /*bsdthread_terminate*/, ptr, 0x2000, 0, 0, 0);
((void (*)()) 0xbad)();
}
static void *bsd_thread_func(void *arg) {
diff --git a/lib/darwin/manual-syscall.h b/lib/darwin/manual-syscall.h
new file mode 100644
index 0000000..4d847ac
--- /dev/null
+++ b/lib/darwin/manual-syscall.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#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 manual_syscall(long s, long a, long b, long c, long d, long e) {
+ 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"
+ : "=r"(out)
+ : "r"(e), "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) {
+ REG(s, eax);
+ OREG(out, eax);
+ __asm__ volatile("push %6; 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"
+ : "=r"(out)
+ : "r"(_s), "g"(a), "g"(b), "g"(c), "g"(d), "g"(e)
+ : "edx", "ecx");
+ return out;
+}
+#elif defined(__arm__)
+static int manual_syscall(long s, long a, long b, long c, long d, long e) {
+ 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}"
+ : "=r"(out)
+ : "r"(e), "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) {
+ 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"
+ : "=r"(out)
+ : "r"(e), "r"(_s), "r"(_a), "r"(_b), "r"(_c), "r"(_d));
+ return out;
+}
+#else
+#error ?
+#endif
diff --git a/lib/execmem.h b/lib/execmem.h
new file mode 100644
index 0000000..f46b7d6
--- /dev/null
+++ b/lib/execmem.h
@@ -0,0 +1,3 @@
+#pragma once
+#include <stdlib.h>
+int execmem_write(void *dest, const void *src, size_t len);
diff --git a/lib/hook-functions.c b/lib/hook-functions.c
index 975520e..e20f069 100644
--- a/lib/hook-functions.c
+++ b/lib/hook-functions.c
@@ -1,5 +1,7 @@
#include "substitute.h"
#include "substitute-internal.h"
+#include "execmem.h"
+#include "stop-other-threads.h"
static uintptr_t patch_callback(UNUSED void *ctx, uintptr_t pc) {
printf("patch_callback: pc=%llx\n", (long long) pc);
diff --git a/lib/stop-other-threads.h b/lib/stop-other-threads.h
new file mode 100644
index 0000000..1f6e639
--- /dev/null
+++ b/lib/stop-other-threads.h
@@ -0,0 +1,9 @@
+#pragma once
+#include <stdint.h>
+
+/* Stop the world; return token to be used for applying PC patches and resuming. */
+int stop_other_threads(void **token_ptr);
+int apply_pc_patch_callback(void *token,
+ uintptr_t (*pc_patch_callback)(void *ctx, uintptr_t pc),
+ void *ctx);
+int resume_other_threads(void *token);
diff --git a/lib/substitute-internal.h b/lib/substitute-internal.h
index 80052f9..0bfff95 100644
--- a/lib/substitute-internal.h
+++ b/lib/substitute-internal.h
@@ -69,10 +69,3 @@ enum {
int substitute_dlopen_in_pid(int pid, const char *filename, int options, char **error);
#endif
-
-/* Stop the world; return token to be used for applying PC patches and resuming. */
-int stop_other_threads(void **token_ptr);
-int apply_pc_patch_callback(void *token,
- uintptr_t (*pc_patch_callback)(void *ctx, uintptr_t pc),
- void *ctx);
-int resume_other_threads(void *token);
diff --git a/lib/substitute.h b/lib/substitute.h
index ada57f2..95f8436 100644
--- a/lib/substitute.h
+++ b/lib/substitute.h
@@ -39,6 +39,7 @@ enum {
/* substitute_hook_functions: mmap or mprotect failure other than ENOMEM
* (preserved in errno on return)
+ * substitute_hook_functions: vm_region failure (errno = 0)
* substitute_hook_objc_message: vm_remap failure
* Most likely to come up with substitute_hook_functions if the kernel is
* preventing pages from being marked executable. */
diff --git a/script/gen-inject-asm.sh b/script/gen-inject-asm.sh
index e2b97ec..eacbbad 100755
--- a/script/gen-inject-asm.sh
+++ b/script/gen-inject-asm.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-echo <<END
+cat <<END
/* Generated by script/gen-inject-asm.sh. The relevant source is in-tree (make
* out/darwin-inject-asm.S), but this file has been checked in too, in case
* your C compiler doesn't support all of these architectures.
@@ -9,7 +9,7 @@ echo <<END
* disabled in case any future Rosetta-like emulator breaks naive attempts to
* inject into foreign-architecture processes), but we need two architectures
* anyway, so the rest are included in case doing so is useful someday. */
- .align 12
+.align 12
.globl _inject_page_start
_inject_page_start:
END
diff --git a/test/test-execmem.c b/test/test-execmem.c
new file mode 100644
index 0000000..5ec64e9
--- /dev/null
+++ b/test/test-execmem.c
@@ -0,0 +1,37 @@
+#include "substitute-internal.h"
+#include "execmem.h"
+#include <stdio.h>
+#include <search.h> /* for the victim */
+#include <errno.h>
+#define NOP_SLED \
+ __asm__ volatile("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;" \
+ "nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;" \
+ "nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;")
+#define OTHER_SIZE 32 /* guess */
+
+int other(size_t a) {
+ if (__builtin_expect(!a, 1))
+ return 6;
+ NOP_SLED;
+ return 0;
+}
+
+__attribute__((section("__TEST,__test"), noinline))
+int test(size_t a) {
+ NOP_SLED;
+ if (!a)
+ return 5;
+ else
+ return 1000;
+}
+
+int main() {
+ printf("this should be 5: %d\n", test(0));
+ printf("=> %d\n", execmem_write(test, other, OTHER_SIZE));
+ printf(" %s\n", strerror(errno));
+ printf("this should be 6: %d\n", test(0));
+ printf("=> %d\n", execmem_write(hcreate, other, OTHER_SIZE));
+ printf(" %s\n", strerror(errno));
+ printf("modified shared cache func: %d\n", hcreate(0));
+
+}
diff --git a/test/test-stop-threads.c b/test/test-stop-threads.c
index be0787d..829e73e 100644
--- a/test/test-stop-threads.c
+++ b/test/test-stop-threads.c
@@ -1,4 +1,5 @@
#include "substitute-internal.h"
+#include "stop-other-threads.h"
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>