aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--ios-bootstrap/posixspawn-hook.c241
-rw-r--r--ios-bootstrap/safety-dance/Info.plistbin1478 -> 2551 bytes
-rw-r--r--ios-bootstrap/unrestrict-me.c32
-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
11 files changed, 484 insertions, 37 deletions
diff --git a/Makefile b/Makefile
index 5c959d8..4499d15 100644
--- a/Makefile
+++ b/Makefile
@@ -71,6 +71,7 @@ LIB_OBJS := \
out/darwin/substrate-compat.o \
out/darwin/stop-other-threads.o \
out/darwin/execmem.o \
+ out/darwin/unrestrict.o \
out/jump-dis.o \
out/transform-dis.o \
out/hook-functions.o \
@@ -161,10 +162,17 @@ out/safety-dance/SafetyDance.app/SafetyDance: $(SD_OBJS) Makefile
ldid -S $@
out/safety-dance/SafetyDance.app/Info.plist: ios-bootstrap/safety-dance/Info.plist Makefile
@mkdir -p $(dir $@)
- plutil -convert binary1 -o $< $@
+ plutil -convert binary1 -o $@ $<
cp ios-bootstrap/safety-dance/white.png out/safety-dance/SafetyDance.app/Default.png
cp ios-bootstrap/safety-dance/white.png out/safety-dance/SafetyDance.app/Default@2x.png
safety-dance: out/safety-dance/SafetyDance.app/SafetyDance out/safety-dance/SafetyDance.app/Info.plist
+all: safety-dance
+
+out/posixspawn-hook.dylib: ios-bootstrap/posixspawn-hook.c out/libsubstitute.dylib
+ $(CC) -dynamiclib -o $@ $< -Lout -lsubstitute
+out/unrestrict-me: ios-bootstrap/unrestrict-me.c out/libsubstitute.dylib
+ $(CC) -o $@ $< -Lout -lsubstitute
+all: out/posixspawn-hook.dylib out/unrestrict-me
endif
diff --git a/ios-bootstrap/posixspawn-hook.c b/ios-bootstrap/posixspawn-hook.c
new file mode 100644
index 0000000..1f1bae1
--- /dev/null
+++ b/ios-bootstrap/posixspawn-hook.c
@@ -0,0 +1,241 @@
+#include "substitute.h"
+#include "substitute-internal.h"
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <spawn.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <malloc/malloc.h>
+
+extern char ***_NSGetEnviron(void);
+
+static __typeof__(posix_spawn) *old_posix_spawn, *old_posix_spawnp,
+ hook_posix_spawn, hook_posix_spawnp;
+static __typeof__(wait4) *old_wait4, hook_wait4;
+static __typeof__(waitpid) *old_waitpid, hook_waitpid;
+
+static bool advance(char **strp, const char *template) {
+ size_t len = strlen(template);
+ if (!strncmp(*strp, template, len)) {
+ *strp += len;
+ return true;
+ }
+ return false;
+}
+
+static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
+ pid_t *restrict pid, const char *restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *restrict attrp,
+ char *const argv[restrict],
+ char *const envp[restrict]) {
+ char *new = NULL;
+ char **new_envp = NULL;
+ char *const *envp_to_use = envp;
+ char *const *my_envp = envp ? envp : *_NSGetEnviron();
+ posix_spawnattr_t my_attr = NULL;
+ /* This mirrors Substrate's logic with safe mode. I don't really
+ * understand the point of the difference between its 'safe' (which removes
+ * Substrate from DYLD_INSERT_LIBRARIES) and 'quit' (which just skips
+ * directly to the original spawn), but I guess I'll just do the same for
+ * maximum safety... */
+ bool safe_mode = false;
+ const char *orig_dyld_insert = "";
+ static const char my_dylib[] =
+ "/Library/Substitute/generic-dyld-inserted.dylib";
+ size_t env_count = 0;
+ for (char *const *ep = my_envp; *ep; ep++) {
+ env_count++;
+ char *env = *ep;
+ if (advance(&env, "_MSSafeMode=") || advance(&env, "_SubstituteSafeMode=")) {
+ if (!strcmp(env, "0") || !strcmp(env, "NO"))
+ continue;
+ else if (!strcmp(env, "1") || !strcmp(env, "YES"))
+ safe_mode = true;
+ else
+ goto skip;
+ } else if (advance(&env, "DYLD_INSERT_LIBRARIES=")) {
+ orig_dyld_insert = env;
+ }
+ }
+ new = malloc(sizeof("DYLD_INSERT_LIBRARIES=") - 1 +
+ sizeof(my_dylib) - 1 +
+ strlen(orig_dyld_insert) + 1);
+ char *newp_orig = stpcpy(new, "DYLD_INSERT_LIBRARIES=");
+ char *newp = newp_orig;
+ const char *p = orig_dyld_insert;
+ while (*p) { /* W.N.H. */
+ const char *next = strchr(p, ':') ?: (p + strlen(p));
+ /* append if it isn't a copy of ours */
+ if (!(next - p == sizeof(my_dylib) - 1 &&
+ memcmp(next, my_dylib, sizeof(my_dylib) - 1))) {
+ if (newp != newp_orig)
+ *newp++ = ':';
+ memcpy(newp, p, next - p);
+ newp += next - p;
+ }
+ }
+ /* append ours if necessary */
+ if (!safe_mode) {
+ if (newp != newp_orig)
+ *newp++ = ':';
+ newp = stpcpy(newp, my_dylib);
+ }
+ /* no libraries? then just get rid of it */
+ if (newp == newp_orig) {
+ free(new);
+ new = NULL;
+ }
+ new_envp = malloc(sizeof(char *) * (env_count + 2));
+ envp_to_use = new_envp;
+ char **outp = new_envp;
+ for (size_t idx = 0; idx < env_count; idx++) {
+ char *env = envp[idx];
+ /* remove *all* D_I_L, including duplicates */
+ if (!advance(&env, "DYLD_INSERT_LIBRARIES="))
+ *outp++ = env;
+ }
+ if (new)
+ *outp++ = new;
+ *outp++ = NULL;
+
+ if (safe_mode)
+ goto skip;
+
+ /* Deal with the dumb __restrict section. A complication is that this
+ * could actually be an exec. */
+ if (attrp) {
+ posix_spawnattr_t attr = *attrp;
+ size_t size = malloc_size(attr);
+ my_attr = malloc(size);
+ if (!my_attr)
+ goto crap;
+ memcpy(my_attr, attr, size);
+ } else {
+ if (posix_spawnattr_init(&my_attr))
+ goto crap;
+ }
+ short flags;
+ if (posix_spawnattr_getflags(&my_attr, &flags))
+ goto crap;
+ bool was_suspended = flags & POSIX_SPAWN_START_SUSPENDED;
+ flags |= POSIX_SPAWN_START_SUSPENDED;
+ if (posix_spawnattr_setflags(&my_attr, flags))
+ goto crap;
+ if (flags & POSIX_SPAWN_SETEXEC) {
+ const char *prog = "/Library/Substitute/unrestrict-me";
+ char pid[32];
+ sprintf(pid, "%ld", (long) getpid());
+ const char *should_resume = was_suspended ? "0" : "1";
+ const char *argv[] = {prog, pid, should_resume, NULL};
+ pid_t prog_pid;
+ if (old_posix_spawn(&prog_pid, prog, NULL, NULL, (char **) argv, envp)) {
+ syslog(LOG_EMERG,
+ "posixspawn-hook: couldn't start unrestrict-me - oh well...");
+ goto skip;
+ }
+ }
+
+ int ret = old(pid, path, file_actions, &my_attr, argv, envp_to_use);
+ if (ret)
+ goto cleanup;
+ /* Since it returned, obviously it was not SETEXEC, so we need to
+ * unrestrict ourself. */
+ int sret = substitute_ios_unrestrict(*pid, !was_suspended);
+ if (sret) {
+ syslog(LOG_EMERG, "posixspawn-hook: substitute_ios_unrestrict => %d", sret);
+ }
+ goto cleanup;
+crap:
+ syslog(LOG_EMERG, "posixspawn-hook: weird error - OOM? skipping our stuff");
+skip:
+ ret = old(pid, path, file_actions, attrp, argv, envp);
+cleanup:
+ free(new_envp);
+ free(new);
+ free(my_attr);
+ return ret;
+}
+
+static void after_wait_generic(pid_t pid, int stat) {
+ (void) pid;
+ (void) stat;
+
+}
+
+int hook_posix_spawn(pid_t *restrict pid, const char *restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *restrict attrp,
+ char *const argv[restrict], char *const envp[restrict]) {
+ return hook_posix_spawn_generic(old_posix_spawn, pid, path, file_actions,
+ attrp, argv, envp);
+}
+
+int hook_posix_spawnp(pid_t *restrict pid, const char *restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *restrict attrp,
+ char *const argv[restrict], char *const envp[restrict]) {
+ return hook_posix_spawn_generic(old_posix_spawnp, pid, path, file_actions,
+ attrp, argv, envp);
+}
+
+pid_t hook_wait4(pid_t pid, int *stat_loc, int options, struct rusage *rusage) {
+ pid_t ret = old_wait4(pid, stat_loc, options, rusage);
+ after_wait_generic(ret, *stat_loc);
+ return ret;
+}
+
+pid_t hook_waitpid(pid_t pid, int *stat_loc, int options) {
+ pid_t ret = old_waitpid(pid, stat_loc, options);
+ after_wait_generic(ret, *stat_loc);
+ return ret;
+}
+
+void substitute_init(struct shuttle *shuttle, UNUSED size_t nshuttle) {
+ /* Note: I'm using interposing to minimize the chance of conflict with
+ * Substrate. This shouldn't actually be necessary, because MSHookProcess,
+ * at least as of the old version I'm looking at the source code of, blocks
+ * until the thread it remotely creates exits, and that thread does
+ * pthread_join on the 'real' pthread it creates to do the dlopen (unlike
+ * the equivalent in Substitute - the difference is to decrease dependence
+ * on pthread internals). substitute_dlopen_in_pid does not, but that's
+ * what the notify port is for. Meanwhile, the jailbreak I have installed
+ * properly runs rc.d sequentially, so the injection tools won't do their
+ * thing at the same time. But just in case any of that doesn't hold up...
+ */
+
+ struct substitute_image *im = substitute_open_image(_dyld_get_image_name(0));
+ if (!im) {
+ syslog(LOG_EMERG, "posixspawn-hook: substitute_open_image failed");
+ goto end;
+ }
+
+ static const struct substitute_import_hook hooks[] = {
+ {"_posix_spawn", hook_posix_spawn, &old_posix_spawn},
+ {"_posix_spawnp", hook_posix_spawnp, &old_posix_spawnp},
+ {"_waitpid", hook_waitpid, &old_waitpid},
+ {"_wait4", hook_wait4, &old_wait4},
+ };
+
+ int err = substitute_interpose_imports(im, hooks, sizeof(hooks)/sizeof(*hooks), 0);
+ if (err) {
+ syslog(LOG_EMERG, "posixspawn-hook: substitute_interpose_imports failed: %s",
+ substitute_strerror(err));
+ goto end;
+ }
+
+end:
+ if (im)
+ substitute_close_image(im);
+
+ mach_port_t notify_port = shuttle[0].u.mach.port;
+ mach_msg_header_t done_hdr;
+ done_hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, 0);
+ done_hdr.msgh_size = sizeof(done_hdr);
+ done_hdr.msgh_remote_port = notify_port;
+ done_hdr.msgh_local_port = 0;
+ done_hdr.msgh_voucher_port = 0;
+ done_hdr.msgh_id = 42;
+ if (mach_msg_send(&done_hdr)) /* MOVE deallocates port */
+ syslog(LOG_EMERG, "posixspawn-hook: mach_msg_send failed");
+}
diff --git a/ios-bootstrap/safety-dance/Info.plist b/ios-bootstrap/safety-dance/Info.plist
index fd6a1b3..f2fa520 100644
--- a/ios-bootstrap/safety-dance/Info.plist
+++ b/ios-bootstrap/safety-dance/Info.plist
Binary files differ
diff --git a/ios-bootstrap/unrestrict-me.c b/ios-bootstrap/unrestrict-me.c
new file mode 100644
index 0000000..718e9f8
--- /dev/null
+++ b/ios-bootstrap/unrestrict-me.c
@@ -0,0 +1,32 @@
+#include "substitute.h"
+#include "substitute-internal.h"
+#include <stdlib.h>
+#include <syslog.h>
+
+int main(int argc, char **argv) {
+ if (argc != 3) {
+ syslog(LOG_EMERG, "unrestrict-me: wrong number of args");
+ return 1;
+ }
+ const char *pids = argv[1];
+ char *end;
+ long pid = strtol(pids, &end, 10);
+ if (!pids[0] || *end) {
+ syslog(LOG_EMERG, "unrestrict-me: pid not an integer");
+ return 1;
+ }
+
+ const char *should_resume = argv[2];
+ if (strcmp(should_resume, "0") && strcmp(should_resume, "1")) {
+ syslog(LOG_EMERG, "unrestrict-me: should_resume not 0 or 1");
+ return 1;
+ }
+
+ int sret = substitute_ios_unrestrict((pid_t) pid, should_resume[0] == '1');
+ if (sret) {
+ syslog(LOG_EMERG, "unrestrict-me: substitute_ios_unrestrict => %d", sret);
+ return 1;
+ }
+
+ return 0;
+}
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.