aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-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
8 files changed, 130 insertions, 65 deletions
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. */