aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorcomex2015-01-28 00:46:51 -0500
committercomex2015-01-28 00:46:51 -0500
commit4b14cb631b3f37502d76fe22aa4d6cd582cf00e4 (patch)
treeb5ab9897d5102e6366dc346fe53d28597a28f797 /lib
parentinitial commit of SafetyDance app (diff)
downloadsubstitute-4b14cb631b3f37502d76fe22aa4d6cd582cf00e4.tar.gz
some more stuff compiles
Diffstat (limited to 'lib')
-rw-r--r--lib/darwin/inject.c8
-rw-r--r--lib/darwin/mach-decls.h31
-rw-r--r--lib/darwin/stop-other-threads.c2
-rw-r--r--lib/darwin/thread-state.h24
-rw-r--r--lib/darwin/unrestrict.c163
-rw-r--r--lib/substitute-internal.h4
-rw-r--r--lib/substitute.h6
7 files changed, 202 insertions, 36 deletions
diff --git a/lib/darwin/inject.c b/lib/darwin/inject.c
index a74b057..76ecbcb 100644
--- a/lib/darwin/inject.c
+++ b/lib/darwin/inject.c
@@ -2,7 +2,7 @@
#include "substitute.h"
#include "substitute-internal.h"
#include "darwin/read.h"
-#include "darwin/thread-state.h"
+#include "darwin/mach-decls.h"
#include <mach/mach.h>
#include <mach-o/dyld_images.h>
#include <dlfcn.h>
@@ -13,12 +13,6 @@
#include <stdio.h>
#include <stdbool.h>
-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);
-
extern const struct dyld_all_image_infos *_dyld_get_all_image_infos();
#define DEFINE_STRUCTS
diff --git a/lib/darwin/mach-decls.h b/lib/darwin/mach-decls.h
new file mode 100644
index 0000000..29ea908
--- /dev/null
+++ b/lib/darwin/mach-decls.h
@@ -0,0 +1,31 @@
+#pragma once
+#include <stdint.h>
+
+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;
+};
+#define _x86_thread_state_32_flavor 1
+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;
+};
+#define _x86_thread_state_64_flavor 4
+struct _arm_thread_state_32 {
+ uint32_t r[13], sp, lr, pc, cpsr;
+};
+#define _arm_thread_state_32_flavor 9
+struct _arm_thread_state_64 {
+ uint64_t x[29], fp, lr, sp, pc;
+ uint32_t cpsr, pad;
+};
+#define _arm_thread_state_64_flavor 6
+
+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);
+kern_return_t mach_vm_region(vm_map_t, mach_vm_address_t *, mach_vm_size_t *, vm_region_flavor_t, vm_region_info_t, mach_msg_type_number_t *, mach_port_t *);
+
diff --git a/lib/darwin/stop-other-threads.c b/lib/darwin/stop-other-threads.c
index 1975b47..b35c04c 100644
--- a/lib/darwin/stop-other-threads.c
+++ b/lib/darwin/stop-other-threads.c
@@ -1,6 +1,6 @@
#include "substitute.h"
#include "substitute-internal.h"
-#include "darwin/thread-state.h"
+#include "darwin/mach-decls.h"
#include <pthread.h>
#include <mach/mach.h>
#include <CoreFoundation/CoreFoundation.h>
diff --git a/lib/darwin/thread-state.h b/lib/darwin/thread-state.h
deleted file mode 100644
index 0bae7be..0000000
--- a/lib/darwin/thread-state.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-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;
-};
-#define _x86_thread_state_32_flavor 1
-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;
-};
-#define _x86_thread_state_64_flavor 4
-struct _arm_thread_state_32 {
- uint32_t r[13], sp, lr, pc, cpsr;
-};
-#define _arm_thread_state_32_flavor 9
-struct _arm_thread_state_64 {
- uint64_t x[29], fp, lr, sp, pc;
- uint32_t cpsr, pad;
-};
-#define _arm_thread_state_64_flavor 6
-
diff --git a/lib/darwin/unrestrict.c b/lib/darwin/unrestrict.c
new file mode 100644
index 0000000..4fd90da
--- /dev/null
+++ b/lib/darwin/unrestrict.c
@@ -0,0 +1,163 @@
+#include "substitute.h"
+#include "substitute-internal.h"
+#include "darwin/mach-decls.h"
+#include <unistd.h>
+#include <mach/vm_region.h>
+
+static int unrestrict_macho_header(void *header, size_t size, bool *did_modify_p) {
+ *did_modify_p = false;
+ struct mach_header *mh = header;
+ if (mh->magic != MH_MAGIC && mh->magic != MH_MAGIC_64)
+ return SUBSTITUTE_ERR_MISC;
+ size_t off = mh->magic == MH_MAGIC_64 ? sizeof(struct mach_header_64)
+ : sizeof(struct mach_header);
+ for (uint32_t i = 0; i < mh->ncmds; i++) {
+ if (off > size || size - off < sizeof(struct load_command))
+ break; /* whatever */
+ struct load_command *lc = header + off;
+ if (lc->cmdsize > size - off)
+ break;
+ #define CASES(code...) \
+ if (lc->cmd == LC_SEGMENT) { \
+ typedef struct segment_command segment_command_y; \
+ typedef struct section section_y; \
+ code \
+ } else if (lc->cmd == LC_SEGMENT_64) { \
+ typedef struct segment_command_64 segment_command_y; \
+ typedef struct section_64 section_y; \
+ code \
+ }
+ CASES(
+ segment_command_y *sc = (void *) lc;
+ if (lc->cmdsize < sizeof(*sc) ||
+ sc->nsects > (lc->cmdsize - sizeof(*sc)) / sizeof(struct section))
+ return SUBSTITUTE_ERR_MISC;
+ if (!strncmp(sc->segname, "__RESTRICT", 16)) {
+ section_y *sect = (void *) (sc + 1);
+ for (uint32_t i = 0; i < sc->nsects; i++, sect++) {
+ if (!strncmp(sect->sectname, "__restrict", 16)) {
+ strcpy(sect->sectname, "\xf0\x9f\x92\xa9");
+ *did_modify_p =true;
+ }
+ }
+ }
+ )
+ #undef CASES
+
+ if (off + lc->cmdsize < off)
+ return SUBSTITUTE_ERR_MISC;
+ off += lc->cmdsize;
+ }
+ return SUBSTITUTE_OK;
+}
+
+EXPORT
+int substitute_ios_unrestrict(pid_t pid, bool should_resume) {
+ mach_port_t task;
+ kern_return_t kr = task_for_pid(mach_task_self(), pid, &task);
+ if (kr)
+ return SUBSTITUTE_ERR_TASK_FOR_PID;
+
+ int ret;
+ vm_address_t header_addr = 0;
+
+ int retries = 0;
+ int wait_us = 1;
+setback:
+ while (1) {
+ /* if calling from unrestrict-me, the process might not have transitioned
+ * yet. if it has, then TASK_DYLD_INFO will be filled with 0. */
+ struct task_dyld_info tdi;
+ mach_msg_type_number_t cnt = TASK_DYLD_INFO_COUNT;
+
+ kern_return_t kr = task_info(task, TASK_DYLD_INFO, (void *) &tdi, &cnt);
+ if (kr || cnt != TASK_DYLD_INFO_COUNT) {
+ ret = SUBSTITUTE_ERR_MISC;
+ goto fail;
+ }
+ if (tdi.all_image_info_size == 0)
+ break;
+ if (retries++ == 20) {
+ ret = SUBSTITUTE_ERR_MISC;
+ goto fail;
+ }
+ wait_us *= 2;
+ if (wait_us > 100000)
+ wait_us = 100000;
+ while (usleep(wait_us))
+ ;
+ }
+
+ /* alrighty then, let's look at the damage. find the first readable
+ * segment */
+ mach_vm_address_t segm_addr = 0;
+ mach_vm_size_t segm_size = 0;
+ vm_region_basic_info_data_64_t info;
+ mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
+ mach_port_t object_name;
+ while (1) {
+ kr = mach_vm_region(task, &segm_addr, &segm_size, VM_REGION_BASIC_INFO_64,
+ (vm_region_info_t) &info, &info_count, &object_name);
+ if (kr == KERN_INVALID_ADDRESS) {
+ /* nothing! maybe it's not there *yet*? this actually is possible */
+ goto setback;
+ } else if (kr) {
+ ret = SUBSTITUTE_ERR_VM;
+ goto fail;
+ }
+ if (info.protection)
+ break;
+
+ segm_addr++;
+ }
+
+ size_t toread = 0x4000;
+ if (segm_size < toread)
+ toread = segm_size;
+ if ((kr = vm_allocate(mach_task_self(), &header_addr, toread,
+ VM_FLAGS_ANYWHERE))) {
+ ret = SUBSTITUTE_ERR_MISC;
+ goto fail;
+ }
+ mach_vm_size_t actual = toread;
+ kr = mach_vm_read_overwrite(task, segm_addr, toread, header_addr,
+ &actual);
+ if (kr || actual != toread) {
+ ret = SUBSTITUTE_ERR_MISC;
+ goto fail;
+ }
+
+ bool did_modify;
+ if ((ret = unrestrict_macho_header((void *) header_addr, toread,
+ &did_modify)))
+ goto fail;
+
+ if (did_modify) {
+ if ((kr = vm_protect(mach_task_self(), header_addr, toread,
+ FALSE, info.protection))) {
+ ret = SUBSTITUTE_ERR_VM;
+ goto fail;
+ }
+ vm_prot_t cur, max;
+ if ((kr = mach_vm_remap(task, &segm_addr, toread, 0,
+ VM_FLAGS_OVERWRITE,
+ mach_task_self(), header_addr, FALSE,
+ &cur, &max, info.inheritance))) {
+ ret = SUBSTITUTE_ERR_VM;
+ goto fail;
+ }
+ }
+
+ ret = SUBSTITUTE_OK;
+fail:
+ if (should_resume) {
+ if ((kr = task_resume(task)))
+ ret = SUBSTITUTE_ERR_MISC;
+ }
+ mach_port_deallocate(mach_task_self(), task);
+ if (header_addr)
+ vm_deallocate(mach_task_self(), header_addr, toread);
+ return ret;
+}
+
+
diff --git a/lib/substitute-internal.h b/lib/substitute-internal.h
index 3fd35bc..b378989 100644
--- a/lib/substitute-internal.h
+++ b/lib/substitute-internal.h
@@ -63,7 +63,7 @@ enum {
* can happen are really complicated and dumb, but generally one solution
* is to be root */
SUBSTITUTE_ERR_TASK_FOR_PID = 1000,
-
+ SUBSTITUTE_ERR_TIMEOUT = 1000,
SUBSTITUTE_ERR_MISC,
};
@@ -85,4 +85,6 @@ struct shuttle {
int substitute_dlopen_in_pid(int pid, const char *filename, int options,
const struct shuttle *shuttle, size_t nshuttle,
char **error);
+
+int substitute_ios_unrestrict(pid_t pid, bool should_resume);
#endif
diff --git a/lib/substitute.h b/lib/substitute.h
index bdd63e0..8764bcf 100644
--- a/lib/substitute.h
+++ b/lib/substitute.h
@@ -172,9 +172,9 @@ struct substitute_import_hook {
* This can be used to 'hook' functions or even exported variables. Compared
* to substitute_hook_functions, it has the following advantages:
*
- * - Because it does not require the ability to patch executable code;
- * accordingly, it can (from a technical rather than policy perspective) be
- * used in sandboxed environments like iOS or PaX MPROTECT.
+ * - It does not require the ability to patch executable code; accordingly, it
+ * can (from a technical rather than policy perspective) be used in sandboxed
+ * environments like iOS or PaX MPROTECT.
* - On platforms without RELRO or similar, it is thread safe, as the patches
* are done using atomic instructions.
* - It does not require architecture specific code.