diff options
author | comex | 2015-02-28 13:16:36 -0500 |
---|---|---|
committer | comex | 2015-02-28 13:16:36 -0500 |
commit | d9a7a8a4d4a23fb65e6319e0e8a435046cc39fea (patch) | |
tree | dd219509fc92e695317b82d5dca43296daaf2ab1 | |
parent | remove deprecated property usage in safety-dance (diff) | |
download | substitute-d9a7a8a4d4a23fb65e6319e0e8a435046cc39fea.tar.gz |
Rename ios-bootstrap to darwin-bootstrap; cleanup posixspawn-hook and unrestrict.
Not tested yet.
-rw-r--r-- | Makefile | 17 | ||||
-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) | bin | 1047 -> 1047 bytes | |||
-rw-r--r-- | darwin-bootstrap/unrestrict.c | 245 | ||||
-rw-r--r-- | ios-bootstrap/unrestrict.c | 115 | ||||
-rw-r--r-- | lib/darwin/unrestrict.c | 145 |
13 files changed, 352 insertions, 318 deletions
@@ -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 Binary files differindex eab5a56..eab5a56 100644 --- a/ios-bootstrap/safety-dance/white.png +++ b/darwin-bootstrap/safety-dance/white.png 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; -} - - |