├── .gitignore ├── .gitmodules ├── Makefile ├── common.h ├── helpers ├── kexecute.c ├── kexecute.h ├── kmem.c ├── kmem.h ├── offsetof.c ├── offsetof.h ├── osobject.c └── osobject.h ├── kern_utils.c ├── kern_utils.h ├── main.c ├── sandbox.c ├── sandbox.h └── unrestrict.c /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | dsym 3 | *.o 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "offset-cache"] 2 | path = offset-cache 3 | url = git@github.com:sbingner/offset-cache.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET = Unrestrict.dylib 2 | OUTDIR ?= bin 3 | DSYMDIR ?= dsym 4 | PREFIX ?= /Library/MobileSubstrate/ServerPlugins 5 | ARCHS ?= arm64 6 | SRC = $(wildcard *.c helpers/*.c offset-cache/*.c) 7 | OBJ = $(SRC:.c=.o) 8 | 9 | CC = xcrun -sdk iphoneos gcc $(patsubst %,-arch %,$(ARCHS)) 10 | LDID = ldid 11 | CFLAGS = -I. -Ihelpers -Ioffset-cache -Wno-deprecated-declarations -g 12 | LDFLAGS = -framework IOKit -framework CoreFoundation 13 | 14 | ifeq ($(DEBUG),1) 15 | CFLAGS += -DDEBUG 16 | endif 17 | 18 | .PHONY: all install clean 19 | 20 | all: $(OUTDIR)/$(TARGET) 21 | 22 | %.o: %.c 23 | $(CC) -c $(CFLAGS) -o $@ $^ 24 | 25 | install: all 26 | install -d "$(DESTDIR)$(PREFIX)" 27 | install $(OUTDIR)/$(TARGET) "$(DESTDIR)$(PREFIX)" 28 | 29 | $(OUTDIR): 30 | mkdir -p $@ 31 | 32 | $(DSYMDIR): 33 | mkdir -p $@ 34 | 35 | $(OUTDIR)/$(TARGET): $(OBJ) | $(OUTDIR) $(DSYMDIR) 36 | $(CC) $(LDFLAGS) -dynamiclib -install_name $(PREFIX)/$(TARGET) -o $@ $^ 37 | dsymutil $@ -out $(DSYMDIR)/$(TARGET).dSYM 38 | strip -S $@ 39 | $(LDID) -S $@ 40 | 41 | install: all 42 | 43 | clean: 44 | rm -rf $(OUTDIR) $(OBJ) 45 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef _UNRESTRICT_COMMON_H 2 | #define _UNRESTRICT_COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | extern bool initialized; 10 | extern uint64_t offset_options; 11 | #define OPT(x) (offset_options?((rk64(offset_options) & OPT_ ##x)?true:false):false) 12 | #define SETOPT(x, val) (offset_options?wk64(offset_options, val?(rk64(offset_options) | OPT_ ##x):(rk64(offset_options) & ~OPT_ ##x)):0) 13 | #define OPT_GET_TASK_ALLOW (1<<0) 14 | #define OPT_CS_DEBUGGED (1<<1) 15 | 16 | extern FILE *log_file; 17 | struct timeval dl_tv; 18 | #define LOG(fmt, args...) do { \ 19 | if (log_file == NULL) { \ 20 | char *log_path; \ 21 | if (asprintf(&log_path, \ 22 | "/var/log/unrestrict-%d.log", getpid()) == -1) { \ 23 | break; \ 24 | } \ 25 | log_file = fopen(log_path, "a"); \ 26 | free(log_path); \ 27 | if (log_file == NULL) break; \ 28 | } \ 29 | gettimeofday(&dl_tv, NULL); \ 30 | fprintf(log_file, "[%ld.%06d] " fmt "\n", dl_tv.tv_sec, \ 31 | dl_tv.tv_usec, ##args); \ 32 | fflush(log_file); \ 33 | } while(0) 34 | #define CROAK(fmt, args...) LOG("%s:%d:%d:" fmt, __FILE__, __LINE__, errno, ##args) 35 | #ifdef DEBUG 36 | #define DEBUGLOG(fmt, args...) LOG(fmt, ##args) 37 | #else 38 | #define DEBUGLOG(fmt, args...) do {} while (0) 39 | #endif // ifdef DEBUG 40 | 41 | #define CACHED_FIND(type, name) \ 42 | type __##name(void); \ 43 | type name(void) { \ 44 | type cached = 0; \ 45 | if (cached == 0) { \ 46 | cached = __##name(); \ 47 | } \ 48 | return cached; \ 49 | } \ 50 | type __##name(void) 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /helpers/kexecute.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "kern_utils.h" 4 | #include "common.h" 5 | #include "kexecute.h" 6 | #include "kmem.h" 7 | #include "offsetof.h" 8 | 9 | mach_port_t prepare_user_client() { 10 | kern_return_t err; 11 | mach_port_t user_client; 12 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot")); 13 | 14 | if (service == IO_OBJECT_NULL) { 15 | DEBUGLOG(" [-] unable to find service"); 16 | exit(EXIT_FAILURE); 17 | } 18 | 19 | err = IOServiceOpen(service, mach_task_self(), 0, &user_client); 20 | if (err != KERN_SUCCESS) { 21 | DEBUGLOG(" [-] unable to get user client connection"); 22 | exit(EXIT_FAILURE); 23 | } 24 | 25 | DEBUGLOG("got user client: 0x%x", user_client); 26 | return user_client; 27 | } 28 | 29 | // TODO: Consider removing this - jailbreakd runs all kernel ops on the main thread 30 | pthread_mutex_t kexecute_lock; 31 | static mach_port_t user_client; 32 | static uint64_t IOSurfaceRootUserClient_port; 33 | static uint64_t IOSurfaceRootUserClient_addr; 34 | static uint64_t fake_vtable; 35 | static uint64_t fake_client; 36 | const int fake_kalloc_size = 0x1000; 37 | 38 | bool init_kexecute() { 39 | user_client = prepare_user_client(); 40 | if (!user_client) return false; 41 | 42 | // From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable 43 | IOSurfaceRootUserClient_port = find_port(user_client); // UserClients are just mach_ports, so we find its address 44 | if (!IOSurfaceRootUserClient_port) return false; 45 | 46 | IOSurfaceRootUserClient_addr = rk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field 47 | if (!IOSurfaceRootUserClient_addr) return false; 48 | 49 | uint64_t IOSurfaceRootUserClient_vtab = rk64(IOSurfaceRootUserClient_addr); // vtables in C++ are at *object 50 | if (!IOSurfaceRootUserClient_vtab) return false; 51 | 52 | // The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one 53 | // Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel 54 | 55 | // Create the vtable in the kernel memory, then copy the existing vtable into there 56 | fake_vtable = kalloc(fake_kalloc_size); 57 | if (!fake_vtable) return false; 58 | 59 | for (int i = 0; i < 0x200; i++) { 60 | wk64(fake_vtable+i*8, rk64(IOSurfaceRootUserClient_vtab+i*8)); 61 | } 62 | 63 | // Create the fake user client 64 | fake_client = kalloc(fake_kalloc_size); 65 | if (!fake_client) return false; 66 | 67 | for (int i = 0; i < 0x200; i++) { 68 | wk64(fake_client+i*8, rk64(IOSurfaceRootUserClient_addr+i*8)); 69 | } 70 | 71 | // Write our fake vtable into the fake user client 72 | wk64(fake_client, fake_vtable); 73 | 74 | // Replace the user client with ours 75 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, fake_client); 76 | 77 | // Now the userclient port we have will look into our fake user client rather than the old one 78 | 79 | // Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;) 80 | wk64(fake_vtable+8*0xB7, offset_add_ret_gadget); 81 | 82 | pthread_mutex_init(&kexecute_lock, NULL); 83 | return true; 84 | } 85 | 86 | void term_kexecute() { 87 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, IOSurfaceRootUserClient_addr); 88 | kfree(fake_vtable, fake_kalloc_size); 89 | kfree(fake_client, fake_kalloc_size); 90 | } 91 | 92 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) { 93 | pthread_mutex_lock(&kexecute_lock); 94 | 95 | // When calling IOConnectTrapX, this makes a call to iokit_user_client_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex 96 | // to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap. 97 | // This jumps to our gadget, which returns +0x40 into our fake user_client, which we can modify. The function is then called on the object. But how C++ actually works is that the 98 | // function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed 99 | // through like normal. 100 | 101 | // Because the gadget gets the trap at user_client+0x40, we have to overwrite the contents of it 102 | // We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents 103 | // (i'm not actually sure if the switch back is necessary but meh) 104 | 105 | uint64_t offx20 = rk64(fake_client+0x40); 106 | uint64_t offx28 = rk64(fake_client+0x48); 107 | wk64(fake_client+0x40, x0); 108 | wk64(fake_client+0x48, addr); 109 | uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6)); 110 | wk64(fake_client+0x40, offx20); 111 | wk64(fake_client+0x48, offx28); 112 | 113 | pthread_mutex_unlock(&kexecute_lock); 114 | 115 | return returnval; 116 | } 117 | -------------------------------------------------------------------------------- /helpers/kexecute.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | mach_port_t prepare_user_client(void); 6 | bool init_kexecute(void); 7 | void term_kexecute(void); 8 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6); 9 | -------------------------------------------------------------------------------- /helpers/kmem.c: -------------------------------------------------------------------------------- 1 | #include "kern_utils.h" 2 | #include "kmem.h" 3 | #include "common.h" 4 | 5 | #define MAX_CHUNK_SIZE 0xFFF 6 | 7 | size_t kread(uint64_t where, void *p, size_t size) { 8 | int rv; 9 | size_t offset = 0; 10 | while (offset < size) { 11 | mach_vm_size_t sz, chunk = MAX_CHUNK_SIZE; 12 | if (chunk > size - offset) { 13 | chunk = size - offset; 14 | } 15 | rv = mach_vm_read_overwrite(tfp0, where + offset, chunk, (mach_vm_address_t)p + offset, &sz); 16 | if (rv || sz == 0) { 17 | DEBUGLOG("[e] error reading kernel @%p", (void *)(offset + where)); 18 | break; 19 | } 20 | offset += sz; 21 | } 22 | return offset; 23 | } 24 | 25 | size_t kwrite(uint64_t where, const void *p, size_t size) { 26 | int rv; 27 | size_t offset = 0; 28 | while (offset < size) { 29 | size_t chunk = MAX_CHUNK_SIZE; 30 | if (chunk > size - offset) { 31 | chunk = size - offset; 32 | } 33 | rv = mach_vm_write(tfp0, where + offset, (mach_vm_offset_t)p + offset, chunk); 34 | if (rv) { 35 | DEBUGLOG("[e] error writing kernel @%p", (void *)(offset + where)); 36 | break; 37 | } 38 | offset += chunk; 39 | } 40 | return offset; 41 | } 42 | 43 | uint64_t kalloc(vm_size_t size) { 44 | mach_vm_address_t address = 0; 45 | mach_vm_allocate(tfp0, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE); 46 | return address; 47 | } 48 | 49 | void kfree(mach_vm_address_t address, vm_size_t size) { 50 | mach_vm_deallocate(tfp0, address, size); 51 | } 52 | 53 | uint16_t rk16(uint64_t kaddr) { 54 | uint16_t val = 0; 55 | kread(kaddr, &val, sizeof(val)); 56 | return val; 57 | } 58 | 59 | uint32_t rk32(uint64_t kaddr) { 60 | uint32_t val = 0; 61 | kread(kaddr, &val, sizeof(val)); 62 | return val; 63 | } 64 | 65 | uint64_t rk64(uint64_t kaddr) { 66 | uint64_t val = 0; 67 | kread(kaddr, &val, sizeof(val)); 68 | return val; 69 | } 70 | 71 | void wk16(uint64_t kaddr, uint16_t val) { 72 | kwrite(kaddr, &val, sizeof(val)); 73 | } 74 | 75 | void wk32(uint64_t kaddr, uint32_t val) { 76 | kwrite(kaddr, &val, sizeof(val)); 77 | } 78 | 79 | void wk64(uint64_t kaddr, uint64_t val) { 80 | kwrite(kaddr, &val, sizeof(val)); 81 | } 82 | 83 | // thx Siguza 84 | typedef struct { 85 | uint64_t prev; 86 | uint64_t next; 87 | uint64_t start; 88 | uint64_t end; 89 | } kmap_hdr_t; 90 | 91 | uint64_t zm_fix_addr(uint64_t addr) { 92 | static kmap_hdr_t zm_hdr = {0, 0, 0, 0}; 93 | 94 | if (zm_hdr.start == 0) { 95 | uint64_t zone_map = rk64(offset_zonemap); 96 | DEBUGLOG("Calculating zm_hdr (offset_zonemap = 0x%llx zone_map = 0x%llx)", offset_zonemap, zone_map); 97 | 98 | // hdr is at offset 0x10, mutexes at start 99 | size_t r = kread(zone_map + 0x10, &zm_hdr, sizeof(zm_hdr)); 100 | 101 | if (r != sizeof(zm_hdr) || zm_hdr.start == 0 || zm_hdr.end == 0) { 102 | DEBUGLOG("kread of zone_map failed!"); 103 | return 0; 104 | } 105 | 106 | if (zm_hdr.end - zm_hdr.start > 0x100000000) { 107 | DEBUGLOG("zone_map is too big, sorry."); 108 | return 0; 109 | } 110 | } 111 | 112 | uint64_t zm_tmp = (zm_hdr.start & 0xffffffff00000000) | (addr & 0xffffffff); 113 | 114 | return zm_tmp < zm_hdr.start ? zm_tmp + 0x100000000 : zm_tmp; 115 | } 116 | 117 | int kstrcmp(uint64_t kstr, const char *str) { 118 | size_t len = strlen(str) + 1; 119 | char *local = malloc(len + 1); 120 | if (!local) CROAK("Unable to malloc"); 121 | local[len] = '\0'; 122 | 123 | int ret = 1; 124 | 125 | if (kread(kstr, local, len) == len) { 126 | ret = strcmp(local, str); 127 | } 128 | 129 | free(local); 130 | 131 | return ret; 132 | } 133 | -------------------------------------------------------------------------------- /helpers/kmem.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint64_t kalloc(vm_size_t size); 4 | void kfree(mach_vm_address_t address, vm_size_t size); 5 | 6 | size_t kread(uint64_t where, void *p, size_t size); 7 | uint16_t rk16(uint64_t kaddr); 8 | uint32_t rk32(uint64_t kaddr); 9 | uint64_t rk64(uint64_t kaddr); 10 | 11 | size_t kwrite(uint64_t where, const void *p, size_t size); 12 | void wk16(uint64_t kaddr, uint16_t val); 13 | void wk32(uint64_t kaddr, uint32_t val); 14 | void wk64(uint64_t kaddr, uint64_t val); 15 | 16 | uint64_t zm_fix_addr(uint64_t addr); 17 | 18 | int kstrcmp(uint64_t kstr, const char *str); 19 | -------------------------------------------------------------------------------- /helpers/offsetof.c: -------------------------------------------------------------------------------- 1 | #define offsetof_p_pid (unsigned)((kCFCoreFoundationVersionNumber >= 1535.12) ? (0x60) : (0x10)) // proc_t::p_pid 2 | #define offsetof_task (unsigned)((kCFCoreFoundationVersionNumber >= 1535.12) ? (0x10) : (0x18)) // proc_t::task 3 | #define offsetof_p_svuid (unsigned)((kCFCoreFoundationVersionNumber >= 1535.12) ? (0x32) : (0x40)) // proc_t::svuid 4 | #define offsetof_p_svgid (unsigned)((kCFCoreFoundationVersionNumber >= 1535.12) ? (0x36) : (0x44)) // proc_t::svgid 5 | #define offsetof_p_ucred (unsigned)((kCFCoreFoundationVersionNumber >= 1535.12) ? (0xf8) : (0x100)) // proc_t::p_ucred 6 | #define offsetof_p_csflags (unsigned)((kCFCoreFoundationVersionNumber >= 1535.12) ? (0x290) : (0x2a8)) // proc_t::p_csflags 7 | #define offsetof_p_p_list (unsigned)(0x8) // proc_t::p_list 8 | #define offsetof_itk_space (unsigned)((kCFCoreFoundationVersionNumber >= 1443.00) ? ((kCFCoreFoundationVersionNumber >= 1535.12) ? (0x300) : (0x308)) : (0x300)) // task_t::itk_space 9 | #define offsetof_bsd_info (unsigned)((kCFCoreFoundationVersionNumber >= 1443.00) ? ((kCFCoreFoundationVersionNumber >= 1535.12) ? (0x358) : (0x368)) : (0x360)) // task_t::bsd_info 10 | #define offsetof_ip_kobject (unsigned)(0x68) // ipc_port_t::ip_kobject 11 | #define offsetof_ipc_space_is_table (unsigned)(0x20) // ipc_space::is_table 12 | 13 | #define offsetof_ucred_cr_uid (unsigned)(0x18) // ucred::cr_uid 14 | #define offsetof_ucred_cr_svuid (unsigned)(0x20) // ucred::cr_svuid 15 | #define offsetof_ucred_cr_groups (unsigned)(0x28) // ucred::cr_groups 16 | #define offsetof_ucred_cr_svgid (unsigned)(0x6c) // ucred::cr_svgid 17 | 18 | #define offsetof_t_flags (unsigned)((kCFCoreFoundationVersionNumber >= 1535.12) ? (0x390) : (0x3a0)) // task::t_flags 19 | -------------------------------------------------------------------------------- /helpers/offsetof.h: -------------------------------------------------------------------------------- 1 | #include "offsetof.c" 2 | -------------------------------------------------------------------------------- /helpers/osobject.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "kern_utils.h" 4 | #include "kexecute.h" 5 | #include "kmem.h" 6 | #include "osobject.h" 7 | #include "common.h" 8 | 9 | // offsets in vtable: 10 | static uint32_t off_OSDictionary_SetObjectWithCharP = sizeof(void*) * 0x1F; 11 | static uint32_t off_OSDictionary_GetObjectWithCharP = sizeof(void*) * 0x26; 12 | static uint32_t off_OSDictionary_Merge = sizeof(void*) * 0x23; 13 | 14 | static uint32_t off_OSArray_Merge = sizeof(void*) * 0x1E; 15 | static uint32_t off_OSArray_RemoveObject = sizeof(void*) * 0x20; 16 | static uint32_t off_OSArray_GetObject = sizeof(void*) * 0x22; 17 | 18 | static uint32_t off_OSObject_Release = sizeof(void*) * 0x05; 19 | static uint32_t off_OSObject_GetRetainCount = sizeof(void*) * 0x03; 20 | static uint32_t off_OSObject_Retain = sizeof(void*) * 0x04; 21 | 22 | static uint32_t off_OSString_GetLength = sizeof(void*) * 0x11; 23 | 24 | // 1 on success, 0 on error 25 | bool OSDictionary_SetItem(uint64_t dict, const char *key, uint64_t val) { 26 | size_t len = strlen(key) + 1; 27 | 28 | uint64_t ks = kalloc(len); 29 | kwrite(ks, key, len); 30 | 31 | uint64_t vtab = rk64(dict); 32 | uint64_t f = rk64(vtab + off_OSDictionary_SetObjectWithCharP); 33 | 34 | int rv = (int) kexecute(f, dict, ks, val, 0, 0, 0, 0); 35 | 36 | kfree(ks, len); 37 | 38 | return rv; 39 | } 40 | 41 | // XXX it can return 0 in lower 32 bits but still be valid 42 | // fix addr of returned value and check if rk64 gives ptr 43 | // to vtable addr saved before 44 | 45 | // address if exists, 0 if not 46 | uint64_t _OSDictionary_GetItem(uint64_t dict, const char *key) { 47 | size_t len = strlen(key) + 1; 48 | 49 | uint64_t ks = kalloc(len); 50 | kwrite(ks, key, len); 51 | 52 | uint64_t vtab = rk64(dict); 53 | uint64_t f = rk64(vtab + off_OSDictionary_GetObjectWithCharP); 54 | 55 | uint64_t rv = kexecute(f, dict, ks, 0, 0, 0, 0, 0); 56 | 57 | kfree(ks, len); 58 | 59 | return rv; 60 | } 61 | 62 | uint64_t OSDictionary_GetItem(uint64_t dict, const char *key) { 63 | uint64_t ret = _OSDictionary_GetItem(dict, key); 64 | 65 | if (ret != 0 && (ret>>32) == 0) { 66 | // XXX can it be not in zalloc?.. 67 | ret = zm_fix_addr(ret); 68 | } 69 | 70 | return ret; 71 | } 72 | 73 | // 1 on success, 0 on error 74 | bool OSDictionary_Merge(uint64_t dict, uint64_t aDict) { 75 | uint64_t vtab = rk64(dict); 76 | uint64_t f = rk64(vtab + off_OSDictionary_Merge); 77 | 78 | return (int) kexecute(f, dict, aDict, 0, 0, 0, 0, 0); 79 | } 80 | 81 | // 1 on success, 0 on error 82 | bool OSArray_Merge(uint64_t array, uint64_t aArray) { 83 | uint64_t vtab = rk64(array); 84 | uint64_t f = rk64(vtab + off_OSArray_Merge); 85 | 86 | return (int) kexecute(f, array, aArray, 0, 0, 0, 0, 0); 87 | } 88 | 89 | uint64_t _OSArray_GetObject(uint64_t array, unsigned int idx){ 90 | uint64_t vtab = rk64(array); 91 | uint64_t f = rk64(vtab + off_OSArray_GetObject); 92 | 93 | return kexecute(f, array, idx, 0, 0, 0, 0, 0); 94 | } 95 | 96 | uint64_t OSArray_GetObject(uint64_t array, unsigned int idx){ 97 | uint64_t ret = _OSArray_GetObject(array, idx); 98 | 99 | if (ret != 0){ 100 | // XXX can it be not in zalloc?.. 101 | ret = zm_fix_addr(ret); 102 | } 103 | return ret; 104 | } 105 | 106 | void OSArray_RemoveObject(uint64_t array, unsigned int idx){ 107 | uint64_t vtab = rk64(array); 108 | uint64_t f = rk64(vtab + off_OSArray_RemoveObject); 109 | 110 | (void)kexecute(f, array, idx, 0, 0, 0, 0, 0); 111 | } 112 | 113 | // XXX error handling just for fun? :) 114 | uint64_t _OSUnserializeXML(const char *buffer) { 115 | size_t len = strlen(buffer) + 1; 116 | 117 | uint64_t ks = kalloc(len); 118 | kwrite(ks, buffer, len); 119 | 120 | uint64_t errorptr = 0; 121 | 122 | uint64_t rv = kexecute(offset_osunserializexml, ks, errorptr, 0, 0, 0, 0, 0); 123 | kfree(ks, len); 124 | 125 | return rv; 126 | } 127 | 128 | uint64_t OSUnserializeXML(const char *buffer) { 129 | uint64_t ret = _OSUnserializeXML(buffer); 130 | 131 | if (ret != 0) { 132 | // XXX can it be not in zalloc?.. 133 | ret = zm_fix_addr(ret); 134 | } 135 | 136 | return ret; 137 | } 138 | 139 | void OSObject_Release(uint64_t osobject) { 140 | uint64_t vtab = rk64(osobject); 141 | uint64_t f = rk64(vtab + off_OSObject_Release); 142 | (void) kexecute(f, osobject, 0, 0, 0, 0, 0, 0); 143 | } 144 | 145 | void OSObject_Retain(uint64_t osobject) { 146 | uint64_t vtab = rk64(osobject); 147 | uint64_t f = rk64(vtab + off_OSObject_Retain); 148 | (void) kexecute(f, osobject, 0, 0, 0, 0, 0, 0); 149 | } 150 | 151 | uint32_t OSObject_GetRetainCount(uint64_t osobject) { 152 | uint64_t vtab = rk64(osobject); 153 | uint64_t f = rk64(vtab + off_OSObject_GetRetainCount); 154 | return (uint32_t) kexecute(f, osobject, 0, 0, 0, 0, 0, 0); 155 | } 156 | 157 | unsigned int OSString_GetLength(uint64_t osstring){ 158 | uint64_t vtab = rk64(osstring); 159 | uint64_t f = rk64(vtab + off_OSString_GetLength); 160 | return (unsigned int)kexecute(f, osstring, 0, 0, 0, 0, 0, 0); 161 | } 162 | 163 | char *OSString_CopyString(uint64_t osstring){ 164 | unsigned int length = OSString_GetLength(osstring); 165 | if (length == 0 || length > 0x100) DEBUGLOG("OSString_CopyString: length=%d", length); 166 | char *str = (char *)malloc(length + 1); 167 | if (!str) { 168 | DEBUGLOG("malloc failed OSString_CopyString: str=%p", str); 169 | } 170 | str[length] = 0; 171 | 172 | kread(OSString_CStringPtr(osstring), str, length); 173 | return str; 174 | } 175 | -------------------------------------------------------------------------------- /helpers/osobject.h: -------------------------------------------------------------------------------- 1 | 2 | #define OSDictionary_ItemCount(dict) rk32(dict+20) 3 | #define OSDictionary_ItemBuffer(dict) rk64(dict+32) 4 | #define OSDictionary_ItemKey(buffer, idx) rk64(buffer+16*idx) 5 | #define OSDictionary_ItemValue(buffer, idx) rk64(buffer+16*idx+8) 6 | #define OSString_CStringPtr(str) rk64(str + 0x10) 7 | #define OSArray_ItemCount(arr) rk32(arr+0x14) 8 | #define OSArray_ItemBuffer(arr) rk64(arr+32) 9 | 10 | // see osobject.c for info 11 | 12 | bool OSDictionary_SetItem(uint64_t dict, const char *key, uint64_t val); 13 | uint64_t OSDictionary_GetItem(uint64_t dict, const char *key); 14 | bool OSDictionary_Merge(uint64_t dict, uint64_t aDict); 15 | void OSArray_RemoveObject(uint64_t array, unsigned int idx); 16 | uint64_t OSArray_GetObject(uint64_t array, unsigned int idx); 17 | bool OSArray_Merge(uint64_t array, uint64_t aArray); 18 | uint64_t OSUnserializeXML(const char *buffer); 19 | 20 | void OSObject_Release(uint64_t osobject); 21 | void OSObject_Retain(uint64_t osobject); 22 | uint32_t OSObject_GetRetainCount(uint64_t osobject); 23 | 24 | unsigned int OSString_GetLength(uint64_t osstring); 25 | char *OSString_CopyString(uint64_t osstring); 26 | -------------------------------------------------------------------------------- /kern_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common.h" 6 | #include "kern_utils.h" 7 | #include "kexecute.h" 8 | #include "kmem.h" 9 | #include "offsetof.h" 10 | #include "osobject.h" 11 | #include "sandbox.h" 12 | 13 | mach_port_t tfp0; 14 | uint64_t kernel_base; 15 | uint64_t kernel_slide; 16 | 17 | uint64_t offset_kernel_task; 18 | uint64_t offset_zonemap; 19 | uint64_t offset_add_ret_gadget; 20 | uint64_t offset_osboolean_true; 21 | uint64_t offset_osboolean_false; 22 | uint64_t offset_osunserializexml; 23 | uint64_t offset_smalloc; 24 | 25 | uint64_t proc_find(pid_t pid) { 26 | static uint64_t kernproc = 0; 27 | if (kernproc == 0) { 28 | kernproc = rk64(rk64(offset_kernel_task) + offsetof_bsd_info); 29 | if (kernproc == 0) { 30 | DEBUGLOG("failed to find kernproc!"); 31 | return 0; 32 | } 33 | } 34 | 35 | uint64_t proc = kernproc; 36 | 37 | if (pid == 0) { 38 | return proc; 39 | } 40 | 41 | while (proc) { 42 | uint32_t found_pid = rk32(proc + offsetof_p_pid); 43 | 44 | if (found_pid == pid) { 45 | return proc; 46 | } 47 | 48 | proc = rk64(proc + offsetof_p_p_list); 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | CACHED_FIND(uint64_t, our_task_addr) { 55 | uint64_t proc = proc_find(getpid()); 56 | if (proc == 0) { 57 | DEBUGLOG("failed to get proc!"); 58 | return 0; 59 | } 60 | uint64_t task_addr = rk64(proc + offsetof_task); 61 | if (task_addr == 0) { 62 | DEBUGLOG("failed to get task_addr!"); 63 | return 0; 64 | } 65 | return task_addr; 66 | } 67 | 68 | uint64_t find_port(mach_port_name_t port) { 69 | uint64_t is_table = 0; 70 | uint64_t task_addr = our_task_addr(); 71 | if (!task_addr) { 72 | DEBUGLOG("failed to get task_addr!"); 73 | return 0; 74 | } 75 | uint64_t itk_space = rk64(task_addr + offsetof_itk_space); 76 | if (!itk_space) { 77 | DEBUGLOG("failed to get itk_space!"); 78 | return 0; 79 | } 80 | is_table = rk64(itk_space + offsetof_ipc_space_is_table); 81 | if (!is_table) { 82 | DEBUGLOG("failed to get is_table!"); 83 | return 0; 84 | } 85 | 86 | uint32_t port_index = port >> 8; 87 | const int sizeof_ipc_entry_t = 0x18; 88 | uint64_t port_addr = rk64(is_table + (port_index * sizeof_ipc_entry_t)); 89 | if (port_addr == 0) { 90 | DEBUGLOG("failed to get port_addr!"); 91 | return 0; 92 | } 93 | return port_addr; 94 | } 95 | 96 | static void set_csflags(uint64_t proc, uint32_t flags, bool value) { 97 | uint32_t csflags = rk32(proc + offsetof_p_csflags); 98 | 99 | if (value == true) { 100 | csflags |= flags; 101 | } else { 102 | csflags &= ~flags; 103 | } 104 | 105 | wk32(proc + offsetof_p_csflags, csflags); 106 | } 107 | 108 | 109 | void fixup_setuid(int pid, uint64_t proc) { 110 | char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; 111 | bzero(pathbuf, sizeof(pathbuf)); 112 | 113 | int ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf)); 114 | if (ret < 0) { 115 | DEBUGLOG("Unable to get path for PID %d", pid); 116 | return; 117 | } 118 | 119 | struct stat file_st; 120 | if (lstat(pathbuf, &file_st) == -1) { 121 | DEBUGLOG("Unable to get stat for file %s", pathbuf); 122 | return; 123 | } 124 | 125 | if (!(file_st.st_mode & S_ISUID) && !(file_st.st_mode & S_ISGID)) { 126 | DEBUGLOG("File is not setuid or setgid: %s", pathbuf); 127 | return; 128 | } 129 | 130 | if (proc == 0) { 131 | DEBUGLOG("Invalid proc for pid %d", pid); 132 | return; 133 | } 134 | 135 | DEBUGLOG("Found proc %llx for pid %d", proc, pid); 136 | 137 | uid_t fileUid = file_st.st_uid; 138 | uid_t fileGid = file_st.st_gid; 139 | 140 | DEBUGLOG("Applying UID %d to process %d", fileUid, pid); 141 | uint64_t ucred = rk64(proc + offsetof_p_ucred); 142 | 143 | if (file_st.st_mode & S_ISUID) { 144 | wk32(proc + offsetof_p_svuid, fileUid); 145 | wk32(ucred + offsetof_ucred_cr_svuid, fileUid); 146 | wk32(ucred + offsetof_ucred_cr_uid, fileUid); 147 | } 148 | 149 | if (file_st.st_mode & S_ISGID) { 150 | wk32(proc + offsetof_p_svgid, fileGid); 151 | wk32(ucred + offsetof_ucred_cr_svgid, fileGid); 152 | wk32(ucred + offsetof_ucred_cr_groups, fileGid); 153 | } 154 | } 155 | 156 | void set_tfplatform(uint64_t proc) { 157 | // task.t_flags & TF_PLATFORM 158 | uint64_t task = rk64(proc + offsetof_task); 159 | uint32_t t_flags = rk32(task + offsetof_t_flags); 160 | if (!(t_flags&TF_PLATFORM)) { 161 | t_flags |= TF_PLATFORM; 162 | wk32(task+offsetof_t_flags, t_flags); 163 | } 164 | } 165 | 166 | const char* abs_path_exceptions[] = { 167 | "/Library", 168 | "/private/var/mobile/Library", 169 | "/System/Library/Caches", 170 | NULL 171 | }; 172 | 173 | static uint64_t exception_osarray_cache = 0; 174 | uint64_t get_exception_osarray(void) { 175 | 176 | if (exception_osarray_cache == 0) { 177 | DEBUGLOG("Generating exception_osarray_cache"); 178 | size_t xmlsize = 0x1000; 179 | size_t len=0; 180 | ssize_t written=0; 181 | char *ents = malloc(xmlsize); 182 | size_t xmlused = sprintf(ents, ""); 183 | for (const char **exception = abs_path_exceptions; *exception; exception++) { 184 | len = strlen(*exception); 185 | len += strlen(""); 186 | while (xmlused + len >= xmlsize) { 187 | xmlsize += 0x1000; 188 | ents = reallocf(ents, xmlsize); 189 | if (!ents) { 190 | CROAK("Unable to reallocate memory"); 191 | return 0; 192 | } 193 | } 194 | written = sprintf(ents + xmlused, "%s/", *exception); 195 | if (written < 0) { 196 | CROAK("Couldn't write string"); 197 | free(ents); 198 | return 0; 199 | } 200 | xmlused += written; 201 | } 202 | len = strlen(""); 203 | if (xmlused + len >= xmlsize) { 204 | xmlsize += len; 205 | ents = reallocf(ents, xmlsize); 206 | if (!ents) { 207 | return 0; 208 | CROAK("Unable to reallocate memory"); 209 | } 210 | } 211 | written = sprintf(ents + xmlused, ""); 212 | 213 | exception_osarray_cache = OSUnserializeXML(ents); 214 | DEBUGLOG("Exceptions stored at 0x%llx: %s", exception_osarray_cache, ents); 215 | free(ents); 216 | } 217 | 218 | return exception_osarray_cache; 219 | } 220 | 221 | void release_exception_osarray(void) { 222 | if (exception_osarray_cache != 0) { 223 | OSObject_Release(exception_osarray_cache); 224 | exception_osarray_cache = 0; 225 | } 226 | } 227 | 228 | static const char *exc_key = "com.apple.security.exception.files.absolute-path.read-only"; 229 | 230 | void set_sandbox_extensions(uint64_t proc) { 231 | uint64_t proc_ucred = rk64(proc + offsetof_p_ucred); 232 | uint64_t sandbox = rk64(rk64(proc_ucred + 0x78) + 0x8 + 0x8); 233 | 234 | if (sandbox == 0) { 235 | DEBUGLOG("no sandbox, skipping (proc: %llx)", proc); 236 | return; 237 | } 238 | 239 | uint64_t ext = 0; 240 | for (const char **exception = abs_path_exceptions; *exception; exception++) { 241 | if (has_file_extension(sandbox, *exception)) { 242 | DEBUGLOG("already has '%s', skipping", *exception); 243 | continue; 244 | } 245 | ext = extension_create_file(*exception, ext); 246 | if (ext == 0) { 247 | DEBUGLOG("extension_create_file(%s) failed, panic!", *exception); 248 | } 249 | } 250 | 251 | if (ext != 0) { 252 | extension_add(ext, sandbox, exc_key); 253 | } 254 | } 255 | 256 | char **copy_amfi_entitlements(uint64_t present) { 257 | unsigned int itemCount = OSArray_ItemCount(present); 258 | uint64_t itemBuffer = OSArray_ItemBuffer(present); 259 | size_t bufferSize = 0x1000; 260 | size_t bufferUsed = 0; 261 | size_t arraySize = (itemCount+1) * sizeof(char*); 262 | char **entitlements = malloc(arraySize + bufferSize); 263 | entitlements[itemCount] = NULL; 264 | 265 | for (int i=0; i bufferSize) { 270 | bufferSize += 0x1000; 271 | entitlements = realloc(entitlements, arraySize + bufferSize); 272 | if (!entitlements) { 273 | CROAK("Unable to reallocate memory"); 274 | return NULL; 275 | } 276 | } 277 | entitlements[i] = (char*)entitlements + arraySize + bufferUsed; 278 | strcpy(entitlements[i], entitlementString); 279 | bufferUsed += len; 280 | } 281 | return entitlements; 282 | } 283 | 284 | void set_amfi_entitlements(uint64_t proc) { 285 | uint64_t proc_ucred = rk64(proc + offsetof_p_ucred); 286 | uint64_t amfi_entitlements = rk64(rk64(proc_ucred + 0x78) + 0x8); 287 | uint64_t sandbox = rk64(rk64(proc_ucred + 0x78) + 0x8 + 0x8); 288 | 289 | bool rv = false; 290 | 291 | uint64_t key = 0; 292 | 293 | key = OSDictionary_GetItem(amfi_entitlements, "com.apple.private.skip-library-validation"); 294 | if (key != offset_osboolean_true) { 295 | rv = OSDictionary_SetItem(amfi_entitlements, "com.apple.private.skip-library-validation", offset_osboolean_true); 296 | if (rv != true) { 297 | DEBUGLOG("failed to set com.apple.private.skip-library-validation!"); 298 | } 299 | } 300 | 301 | if (OPT(GET_TASK_ALLOW)) { 302 | key = OSDictionary_GetItem(amfi_entitlements, "get-task-allow"); 303 | if (key != offset_osboolean_true) { 304 | rv = OSDictionary_SetItem(amfi_entitlements, "get-task-allow", offset_osboolean_true); 305 | if (rv != true) { 306 | DEBUGLOG("failed to set get-task-allow!"); 307 | } 308 | } 309 | } 310 | 311 | if (!sandbox) { 312 | DEBUGLOG("Skipping exceptions because no sandbox"); 313 | return; 314 | } 315 | 316 | uint64_t present = OSDictionary_GetItem(amfi_entitlements, exc_key); 317 | 318 | if (present == 0) { 319 | DEBUGLOG("present=NULL; setting to %llx", get_exception_osarray()); 320 | rv = OSDictionary_SetItem(amfi_entitlements, exc_key, get_exception_osarray()); 321 | if (rv != true) { 322 | DEBUGLOG("failed to set %s", exc_key); 323 | } 324 | return; 325 | } else if (present == get_exception_osarray()) { 326 | DEBUGLOG("Exceptions already set to our array"); 327 | return; 328 | } 329 | 330 | char **currentExceptions = copy_amfi_entitlements(present); 331 | Boolean foundEntitlements = true; 332 | 333 | for (const char **exception = abs_path_exceptions; *exception && foundEntitlements; exception++) { 334 | DEBUGLOG("Looking for %s", *exception); 335 | Boolean foundException = false; 336 | for (char **entitlementString = currentExceptions; *entitlementString && !foundException; entitlementString++) { 337 | char *ent = strdup(*entitlementString); 338 | int lastchar = strlen(ent) - 1; 339 | if (ent[lastchar] == '/') ent[lastchar] = '\0'; 340 | 341 | if (strcasecmp(ent, *exception) == 0) { 342 | DEBUGLOG("found existing exception: %s", *entitlementString); 343 | foundException = true; 344 | } 345 | free(ent); 346 | } 347 | if (!foundException) { 348 | foundEntitlements = false; 349 | DEBUGLOG("did not find exception: %s", *exception); 350 | } 351 | } 352 | free(currentExceptions); 353 | 354 | if (!foundEntitlements) { 355 | DEBUGLOG("Merging exceptions"); 356 | // FIXME: This could result in duplicate entries but that seems better than always kexecuting many times 357 | // When this is fixed, update the loop above to not stop on the first missing exception 358 | rv = OSArray_Merge(present, get_exception_osarray()); 359 | } else { 360 | DEBUGLOG("All exceptions present"); 361 | rv = true; 362 | } 363 | 364 | if (rv != true) { 365 | DEBUGLOG("Setting exc FAILED! amfi_entitlements: 0x%llx present: 0x%llx", amfi_entitlements, present); 366 | } 367 | } 368 | 369 | void fixup_tfplatform(uint64_t proc) { 370 | uint64_t proc_ucred = rk64(proc + offsetof_p_ucred); 371 | uint64_t amfi_entitlements = rk64(rk64(proc_ucred + 0x78) + 0x8); 372 | 373 | uint64_t key = OSDictionary_GetItem(amfi_entitlements, "platform-application"); 374 | if (key == offset_osboolean_true) { 375 | DEBUGLOG("platform-application is set"); 376 | set_tfplatform(proc); 377 | set_csflags(proc, CS_PLATFORM_BINARY, true); 378 | } else { 379 | DEBUGLOG("platform-application is not set"); 380 | } 381 | } 382 | 383 | void fixup_sandbox(uint64_t proc) { 384 | set_sandbox_extensions(proc); 385 | } 386 | 387 | void fixup_cs_valid(uint64_t proc) { 388 | set_csflags(proc, CS_VALID, true); 389 | } 390 | 391 | void fixup_cs_flags(uint64_t proc) { 392 | int flags = 0; 393 | if (OPT(GET_TASK_ALLOW)) { 394 | DEBUGLOG("adding get-task-allow"); 395 | flags |= CS_GET_TASK_ALLOW; 396 | } 397 | if (OPT(CS_DEBUGGED)) { 398 | DEBUGLOG("setting CS_DEBUGGED :("); 399 | flags |= CS_DEBUGGED; 400 | } 401 | if (flags) { 402 | set_csflags(proc, flags, true); 403 | } 404 | } 405 | 406 | void fixup(pid_t pid) { 407 | uint64_t proc = proc_find(pid); 408 | if (proc == 0) { 409 | DEBUGLOG("failed to find proc for pid %d!", pid); 410 | return; 411 | } 412 | 413 | DEBUGLOG("fixup_setuid"); 414 | fixup_setuid(pid, proc); 415 | DEBUGLOG("fixup_sandbox"); 416 | fixup_sandbox(proc); 417 | DEBUGLOG("fixup_tfplatform"); 418 | fixup_tfplatform(proc); 419 | DEBUGLOG("fixup_cs_flags"); 420 | fixup_cs_flags(proc); 421 | DEBUGLOG("set_amfi_entitlements"); 422 | set_amfi_entitlements(proc); 423 | } 424 | 425 | void kern_utils_cleanup() { 426 | release_exception_osarray(); 427 | } 428 | -------------------------------------------------------------------------------- /kern_utils.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #import 8 | 9 | /****** IOKit/IOKitLib.h *****/ 10 | typedef mach_port_t io_service_t; 11 | typedef mach_port_t io_connect_t; 12 | 13 | extern const mach_port_t kIOMasterPortDefault; 14 | #define IO_OBJECT_NULL (0) 15 | 16 | kern_return_t 17 | IOConnectCallAsyncMethod( 18 | mach_port_t connection, 19 | uint32_t selector, 20 | mach_port_t wakePort, 21 | uint64_t* reference, 22 | uint32_t referenceCnt, 23 | const uint64_t* input, 24 | uint32_t inputCnt, 25 | const void* inputStruct, 26 | size_t inputStructCnt, 27 | uint64_t* output, 28 | uint32_t* outputCnt, 29 | void* outputStruct, 30 | size_t* outputStructCntP); 31 | 32 | kern_return_t 33 | IOConnectCallMethod( 34 | mach_port_t connection, 35 | uint32_t selector, 36 | const uint64_t* input, 37 | uint32_t inputCnt, 38 | const void* inputStruct, 39 | size_t inputStructCnt, 40 | uint64_t* output, 41 | uint32_t* outputCnt, 42 | void* outputStruct, 43 | size_t* outputStructCntP); 44 | 45 | io_service_t 46 | IOServiceGetMatchingService( 47 | mach_port_t _masterPort, 48 | CFDictionaryRef matching); 49 | 50 | CFMutableDictionaryRef 51 | IOServiceMatching( 52 | const char* name); 53 | 54 | kern_return_t 55 | IOServiceOpen( 56 | io_service_t service, 57 | task_port_t owningTask, 58 | uint32_t type, 59 | io_connect_t* connect ); 60 | 61 | kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6); 62 | kern_return_t mach_vm_read(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, vm_offset_t *data, mach_msg_type_number_t *dataCnt); 63 | kern_return_t mach_vm_read_overwrite(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, mach_vm_address_t data, mach_vm_size_t *outsize); 64 | kern_return_t mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt); 65 | kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags); 66 | kern_return_t mach_vm_deallocate(vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); 67 | 68 | #define PROC_PIDPATHINFO_MAXSIZE (4 * MAXPATHLEN) 69 | int proc_pidpath(pid_t pid, void *buffer, uint32_t buffersize); 70 | 71 | #define TF_PLATFORM 0x400 72 | 73 | #define CS_VALID 0x0000001 /* dynamically valid */ 74 | #define CS_ADHOC 0x0000002 /* ad hoc signed */ 75 | #define CS_GET_TASK_ALLOW 0x0000004 /* has get-task-allow entitlement */ 76 | #define CS_INSTALLER 0x0000008 /* has installer entitlement */ 77 | 78 | #define CS_HARD 0x0000100 /* don't load invalid pages */ 79 | #define CS_KILL 0x0000200 /* kill process if it becomes invalid */ 80 | #define CS_CHECK_EXPIRATION 0x0000400 /* force expiration checking */ 81 | #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */ 82 | #define CS_ENFORCEMENT 0x0001000 /* require enforcement */ 83 | #define CS_REQUIRE_LV 0x0002000 /* require library validation */ 84 | #define CS_ENTITLEMENTS_VALIDATED 0x0004000 85 | 86 | #define CS_ALLOWED_MACHO 0x00ffffe 87 | 88 | #define CS_EXEC_SET_HARD 0x0100000 /* set CS_HARD on any exec'ed process */ 89 | #define CS_EXEC_SET_KILL 0x0200000 /* set CS_KILL on any exec'ed process */ 90 | #define CS_EXEC_SET_ENFORCEMENT 0x0400000 /* set CS_ENFORCEMENT on any exec'ed process */ 91 | #define CS_EXEC_SET_INSTALLER 0x0800000 /* set CS_INSTALLER on any exec'ed process */ 92 | 93 | #define CS_KILLED 0x1000000 /* was killed by kernel for invalidity */ 94 | #define CS_DYLD_PLATFORM 0x2000000 /* dyld used to load this is a platform binary */ 95 | #define CS_PLATFORM_BINARY 0x4000000 /* this is a platform binary */ 96 | #define CS_PLATFORM_PATH 0x8000000 /* platform binary by the fact of path (osx only) */ 97 | 98 | #define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */ 99 | #define CS_SIGNED 0x20000000 /* process has a signature (may have gone invalid) */ 100 | #define CS_DEV_CODE 0x40000000 /* code is dev signed, cannot be loaded into prod signed code */ 101 | 102 | extern mach_port_t tfp0; 103 | extern uint64_t kernel_base; 104 | extern uint64_t kernel_slide; 105 | 106 | extern uint64_t offset_kernel_task; 107 | extern uint64_t offset_zonemap; 108 | extern uint64_t offset_add_ret_gadget; 109 | extern uint64_t offset_osboolean_true; 110 | extern uint64_t offset_osboolean_false; 111 | extern uint64_t offset_osunserializexml; 112 | extern uint64_t offset_smalloc; 113 | 114 | uint64_t find_port(mach_port_name_t port); 115 | 116 | uint64_t proc_find(pid_t pid); 117 | void kern_utils_cleanup(void); 118 | 119 | void platformize(uint64_t proc); 120 | void fixup(pid_t pid); 121 | void fixup_setuid(pid_t pid, uint64_t proc); 122 | void fixup_sandbox(uint64_t proc); 123 | void fixup_cs_valid(uint64_t proc); 124 | void fixup_get_task_allow(uint64_t proc); 125 | 126 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "common.h" 10 | #include "kern_utils.h" 11 | #include "kexecute.h" 12 | #include "kmem.h" 13 | #include "offsetcache.h" 14 | #include "osobject.h" 15 | #include "offsetof.h" 16 | 17 | bool initialized = false; 18 | uint64_t offset_options = 0; 19 | 20 | __attribute__((constructor)) 21 | void ctor() { 22 | bool found_offsets = false; 23 | kern_return_t err; 24 | 25 | DEBUGLOG("the fun and games shall begin! (applying lube...)"); 26 | 27 | // tfp0, kexecute 28 | err = host_get_special_port(mach_host_self(), HOST_LOCAL_NODE, 4, &tfp0); 29 | if (err != KERN_SUCCESS) { 30 | DEBUGLOG("host_get_special_port 4: %s", mach_error_string(err)); 31 | tfp0 = KERN_INVALID_TASK; 32 | return; 33 | } 34 | DEBUGLOG("tfp0: %x", tfp0); 35 | 36 | struct task_dyld_info dyld_info = { 0 }; 37 | mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; 38 | if (task_info(tfp0, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count) == 0 && 39 | dyld_info.all_image_info_addr != 0 && 40 | dyld_info.all_image_info_addr != dyld_info.all_image_info_size + 0xfffffff007004000) { 41 | kernel_slide = dyld_info.all_image_info_size; 42 | size_t blob_size = rk64(dyld_info.all_image_info_addr); 43 | DEBUGLOG("Restoring persisted offsets cache length %zu from 0x%llx", blob_size, dyld_info.all_image_info_addr); 44 | struct cache_blob *blob = create_cache_blob(blob_size); 45 | if (kread(dyld_info.all_image_info_addr, blob, blob_size)) import_cache_blob(blob); 46 | free(blob); 47 | if (get_offset("kernel_slide") == kernel_slide) { 48 | found_offsets = true; 49 | if (get_offset("kernel_base")) { 50 | kernel_base = get_offset("kernel_base"); 51 | } else { 52 | DEBUGLOG("Didn't get kernel_base from cache???"); 53 | kernel_base = dyld_info.all_image_info_size + 0xfffffff007004000; 54 | } 55 | 56 | offset_kernel_task = get_offset("kernel_task"); 57 | offset_zonemap = get_offset("zone_map_ref"); 58 | 59 | offset_add_ret_gadget = get_offset("add_x0_x0_0x40_ret"); 60 | offset_osboolean_true = rk64(get_offset("OSBoolean_True")); 61 | offset_osboolean_false = rk64(get_offset("OSBoolean_True") + sizeof(void *)); 62 | offset_osunserializexml = get_offset("osunserializexml"); 63 | offset_smalloc = get_offset("smalloc"); 64 | offset_options = get_offset("unrestrict-options"); 65 | DEBUGLOG("options: 0x%llx, OPT_GET_TASK_ALLOW:%d OPT_CS_DEBUGGED:%d", offset_options, OPT(GET_TASK_ALLOW), OPT(CS_DEBUGGED)); 66 | } 67 | } 68 | 69 | if (!found_offsets) { 70 | CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("/jb/offsets.plist"), kCFURLPOSIXPathStyle, false); 71 | if (fileURL == NULL) { 72 | DEBUGLOG("Unable to create URL"); 73 | return; 74 | } 75 | CFDataRef off_file_data; 76 | SInt32 errorCode; 77 | Boolean status = CFURLCreateDataAndPropertiesFromResource( 78 | kCFAllocatorDefault, fileURL, &off_file_data, 79 | NULL, NULL, &errorCode); 80 | 81 | CFRelease(fileURL); 82 | if (!status) { 83 | DEBUGLOG("Unable to read /jb/offsets.plist"); 84 | return; 85 | } 86 | 87 | DEBUGLOG("off_file_data: %p", off_file_data); 88 | CFPropertyListRef offsets = CFPropertyListCreateWithData(kCFAllocatorDefault, (CFDataRef)off_file_data, kCFPropertyListImmutable, NULL, NULL); 89 | CFRelease(off_file_data); 90 | if (offsets == NULL) { 91 | DEBUGLOG("Unable to convert /jb/offsets.plist to property list"); 92 | return; 93 | } 94 | 95 | if (CFGetTypeID(offsets) != CFDictionaryGetTypeID()) { 96 | DEBUGLOG("/jb/offsets.plist did not convert to a dictionary"); 97 | CFRelease(offsets); 98 | return; 99 | } 100 | 101 | // TODO: CFStringGetCStringPtr is not to be relied upon like this... bad things will happen if this is not fixed 102 | kernel_base = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("KernelBase")), kCFStringEncodingUTF8), NULL, 16); 103 | kernel_slide = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("KernelSlide")), kCFStringEncodingUTF8), NULL, 16); 104 | 105 | offset_kernel_task = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("KernelTask")), kCFStringEncodingUTF8), NULL, 16); 106 | offset_zonemap = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("ZoneMapOffset")), kCFStringEncodingUTF8), NULL, 16); 107 | 108 | offset_add_ret_gadget = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("AddRetGadget")), kCFStringEncodingUTF8), NULL, 16); 109 | offset_osboolean_true = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("OSBooleanTrue")), kCFStringEncodingUTF8), NULL, 16); 110 | offset_osboolean_false = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("OSBooleanFalse")), kCFStringEncodingUTF8), NULL, 16); 111 | offset_osunserializexml = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("OSUnserializeXML")), kCFStringEncodingUTF8), NULL, 16); 112 | offset_smalloc = strtoull(CFStringGetCStringPtr(CFDictionaryGetValue(offsets, CFSTR("Smalloc")), kCFStringEncodingUTF8), NULL, 16); 113 | CFRelease(offsets); 114 | found_offsets = true; 115 | } 116 | 117 | DEBUGLOG("kern base: %llx, slide: %llx", kernel_base, kernel_slide); 118 | DEBUGLOG("offset_kernel_task: %llx", offset_kernel_task); 119 | DEBUGLOG("offset_zonemap: %llx", offset_zonemap); 120 | DEBUGLOG("offset_add_ret_gadget: %llx", offset_add_ret_gadget); 121 | DEBUGLOG("offset_osboolean_true: %llx", offset_osboolean_true); 122 | DEBUGLOG("offset_osboolean_false: %llx", offset_osboolean_false); 123 | DEBUGLOG("offset_osunserializexml: %llx", offset_osunserializexml); 124 | DEBUGLOG("offset_smalloc: %llx", offset_smalloc); 125 | 126 | #define MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT 6 127 | extern int memorystatus_control(uint32_t command, int32_t pid, uint32_t flags, void *buffer, size_t buffersize); 128 | 129 | if (found_offsets && init_kexecute() && OSDictionary_SetItem(rk64(rk64(rk64(proc_find(getpid()) + offsetof_p_ucred) + 0x78) + 0x8), "com.apple.private.memorystatus", offset_osboolean_true) && memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, getpid(), 0, NULL, 0) == 0) { 130 | DEBUGLOG("Initialized successfully!"); 131 | initialized = true; 132 | } else { 133 | DEBUGLOG("Failed to initialize kexecute :("); 134 | } 135 | } 136 | 137 | __attribute__((destructor)) 138 | void dtor() { 139 | DEBUGLOG("Terminating kexecute"); 140 | kern_utils_cleanup(); 141 | term_kexecute(); 142 | } 143 | -------------------------------------------------------------------------------- /sandbox.c: -------------------------------------------------------------------------------- 1 | #include "kmem.h" 2 | #include "kern_utils.h" 3 | #include "sandbox.h" 4 | #include "kexecute.h" 5 | #include "common.h" 6 | 7 | typedef uint64_t extension_hdr_t; 8 | typedef uint64_t extension_t; 9 | 10 | struct extension_hdr11 { 11 | /* 0x00 */ extension_hdr_t next; 12 | /* 0x08 */ uint64_t desc; 13 | /* 0x10 */ extension_t ext_lst; 14 | /* 0x18 */ 15 | }; 16 | 17 | struct extension11 { 18 | /* 0x00 */ extension_t next; 19 | /* 0x08 */ uint64_t desc; // always 0xffffffffffffffff 20 | /* 0x10 */ uint64_t ext_lst; // zero, since it's extension and not a header 21 | /* 0x18 */ uint8_t something[32]; // zeroed from what I've seen 22 | /* 0x38 */ uint32_t type; // see ext_type enum 23 | /* 0x3c */ uint32_t subtype; // either 0 or 4 (or whatever unhex gave?..) 24 | /* 0x40 */ uint64_t data; // a c string, meaning depends on type and hdr which had this extension 25 | /* 0x48 */ uint64_t data_len; // strlen(data) 26 | /* 0x50 */ uint64_t unk0; // always 0 27 | /* 0x58 */ uint64_t unk1; // always 0xdeadbeefdeadbeef 28 | /* 0x60 */ 29 | }; 30 | 31 | struct extension_hdr12 { 32 | /* 0x00 */ extension_hdr_t next; 33 | /* 0x8 */ extension_t ext_lst; 34 | /* 0x10 */ char desc[]; 35 | /* 0x18 */ 36 | }; 37 | 38 | struct extension12 { 39 | extension_t next; 40 | uint64_t desc; // always 0xffffffffffffffff; 41 | uint8_t something[20]; // all zero 42 | uint16_t num; // one 43 | uint8_t type; // see ext_type enum 44 | uint8_t num3; // one 45 | uint32_t subtype; // either 0 or 4 (or whatever unhex gave?..) 46 | uint32_t num4; // another number 47 | void* data; // a c string, meaning depends on type and hdr which had this extension 48 | uint64_t data_len; // strlen(data) 49 | uint16_t num5; // another one! 50 | uint8_t something_2[14]; // something v2.0 51 | uint64_t ptr3; // it's always 0 for files 52 | uint64_t ptr4; // idk 53 | }; 54 | 55 | uint64_t _smalloc(uint64_t size) { 56 | return kexecute(offset_smalloc, size, 0, 0, 0, 0, 0, 0); 57 | } 58 | 59 | uint64_t smalloc(uint64_t size) { 60 | uint64_t ret = _smalloc(size); 61 | 62 | if (ret != 0) { 63 | // IOAlloc's of small size go to zalloc 64 | ret = zm_fix_addr(ret); 65 | } 66 | 67 | return ret; 68 | } 69 | 70 | uint64_t sstrdup(const char* s) { 71 | size_t slen = strlen(s) + 1; 72 | 73 | uint64_t ks = smalloc(slen); 74 | if (ks) { 75 | kwrite(ks, s, slen); 76 | } 77 | 78 | return ks; 79 | } 80 | 81 | // Notice: path should *not* end with '/' ! 82 | uint64_t extension_create_file(const char* path, uint64_t nextptr) { 83 | size_t slen = strlen(path); 84 | 85 | if (path[slen - 1] == '/') { 86 | DEBUGLOG("No traling slash in path pls"); 87 | return 0; 88 | } 89 | 90 | uint64_t ext_p = 0; 91 | 92 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 93 | ext_p = smalloc(sizeof(struct extension12)); 94 | } else { 95 | ext_p = smalloc(sizeof(struct extension11)); 96 | } 97 | 98 | uint64_t ks = sstrdup(path); 99 | 100 | if (ext_p && ks) { 101 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 102 | struct extension12 ext; 103 | bzero(&ext, sizeof(ext)); 104 | ext.next = (extension_t)nextptr; 105 | ext.desc = 0xffffffffffffffff; 106 | 107 | ext.type = 0; 108 | ext.subtype = 0; 109 | 110 | ext.data = (void*)ks; 111 | ext.data_len = slen; 112 | 113 | ext.num = 1; 114 | ext.num3 = 1; 115 | 116 | kwrite(ext_p, &ext, sizeof(ext)); 117 | } else { 118 | struct extension11 ext; 119 | bzero(&ext, sizeof(ext)); 120 | ext.next = nextptr; 121 | ext.desc = 0xffffffffffffffff; 122 | 123 | ext.data = ks; 124 | ext.data_len = slen; 125 | 126 | kwrite(ext_p, &ext, sizeof(ext)); 127 | } 128 | } else { 129 | // XXX oh no a leak 130 | } 131 | 132 | return ext_p; 133 | } 134 | 135 | // get 64 higher bits of 64bit int multiplication 136 | // https://stackoverflow.com/a/28904636 137 | // ofc in asm it's done with 1 instruction huh 138 | // XXX there has to be a cleaner way utilizing hardware support 139 | uint64_t mulhi(uint64_t a, uint64_t b) { 140 | uint64_t a_lo = (uint32_t)a; 141 | uint64_t a_hi = a >> 32; 142 | uint64_t b_lo = (uint32_t)b; 143 | uint64_t b_hi = b >> 32; 144 | 145 | uint64_t a_x_b_hi = a_hi * b_hi; 146 | uint64_t a_x_b_mid = a_hi * b_lo; 147 | uint64_t b_x_a_mid = b_hi * a_lo; 148 | uint64_t a_x_b_lo = a_lo * b_lo; 149 | 150 | uint64_t carry_bit = ((uint64_t)(uint32_t)a_x_b_mid + 151 | (uint64_t)(uint32_t)b_x_a_mid + 152 | (a_x_b_lo >> 32) ) >> 32; 153 | 154 | uint64_t multhi = a_x_b_hi + 155 | (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + 156 | carry_bit; 157 | 158 | return multhi; 159 | } 160 | 161 | int hashing_magic(const char *desc) { 162 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 163 | unsigned int hashed; 164 | char ch, ch2; 165 | char *chp; 166 | 167 | ch = *desc; 168 | 169 | if (*desc) { 170 | chp = (char *)(desc + 1); 171 | hashed = 0x1505; 172 | 173 | do { 174 | hashed = 33 * hashed + ch; 175 | ch2 = *chp++; 176 | ch = ch2; 177 | } 178 | while (ch2); 179 | } 180 | else hashed = 0x1505; 181 | 182 | return hashed % 9; 183 | } else { 184 | // inlined into exception_add 185 | uint64_t hashed = 0x1505; 186 | 187 | // if desc == NULL, then returned value would be 8 188 | // APPL optimizes it for some reason 189 | // but meh, desc should never be NULL or you get 190 | // null dereference in exception_add 191 | // if (desc == NULL) return 8; 192 | 193 | if (desc != NULL) { 194 | for (const char* dp = desc; *dp != '\0'; ++dp) { 195 | hashed += hashed << 5; 196 | hashed += (int64_t) *dp; 197 | } 198 | } 199 | 200 | uint64_t magic = 0xe38e38e38e38e38f; 201 | 202 | uint64_t hi = mulhi(hashed, magic); 203 | hi >>= 3; 204 | hi = (hi<<3) + hi; 205 | 206 | hashed -= hi; 207 | 208 | return hashed; 209 | } 210 | } 211 | 212 | static const char *ent_key = "com.apple.security.exception.files.absolute-path.read-only"; 213 | 214 | uint64_t make_ext_hdr(const char* key, uint64_t ext_lst) { 215 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 216 | struct extension_hdr12 hdr; 217 | 218 | uint64_t khdr = smalloc(sizeof(hdr) + strlen(key) + 1); 219 | if (!khdr) { 220 | CROAK("Unable to smalloc"); 221 | return 0; 222 | } 223 | 224 | // we add headers to end 225 | hdr.next = 0; 226 | hdr.ext_lst = ext_lst; 227 | 228 | kwrite(khdr, &hdr, sizeof(hdr)); 229 | kwrite(khdr + offsetof(struct extension_hdr12, desc), key, strlen(key) + 1); 230 | 231 | return khdr; 232 | } else { 233 | struct extension_hdr11 hdr; 234 | 235 | uint64_t khdr = smalloc(sizeof(hdr)); 236 | 237 | if (khdr) { 238 | // we add headers to end 239 | hdr.next = 0; 240 | hdr.desc = sstrdup(key); 241 | if (hdr.desc == 0) { 242 | // XXX leak 243 | return 0; 244 | } 245 | 246 | hdr.ext_lst = ext_lst; 247 | kwrite(khdr, &hdr, sizeof(hdr)); 248 | } 249 | 250 | return khdr; 251 | } 252 | } 253 | 254 | void extension_add(uint64_t ext, uint64_t sb, const char* desc) { 255 | // XXX patchfinder + kexecute would be way better 256 | 257 | int slot = hashing_magic(ent_key); 258 | 259 | uint64_t insert_at_p = 0; 260 | 261 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 262 | uint64_t ext_table = rk64(sb + sizeof(void *)); 263 | insert_at_p = ext_table + slot * sizeof(void*); 264 | } else { 265 | insert_at_p = sb + sizeof(void*) + slot * sizeof(void*); 266 | } 267 | 268 | uint64_t insert_at = rk64(insert_at_p); 269 | 270 | while (insert_at != 0) { 271 | uint64_t kdsc = 0; 272 | 273 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 274 | kdsc = insert_at + offsetof(struct extension_hdr12, desc); 275 | } else { 276 | kdsc = rk64(insert_at + offsetof(struct extension_hdr11, desc)); 277 | } 278 | 279 | if (kstrcmp(kdsc, desc) == 0) { 280 | break; 281 | } 282 | 283 | insert_at_p = insert_at; 284 | insert_at = rk64(insert_at); 285 | } 286 | 287 | if (insert_at == 0) { 288 | insert_at = make_ext_hdr(ent_key, ext); 289 | wk64(insert_at_p, insert_at); 290 | } else { 291 | // XXX no duplicate check 292 | 293 | uint64_t ext_lst_p = 0; 294 | 295 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 296 | ext_lst_p = insert_at + offsetof(struct extension_hdr12, ext_lst); 297 | } else { 298 | ext_lst_p = insert_at + offsetof(struct extension_hdr11, ext_lst); 299 | } 300 | 301 | uint64_t ext_lst = rk64(ext_lst_p); 302 | 303 | while (ext_lst != 0) { 304 | DEBUGLOG("ext_lst_p = 0x%llx ext_lst = 0x%llx", ext_lst_p, ext_lst); 305 | 306 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 307 | ext_lst_p = ext_lst + offsetof(struct extension12, next); 308 | } else { 309 | ext_lst_p = ext_lst + offsetof(struct extension11, next); 310 | } 311 | 312 | ext_lst = rk64(ext_lst_p); 313 | } 314 | 315 | DEBUGLOG("ext_lst_p = 0x%llx ext_lst = 0x%llx", ext_lst_p, ext_lst); 316 | 317 | wk64(ext_lst_p, ext); 318 | } 319 | } 320 | 321 | // 1 if yes 322 | int has_file_extension(uint64_t sb, const char* path) { 323 | const char* desc = ent_key; 324 | int found = 0; 325 | 326 | int slot = hashing_magic(ent_key); 327 | 328 | uint64_t insert_at_p = 0; 329 | 330 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 331 | uint64_t ext_table = rk64(sb + sizeof(void *)); 332 | insert_at_p = ext_table + slot * sizeof(void*); 333 | } else { 334 | insert_at_p = sb + sizeof(void*) + slot * sizeof(void*); 335 | } 336 | 337 | uint64_t insert_at = rk64(insert_at_p); 338 | 339 | while (insert_at != 0) { 340 | uint64_t kdsc = 0; 341 | 342 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 343 | kdsc = insert_at + offsetof(struct extension_hdr12, desc); 344 | } else { 345 | kdsc = rk64(insert_at + offsetof(struct extension_hdr11, desc)); 346 | } 347 | 348 | if (kstrcmp(kdsc, desc) == 0) { 349 | break; 350 | } 351 | 352 | insert_at_p = insert_at; 353 | insert_at = rk64(insert_at); 354 | } 355 | 356 | if (insert_at != 0) { 357 | uint64_t ext_lst = 0; 358 | 359 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 360 | ext_lst = rk64(insert_at + offsetof(struct extension_hdr12, ext_lst)); 361 | } else { 362 | ext_lst = rk64(insert_at + offsetof(struct extension_hdr11, ext_lst)); 363 | } 364 | 365 | uint64_t plen = strlen(path); 366 | char *exist = malloc(plen + 1); 367 | if (!exist) { 368 | CROAK("Unable to malloc"); 369 | } 370 | exist[plen] = '\0'; 371 | 372 | while (ext_lst != 0) { 373 | // XXX no type/subtype check 374 | 375 | uint64_t data_len = 0; 376 | 377 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 378 | data_len = rk64(ext_lst + offsetof(struct extension12, data_len)); 379 | } else { 380 | data_len = rk64(ext_lst + offsetof(struct extension11, data_len)); 381 | } 382 | 383 | if (data_len == plen) { 384 | uint64_t data = 0; 385 | 386 | if (kCFCoreFoundationVersionNumber >= 1535.12) { 387 | data = ext_lst + offsetof(struct extension12, data); 388 | } else { 389 | data = rk64(ext_lst + offsetof(struct extension11, data)); 390 | } 391 | 392 | kread(data, exist, plen); 393 | 394 | if (strcmp(path, exist) == 0) { 395 | found = 1; 396 | break; 397 | } 398 | } 399 | 400 | ext_lst = rk64(ext_lst); 401 | } 402 | 403 | free(exist); 404 | } 405 | 406 | return found; 407 | } 408 | -------------------------------------------------------------------------------- /sandbox.h: -------------------------------------------------------------------------------- 1 | 2 | // see https://stek29.rocks/2018/01/26/sandbox.html 3 | 4 | void extension_add(uint64_t ext, uint64_t sb, const char* desc); 5 | uint64_t extension_create_file(const char* path, uint64_t nextptr); 6 | int has_file_extension(uint64_t sb, const char* path); 7 | -------------------------------------------------------------------------------- /unrestrict.c: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import "kern_utils.h" 7 | #import "common.h" 8 | 9 | FILE *log_file = NULL; 10 | 11 | #define CS_OPS_STATUS 0 /* return status */ 12 | int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); 13 | 14 | bool MSunrestrict0(mach_port_t task) { 15 | if (!initialized) return true; 16 | 17 | char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; 18 | bzero(pathbuf, sizeof(pathbuf)); 19 | 20 | pid_t pid; 21 | if ( (pid_for_task(task, &pid) != 0) || pid <= 1) { 22 | return true; 23 | } 24 | proc_pidpath(pid, pathbuf, sizeof(pathbuf)); 25 | 26 | if (strcmp(pathbuf, "/usr/libexec/xpcproxy")==0 || 27 | strcmp(pathbuf, "/usr/libexec/securityd")==0 || 28 | strcmp(pathbuf, "/usr/libexec/trustd")==0) { 29 | return true; 30 | } 31 | 32 | DEBUGLOG("%s: (%d) fixing up", pathbuf, pid); 33 | fixup(pid); 34 | return true; 35 | } 36 | 37 | bool MSrevalidate0(mach_port_t task) { 38 | if (!initialized) return true; 39 | 40 | char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; 41 | bzero(pathbuf, sizeof(pathbuf)); 42 | 43 | pid_t pid; 44 | if ( (pid_for_task(task, &pid) != 0) || pid <= 1) { 45 | return true; 46 | } 47 | proc_pidpath(pid, pathbuf, sizeof(pathbuf)); 48 | 49 | if (strcmp(pathbuf, "/usr/libexec/xpcproxy")==0 || 50 | strcmp(pathbuf, "/usr/libexec/securityd")==0 || 51 | strcmp(pathbuf, "/usr/libexec/trustd")==0) { 52 | return true; 53 | } 54 | 55 | uint32_t status; 56 | if (csops(pid, CS_OPS_STATUS, &status, sizeof(status)) < 0) 57 | return true; 58 | 59 | if ((status & CS_VALID) == 0) { 60 | uint64_t proc = proc_find(pid); 61 | if (proc == 0) { 62 | DEBUGLOG("failed to find proc for pid %d!", pid); 63 | return true; 64 | } 65 | 66 | fixup_cs_valid(proc); 67 | } 68 | 69 | return true; 70 | } 71 | --------------------------------------------------------------------------------