aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcomex2015-02-28 13:16:36 -0500
committercomex2015-02-28 13:16:36 -0500
commitd9a7a8a4d4a23fb65e6319e0e8a435046cc39fea (patch)
treedd219509fc92e695317b82d5dca43296daaf2ab1
parentremove deprecated property usage in safety-dance (diff)
downloadsubstitute-d9a7a8a4d4a23fb65e6319e0e8a435046cc39fea.tar.gz
Rename ios-bootstrap to darwin-bootstrap; cleanup posixspawn-hook and unrestrict.
Not tested yet.
-rw-r--r--Makefile17
-rw-r--r--darwin-bootstrap/bundle-loader.m (renamed from ios-bootstrap/bundle-loader.m)0
-rw-r--r--darwin-bootstrap/ib-log.h (renamed from ios-bootstrap/ib-log.h)0
-rw-r--r--darwin-bootstrap/inject-into-launchd.c (renamed from ios-bootstrap/inject-into-launchd.c)3
-rw-r--r--darwin-bootstrap/posixspawn-hook.c (renamed from ios-bootstrap/posixspawn-hook.c)145
-rw-r--r--darwin-bootstrap/safety-dance/AutoGrid.h (renamed from ios-bootstrap/safety-dance/AutoGrid.h)0
-rw-r--r--darwin-bootstrap/safety-dance/AutoGrid.m (renamed from ios-bootstrap/safety-dance/AutoGrid.m)0
-rw-r--r--darwin-bootstrap/safety-dance/Info.plist (renamed from ios-bootstrap/safety-dance/Info.plist)0
-rw-r--r--darwin-bootstrap/safety-dance/main.m (renamed from ios-bootstrap/safety-dance/main.m)0
-rw-r--r--darwin-bootstrap/safety-dance/white.png (renamed from ios-bootstrap/safety-dance/white.png)bin1047 -> 1047 bytes
-rw-r--r--darwin-bootstrap/unrestrict.c245
-rw-r--r--ios-bootstrap/unrestrict.c115
-rw-r--r--lib/darwin/unrestrict.c145
13 files changed, 352 insertions, 318 deletions
diff --git a/Makefile b/Makefile
index c94e4e7..50c4953 100644
--- a/Makefile
+++ b/Makefile
@@ -70,7 +70,6 @@ LIB_OBJS := \
out/darwin/read.o \
out/darwin/substrate-compat.o \
out/darwin/execmem.o \
- out/darwin/unrestrict.o \
out/jump-dis.o \
out/transform-dis.o \
out/hook-functions.o \
@@ -171,29 +170,29 @@ $(foreach arch,i386 x86_64 armv7 arm64,$(eval $(transform-dis-cases)))
# iOS bootstrap...
ifneq (,$(IS_IOS))
SD_OBJS := out/safety-dance/main.o out/safety-dance/AutoGrid.o
-out/safety-dance/%.o: ios-bootstrap/safety-dance/%.m ios-bootstrap/safety-dance/*.h Makefile
+out/safety-dance/%.o: darwin-bootstrap/safety-dance/%.m darwin-bootstrap/safety-dance/*.h Makefile
@mkdir -p $(dir $@)
$(CC) -c -o $@ $< -fobjc-arc -Wno-unused-parameter
out/safety-dance/SafetyDance.app/SafetyDance: $(SD_OBJS) Makefile
@mkdir -p $(dir $@)
$(CC) -o $@ $(SD_OBJS) $(IOS_APP_LDFLAGS)
ldid -S $@
-out/safety-dance/SafetyDance.app/Info.plist: ios-bootstrap/safety-dance/Info.plist Makefile
+out/safety-dance/SafetyDance.app/Info.plist: darwin-bootstrap/safety-dance/Info.plist Makefile
@mkdir -p $(dir $@)
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
+ cp darwin-bootstrap/safety-dance/white.png out/safety-dance/SafetyDance.app/Default.png
+ cp darwin-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
+out/posixspawn-hook.dylib: darwin-bootstrap/posixspawn-hook.c out/libsubstitute.dylib
$(CC) -dynamiclib -o $@ $< -Lout -lsubstitute
-out/bundle-loader.dylib: ios-bootstrap/bundle-loader.m out/libsubstitute.dylib
+out/bundle-loader.dylib: darwin-bootstrap/bundle-loader.m out/libsubstitute.dylib
$(CC) -dynamiclib -o $@ $< -fobjc-arc -Lout -framework Foundation -framework CoreFoundation
-out/unrestrict: ios-bootstrap/unrestrict.c ios-bootstrap/ib-log.h out/libsubstitute.dylib
+out/unrestrict: darwin-bootstrap/unrestrict.c darwin-bootstrap/ib-log.h out/libsubstitute.dylib
$(CC) -o $@ $< -Lout -lsubstitute
ldid -Sent.plist $@
-out/inject-into-launchd: ios-bootstrap/inject-into-launchd.c ios-bootstrap/ib-log.h out/libsubstitute.dylib
+out/inject-into-launchd: darwin-bootstrap/inject-into-launchd.c darwin-bootstrap/ib-log.h out/libsubstitute.dylib
$(CC) -o $@ $< -Lout -lsubstitute -framework IOKit -framework CoreFoundation
ldid -Sent.plist $@
all: out/posixspawn-hook.dylib out/bundle-loader.dylib out/unrestrict out/inject-into-launchd
diff --git a/ios-bootstrap/bundle-loader.m b/darwin-bootstrap/bundle-loader.m
index fec2513..fec2513 100644
--- a/ios-bootstrap/bundle-loader.m
+++ b/darwin-bootstrap/bundle-loader.m
diff --git a/ios-bootstrap/ib-log.h b/darwin-bootstrap/ib-log.h
index fee70b5..fee70b5 100644
--- a/ios-bootstrap/ib-log.h
+++ b/darwin-bootstrap/ib-log.h
diff --git a/ios-bootstrap/inject-into-launchd.c b/darwin-bootstrap/inject-into-launchd.c
index 4396f14..539b1a2 100644
--- a/ios-bootstrap/inject-into-launchd.c
+++ b/darwin-bootstrap/inject-into-launchd.c
@@ -1,3 +1,6 @@
+/* This is an iOS executable, placed in /etc/rc.d, that injects
+ * posixspawn-hook.dylib into launchd (pid 1). */
+
#define IB_LOG_NAME "iil"
#include "ib-log.h"
#include "substitute.h"
diff --git a/ios-bootstrap/posixspawn-hook.c b/darwin-bootstrap/posixspawn-hook.c
index 80339e1..5acb30b 100644
--- a/ios-bootstrap/posixspawn-hook.c
+++ b/darwin-bootstrap/posixspawn-hook.c
@@ -1,15 +1,29 @@
+/* This library is loaded into launchd, and from there into xpcproxy, which
+ * launchd (always?) uses as an intermediary to exec its processes; its main
+ * purpose is to ensure that bundle-loader.dylib is specified in
+ * DYLD_INSERT_LIBRARIES when launching such processes. In the interests of
+ * not making ssh really weird (and because it's what Substrate does), this is
+ * separate from bundle-loader itself, so any processes that do their own
+ * spawning won't get the environment override.
+ *
+ * It also handles the sandbox override for substituted. */
+
#define IB_LOG_NAME "posixspawn-hook"
#include "ib-log.h"
#include "substitute.h"
#include "substitute-internal.h"
#include <mach/mach.h>
#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
#include <spawn.h>
#include <sys/wait.h>
#include <syslog.h>
#include <malloc/malloc.h>
#include <assert.h>
#include <errno.h>
+#include <arpa/inet.h>
+#include <libkern/OSByteOrder.h>
extern char ***_NSGetEnviron(void);
@@ -47,6 +61,77 @@ static bool spawn_unrestrict(pid_t pid, bool should_resume, bool is_exec) {
return true;
}
+static bool looks_restricted(const char *filename) {
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ ib_log("open '%s': %s", filename, strerror(errno));
+ return false;
+ }
+ uint32_t offset = 0;
+ union {
+ uint32_t magic;
+ struct {
+ struct fat_header fh;
+ struct fat_arch fa1;
+ };
+ struct mach_header mh;
+ } u;
+ if (read(fd, &u, sizeof(u)) != sizeof(u)) {
+ ib_log("read header for '%s': %s", filename, strerror(errno));
+ return false;
+ }
+ if (ntohl(u.magic) == FAT_MAGIC) {
+ /* Fat binary - to avoid needing to replicate grade_binary in the
+ * kernel, we assume all architectures have the same restrict-ness. */
+ if (u.fh.nfat_arch == 0)
+ return false;
+ offset = ntohl(u.fa1.offset);
+ if (pread(fd, &u, sizeof(u), offset) != sizeof(u)) {
+ ib_log("read header (inside fat) for '%s': %s",
+ filename, strerror(errno));
+ return false;
+ }
+ }
+ bool swap, is64;
+ switch (u.magic) {
+ case MH_MAGIC:
+ swap = false;
+ is64 = false;
+ break;
+ case MH_MAGIC_64:
+ swap = false;
+ is64 = true;
+ break;
+ case MH_CIGAM:
+ swap = true;
+ is64 = false;
+ break;
+ case MH_CIGAM_64:
+ swap = true;
+ is64 = true;
+ break;
+ default:
+ ib_log("bad mach-o magic for '%s'", filename);
+ return false;
+ }
+ uint32_t sizeofcmds = u.mh.sizeofcmds;
+ if (swap)
+ sizeofcmds = OSSwapInt32(sizeofcmds);
+ offset += is64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header);
+ char *cmds_buf = malloc(sizeofcmds);
+ ssize_t actual = pread(fd, cmds_buf, sizeofcmds, offset);
+ if (actual < 0 || (uint32_t) actual != sizeofcmds) {
+ ib_log("read load cmds for '%s': %s", filename, strerror(errno));
+ free(cmds_buf);
+ return false;
+ }
+ /* overestimation is fine here */
+ const char sectname[] = "__restrict";
+ bool ret = !!memmem(cmds_buf, sizeofcmds, sectname, sizeof(sectname));
+ free(cmds_buf);
+ return ret;
+}
+
static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
pid_t *restrict pidp, const char *restrict path,
const posix_spawn_file_actions_t *file_actions,
@@ -90,25 +175,10 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
* maximum safety... */
bool safe_mode = false;
- /* If Foundation is loaded into notifyd, the system doesn't boot. I spent
- * some time trying to figure out why, but managed to brick my device
- * instead (no idea how that happened either). I want to solve this before
- * a stable release, but this works for now.
- * n.b. Substrate isn't affected by this because it uses only
- * CoreFoundation, not Foundation. However, CoreFoundation is pretty big
- * itself, and also brings in libobjc, so it's not necessarily that
- * principled to switch. I suppose principled might be to extract the
- * plist code from CF... */
- if (!strcmp(path, "/usr/sbin/notifyd")) {
- /* why? */
- safe_mode = true;
- }
-
-
const char *orig_dyld_insert = "";
- static const char my_dylib_1[] =
+ static const char bl_dylib[] =
"/Library/Substitute/bundle-loader.dylib";
- static const char my_dylib_2[] =
+ static const char psh_dylib[] =
"/Library/Substitute/posixspawn-hook.dylib";
size_t env_count = 0;
for (char *const *ep = my_envp; *ep; ep++) {
@@ -126,7 +196,7 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
}
}
new = malloc(sizeof("DYLD_INSERT_LIBRARIES=") - 1 +
- sizeof(my_dylib_2) /* not - 1, because : */ +
+ sizeof(psh_dylib) /* not - 1, because : */ +
strlen(orig_dyld_insert) + 1);
char *newp_orig = stpcpy(new, "DYLD_INSERT_LIBRARIES=");
char *newp = newp_orig;
@@ -135,10 +205,10 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
const char *next = strchr(p, ':') ?: (p + strlen(p));
/* append if it isn't one of ours */
bool is_substitute =
- (next - p == sizeof(my_dylib_1) - 1 &&
- !memcmp(next, my_dylib_1, sizeof(my_dylib_1) - 1)) ||
- (next - p == sizeof(my_dylib_2) - 1 &&
- !memcmp(next, my_dylib_2, sizeof(my_dylib_2) - 1));
+ (next - p == sizeof(bl_dylib) - 1 &&
+ !memcmp(next, bl_dylib, sizeof(bl_dylib) - 1)) ||
+ (next - p == sizeof(psh_dylib) - 1 &&
+ !memcmp(next, psh_dylib, sizeof(psh_dylib) - 1));
if (!is_substitute) {
if (newp != newp_orig)
*newp++ = ':';
@@ -154,8 +224,8 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
if (newp != newp_orig)
*newp++ = ':';
const char *dylib_to_add = !strcmp(path, "/usr/libexec/xpcproxy")
- ? my_dylib_2
- : my_dylib_1;
+ ? psh_dylib
+ : bl_dylib;
newp = stpcpy(newp, dylib_to_add);
}
if (IB_VERBOSE)
@@ -182,17 +252,10 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
goto skip;
- /* XXX Even async on a separate thread, task_for_pid from launchd hangs in
- * kernel - AMFI permitUnrestrictedDebugging waiting on some mach port.
- * Normally (from other processes) task_for_pid doesn't even ask amfid
- * because we have the right entitlements, but it doesn't usually *hang*.
- * Probably should do what Substrate does and only launch a process for the
- * few actually restricted executables. I was originally hesitant about
- * this because of complications with fat files. Whatever. */
- bool need_unrestrict = getuid() == 0;
-
/* Deal with the dumb __restrict section. A complication is that this
* could actually be an exec. */
+ bool need_unrestrict = looks_restricted(path);
+
/* TODO skip this if Substrate is doing it anyway */
bool was_suspended;
if (need_unrestrict) {
@@ -224,24 +287,8 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
/* Since it returned, obviously it was not SETEXEC, so we need to
* unrestrict it ourself. */
pid_t pid = *pidp;
-#if 0
- dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
- 0);
- dispatch_async(q, ^{
- char *error;
- ib_log("unrestricting %d", pid);
- int sret = substitute_ios_unrestrict(pid, !was_suspended, &error);
- ib_log("unrestricting done");
- if (sret) {
- ib_log("posixspawn-hook: substitute_ios_unrestrict => %d (%s)",
- sret, error);
- }
- free(error);
- });
-#else
if (need_unrestrict)
spawn_unrestrict(pid, !was_suspended, false);
-#endif
goto cleanup;
crap:
ib_log("posixspawn-hook: weird error - OOM? skipping our stuff");
diff --git a/ios-bootstrap/safety-dance/AutoGrid.h b/darwin-bootstrap/safety-dance/AutoGrid.h
index 335381a..335381a 100644
--- a/ios-bootstrap/safety-dance/AutoGrid.h
+++ b/darwin-bootstrap/safety-dance/AutoGrid.h
diff --git a/ios-bootstrap/safety-dance/AutoGrid.m b/darwin-bootstrap/safety-dance/AutoGrid.m
index 2a8d3ed..2a8d3ed 100644
--- a/ios-bootstrap/safety-dance/AutoGrid.m
+++ b/darwin-bootstrap/safety-dance/AutoGrid.m
diff --git a/ios-bootstrap/safety-dance/Info.plist b/darwin-bootstrap/safety-dance/Info.plist
index f2fa520..f2fa520 100644
--- a/ios-bootstrap/safety-dance/Info.plist
+++ b/darwin-bootstrap/safety-dance/Info.plist
diff --git a/ios-bootstrap/safety-dance/main.m b/darwin-bootstrap/safety-dance/main.m
index 0d05e00..0d05e00 100644
--- a/ios-bootstrap/safety-dance/main.m
+++ b/darwin-bootstrap/safety-dance/main.m
diff --git a/ios-bootstrap/safety-dance/white.png b/darwin-bootstrap/safety-dance/white.png
index eab5a56..eab5a56 100644
--- a/ios-bootstrap/safety-dance/white.png
+++ b/darwin-bootstrap/safety-dance/white.png
Binary files differ
diff --git a/darwin-bootstrap/unrestrict.c b/darwin-bootstrap/unrestrict.c
new file mode 100644
index 0000000..c2f13bb
--- /dev/null
+++ b/darwin-bootstrap/unrestrict.c
@@ -0,0 +1,245 @@
+/* This is an iOS executable spawned from posixspawn-hook.dylib, which accesses
+ * a process and changes the name of any __RESTRICT,__restrict sections in the
+ * main executable in memory. Doing so prevents dyld from refusing to honor
+ * DYLD_INSERT_LIBRARIES (which is a pretty dumb protection mechanism in the
+ * first place, even on OS X).
+ *
+ * It exists as a separate executable because (a) such processes may be
+ * launched with POSIX_SPAWN_SETEXEC, which makes posix_spawn act like exec and
+ * replace the current process, and (b) if they're not, launchd (into which
+ * posixspawn-hook is injected) still can't task_for_pid the child process
+ * itself, because it doesn't have the right entitlements.
+*/
+
+#define IB_LOG_NAME "unrestrict"
+#include "ib-log.h"
+#include "darwin/mach-decls.h"
+#include "substitute.h"
+#include "substitute-internal.h"
+#include <stdlib.h>
+#include <syslog.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+
+#define PROC_PIDFDVNODEINFO 1
+#define PROC_PIDFDVNODEINFO_SIZE 176
+int proc_pidfdinfo(int, int, int, void *, int);
+
+static bool unrestrict_macho_header(void *header, size_t size) {
+ struct mach_header *mh = header;
+ if (mh->magic != MH_MAGIC && mh->magic != MH_MAGIC_64) {
+ ib_log("bad mach-o magic");
+ return false;
+ }
+ bool did_modify = false;
+ 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)) {
+ ib_log("bad segment_command");
+ return false;
+ }
+ 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 = true;
+ }
+ }
+ }
+ )
+ #undef CASES
+
+ if (off + lc->cmdsize < off) {
+ ib_log("overflowing lc->cmdsize");
+ return false;
+ }
+ off += lc->cmdsize;
+ }
+ return did_modify;
+}
+
+static void unrestrict(task_t task) {
+ vm_address_t header_addr = 0;
+ kern_return_t kr;
+
+ /* alrighty then, let's look at the damage. find the first readable
+ * segment */
+setback:;
+ 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 */
+ usleep(10);
+ goto setback;
+ } else if (kr) {
+ ib_log("mach_vm_region(%lx): %x", (long) segm_addr, kr);
+ return;
+ }
+ 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))) {
+ ib_log("vm_allocate(%zx): %x", toread, kr);
+ return;
+ }
+ mach_vm_size_t actual = toread;
+ kr = mach_vm_read_overwrite(task, segm_addr, toread, header_addr,
+ &actual);
+ if (kr || actual != toread) {
+ ib_log("mach_vm_read_overwrite: %x", kr);
+ return;
+ }
+
+ bool did_modify = unrestrict_macho_header((void *) header_addr, toread);
+
+ if (did_modify) {
+ if ((kr = vm_protect(mach_task_self(), header_addr, toread,
+ FALSE, info.protection))) {
+ ib_log("vm_protect(%lx=>%d): %x",
+ (long) header_addr, info.protection, kr);
+ return;
+ }
+ 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))) {
+ ib_log("mach_vm_remap(%lx=>%lx size=%zx): %x",
+ (long) header_addr, (long) segm_addr, toread, kr);
+ return;
+ }
+ }
+}
+
+
+
+int main(int argc, char **argv) {
+ if (argc != 4) {
+ ib_log("wrong number of args");
+ return 1;
+ }
+
+ const char *pids = argv[1];
+ char *end;
+ long pid = strtol(pids, &end, 10);
+ if (!pids[0] || *end) {
+ ib_log("pid not an integer");
+ return 1;
+ }
+
+ const char *should_resume = argv[2];
+ if (strcmp(should_resume, "0") && strcmp(should_resume, "1")) {
+ ib_log("should_resume not 0 or 1");
+ return 1;
+ }
+
+ const char *is_exec = argv[3];
+ if (strcmp(is_exec, "0") && strcmp(is_exec, "1")) {
+ ib_log("is_exec not 0 or 1");
+ return 1;
+ }
+
+ /* double fork to avoid zombies */
+ int ret = fork();
+ if (ret == -1) {
+ ib_log("fork: %s", strerror(errno));
+ return 1;
+ } else if (ret) {
+ return 0;
+ }
+
+ if (IB_VERBOSE) {
+ ib_log("unrestricting %ld (sr=%s, ie=%s)", pid,
+ should_resume, is_exec);
+ }
+
+ int ec = 1;
+
+ mach_port_t task;
+ kern_return_t kr = task_for_pid(mach_task_self(), (pid_t) pid, &task);
+ if (kr) {
+ ib_log("TFP fail: %d", kr);
+ goto fail;
+ }
+
+ if (is_exec[0] == '1') {
+ int retries = 0;
+ int wait_us = 1;
+ while (1) {
+ /* The process might not have transitioned yet. We set up a dummy fd
+ * 255 in the parent process which was marked CLOEXEC, so test if that
+ * still exists. AFAICT, Substrate's equivalent to this is not
+ * actually correct.
+ * TODO cleanup
+ */
+ char buf[PROC_PIDFDVNODEINFO_SIZE];
+ /* A bug in proc_pidfdinfo makes it never return -1. Yuck. */
+ errno = 0;
+ proc_pidfdinfo(pid, 255, PROC_PIDFDVNODEINFO, buf, sizeof(buf));
+ if (errno == EBADF) {
+ break;
+ } else if (errno) {
+ ib_log("proc_pidfdinfo: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (retries++ == 20) {
+ ib_log("still in parent process after 20 retries");
+ goto fail;
+ }
+ wait_us *= 2;
+ if (wait_us > 200000)
+ wait_us = 200000;
+ while (usleep(wait_us))
+ ;
+ }
+ }
+
+ unrestrict(task);
+
+fail:
+ if (should_resume[0] == '1') {
+ if ((kill(pid, SIGCONT))) {
+ ib_log("kill SIGCONT: %s", strerror(errno));
+ return 1;
+ }
+ }
+
+ return ec;
+}
diff --git a/ios-bootstrap/unrestrict.c b/ios-bootstrap/unrestrict.c
deleted file mode 100644
index 3c6ff56..0000000
--- a/ios-bootstrap/unrestrict.c
+++ /dev/null
@@ -1,115 +0,0 @@
-#define IB_LOG_NAME "unrestrict"
-#include "ib-log.h"
-#include "substitute.h"
-#include "substitute-internal.h"
-#include <stdlib.h>
-#include <syslog.h>
-#include <signal.h>
-#include <errno.h>
-#include <stdio.h>
-
-#define PROC_PIDFDVNODEINFO 1
-#define PROC_PIDFDVNODEINFO_SIZE 176
-int proc_pidfdinfo(int, int, int, void *, int);
-
-int main(int argc, char **argv) {
- if (argc != 4) {
- ib_log("unrestrict: wrong number of args");
- return 1;
- }
-
- const char *pids = argv[1];
- char *end;
- long pid = strtol(pids, &end, 10);
- if (!pids[0] || *end) {
- ib_log("unrestrict: pid not an integer");
- return 1;
- }
-
- const char *should_resume = argv[2];
- if (strcmp(should_resume, "0") && strcmp(should_resume, "1")) {
- ib_log("unrestrict: should_resume not 0 or 1");
- return 1;
- }
-
- const char *is_exec = argv[3];
- if (strcmp(is_exec, "0") && strcmp(is_exec, "1")) {
- ib_log("unrestrict: is_exec not 0 or 1");
- return 1;
- }
-
- /* double fork to avoid zombies */
- int ret = fork();
- if (ret == -1) {
- ib_log("unrestrict: fork: %s", strerror(errno));
- return 1;
- } else if (ret) {
- return 0;
- }
-
- if (IB_VERBOSE) {
- ib_log("unrestrict: unrestricting %ld (sr=%s, ie=%s)", pid,
- should_resume, is_exec);
- }
-
- int ec = 1;
-
- mach_port_t task;
- kern_return_t kr = task_for_pid(mach_task_self(), (pid_t) pid, &task);
- if (kr) {
- ib_log("unrestrict: TFP fail: %d", kr);
- goto fail;
- }
-
- if (is_exec[0] == '1') {
- int retries = 0;
- int wait_us = 1;
- while (1) {
- /* The process might not have transitioned yet. We set up a dummy fd
- * 255 in the parent process which was marked CLOEXEC, so test if that
- * still exists. AFAICT, Substrate's equivalent to this is not
- * actually correct.
- * TODO cleanup
- */
- char buf[PROC_PIDFDVNODEINFO_SIZE];
- /* A bug in proc_pidfdinfo makes it never return -1. Yuck. */
- errno = 0;
- proc_pidfdinfo(pid, 255, PROC_PIDFDVNODEINFO, buf, sizeof(buf));
- if (errno == EBADF) {
- break;
- } else if (errno) {
- ib_log("proc_pidfdinfo: %s", strerror(errno));
- goto fail;
- }
-
- if (retries++ == 20) {
- ib_log("still in parent process after 20 retries");
- goto fail;
- }
- wait_us *= 2;
- if (wait_us > 200000)
- wait_us = 200000;
- while (usleep(wait_us))
- ;
- }
- }
-
- char *err = NULL;
- int sret = substitute_ios_unrestrict(task, &err);
- if (sret) {
- ib_log("unrestrict: substitute_ios_unrestrict => %d (%s)",
- sret, err);
- ec = 1;
- }
-
- ec = 0;
-fail:
- if (should_resume[0] == '1') {
- if ((kill(pid, SIGCONT))) {
- ib_log("unrestrict: kill SIGCONT: %s", strerror(errno));
- return 1;
- }
- }
-
- return ec;
-}
diff --git a/lib/darwin/unrestrict.c b/lib/darwin/unrestrict.c
deleted file mode 100644
index a0b6ade..0000000
--- a/lib/darwin/unrestrict.c
+++ /dev/null
@@ -1,145 +0,0 @@
-#include "substitute.h"
-#include "substitute-internal.h"
-#include "darwin/mach-decls.h"
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <mach/vm_region.h>
-
-static int unrestrict_macho_header(void *header, size_t size, bool *did_modify_p,
- char **error) {
- *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)) {
- asprintf(error, "bad segment_command");
- 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) {
- asprintf(error, "overflowing lc->cmdsize");
- return SUBSTITUTE_ERR_MISC;
- }
- off += lc->cmdsize;
- }
- return SUBSTITUTE_OK;
-}
-
-EXPORT
-int substitute_ios_unrestrict(task_t task, char **error) {
- *error = NULL;
-
- int ret;
- vm_address_t header_addr = 0;
- kern_return_t kr;
-
- /* alrighty then, let's look at the damage. find the first readable
- * segment */
-setback:;
- 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 */
- usleep(10);
- goto setback;
- } else if (kr) {
- asprintf(error, "mach_vm_region(%lx): %x", (long) segm_addr, 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))) {
- asprintf(error, "vm_allocate(%zx): %x", toread, kr);
- 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) {
- asprintf(error, "mach_vm_read_overwrite: %x", kr);
- ret = SUBSTITUTE_ERR_MISC;
- goto fail;
- }
-
- bool did_modify;
- if ((ret = unrestrict_macho_header((void *) header_addr, toread,
- &did_modify, error)))
- goto fail;
-
- if (did_modify) {
- if ((kr = vm_protect(mach_task_self(), header_addr, toread,
- FALSE, info.protection))) {
- asprintf(error, "vm_protect(%lx=>%d): %x",
- (long) header_addr, info.protection, kr);
- 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))) {
- asprintf(error, "mach_vm_remap(%lx=>%lx * %zx): %x",
- (long) header_addr, (long) segm_addr, toread, kr);
- ret = SUBSTITUTE_ERR_VM;
- goto fail;
- }
- }
-
- ret = SUBSTITUTE_OK;
-fail:
- if (header_addr)
- vm_deallocate(mach_task_self(), header_addr, toread);
- return ret;
-}
-
-