aboutsummaryrefslogtreecommitdiff
path: root/lib/darwin/execmem.c
diff options
context:
space:
mode:
authorcomex2015-01-24 20:04:20 -0500
committercomex2015-01-24 20:06:46 -0500
commitd89af8f6d5f3b971dda663081c03d790437c2f03 (patch)
tree2c17f0247ff3eef7d4338cadb069b1334ba785c6 /lib/darwin/execmem.c
parentCurrent version of generic-dis-*, for posterity (I'm sure it'll need to be ch... (diff)
downloadsubstitute-d89af8f6d5f3b971dda663081c03d790437c2f03.tar.gz
Add function to deal with mprotecting RW and back. A bit more complex than the minimum would be...
(and minor build fixes)
Diffstat (limited to 'lib/darwin/execmem.c')
-rw-r--r--lib/darwin/execmem.c57
1 files changed, 57 insertions, 0 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;
+}