├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── ent.xml ├── example ├── Makefile ├── README.md ├── kaddr_of_port.c ├── kernel_thread.c ├── open1_hook.c ├── shmem.c └── user_client_monitor.c ├── include ├── asm │ ├── asm.h │ └── asm_support.h ├── common │ ├── common.h │ └── preboot_hook.h ├── pf │ ├── 13 │ │ └── pf.h │ ├── 14 │ │ └── pf.h │ ├── 15 │ │ └── pf.h │ ├── offsets.h │ ├── pf_common.h │ └── pfs.h ├── pongo.h └── xnuspy │ ├── el1 │ ├── debug.h │ ├── externs.h │ ├── libc.h │ ├── mem.h │ ├── pte.h │ ├── tramp.h │ ├── utils.h │ └── wrappers.h │ ├── el3 │ └── kpp.h │ ├── xnuspy_cache.h │ ├── xnuspy_ctl.h │ └── xnuspy_structs.h ├── klog ├── Makefile ├── README.md └── klog.c ├── loader ├── Makefile └── loader.c ├── module ├── Makefile ├── README.md ├── common │ ├── Makefile │ ├── asm.c │ └── common.c ├── el1 │ ├── Makefile │ ├── hook_system_check_sysctlbyname_hook.h │ ├── hook_system_check_sysctlbyname_hook.s │ ├── xnuspy_ctl │ │ ├── Makefile │ │ ├── debug.c │ │ ├── libc.c │ │ ├── mem.c │ │ ├── pte.c │ │ ├── tramp.c │ │ ├── utils.c │ │ ├── wrappers.c │ │ └── xnuspy_ctl.c │ ├── xnuspy_ctl_tramp.h │ └── xnuspy_ctl_tramp.s ├── el3 │ ├── Makefile │ ├── kpp.c │ └── kpp.s ├── pf │ ├── 13 │ │ ├── Makefile │ │ └── pf.c │ ├── 14 │ │ ├── Makefile │ │ └── pf.c │ ├── 15 │ │ ├── Makefile │ │ └── pf.c │ ├── Makefile │ └── README.md ├── preboot_hook.c └── xnuspy.c ├── opdump ├── Makefile └── opdump.c └── open1_hook.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | *.ld 4 | *.o 5 | *.dSYM 6 | 7 | # Auto-generated header files containing byte arrays 8 | *_instrs.h 9 | include/xnuspy/el3/kpp_patches.h 10 | 11 | # Binaries generated at build-time (probably not the best way of doing this) 12 | example/user_client_monitor 13 | loader/loader 14 | module/el1/hook_system_check_sysctlbyname_hook 15 | module/el1/xnuspy_ctl/xnuspy_ctl 16 | module/el1/xnuspy_ctl_tramp 17 | module/el3/kpp 18 | module/xnuspy 19 | opdump/opdump 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Justin Sherman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RP = $(realpath $(shell pwd)) 2 | 3 | export RP 4 | 5 | TARGET_DIRS = loader opdump module klog example 6 | 7 | all : $(TARGET_DIRS) 8 | 9 | .PHONY: all $(TARGET_DIRS) 10 | 11 | $(TARGET_DIRS) : 12 | $(MAKE) -C $@ 13 | -------------------------------------------------------------------------------- /ent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.apple.wifi_apsta.event_monitor 5 | 6 | com.apple.private.aets.user-access 7 | 8 | com.apple.developer.driverkit 9 | 10 | platform-application 11 | 12 | run-unsigned-code 13 | 14 | get-task-allow 15 | 16 | task_for_pid-allow 17 | 18 | com.apple.system-task-ports 19 | 20 | com.apple.private.security.container-required 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | SDK = $(shell xcrun --sdk iphoneos --show-sdk-path) 2 | CC = $(shell xcrun --sdk $(SDK) --find clang) 3 | CFLAGS = -isysroot $(SDK) -arch arm64 -fno-stack-protector -D_FORTIFY_SOURCE=0 4 | CFLAGS += -Wno-deprecated-declarations 5 | 6 | # If user invokes make in this directory, $(RP) will not be defined 7 | ifndef RP 8 | CFLAGS += -I../include 9 | else 10 | CFLAGS += -I$(RP)/include 11 | endif 12 | 13 | XNUSPY_HEADER = ../include/xnuspy/xnuspy_ctl.h 14 | 15 | all : open1_hook user_client_monitor kernel_thread kaddr_of_port shmem 16 | 17 | .PHONY : upload 18 | 19 | upload : open1_hook user_client_monitor kernel_thread kaddr_of_port shmem 20 | rsync -sz -e 'ssh -p 2222' open1_hook root@localhost:/var/root 21 | rsync -sz -e 'ssh -p 2222' user_client_monitor root@localhost:/var/root 22 | rsync -sz -e 'ssh -p 2222' kernel_thread root@localhost:/var/root 23 | rsync -sz -e 'ssh -p 2222' kaddr_of_port root@localhost:/var/root 24 | rsync -sz -e 'ssh -p 2222' shmem root@localhost:/var/root 25 | 26 | open1_hook : open1_hook.c $(XNUSPY_HEADER) 27 | $(CC) $(CFLAGS) open1_hook.c -o open1_hook 28 | ldid -S../ent.xml ./open1_hook 29 | 30 | user_client_monitor : user_client_monitor.c $(XNUSPY_HEADER) 31 | $(CC) $(CFLAGS) user_client_monitor.c -o user_client_monitor 32 | ldid -S../ent.xml ./user_client_monitor 33 | 34 | kernel_thread : kernel_thread.c $(XNUSPY_HEADER) 35 | $(CC) $(CFLAGS) kernel_thread.c -o kernel_thread 36 | ldid -S../ent.xml ./kernel_thread 37 | 38 | kaddr_of_port : kaddr_of_port.c $(XNUSPY_HEADER) 39 | $(CC) $(CFLAGS) kaddr_of_port.c -o kaddr_of_port 40 | ldid -S../ent.xml ./kaddr_of_port 41 | 42 | shmem : shmem.c $(XNUSPY_HEADER) 43 | $(CC) $(CFLAGS) shmem.c -o shmem 44 | ldid -S../ent.xml ./shmem 45 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | Porting these to other kernels is just a matter of swapping out offsets. 3 | 4 | Run `make` in this directory to build all of them. `make upload` will 5 | upload them to your device, but you may have to swap out the port number. 6 | 7 | ### open1_hook 8 | This hooks `open1` and logs a message about the file a process just tried 9 | to open, while at the same time preventing everyone from `open`'ing 10 | `/var/mobile/testfile.txt`. 11 | 12 | Some offsets I already have for `open1`: 13 | 14 | ``` 15 | iPhone X 13.3.1: 0xfffffff007d70534 16 | iPhone X 15.0: 0xfffffff007d574f4 17 | iPhone 8 13.6.1: 0xfffffff007d99c1c 18 | iPhone 8 14.6: 0xfffffff007c4c8c4 19 | iPhone 8 14.8: 0xfffffff007c4c930 20 | iPhone 7 14.1: 0xfffffff00730aa64 21 | iPhone 7 14.5: 0xfffffff00733b960 22 | iPhone 7 15.0: 0xfffffff00736addc 23 | iPhone SE (2016) 14.3: 0xfffffff0072da190 24 | iPhone SE (2016) 14.5: 0xfffffff0072fd3dc 25 | ``` 26 | 27 | ### user_client_monitor 28 | This hooks `is_io_service_open_extended` and logs a descriptive message every 29 | time any process opens a new IOKit user client. 30 | 31 | Some offsets I already have for this: 32 | 33 | ``` 34 | iPhone X 13.3.1: 35 | getClassName: 0xfffffff0080bf600 36 | is_io_service_open_extended: 0xfffffff008168d28 37 | 38 | iPhone X 15.0: 39 | getClassName: 0xfffffff0080e8ba0 40 | is_io_service_open_extended: 0xfffffff0081c1580 41 | 42 | iPhone 8 13.6.1: 43 | getClassName: 0xfffffff0080ec9a8 44 | is_io_service_open_extended: 0xfffffff0081994dc 45 | 46 | iPhone 8 14.6: 47 | getClassName: 0xfffffff007fbd108 48 | is_io_service_open_extended: 0xfffffff008085dd0 49 | 50 | iPhone 8 14.8: 51 | getClassName: 0xfffffff007fbd400 52 | is_io_service_open_extended: 0xfffffff0080861a0 53 | 54 | iPhone 7 14.1: 55 | getClassName: 0xfffffff00765be54 56 | is_io_service_open_extended: 0xfffffff00770d114 57 | 58 | iPhone 7 14.5: 59 | getClassName: 0xfffffff00769130c 60 | is_io_service_open_extended: 0xfffffff0077473a8 61 | 62 | iPhone 7 15.0: 63 | getClassName: 0xfffffff0076dcac4 64 | is_io_service_open_extended: 0xfffffff0077a1f2c 65 | 66 | iPhone SE (2016) 14.3: 67 | getClassName: 0xfffffff00762e3e4 68 | is_io_service_open_extended: 0xfffffff0076e3104 69 | 70 | iPhone SE (2016) 14.5: 71 | getClassName: 0xfffffff007652c80 72 | is_io_service_open_extended: 0xfffffff007708dac 73 | ``` 74 | 75 | ### kernel_thread 76 | This hooks `hookme`, invokes `xnuspy_ctl` to call it, starts up a kernel 77 | thread, and registers a death callback. 78 | 79 | ### kaddr_of_port 80 | This uses `XNUSPY_KREAD` to determine the kernel addres of a userspace 81 | Mach port handle. 82 | 83 | Some offsets I already have for `offsetof(struct task, itk_space)`: 84 | 85 | ``` 86 | iPhone 8 13.6.1: 0x320 87 | iPhone X 13.3.1: 0x320 88 | iPhone 7 14.1: 0x330 89 | ``` 90 | 91 | ### shmem 92 | Demonstrates kernel-user shared data synchronization with shared memory 93 | returned by `mkshmem_ktou`. 94 | -------------------------------------------------------------------------------- /example/kaddr_of_port.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | static long SYS_xnuspy_ctl = 0; 14 | 15 | /* https://gist.github.com/ccbrown/9722406 */ 16 | void kdump(const void *kaddr, size_t size) { 17 | char *data = malloc(size); 18 | if(syscall(SYS_xnuspy_ctl, XNUSPY_KREAD, kaddr, data, size)){ 19 | printf("%s: kread failed: %s\n", __func__, strerror(errno)); 20 | return; 21 | } 22 | char ascii[17]; 23 | size_t i, j; 24 | ascii[16] = '\0'; 25 | for (i = 0; i < size; ++i) { 26 | printf("%02X ", ((unsigned char*)data)[i]); 27 | if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { 28 | ascii[i % 16] = ((unsigned char*)data)[i]; 29 | } else { 30 | ascii[i % 16] = '.'; 31 | } 32 | if ((i+1) % 8 == 0 || i+1 == size) { 33 | printf(" "); 34 | if ((i+1) % 16 == 0) { 35 | printf("| %s \n", ascii); 36 | } else if (i+1 == size) { 37 | ascii[(i+1) % 16] = '\0'; 38 | if ((i+1) % 16 <= 8) { 39 | printf(" "); 40 | } 41 | for (j = (i+1) % 16; j < 16; ++j) { 42 | printf(" "); 43 | } 44 | printf("| %s \n", ascii); 45 | } 46 | } 47 | } 48 | free(data); 49 | } 50 | 51 | struct ipc_entry { 52 | uint64_t ie_object; 53 | uint32_t ie_bits; 54 | uint32_t ie_dist : 12; 55 | uint32_t ie_index : 20; 56 | union { 57 | uint32_t next; 58 | uint32_t request; 59 | } index; 60 | }; 61 | 62 | struct ipc_space { 63 | struct { 64 | uint64_t data; 65 | uint32_t type; 66 | uint32_t pad; 67 | } is_lock_data; 68 | uint32_t is_bits; 69 | uint32_t is_table_size; 70 | uint32_t is_table_hashed; 71 | uint32_t is_table_free; 72 | struct ipc_entry *is_table; 73 | uint64_t is_task; 74 | uint64_t is_table_next; 75 | uint32_t is_low_mod; 76 | uint32_t is_high_mod; 77 | 78 | /* other stuff that isn't needed */ 79 | }; 80 | 81 | static uint64_t kaddr_of_port(mach_port_t p){ 82 | uint64_t tp; 83 | int ret = syscall(SYS_xnuspy_ctl, XNUSPY_GET_CURRENT_THREAD, &tp, 0, 0); 84 | 85 | if(ret){ 86 | printf("%s: XNUSPY_GET_CURRENT_THREAD failed: %s\n", __func__, 87 | strerror(errno)); 88 | return 0; 89 | } 90 | 91 | uint64_t offsetof_map; 92 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, OFFSETOF_STRUCT_THREAD_MAP, 93 | &offsetof_map, 0); 94 | 95 | if(ret){ 96 | printf("%s: getting map offset failed: %s\n", __func__, 97 | strerror(errno)); 98 | return 0; 99 | } 100 | 101 | /* task pointer is conveniently right before map pointer for all 102 | * my phones */ 103 | uint64_t task; 104 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_KREAD, tp + (offsetof_map - 8), &task, 105 | sizeof(task)); 106 | 107 | if(ret){ 108 | printf("%s: kread failed for task: %s\n", __func__, strerror(errno)); 109 | return 0; 110 | } 111 | 112 | if(!task){ 113 | printf("%s: task NULL?\n", __func__); 114 | return 0; 115 | } 116 | 117 | /* Offsets: 118 | * iPhone 8 13.6.1: 0x320 119 | * iPhone X 13.3.1: 0x320 120 | * iPhone 7 14.1: 0x330 121 | */ 122 | uint64_t itk_space; 123 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_KREAD, task + 0x320, &itk_space, 124 | sizeof(itk_space)); 125 | 126 | if(ret){ 127 | printf("%s: kread failed for itk_space: %s\n", __func__, 128 | strerror(errno)); 129 | return 0; 130 | } 131 | 132 | struct ipc_entry *is_tablep = NULL; 133 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_KREAD, 134 | itk_space + __builtin_offsetof(struct ipc_space, is_table), 135 | &is_tablep, sizeof(is_tablep)); 136 | 137 | if(ret){ 138 | printf("%s: kread for is_table failed: %s\n", __func__, 139 | strerror(errno)); 140 | return 0; 141 | } 142 | 143 | uint64_t kaddr; 144 | struct ipc_entry *entryp = is_tablep + (p >> 8); 145 | 146 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_KREAD, entryp, &kaddr, sizeof(kaddr)); 147 | 148 | if(ret){ 149 | printf("%s: kread for ie_object failed: %s\n", __func__, 150 | strerror(errno)); 151 | return 0; 152 | } 153 | 154 | return kaddr; 155 | } 156 | 157 | int main(int argc, char **argv){ 158 | size_t oldlen = sizeof(long); 159 | int ret = sysctlbyname("kern.xnuspy_ctl_callnum", &SYS_xnuspy_ctl, 160 | &oldlen, NULL, 0); 161 | 162 | if(ret == -1){ 163 | printf("sysctlbyname with kern.xnuspy_ctl_callnum failed: %s\n", 164 | strerror(errno)); 165 | return 1; 166 | } 167 | 168 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CHECK_IF_PATCHED, 0, 0, 0); 169 | 170 | if(ret != 999){ 171 | printf("xnuspy_ctl isn't present?\n"); 172 | return 1; 173 | } 174 | 175 | uint64_t taskport_kaddr = kaddr_of_port(mach_task_self()); 176 | 177 | if(!taskport_kaddr) 178 | return 1; 179 | 180 | printf("mach_task_self() @ %#llx\n", taskport_kaddr); 181 | 182 | kdump((void *)taskport_kaddr, 0xa8); 183 | 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /example/kernel_thread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | __attribute__ ((naked)) static void *current_thread(void){ 14 | asm("" 15 | "mrs x0, tpidr_el1\n" 16 | "ret\n" 17 | ); 18 | } 19 | 20 | typedef void (*thread_continue_t)(void *param, int wait_result); 21 | 22 | static void (*IOSleep)(unsigned int millis); 23 | static kern_return_t (*kernel_thread_start)(thread_continue_t cont, void *param, 24 | void **thread); 25 | static void (*kprintf)(const char *fmt, ...); 26 | static void (*thread_deallocate)(void *thread); 27 | static void (*_thread_terminate)(void *thread); 28 | 29 | static uint64_t kernel_slide, hookme_addr; 30 | 31 | static int time_to_die = 0; 32 | 33 | static void kernel_thread_fxn(void *param, int wait_result){ 34 | while(!time_to_die){ 35 | kprintf("%s: alive, but at what cost?\n", __func__); 36 | IOSleep(1000); 37 | } 38 | 39 | kprintf("%s: goodbye\n", __func__); 40 | 41 | _thread_terminate(current_thread()); 42 | 43 | /* We shouldn't reach here */ 44 | 45 | kprintf("%s: we are still alive?\n", __func__); 46 | } 47 | 48 | static void death_callback(void){ 49 | kprintf("%s: called\n", __func__); 50 | time_to_die = 1; 51 | } 52 | 53 | static int kernel_thread_made = 0; 54 | 55 | static void hookme_hook(void *arg){ 56 | kprintf("%s: we were called!\n", __func__); 57 | 58 | if(kernel_thread_made) 59 | return; 60 | 61 | void *thread; 62 | kern_return_t kret = kernel_thread_start(kernel_thread_fxn, NULL, &thread); 63 | 64 | if(kret) 65 | kprintf("%s: could not make kernel thread: %#x\n", __func__, kret); 66 | else{ 67 | /* Throw away the reference from kernel_thread_start */ 68 | thread_deallocate(thread); 69 | kernel_thread_made = 1; 70 | kprintf("%s: created kernel thread\n", __func__); 71 | } 72 | } 73 | 74 | static long SYS_xnuspy_ctl = 0; 75 | 76 | static int gather_kernel_offsets(void){ 77 | int ret; 78 | 79 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, IOSLEEP, &IOSleep, 0); 80 | 81 | if(ret){ 82 | printf("Failed getting IOSleep\n"); 83 | return ret; 84 | } 85 | 86 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, KERNEL_THREAD_START, 87 | &kernel_thread_start, 0); 88 | 89 | if(ret){ 90 | printf("Failed getting kernel_thread_start\n"); 91 | return ret; 92 | } 93 | 94 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, THREAD_DEALLOCATE, 95 | &thread_deallocate, 0); 96 | 97 | if(ret){ 98 | printf("Failed getting thread_deallocate\n"); 99 | return ret; 100 | } 101 | 102 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, THREAD_TERMINATE, 103 | &_thread_terminate, 0); 104 | 105 | if(ret){ 106 | printf("Failed getting thread_terminate\n"); 107 | return ret; 108 | } 109 | 110 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, KPRINTF, &kprintf, 0); 111 | 112 | if(ret){ 113 | printf("Failed getting kprintf\n"); 114 | return ret; 115 | } 116 | 117 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, KERNEL_SLIDE, 118 | &kernel_slide, 0); 119 | 120 | if(ret){ 121 | printf("Failed getting kernel slide\n"); 122 | return ret; 123 | } 124 | 125 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, HOOKME, &hookme_addr, 0); 126 | 127 | if(ret){ 128 | printf("Failed getting hookme\n"); 129 | return ret; 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | int main(int argc, char **argv){ 136 | size_t oldlen = sizeof(long); 137 | int ret = sysctlbyname("kern.xnuspy_ctl_callnum", &SYS_xnuspy_ctl, 138 | &oldlen, NULL, 0); 139 | 140 | if(ret == -1){ 141 | printf("sysctlbyname with kern.xnuspy_ctl_callnum failed: %s\n", 142 | strerror(errno)); 143 | return 1; 144 | } 145 | 146 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CHECK_IF_PATCHED, 0, 0, 0); 147 | 148 | if(ret != 999){ 149 | printf("xnuspy_ctl isn't present?\n"); 150 | return 1; 151 | } 152 | 153 | ret = gather_kernel_offsets(); 154 | 155 | if(ret){ 156 | printf("something failed: %s\n", strerror(errno)); 157 | return 1; 158 | } 159 | 160 | /* xnuspy does not operate on slid addresses */ 161 | hookme_addr -= kernel_slide; 162 | 163 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_INSTALL_HOOK, hookme_addr, 164 | hookme_hook, NULL); 165 | 166 | if(ret){ 167 | printf("Could not hook hookme: %s\n", strerror(errno)); 168 | return 1; 169 | } 170 | 171 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_REGISTER_DEATH_CALLBACK, 172 | death_callback, 0, 0); 173 | 174 | if(ret){ 175 | printf("Could not register death callback: %s\n", strerror(errno)); 176 | return 1; 177 | } 178 | 179 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CALL_HOOKME, 0, 0, 0); 180 | 181 | if(ret){ 182 | printf("Calling hookme not supported\n"); 183 | return 1; 184 | } 185 | 186 | printf("Ctrl C or enter to quit and invoke death callback\n"); 187 | getchar(); 188 | 189 | return 0; 190 | } 191 | -------------------------------------------------------------------------------- /example/open1_hook.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | static void (*_bzero)(void *p, size_t n); 14 | static int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); 15 | static void *(*current_proc)(void); 16 | static void (*kprintf)(const char *, ...); 17 | static void (*proc_name)(int pid, char *buf, int size); 18 | static pid_t (*proc_pid)(void *); 19 | static int (*_strcmp)(const char *s1, const char *s2); 20 | static void *(*unified_kalloc)(size_t sz); 21 | static void (*unified_kfree)(void *ptr); 22 | 23 | static uint64_t kernel_slide; 24 | 25 | static uint8_t curcpu(void){ 26 | uint64_t mpidr_el1; 27 | asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr_el1)); 28 | return (uint8_t)(mpidr_el1 & 0xff); 29 | } 30 | 31 | static pid_t caller_pid(void){ 32 | return proc_pid(current_proc()); 33 | } 34 | 35 | /* bsd/sys/uio.h */ 36 | enum uio_seg { 37 | UIO_USERSPACE = 0, /* kernel address is virtual, to/from user virtual */ 38 | UIO_SYSSPACE = 2, /* kernel address is virtual, to/from system virtual */ 39 | UIO_USERSPACE32 = 5, /* kernel address is virtual, to/from user 32-bit virtual */ 40 | UIO_USERSPACE64 = 8, /* kernel address is virtual, to/from user 64-bit virtual */ 41 | UIO_SYSSPACE32 = 11 /* deprecated */ 42 | }; 43 | 44 | #define UIO_SEG_IS_USER_SPACE( a_uio_seg ) \ 45 | ( (a_uio_seg) == UIO_USERSPACE64 || (a_uio_seg) == UIO_USERSPACE32 || \ 46 | (a_uio_seg) == UIO_USERSPACE ) 47 | 48 | /* bsd/sys/namei.h */ 49 | #define PATHBUFLEN 256 50 | 51 | struct nameidata { 52 | char * /* __user */ ni_dirp; 53 | enum uio_seg ni_segflag; 54 | /* ... */ 55 | }; 56 | 57 | #define BLOCKED_FILE "/var/mobile/testfile.txt" 58 | 59 | static int (*open1_orig)(void *vfsctx, struct nameidata *ndp, int uflags, 60 | void *vap, void *fp_zalloc, void *cra, int32_t *retval); 61 | 62 | static int open1(void *vfsctx, struct nameidata *ndp, int uflags, 63 | void *vap, void *fp_zalloc, void *cra, int32_t *retval){ 64 | char *path = NULL; 65 | 66 | if(!(ndp->ni_dirp && UIO_SEG_IS_USER_SPACE(ndp->ni_segflag))) 67 | goto orig; 68 | 69 | size_t sz = PATHBUFLEN; 70 | 71 | if(!(path = unified_kalloc(sz))) 72 | goto orig; 73 | 74 | _bzero(path, sz); 75 | 76 | size_t pathlen = 0; 77 | int res = copyinstr(ndp->ni_dirp, path, sz, &pathlen); 78 | 79 | if(res) 80 | goto orig; 81 | 82 | path[pathlen - 1] = '\0'; 83 | 84 | uint8_t cpu = curcpu(); 85 | pid_t caller = caller_pid(); 86 | 87 | char *caller_name = unified_kalloc(MAXCOMLEN + 1); 88 | 89 | if(!caller_name) 90 | goto orig; 91 | 92 | /* proc_name doesn't bzero for some version of iOS 13 */ 93 | _bzero(caller_name, MAXCOMLEN + 1); 94 | proc_name(caller, caller_name, MAXCOMLEN + 1); 95 | 96 | kprintf("%s: (CPU %d): '%s' (%d) wants to open '%s'\n", __func__, cpu, 97 | caller_name, caller, path); 98 | 99 | unified_kfree(caller_name); 100 | 101 | if(_strcmp(path, BLOCKED_FILE) == 0){ 102 | kprintf("%s: denying open for '%s'\n", __func__, path); 103 | unified_kfree(path); 104 | *retval = -1; 105 | return ENOENT; 106 | } 107 | 108 | orig:; 109 | if(path) 110 | unified_kfree(path); 111 | 112 | return open1_orig(vfsctx, ndp, uflags, vap, fp_zalloc, cra, retval); 113 | } 114 | 115 | static long SYS_xnuspy_ctl = 0; 116 | 117 | static int gather_kernel_offsets(void){ 118 | int ret; 119 | #define GET(a, b) \ 120 | do { \ 121 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, a, b, 0); \ 122 | if(ret){ \ 123 | printf("%s: failed getting %s\n", __func__, #a); \ 124 | return ret; \ 125 | } \ 126 | } while (0) 127 | 128 | GET(BZERO, &_bzero); 129 | GET(COPYINSTR, ©instr); 130 | GET(CURRENT_PROC, ¤t_proc); 131 | GET(KPRINTF, &kprintf); 132 | GET(PROC_NAME, &proc_name); 133 | GET(PROC_PID, &proc_pid); 134 | GET(STRCMP, &_strcmp); 135 | GET(UNIFIED_KALLOC, &unified_kalloc); 136 | GET(UNIFIED_KFREE, &unified_kfree); 137 | 138 | return 0; 139 | } 140 | 141 | int main(int argc, char **argv){ 142 | size_t oldlen = sizeof(long); 143 | int ret = sysctlbyname("kern.xnuspy_ctl_callnum", &SYS_xnuspy_ctl, 144 | &oldlen, NULL, 0); 145 | 146 | if(ret == -1){ 147 | printf("sysctlbyname with kern.xnuspy_ctl_callnum failed: %s\n", 148 | strerror(errno)); 149 | return 1; 150 | } 151 | 152 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CHECK_IF_PATCHED, 0, 0, 0); 153 | 154 | if(ret != 999){ 155 | printf("xnuspy_ctl isn't present?\n"); 156 | return 1; 157 | } 158 | 159 | ret = gather_kernel_offsets(); 160 | 161 | if(ret){ 162 | printf("something failed: %s\n", strerror(errno)); 163 | return 1; 164 | } 165 | 166 | /* iPhone X 15.0 */ 167 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_INSTALL_HOOK, 0xfffffff007d574f4, 168 | open1, &open1_orig); 169 | 170 | if(ret){ 171 | printf("Could not hook open1: %s\n", strerror(errno)); 172 | return 1; 173 | } 174 | 175 | for(;;){ 176 | int fd = open(BLOCKED_FILE, O_CREAT); 177 | 178 | if(fd == -1) 179 | printf("open failed: %s\n", strerror(errno)); 180 | else{ 181 | printf("Got valid fd? %d\n", fd); 182 | close(fd); 183 | } 184 | 185 | sleep(1); 186 | } 187 | 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /example/shmem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | static kuslck_t g_kuslck = KUSLCK_INITIALIZER; 15 | 16 | static bool g_dead = false; 17 | static bool g_go = false; 18 | static bool g_made = false; 19 | static bool g_kernel_racer_finished = false; 20 | 21 | static uint64_t *g_kernel_valp = NULL; 22 | static struct xnuspy_shmem g_kernel_valp_shmem; 23 | 24 | __attribute__ ((naked)) static void *current_thread(void){ 25 | asm("" 26 | "mrs x0, tpidr_el1\n" 27 | "ret\n" 28 | ); 29 | } 30 | 31 | static uint64_t kernel_slide, hookme_addr; 32 | static long SYS_xnuspy_ctl = 0; 33 | static void *kernel_map; 34 | 35 | typedef void (*thread_continue_t)(void *param, int wait_result); 36 | 37 | static void (*IOSleep)(unsigned int millis); 38 | static kern_return_t (*kernel_thread_start)(thread_continue_t cont, void *param, 39 | void **thread); 40 | static void (*kprintf)(const char *fmt, ...); 41 | static void (*thread_deallocate)(void *thread); 42 | static void (*_thread_terminate)(void *thread); 43 | 44 | static int (*mkshmem_ktou)(uint64_t kaddr, uint64_t sz, vm_prot_t prot, 45 | struct xnuspy_shmem *shmemp); 46 | static int (*mkshmem_utok)(uint64_t uaddr, uint64_t sz, vm_prot_t prot, 47 | struct xnuspy_shmem *shmemp); 48 | static int (*mkshmem_raw)(uint64_t addr, uint64_t sz, vm_prot_t prot, 49 | void *from, void *to, struct xnuspy_shmem *shmemp); 50 | static int (*shmem_destroy)(struct xnuspy_shmem *); 51 | 52 | static kern_return_t (*vm_allocate_external)(void *map, uint64_t *address, 53 | uint64_t size, int flags); 54 | 55 | static void kernel_racer(void *param, int wait_result){ 56 | while(!g_go){ 57 | if(g_dead) 58 | break; 59 | } 60 | 61 | for(int i=0; i<500; i++){ 62 | kuslck_lock(g_kuslck); 63 | (*g_kernel_valp)++; 64 | kuslck_unlock(g_kuslck); 65 | } 66 | 67 | g_kernel_racer_finished = true; 68 | 69 | _thread_terminate(current_thread()); 70 | } 71 | 72 | static void death_callback(void){ 73 | kprintf("%s: called\n", __func__); 74 | shmem_destroy(&g_kernel_valp_shmem); 75 | g_dead = true; 76 | } 77 | 78 | static void hookme_hook(void *arg){ 79 | if(g_made) 80 | return; 81 | 82 | void *thread; 83 | kern_return_t kret = kernel_thread_start(kernel_racer, NULL, &thread); 84 | 85 | if(kret){ 86 | kprintf("%s: kernel_thread_start returned %#x\n", __func__, kret); 87 | return; 88 | } 89 | 90 | thread_deallocate(thread); 91 | 92 | kret = vm_allocate_external(kernel_map, (uint64_t *)&g_kernel_valp, 93 | 0x4000, VM_FLAGS_ANYWHERE); 94 | 95 | if(kret){ 96 | kprintf("%s: mach_vm_allocate_external: %#x\n", __func__, kret); 97 | return; 98 | } 99 | 100 | int res = mkshmem_ktou((uint64_t)g_kernel_valp, 0x4000, VM_PROT_READ | 101 | VM_PROT_WRITE, &g_kernel_valp_shmem); 102 | 103 | if(res){ 104 | kprintf("%s: mkshmem_ktou failed: %d\n", __func__, res); 105 | return; 106 | } 107 | 108 | kprintf("%s: returned shmem: %p\n", __func__, 109 | g_kernel_valp_shmem.shm_base); 110 | 111 | g_made = true; 112 | } 113 | 114 | static int gather_kernel_offsets(void){ 115 | int ret; 116 | #define GET(a, b) \ 117 | do { \ 118 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, a, b, 0); \ 119 | if(ret){ \ 120 | printf("%s: failed getting %s\n", __func__, #a); \ 121 | return ret; \ 122 | } \ 123 | } while (0) 124 | 125 | GET(KERNEL_THREAD_START, &kernel_thread_start); 126 | GET(KPRINTF, &kprintf); 127 | GET(THREAD_DEALLOCATE, &thread_deallocate); 128 | GET(THREAD_TERMINATE, &_thread_terminate); 129 | GET(KERNEL_SLIDE, &kernel_slide); 130 | GET(HOOKME, &hookme_addr); 131 | GET(MKSHMEM_KTOU, &mkshmem_ktou); 132 | GET(MKSHMEM_UTOK, &mkshmem_utok); 133 | GET(MKSHMEM_RAW, &mkshmem_raw); 134 | GET(SHMEM_DESTROY, &shmem_destroy); 135 | GET(KERNEL_MAP, &kernel_map); 136 | GET(VM_ALLOCATE_EXTERNAL, &vm_allocate_external); 137 | 138 | hookme_addr -= kernel_slide; 139 | 140 | return 0; 141 | } 142 | 143 | static void *userspace_racer(void *arg){ 144 | while(!g_go){} 145 | 146 | uint64_t *user_valp = g_kernel_valp_shmem.shm_base; 147 | 148 | for(int i=0; i<500; i++){ 149 | kuslck_lock(g_kuslck); 150 | (*user_valp)++; 151 | kuslck_unlock(g_kuslck); 152 | } 153 | 154 | return NULL; 155 | } 156 | 157 | int main(int argc, char **argv){ 158 | size_t oldlen = sizeof(long); 159 | int ret = sysctlbyname("kern.xnuspy_ctl_callnum", &SYS_xnuspy_ctl, 160 | &oldlen, NULL, 0); 161 | 162 | if(ret == -1){ 163 | printf("sysctlbyname with kern.xnuspy_ctl_callnum failed: %s\n", 164 | strerror(errno)); 165 | return 1; 166 | } 167 | 168 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CHECK_IF_PATCHED, 0, 0, 0); 169 | 170 | if(ret != 999){ 171 | printf("xnuspy_ctl isn't present?\n"); 172 | return 1; 173 | } 174 | 175 | ret = gather_kernel_offsets(); 176 | 177 | if(ret){ 178 | printf("something failed: %s\n", strerror(errno)); 179 | return 1; 180 | } 181 | 182 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_INSTALL_HOOK, hookme_addr, 183 | hookme_hook, NULL); 184 | 185 | if(ret){ 186 | printf("Could not hook hookme: %s\n", strerror(errno)); 187 | return 1; 188 | } 189 | 190 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_REGISTER_DEATH_CALLBACK, 191 | death_callback, 0, 0); 192 | 193 | if(ret){ 194 | printf("Could not register death callback: %s\n", strerror(errno)); 195 | return 1; 196 | } 197 | 198 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CALL_HOOKME, 0, 0, 0); 199 | 200 | if(ret){ 201 | printf("Calling hookme not supported\n"); 202 | return 1; 203 | } 204 | 205 | sleep(1); 206 | 207 | pthread_t pt; 208 | pthread_create(&pt, NULL, userspace_racer, NULL); 209 | 210 | g_go = true; 211 | 212 | while(!g_kernel_racer_finished){} 213 | 214 | sleep(2); 215 | 216 | uint64_t result = *(uint64_t *)g_kernel_valp_shmem.shm_base; 217 | 218 | if(result != 1000) 219 | printf("Got unexpected result %lld\n", result); 220 | else 221 | printf("Correct result! %lld\n", result); 222 | 223 | return 0; 224 | } 225 | -------------------------------------------------------------------------------- /example/user_client_monitor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | static void *(*current_proc)(void); 14 | static void (*kprintf)(const char *, ...); 15 | static pid_t (*proc_pid)(void *); 16 | 17 | static uint64_t kernel_slide; 18 | 19 | static uint8_t curcpu(void){ 20 | uint64_t mpidr_el1; 21 | asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr_el1)); 22 | return (uint8_t)(mpidr_el1 & 0xff); 23 | } 24 | 25 | static pid_t caller_pid(void){ 26 | return proc_pid(current_proc()); 27 | } 28 | 29 | static const char *(*getClassName)(const void *OSObject); 30 | 31 | struct IOUserClient_vtab { 32 | uint8_t pad0[0x118]; 33 | void *(*getProperty)(void *this, const char *key); 34 | uint8_t pad120[0x370 - 0x120]; 35 | void *(*getProvider)(void *this); 36 | }; 37 | 38 | struct IOUserClient { 39 | struct IOUserClient_vtab *vt; 40 | }; 41 | 42 | static kern_return_t (*is_io_service_open_extended_orig)(void *_service, 43 | void *owning_task, uint32_t connect_type, NDR_record_t ndr, 44 | char *properties, mach_msg_type_number_t properties_cnt, 45 | kern_return_t *result, struct IOUserClient **connection); 46 | 47 | static kern_return_t is_io_service_open_extended(void *_service, 48 | void *owning_task, uint32_t connect_type, NDR_record_t ndr, 49 | char *properties, mach_msg_type_number_t properties_cnt, 50 | kern_return_t *result, struct IOUserClient **connection){ 51 | uint8_t cpu = curcpu(); 52 | pid_t cpid = caller_pid(); 53 | uint64_t caller = (uint64_t)__builtin_return_address(0); 54 | 55 | kern_return_t kret = is_io_service_open_extended_orig(_service, owning_task, 56 | connect_type, ndr, properties, properties_cnt, result, 57 | connection); 58 | 59 | kprintf("user_client_monitor: (CPU %d, unslid caller %#llx, pid %d): connect " 60 | "type %#x: ", cpu, caller - kernel_slide, cpid, connect_type); 61 | 62 | if(*result != KERN_SUCCESS){ 63 | kprintf("failed. Returned %#x, result = %#x\n", kret, *result); 64 | return kret; 65 | } 66 | 67 | struct IOUserClient *client = *connection; 68 | 69 | kprintf("opened user client = %#llx ", client); 70 | 71 | if(!client){ 72 | kprintf("\n"); 73 | return kret; 74 | } 75 | 76 | const char *class_name = getClassName(client); 77 | 78 | if(!class_name){ 79 | kprintf("getClassName failed.\n"); 80 | return kret; 81 | } 82 | 83 | kprintf("class: '%s'", class_name); 84 | 85 | /* IOService */ 86 | void *provider = client->vt->getProvider(client); 87 | 88 | if(!provider) 89 | kprintf(" unknown provider"); 90 | else{ 91 | const char *provider_class_name = getClassName(provider); 92 | 93 | if(provider_class_name) 94 | kprintf(" provider: '%s'", provider_class_name); 95 | } 96 | 97 | /* OSString */ 98 | void *creator_name_prop = client->vt->getProperty(client, "IOUserClientCreator"); 99 | 100 | if(!creator_name_prop){ 101 | kprintf(" unknown creator\n"); 102 | return kret; 103 | } 104 | 105 | const char *creator_name = *(const char **)((uint8_t *)creator_name_prop + 0x10); 106 | 107 | if(!creator_name){ 108 | kprintf(" unknown creator\n"); 109 | return kret; 110 | } 111 | 112 | kprintf(" creator: '%s'\n", creator_name); 113 | 114 | return kret; 115 | } 116 | 117 | static kern_return_t (*is_io_connect_method)(struct IOUserClient *, 118 | uint32_t selector, uint64_t *scalar_input, 119 | uint32_t scalar_input_sz, uint8_t *struct_input, 120 | uint32_t struct_input_sz, uint64_t ool_input, uint64_t ool_input_sz, 121 | uint8_t *struct_output, uint32_t *struct_output_szp, 122 | uint64_t *scalar_output, uint64_t *scalar_output_szp, 123 | uint64_t ool_output, uint64_t *ool_output_szp); 124 | 125 | static kern_return_t _is_io_connect_method(struct IOUserClient *uc, 126 | uint32_t selector, uint64_t *scalar_input, 127 | uint32_t scalar_input_sz, uint8_t *struct_input, 128 | uint32_t struct_input_sz, uint64_t ool_input, uint64_t ool_input_sz, 129 | uint8_t *struct_output, uint32_t *struct_output_szp, 130 | uint64_t *scalar_output, uint64_t *scalar_output_szp, 131 | uint64_t ool_output, uint64_t *ool_output_szp){ 132 | kern_return_t kret = is_io_connect_method(uc, selector, scalar_input, 133 | scalar_input_sz, struct_input, struct_input_sz, ool_input, 134 | ool_input_sz, struct_output, struct_output_szp, scalar_output, 135 | scalar_output_szp, ool_output, ool_output_szp); 136 | 137 | const char *class_name = getClassName(uc); 138 | 139 | if(!class_name) 140 | return kret; 141 | 142 | kprintf("user_client_monitor: '%s' invoked external method %d\n", 143 | class_name, selector); 144 | 145 | return kret; 146 | } 147 | 148 | static long SYS_xnuspy_ctl = 0; 149 | 150 | static int gather_kernel_offsets(void){ 151 | int ret; 152 | 153 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, CURRENT_PROC, 154 | ¤t_proc, 0); 155 | 156 | if(ret){ 157 | printf("Failed getting current_proc\n"); 158 | return ret; 159 | } 160 | 161 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, KPRINTF, &kprintf, 0); 162 | if(ret){ 163 | printf("Failed getting kprintf\n"); 164 | return ret; 165 | } 166 | 167 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, PROC_PID, &proc_pid, 0); 168 | 169 | if(ret){ 170 | printf("Failed getting proc_pid\n"); 171 | return ret; 172 | } 173 | 174 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CACHE_READ, KERNEL_SLIDE, &kernel_slide, 0); 175 | 176 | if(ret){ 177 | printf("Failed getting kernel slide\n"); 178 | return ret; 179 | } 180 | 181 | return 0; 182 | } 183 | 184 | int main(int argc, char **argv){ 185 | size_t oldlen = sizeof(long); 186 | int ret = sysctlbyname("kern.xnuspy_ctl_callnum", &SYS_xnuspy_ctl, 187 | &oldlen, NULL, 0); 188 | 189 | if(ret == -1){ 190 | printf("sysctlbyname with kern.xnuspy_ctl_callnum failed: %s\n", 191 | strerror(errno)); 192 | return 1; 193 | } 194 | 195 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_CHECK_IF_PATCHED, 0, 0, 0); 196 | 197 | if(ret != 999){ 198 | printf("xnuspy_ctl isn't present?\n"); 199 | return 1; 200 | } 201 | 202 | ret = gather_kernel_offsets(); 203 | 204 | if(ret){ 205 | printf("something failed: %s\n", strerror(errno)); 206 | return 1; 207 | } 208 | 209 | /* iPhone X 15.0 */ 210 | getClassName = (const char *(*)(const void *))(0xfffffff0080e8ba0 + kernel_slide); 211 | 212 | printf("kernel slide: %#llx\n", kernel_slide); 213 | printf("current_proc @ %#llx\n", (uint64_t)current_proc); 214 | printf("getClassName @ %#llx\n", (uint64_t)getClassName); 215 | printf("kprintf @ %#llx\n", (uint64_t)kprintf); 216 | printf("proc_pid @ %#llx\n", (uint64_t)proc_pid); 217 | 218 | ret = syscall(SYS_xnuspy_ctl, XNUSPY_INSTALL_HOOK, 0xfffffff0081c1580, 219 | is_io_service_open_extended, &is_io_service_open_extended_orig); 220 | 221 | if(ret){ 222 | printf("Could not hook is_io_service_open_extended: %s\n", 223 | strerror(errno)); 224 | return 1; 225 | } 226 | 227 | /* XXX Optional */ 228 | /* ret = syscall(SYS_xnuspy_ctl, XNUSPY_INSTALL_HOOK, 0xfffffff0081c68ec, */ 229 | /* _is_io_connect_method, &is_io_connect_method); */ 230 | 231 | /* if(ret){ */ 232 | /* printf("Could not hook is_io_connect_method: %s\n", */ 233 | /* strerror(errno)); */ 234 | /* return 1; */ 235 | /* } */ 236 | 237 | printf("Hit enter to quit\n"); 238 | getchar(); 239 | 240 | return 0; 241 | } 242 | -------------------------------------------------------------------------------- /include/asm/asm.h: -------------------------------------------------------------------------------- 1 | #ifndef ASM 2 | #define ASM 3 | 4 | #include 5 | 6 | uint64_t sign_extend(uint64_t, uint32_t); 7 | 8 | uint32_t assemble_adrp(uint64_t, uint64_t, uint32_t); 9 | uint32_t assemble_b(uint64_t, uint64_t); 10 | uint32_t assemble_bl(uint64_t, uint64_t); 11 | uint32_t assemble_csel(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); 12 | uint32_t assemble_mov(uint32_t, uint32_t, uint32_t); 13 | uint32_t assemble_immediate_add(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); 14 | uint32_t assemble_immediate_cmp(uint32_t, uint32_t, uint32_t, uint32_t); 15 | uint32_t assemble_immediate_ldr(uint32_t, uint32_t, uint32_t); 16 | uint32_t assemble_immediate_prfm(uint32_t, uint32_t); 17 | uint32_t assemble_ldrsw(uint32_t, uint32_t); 18 | uint32_t assemble_simd_fp_ldr(uint32_t, uint32_t, uint32_t, uint32_t); 19 | 20 | uint32_t bits(uint64_t, uint64_t, uint64_t); 21 | 22 | uint64_t get_add_imm(uint32_t); 23 | 24 | uint64_t get_adr_target(uint32_t *); 25 | uint64_t get_adrp_target(uint32_t *); 26 | uint64_t get_adrp_add_target(uint32_t *); 27 | uint64_t get_adrp_ldr_target(uint32_t *); 28 | uint64_t get_pc_rel_target(uint32_t *); 29 | 30 | uint64_t get_branch_dst(uint32_t, uint32_t *); 31 | uint32_t *get_branch_dst_ptr(uint32_t *); 32 | uint64_t get_compare_and_branch_dst(uint32_t, uint32_t *); 33 | uint64_t get_cond_branch_dst(uint32_t, uint32_t *); 34 | uint64_t get_test_and_branch_dst(uint32_t, uint32_t *); 35 | 36 | void write_blr(uint32_t, uint32_t *, uint64_t); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/asm/asm_support.h: -------------------------------------------------------------------------------- 1 | #ifndef ASM_SUPPORT 2 | #define ASM_SUPPORT 3 | 4 | #define OPCODE_PLACEHOLDER_BYTE (0x41) 5 | #define OPCODE_PLACEHOLDER (0x41414141) 6 | #define QWORD_PLACEHOLDER (0x4142434445464748) 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/common/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON 2 | #define COMMON 3 | 4 | #include 5 | #include 6 | 7 | bool is_15_x__pongo(void); 8 | bool is_14_5_and_above__pongo(void); 9 | bool is_14_x_and_above__pongo(void); 10 | bool is_14_x_and_below__pongo(void); 11 | bool is_14_x__pongo(void); 12 | bool is_13_x__pongo(void); 13 | 14 | int atoi(const char *); 15 | int isdigit(int); 16 | 17 | char *strcpy(char *, const char *); 18 | char *strstr(const char *, const char *); 19 | 20 | __attribute__ ((noreturn)) void xnuspy_fatal_error(void); 21 | 22 | extern struct mach_header_64 *mh_execute_header; 23 | extern uint64_t kernel_slide; 24 | 25 | extern void (*next_preboot_hook)(void); 26 | 27 | #define iOS_13_x (19) 28 | #define iOS_14_x (20) 29 | #define iOS_15_x (21) 30 | 31 | #define VERSION_BIAS iOS_13_x 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/common/preboot_hook.h: -------------------------------------------------------------------------------- 1 | #ifndef PREBOOT_HOOK 2 | #define PREBOOT_HOOK 3 | 4 | void xnuspy_preboot_hook(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/pf/13/pf.h: -------------------------------------------------------------------------------- 1 | #ifndef PF_13 2 | #define PF_13 3 | 4 | #include 5 | 6 | typedef struct xnu_pf_patch xnu_pf_patch_t; 7 | 8 | bool sysent_finder_13(xnu_pf_patch_t *, void *); 9 | bool kalloc_canblock_finder_13(xnu_pf_patch_t *, void *); 10 | bool kfree_addr_finder_13(xnu_pf_patch_t *, void *); 11 | bool ExceptionVectorsBase_finder_13(xnu_pf_patch_t *, void *); 12 | bool sysctl__kern_children_finder_13(xnu_pf_patch_t *, void *); 13 | bool sysctl_register_oid_finder_13(xnu_pf_patch_t *, void *); 14 | bool sysctl_handle_long_finder_13(xnu_pf_patch_t *, void *); 15 | bool name2oid_and_its_dependencies_finder_13(xnu_pf_patch_t *, void *); 16 | bool hook_system_check_sysctlbyname_finder_13(xnu_pf_patch_t *, void *); 17 | bool lck_grp_alloc_init_finder_13(xnu_pf_patch_t *, void *); 18 | bool lck_rw_alloc_init_finder_13(xnu_pf_patch_t *, void *); 19 | bool bcopy_phys_finder_13(xnu_pf_patch_t *, void *); 20 | bool phystokv_finder_13(xnu_pf_patch_t *, void *); 21 | bool ktrr_lockdown_patcher_13(xnu_pf_patch_t *, void *); 22 | bool amcc_lockdown_patcher_13(xnu_pf_patch_t *, void *); 23 | bool copyin_finder_13(xnu_pf_patch_t *, void *); 24 | bool copyout_finder_13(xnu_pf_patch_t *, void *); 25 | bool IOSleep_finder_13(xnu_pf_patch_t *, void *); 26 | bool kprintf_finder_13(xnu_pf_patch_t *, void *); 27 | bool kernel_map_vm_deallocate_vm_map_unwire_finder_13(xnu_pf_patch_t *, void *); 28 | bool kernel_thread_start_thread_deallocate_finder_13(xnu_pf_patch_t *, void *); 29 | bool mach_make_memory_entry_64_finder_13(xnu_pf_patch_t *, void *); 30 | bool offsetof_struct_thread_map_finder_13(xnu_pf_patch_t *, void *); 31 | bool proc_stuff0_finder_13(xnu_pf_patch_t *, void *); 32 | bool proc_stuff1_finder_13(xnu_pf_patch_t *, void *); 33 | bool allproc_finder_13(xnu_pf_patch_t *, void *); 34 | bool misc_lck_stuff_finder_13(xnu_pf_patch_t *, void *); 35 | bool vm_map_wire_external_finder_13(xnu_pf_patch_t *, void *); 36 | bool mach_vm_map_external_finder_13(xnu_pf_patch_t *, void *); 37 | bool ipc_port_release_send_finder_13(xnu_pf_patch_t *, void *); 38 | bool lck_rw_free_finder_13(xnu_pf_patch_t *, void *); 39 | bool lck_grp_free_finder_13(xnu_pf_patch_t *, void *); 40 | bool doprnt_hide_pointers_patcher_13(xnu_pf_patch_t *, void *); 41 | bool copyinstr_finder_13(xnu_pf_patch_t *, void *); 42 | bool thread_terminate_finder_13(xnu_pf_patch_t *, void *); 43 | bool pinst_set_tcr_patcher_13(xnu_pf_patch_t *, void *); 44 | bool msr_tcr_el1_x18_patcher_13(xnu_pf_patch_t *, void *); 45 | bool proc_name_snprintf_strlen_finder_13(xnu_pf_patch_t *, void *); 46 | bool strncmp_finder_13(xnu_pf_patch_t *, void *); 47 | bool memset_finder_13(xnu_pf_patch_t *, void *); 48 | bool memmove_finder_13(xnu_pf_patch_t *, void *); 49 | bool panic_finder_13(xnu_pf_patch_t *, void *); 50 | bool mach_to_bsd_errno_finder_13(xnu_pf_patch_t *, void *); 51 | bool vm_allocate_external_finder_13(xnu_pf_patch_t *, void *); 52 | bool vm_map_deallocate_offsetof_vm_map_refcnt_finder_13(xnu_pf_patch_t *, void *); 53 | bool IOLog_finder_13(xnu_pf_patch_t *, void *); 54 | bool lck_mtx_lock_finder_13(xnu_pf_patch_t *, void *); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/pf/14/pf.h: -------------------------------------------------------------------------------- 1 | #ifndef PF_14 2 | #define PF_14 3 | 4 | #include 5 | 6 | typedef struct xnu_pf_patch xnu_pf_patch_t; 7 | 8 | bool kalloc_external_finder_14(xnu_pf_patch_t *, void *); 9 | bool kfree_ext_finder_14(xnu_pf_patch_t *, void *); 10 | bool ExceptionVectorsBase_finder_14(xnu_pf_patch_t *, void *); 11 | bool sysctl__kern_children_and_register_oid_finder_14(xnu_pf_patch_t *, void *); 12 | bool lck_grp_alloc_init_finder_14(xnu_pf_patch_t *, void *); 13 | bool lck_rw_alloc_init_finder_14(xnu_pf_patch_t *, void *); 14 | bool ktrr_lockdown_patcher_14(xnu_pf_patch_t *, void *); 15 | bool amcc_ctrr_lockdown_patcher_14(xnu_pf_patch_t *, void *); 16 | bool name2oid_and_its_dependencies_finder_14(xnu_pf_patch_t *, void *); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/pf/15/pf.h: -------------------------------------------------------------------------------- 1 | #ifndef PF_15 2 | #define PF_15 3 | 4 | #include 5 | 6 | typedef struct xnu_pf_patch xnu_pf_patch_t; 7 | 8 | bool ipc_port_release_send_finder_15(xnu_pf_patch_t *, void *); 9 | bool proc_name_snprintf_strlen_finder_15(xnu_pf_patch_t *, void *); 10 | bool current_proc_finder_15(xnu_pf_patch_t *, void *); 11 | bool vm_map_unwire_nested_finder_15(xnu_pf_patch_t *, void *); 12 | bool kernel_map_finder_15(xnu_pf_patch_t *, void *); 13 | bool vm_deallocate_finder_15(xnu_pf_patch_t *, void *); 14 | bool proc_list_mlock_lck_mtx_lock_unlock_finder_15(xnu_pf_patch_t *, void *); 15 | bool lck_grp_free_finder_15(xnu_pf_patch_t *, void *); 16 | bool proc_ref_rele_finder_15(xnu_pf_patch_t *, void *); 17 | bool lck_rw_alloc_init_finder_15(xnu_pf_patch_t *, void *); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/pf/offsets.h: -------------------------------------------------------------------------------- 1 | #ifndef OFFSETS 2 | #define OFFSETS 3 | 4 | #include 5 | 6 | extern uint64_t *xnuspy_cache_base; 7 | 8 | /* This file contains offsets which will be written to the xnuspy cache 9 | * as well as offsets needed before XNU boots. */ 10 | 11 | /* NOT a kernel virtual address */ 12 | extern uint64_t g_sysent_addr; 13 | 14 | /* iOS 13.x: kalloc_canblock 15 | * iOS 14.x and iOS 15.x: kalloc_external */ 16 | extern uint64_t g_kalloc_canblock_addr; 17 | extern uint64_t g_kalloc_external_addr; 18 | 19 | /* iOS 13.x: kfree_addr 20 | * iOS 14.x and iOS 15.x: kfree_ext */ 21 | extern uint64_t g_kfree_addr_addr; 22 | extern uint64_t g_kfree_ext_addr; 23 | 24 | extern uint64_t g_sysctl__kern_children_addr; 25 | extern uint64_t g_sysctl_register_oid_addr; 26 | extern uint64_t g_sysctl_handle_long_addr; 27 | extern uint64_t g_name2oid_addr; 28 | extern uint64_t g_sysctl_geometry_lock_addr; 29 | extern uint64_t g_lck_rw_done_addr; 30 | extern uint64_t g_h_s_c_sbn_branch_addr; 31 | extern uint64_t g_h_s_c_sbn_epilogue_addr; 32 | extern uint64_t g_lck_grp_alloc_init_addr; 33 | extern uint64_t g_lck_rw_alloc_init_addr; 34 | extern uint64_t g_exec_scratch_space_addr; 35 | extern uint64_t g_exec_scratch_space_size; 36 | extern uint32_t *g_ExceptionVectorsBase_stream; 37 | extern uint64_t g_bcopy_phys_addr; 38 | extern uint64_t g_phystokv_addr; 39 | extern uint64_t g_copyin_addr; 40 | extern uint64_t g_copyout_addr; 41 | extern uint64_t g_IOSleep_addr; 42 | extern uint64_t g_kprintf_addr; 43 | extern uint64_t g_vm_map_unwire_addr; 44 | extern uint64_t g_vm_map_unwire_nested_addr; 45 | extern uint64_t g_vm_deallocate_addr; 46 | extern uint64_t g_kernel_map_addr; 47 | extern uint64_t g_kernel_thread_start_addr; 48 | extern uint64_t g_thread_deallocate_addr; 49 | extern uint64_t g_mach_make_memory_entry_64_addr; 50 | extern uint64_t g_offsetof_struct_thread_map; 51 | extern uint64_t g_current_proc_addr; 52 | extern uint64_t g_proc_list_lock_addr; 53 | extern uint64_t g_proc_ref_locked_addr; 54 | extern uint64_t g_proc_list_mlock_addr; 55 | extern uint64_t g_lck_mtx_lock_addr; 56 | extern uint64_t g_lck_mtx_unlock_addr; 57 | extern uint64_t g_proc_rele_locked_addr; 58 | extern uint64_t g_proc_uniqueid_addr; 59 | extern uint64_t g_proc_pid_addr; 60 | extern uint64_t g_allproc_addr; 61 | extern uint64_t g_lck_rw_lock_shared_addr; 62 | extern uint64_t g_lck_rw_lock_shared_to_exclusive_addr; 63 | extern uint64_t g_lck_rw_lock_exclusive_addr; 64 | extern uint64_t g_vm_map_wire_external_addr; 65 | extern uint64_t g_mach_vm_map_external_addr; 66 | 67 | /* Only for <14.5 */ 68 | extern uint64_t g_ipc_port_release_send_addr; 69 | 70 | /* Only for >=14.5 */ 71 | extern uint64_t g_ipc_port_release_send_and_unlock_addr; 72 | 73 | extern uint64_t g_lck_rw_free_addr; 74 | extern uint64_t g_lck_grp_free_addr; 75 | extern int g_patched_doprnt_hide_pointers; 76 | extern uint64_t g_copyinstr_addr; 77 | extern uint64_t g_thread_terminate_addr; 78 | extern int g_patched_pinst_set_tcr; 79 | extern int g_patched_all_msr_tcr_el1_x18; 80 | extern uint64_t g_snprintf_addr; 81 | extern uint64_t g_strlen_addr; 82 | extern uint64_t g_proc_name_addr; 83 | extern uint64_t g_strncmp_addr; 84 | extern uint64_t g_memset_addr; 85 | extern uint64_t g_memmove_addr; 86 | extern uint64_t g_panic_addr; 87 | extern uint64_t g_mach_to_bsd_errno_addr; 88 | extern uint64_t g_xnuspy_sysctl_mib_ptr; 89 | extern uint64_t g_xnuspy_sysctl_mib_count_ptr; 90 | extern uint64_t g_xnuspy_ctl_callnum; 91 | extern uint64_t g_kern_version_major; 92 | extern uint64_t g_kern_version_minor; 93 | 94 | /* Only for >=14.5 && <15.0 */ 95 | extern uint64_t g_io_lock_addr; 96 | 97 | /* Only for >=15.0 */ 98 | extern uint64_t g_ipc_object_lock_addr; 99 | 100 | extern uint64_t g_vm_allocate_external_addr; 101 | extern uint64_t g_vm_map_deallocate_addr; 102 | extern uint64_t g_offsetof_struct_vm_map_refcnt; 103 | extern uint64_t g_IOLog_addr; 104 | 105 | /* Following two are only valid on iOS 15+ */ 106 | extern uint64_t g_proc_ref_addr; 107 | extern uint64_t g_proc_rele_addr; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /include/pf/pf_common.h: -------------------------------------------------------------------------------- 1 | #ifndef PF_COMMON 2 | #define PF_COMMON 3 | 4 | #include 5 | #include 6 | 7 | typedef struct xnu_pf_patch xnu_pf_patch_t; 8 | 9 | struct pf { 10 | const char *pf_name; 11 | uint64_t pf_matches[8]; 12 | uint64_t pf_masks[8]; 13 | uint32_t pf_mmcount; 14 | /* XNU_PF_ACCESS_8BIT, etc */ 15 | uint32_t pf_access_type; 16 | bool (*pf_callback)(xnu_pf_patch_t *, void *); 17 | /* If applicable, the name of the kext used with xnu_pf_get_kext_header 18 | * If not applicable, NULL */ 19 | const char *pf_kext; 20 | const char *pf_segment; 21 | /* If applicable, the section used with xnu_pf_section 22 | * If not applicable, NULL */ 23 | const char *pf_section; 24 | uint8_t pf_unused; 25 | }; 26 | 27 | #define LISTIZE(...) __VA_ARGS__ 28 | 29 | #define PF_DECL32(name, matches, masks, mmcount, callback, seg) \ 30 | { \ 31 | .pf_name = name, \ 32 | .pf_matches = matches, \ 33 | .pf_masks = masks, \ 34 | .pf_mmcount = mmcount, \ 35 | .pf_access_type = XNU_PF_ACCESS_32BIT, \ 36 | .pf_callback = callback, \ 37 | .pf_kext = NULL, \ 38 | .pf_segment = seg, \ 39 | .pf_section = NULL, \ 40 | .pf_unused = 0, \ 41 | } 42 | 43 | #define PF_DECL_FULL(name, matches, masks, mmcount, access, callback, kext, seg, sect) \ 44 | { \ 45 | .pf_name = name, \ 46 | .pf_matches = matches, \ 47 | .pf_masks = masks, \ 48 | .pf_mmcount = mmcount, \ 49 | .pf_access_type = access, \ 50 | .pf_callback = callback, \ 51 | .pf_kext = kext, \ 52 | .pf_segment = seg, \ 53 | .pf_section = sect, \ 54 | .pf_unused = 0, \ 55 | } 56 | 57 | #define PF_UNUSED { .pf_unused = 1 } 58 | 59 | #define PF_END { .pf_unused = 0x41 } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /include/xnuspy/el1/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG 2 | #define DEBUG 3 | 4 | #include 5 | 6 | #if defined(XNUSPY_DEBUG) 7 | #define DEBUG_SPEW(fmt, args...) kprintf(fmt, ##args) 8 | #else 9 | #define DEBUG_SPEW(fmt, args...) 10 | #endif 11 | 12 | #if defined(XNUSPY_SERIAL) 13 | #define SERIAL_SPEW(fmt, args...) IOLog(fmt, ##args) 14 | #else 15 | #define SERIAL_SPEW(fmt, args...) 16 | #endif 17 | 18 | #define SPYDBG(fmt, args...) \ 19 | do { \ 20 | DEBUG_SPEW(fmt, ##args); \ 21 | SERIAL_SPEW(fmt, ##args); \ 22 | } while (0) \ 23 | 24 | void desc_freelist(void); 25 | void desc_xnuspy_shmem(struct xnuspy_shmem *); 26 | /* XXX ONLY meant to be called from xnuspy_gc_thread, hence the lack 27 | * of locking. */ 28 | void desc_unmaplist(void); 29 | void desc_usedlist(void); 30 | 31 | void desc_xnuspy_mapping(struct xnuspy_mapping *); 32 | void desc_xnuspy_mapping_metadata(struct xnuspy_mapping_metadata *); 33 | void desc_xnuspy_tramp(struct xnuspy_tramp *, uint32_t); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /include/xnuspy/el1/externs.h: -------------------------------------------------------------------------------- 1 | #ifndef EXTERNS 2 | #define EXTERNS 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #undef PAGE_SIZE 11 | #define PAGE_SIZE (0x4000uLL) 12 | 13 | #define iOS_13_x (19) 14 | #define iOS_14_x (20) 15 | #define iOS_15_x (21) 16 | 17 | #define MAP_MEM_VM_SHARE 0x400000 /* extract a VM range for remap */ 18 | 19 | typedef unsigned int lck_rw_type_t; 20 | 21 | typedef void (*thread_continue_t)(void *param, int wait_result); 22 | 23 | typedef struct __lck_rw_t__ lck_rw_t; 24 | 25 | /* Start kernel offsets */ 26 | 27 | extern void **allprocp; 28 | extern void (*bcopy_phys)(uint64_t src, uint64_t dst, 29 | vm_size_t bytes); 30 | extern int (*copyin)(const void *uaddr, void *kaddr, 31 | vm_size_t nbytes); 32 | extern int (*copyinstr)(const void *uaddr, void *kaddr, 33 | size_t len, size_t *done); 34 | extern int (*copyout)(const void *kaddr, uint64_t uaddr, 35 | vm_size_t nbytes); 36 | extern void *(*current_proc)(void); 37 | extern uint64_t hookme_in_range; 38 | extern uint64_t iOS_version; 39 | extern void (*io_lock)(void *io); 40 | extern void (*ipc_object_lock)(void *obj); 41 | extern void (*IOLog)(const char *fmt, ...); 42 | extern void (*IOSleep)(unsigned int millis); 43 | extern void (*ipc_port_release_send)(void *port); 44 | extern void (*ipc_port_release_send_and_unlock)(void *port); 45 | extern void *(*kalloc_canblock)(vm_size_t *sizep, bool canblock, 46 | void *site); 47 | extern void *(*kalloc_external)(vm_size_t sz); 48 | extern uint64_t kern_version_minor; 49 | extern void **kernel_mapp; 50 | extern uint64_t kernel_slide; 51 | extern kern_return_t (*kernel_thread_start)(thread_continue_t cont, 52 | void *parameter, void **new_thread); 53 | extern void (*kfree_addr)(void *addr); 54 | extern void (*kfree_ext)(void *kheap, void *addr, 55 | vm_size_t sz); 56 | extern void (*kprintf)(const char *fmt, ...); 57 | extern void *(*lck_grp_alloc_init)(const char *grp_name, 58 | void *attr); 59 | extern void (*lck_grp_free)(void *grp); 60 | extern void (*lck_mtx_lock)(void *lock); 61 | extern void (*lck_mtx_unlock)(void *lock); 62 | extern lck_rw_t *(*lck_rw_alloc_init)(void *grp, void *attr); 63 | extern uint32_t (*lck_rw_done)(lck_rw_t *lock); 64 | extern void (*lck_rw_free)(lck_rw_t *lock, void *grp); 65 | extern void (*lck_rw_lock_exclusive)(void *lock); 66 | extern void (*lck_rw_lock_shared)(void *lock); 67 | extern int (*lck_rw_lock_shared_to_exclusive)(lck_rw_t *lck); 68 | extern kern_return_t (*_mach_make_memory_entry_64)(void *target_map, 69 | uint64_t *size, uint64_t offset, vm_prot_t prot, void **object_handle, 70 | void *parent_handle); 71 | extern int (*mach_to_bsd_errno)(kern_return_t mach_err); 72 | extern kern_return_t (*mach_vm_map_external)(void *target_map, 73 | uint64_t *address, uint64_t size, uint64_t mask, int flags, 74 | void *memory_object, uint64_t offset, int copy, 75 | vm_prot_t cur_protection, vm_prot_t max_protection, 76 | vm_inherit_t inheritance); 77 | extern void *(*_memmove)(void *dest, const void *src, size_t n); 78 | extern void *(*_memset)(void *s, int c, size_t n); 79 | extern uint64_t offsetof_struct_thread_map; 80 | extern uint64_t offsetof_struct_vm_map_refcnt; 81 | extern __attribute__ ((noreturn)) void (*_panic)(const char *fmt, ...); 82 | extern uint64_t (*phystokv)(uint64_t pa); 83 | extern void **proc_list_mlockp; 84 | extern void (*proc_name)(int pid, char *buf, int size); 85 | extern pid_t (*proc_pid)(void *proc); 86 | extern void *(*proc_ref)(void *proc, bool w1); 87 | extern void *(*proc_ref_locked)(void *proc); 88 | extern int (*proc_rele)(void *proc); 89 | extern void (*proc_rele_locked)(void *proc); 90 | extern uint64_t (*proc_uniqueid)(void *proc); 91 | extern int (*_snprintf)(char *str, size_t size, const char *fmt, ...); 92 | extern size_t (*_strlen)(const char *s); 93 | extern int (*_strncmp)(const char *s1, const char *s2, size_t n); 94 | extern void (*thread_deallocate)(void *thread); 95 | extern void (*_thread_terminate)(void *thread); 96 | extern kern_return_t (*vm_allocate_external)(void *map, uint64_t *addr, 97 | uint64_t size, int flags); 98 | extern kern_return_t (*_vm_deallocate)(void *map, 99 | uint64_t start, uint64_t size); 100 | extern void (*vm_map_deallocate)(void *map); 101 | extern kern_return_t (*vm_map_unwire)(void *map, uint64_t start, 102 | uint64_t end, int user); 103 | extern kern_return_t (*vm_map_unwire_nested)(void *map, uint64_t start, 104 | uint64_t end, int user, uint64_t map_pmap, uint64_t pmap_addr); 105 | extern kern_return_t (*vm_map_wire_external)(void *map, 106 | uint64_t start, uint64_t end, vm_prot_t prot, int user_wire); 107 | extern struct xnuspy_tramp *xnuspy_tramp_mem; 108 | extern struct xnuspy_tramp *xnuspy_tramp_mem_end; 109 | 110 | /* End kernel offsets */ 111 | 112 | extern STAILQ_HEAD(, stailq_entry) freelist; 113 | extern STAILQ_HEAD(, stailq_entry) usedlist; 114 | extern STAILQ_HEAD(, stailq_entry) unmaplist; 115 | 116 | extern lck_rw_t *xnuspy_rw_lck; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /include/xnuspy/el1/libc.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBC 2 | #define LIBC 3 | 4 | #include 5 | 6 | void bzero(void *p, size_t n); 7 | void *memchr(const void *s, int c, size_t n); 8 | int memcmp(const void *s1, const void *s2, size_t n); 9 | void *memmem(const void *big, size_t blen, const void *little, size_t llen); 10 | void *memrchr(const void *s, int c, size_t n); 11 | char *strchr(const char *s, int c); 12 | char *strrchr(const char *s, int c); 13 | int strcmp(const char *s1, const char *s2); 14 | char *strstr(const char *big, const char *little); 15 | char *strnstr(const char *big, const char *little, size_t len); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/xnuspy/el1/mem.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM 2 | #define MEM 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | __attribute__((naked)) uint64_t kvtophys(uint64_t); 9 | __attribute__((naked)) uint64_t uvtophys(uint64_t); 10 | 11 | void dcache_clean_PoU(void *address, size_t size); 12 | void icache_invalidate_PoU(void *address, size_t size); 13 | 14 | int kprotect(void *, uint64_t, vm_prot_t); 15 | int uprotect(void *, uint64_t, vm_prot_t); 16 | 17 | void kwrite_static(void *, void *, size_t); 18 | void kwrite_instr(uint64_t, uint32_t); 19 | 20 | int mkshmem_ktou(uint64_t, uint64_t, vm_prot_t, struct xnuspy_shmem *); 21 | int mkshmem_utok(uint64_t, uint64_t, vm_prot_t, struct xnuspy_shmem *); 22 | int mkshmem_raw(uint64_t, uint64_t, vm_prot_t, struct _vm_map *, 23 | struct _vm_map *, struct xnuspy_shmem *); 24 | 25 | int shmem_destroy(struct xnuspy_shmem *); 26 | 27 | void *unified_kalloc(size_t); 28 | void unified_kfree(void *); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/xnuspy/el1/pte.h: -------------------------------------------------------------------------------- 1 | #ifndef PTE 2 | #define PTE 3 | 4 | #include 5 | 6 | typedef uint64_t pte_t; 7 | 8 | pte_t *el0_ptep(void *); 9 | pte_t *el1_ptep(void *); 10 | 11 | void tlb_flush(void); 12 | 13 | #define ARM_TTE_TABLE_MASK (0x0000ffffffffc000) 14 | 15 | #define ARM_16K_TT_L1_SHIFT (36) 16 | #define ARM_16K_TT_L2_SHIFT (25) 17 | #define ARM_16K_TT_L3_SHIFT (14) 18 | 19 | #define ARM_TT_L1_SHIFT ARM_16K_TT_L1_SHIFT 20 | #define ARM_TT_L2_SHIFT ARM_16K_TT_L2_SHIFT 21 | #define ARM_TT_L3_SHIFT ARM_16K_TT_L3_SHIFT 22 | 23 | #define ARM_16K_TT_L1_INDEX_MASK (0x00007ff000000000) 24 | #define ARM_16K_TT_L2_INDEX_MASK (0x0000000ffe000000) 25 | #define ARM_16K_TT_L3_INDEX_MASK (0x0000000001ffc000) 26 | 27 | #define ARM_TT_L1_INDEX_MASK ARM_16K_TT_L1_INDEX_MASK 28 | #define ARM_TT_L2_INDEX_MASK ARM_16K_TT_L2_INDEX_MASK 29 | #define ARM_TT_L3_INDEX_MASK ARM_16K_TT_L3_INDEX_MASK 30 | 31 | #define ARM_PTE_NX (0x0040000000000000uLL) 32 | #define ARM_PTE_PNX (0x0020000000000000uLL) 33 | 34 | #define ARM_PTE_APMASK (0xc0uLL) 35 | #define ARM_PTE_AP(x) ((x) << 6) 36 | 37 | #define AP_RWNA (0x0) /* priv=read-write, user=no-access */ 38 | #define AP_RWRW (0x1) /* priv=read-write, user=read-write */ 39 | #define AP_RONA (0x2) /* priv=read-only, user=no-access */ 40 | #define AP_RORO (0x3) /* priv=read-only, user=read-only */ 41 | #define AP_MASK (0x3) /* mask to find ap bits */ 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /include/xnuspy/el1/tramp.h: -------------------------------------------------------------------------------- 1 | #ifndef TRAMP 2 | #define TRAMP 3 | 4 | #include 5 | 6 | void generate_original_tramp(uint64_t, uint32_t *, uint32_t *); 7 | void generate_replacement_tramp(uint32_t *); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/xnuspy/el1/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS 2 | #define UTILS 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | __attribute__ ((naked)) uint64_t current_thread(void); 10 | struct _vm_map *current_map(void); 11 | void vm_map_reference(void *); 12 | 13 | bool is_15_x(void); 14 | bool is_14_5_and_above(void); 15 | bool is_14_x_and_above(void); 16 | bool is_14_x_and_below(void); 17 | bool is_14_x(void); 18 | bool is_13_x(void); 19 | 20 | void *get_proc_list_mlock(void); 21 | void proc_list_lock(void); 22 | void proc_list_unlock(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/xnuspy/el1/wrappers.h: -------------------------------------------------------------------------------- 1 | #ifndef WRAPPERS 2 | #define WRAPPERS 3 | 4 | #include 5 | 6 | void ipc_port_release_send_wrapper(void *); 7 | kern_return_t vm_map_unwire_wrapper(void *, uint64_t, uint64_t, int); 8 | void *proc_ref_wrapper(void *, bool); 9 | int proc_rele_wrapper(void *, bool); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /include/xnuspy/el3/kpp.h: -------------------------------------------------------------------------------- 1 | #ifndef KPP 2 | #define KPP 3 | 4 | void patch_kpp(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/xnuspy/xnuspy_cache.h: -------------------------------------------------------------------------------- 1 | #ifndef XNUSPY_CACHE 2 | #define XNUSPY_CACHE 3 | 4 | #define SYSCTL__KERN_CHILDREN_PTR (0x0) 5 | #define SYSCTL_REGISTER_OID (0x8) 6 | #define SYSCTL_HANDLE_LONG (0x10) 7 | #define NAME2OID (0x18) 8 | #define SYSCTL_GEOMETRY_LOCK_PTR (0x20) 9 | #define LCK_RW_LOCK_SHARED (0x28) 10 | #define LCK_RW_DONE (0x30) 11 | #define DID_REGISTER_SYSCTL (0x38) 12 | #define H_S_C_SBN_EPILOGUE_ADDR (0x40) 13 | #define XNUSPY_SYSCTL_MIB_PTR (0x48) 14 | #define XNUSPY_SYSCTL_MIB_COUNT_PTR (0x50) 15 | #define XNUSPY_CTL_CALLNUM (0x58) 16 | #define IOS_VERSION (0x60) 17 | #define XNUSPY_CTL_ENTRYPOINT (0x68) 18 | #define XNUSPY_CTL_CODESTART (0x70) 19 | #define XNUSPY_CTL_CODESZ (0x78) 20 | #define XNUSPY_CTL_IS_RX (0x80) 21 | #define PHYSTOKV (0x88) 22 | #define BCOPY_PHYS (0x90) 23 | 24 | /* for kalloc/kfree, one of these will written to the cache depending 25 | * on iOS version 26 | */ 27 | #define KALLOC_CANBLOCK (0x98) 28 | #define KALLOC_EXTERNAL (0x98) 29 | 30 | #define KFREE_ADDR (0xa0) 31 | #define KFREE_EXT (0xa0) 32 | 33 | #define iOS_13_x (19) 34 | #define iOS_14_x (20) 35 | #define iOS_15_x (21) 36 | 37 | #define KERN_VERSION_MINOR (0xa8) 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/xnuspy/xnuspy_ctl.h: -------------------------------------------------------------------------------- 1 | #ifndef XNUSPY_CTL 2 | #define XNUSPY_CTL 3 | 4 | #include 5 | 6 | /* Flavors for xnuspy_ctl */ 7 | enum { 8 | XNUSPY_CHECK_IF_PATCHED = 0, 9 | XNUSPY_INSTALL_HOOK, 10 | XNUSPY_REGISTER_DEATH_CALLBACK, 11 | XNUSPY_CALL_HOOKME, 12 | XNUSPY_CACHE_READ, 13 | XNUSPY_KREAD, 14 | XNUSPY_KWRITE, 15 | XNUSPY_GET_CURRENT_THREAD, 16 | #ifdef XNUSPY_PRIVATE 17 | XNUSPY_MAX_FLAVOR = XNUSPY_GET_CURRENT_THREAD, 18 | #endif 19 | }; 20 | 21 | /* Values for XNUSPY_CACHE_READ - keep this alphabetical so it's 22 | * easier to find things */ 23 | 24 | #ifdef XNUSPY_PRIVATE 25 | enum xnuspy_cache_id { 26 | #else 27 | enum { 28 | #endif 29 | /* struct proclist allproc @ bsd/sys/proc_internal.h */ 30 | ALLPROC = 0, 31 | BCOPY_PHYS, 32 | BZERO, 33 | COPYIN, 34 | COPYINSTR, 35 | COPYOUT, 36 | 37 | /* Idential to XNU's implementation */ 38 | CURRENT_MAP, 39 | 40 | CURRENT_PROC, 41 | 42 | /* Only valid for iOS 14.5 - iOS 14.8, inclusive. EINVAL will be 43 | * returned otherwise. */ 44 | IO_LOCK, 45 | 46 | /* Only valid for iOS 15.x. EINVAL will be returned otherwise. */ 47 | IPC_OBJECT_LOCK, 48 | 49 | IOLOG, 50 | IOSLEEP, 51 | 52 | /* Only valid for < iOS 14.5. EINVAL will be returned otherwise. */ 53 | IPC_PORT_RELEASE_SEND, 54 | 55 | /* Only valid for >= iOS 14.5. EINVAL will be returned otherwise. */ 56 | IPC_PORT_RELEASE_SEND_AND_UNLOCK, 57 | 58 | /* Selects the correct way to release a send right based on the 59 | * kernel version. Parameters are the same as XNU's 60 | * ipc_port_release_send. */ 61 | IPC_PORT_RELEASE_SEND_WRAPPER, 62 | 63 | /* Only valid for iOS 13.x. EINVAL will be returned otherwise. */ 64 | KALLOC_CANBLOCK, 65 | 66 | /* Only valid for iOS 14.x and iOS 15.x. EINVAL will be returned 67 | * otherwise. */ 68 | KALLOC_EXTERNAL, 69 | 70 | /* vm_map_t kernel_map @ osfmk/vm/vm_kern.h */ 71 | KERNEL_MAP, 72 | 73 | KERNEL_THREAD_START, 74 | 75 | /* Only valid for iOS 13.x. EINVAL will be returned otherwise. */ 76 | KFREE_ADDR, 77 | 78 | /* Only valid for iOS 14.x and iOS 15.x. EINVAL will be returned 79 | * otherwise. */ 80 | KFREE_EXT, 81 | 82 | KPRINTF, 83 | LCK_GRP_ALLOC_INIT, 84 | LCK_GRP_FREE, 85 | LCK_MTX_LOCK, 86 | LCK_MTX_UNLOCK, 87 | LCK_RW_ALLOC_INIT, 88 | LCK_RW_DONE, 89 | LCK_RW_FREE, 90 | LCK_RW_LOCK_EXCLUSIVE, 91 | LCK_RW_LOCK_SHARED, 92 | LCK_RW_LOCK_SHARED_TO_EXCLUSIVE, 93 | MACH_MAKE_MEMORY_ENTRY_64, 94 | MACH_TO_BSD_ERRNO, 95 | MACH_VM_MAP_EXTERNAL, 96 | MEMCHR, 97 | MEMCMP, 98 | MEMMEM, 99 | MEMMOVE, 100 | MEMRCHR, 101 | MEMSET, 102 | PANIC, 103 | PHYSTOKV, 104 | 105 | /* Selects the correct way to take proc_list_mlock based 106 | * on the kernel version. 107 | * 108 | * void proc_list_lock(void); 109 | * 110 | */ 111 | PROC_LIST_LOCK, 112 | 113 | /* lck_mtx_t *proc_list_mlock @ bsd/sys/proc_internal.h */ 114 | PROC_LIST_MLOCK, 115 | 116 | /* Selects the correct way to release proc_list_mlock based 117 | * on the kernel version. 118 | * 119 | * void proc_list_unlock(void); 120 | * 121 | */ 122 | PROC_LIST_UNLOCK, 123 | 124 | PROC_NAME, 125 | PROC_PID, 126 | 127 | /* Only valid for 15.x. EINVAL will be returned otherwise. 128 | * Until 15 sources come out, here's what I think the function 129 | * signature is: 130 | * 131 | * proc_t proc_ref(proc_t proc, bool holding_proc_list_mlock); 132 | * 133 | * You can find a call to it in proc_exit. It looks like it is good 134 | * practice to make sure the returned proc pointer was the same one 135 | * as you passed in. Not sure what the return value being different 136 | * than the first parameter indicates... */ 137 | PROC_REF, 138 | 139 | /* Only valid for 13.x and 14.x. EINVAL will be returned otherwise. 140 | * This function assumes the caller holds proc_list_mlock. */ 141 | PROC_REF_LOCKED, 142 | 143 | /* Selects the correct way to take a reference on a proc structure 144 | * based on the kernel version. 145 | * 146 | * void *proc_ref_wrapper(void *proc, bool holding_proc_list_mlock); 147 | * 148 | * If you are on iOS 13.x or iOS 14.x and you pass false for the 149 | * second parameter, this function takes proc_list_mlock before 150 | * calling proc_ref_locked and releases it after that returns. If 151 | * you are on iOS 15.x, this tail calls proc_ref. Return value 152 | * is either the return value of proc_ref or proc_ref_locked. */ 153 | PROC_REF_WRAPPER, 154 | 155 | /* Only valid for 15.x. EINVAL will be returned otherwise. 156 | * This function assumes the caller DOES NOT hold proc_list_mlock, 157 | * though I'm not sure if it's safe to hold that mutex and call this 158 | * function. 159 | * Until 15 sources come out, here's the function signature: 160 | * 161 | * int proc_rele(proc_t proc); 162 | * 163 | * Seems to always return 0. */ 164 | PROC_RELE, 165 | 166 | /* Only valid for 13.x and 14.x. EINVAL will be returned otherwise. 167 | * This function assumes the caller holds proc_list_mlock. */ 168 | PROC_RELE_LOCKED, 169 | 170 | /* Selects the correct way to release a reference on a proc structure 171 | * based on the kernel version. 172 | * 173 | * int proc_rele_wrapper(void *proc, bool holding_proc_list_mlock); 174 | * 175 | * If you are on iOS 13.x or iOS 14.x and you pass false for the 176 | * second parameter, this function takes proc_list_mlock before 177 | * calling proc_rele_locked and releases it after that returns. If 178 | * you are on iOS 15.x, this tail calls proc_rele and the second 179 | * parameter is ignored. Return value is either the return value 180 | * of proc_ref (for iOS 15.x) or zero (for iOS 13.x and iOS 14.x) */ 181 | PROC_RELE_WRAPPER, 182 | 183 | PROC_UNIQUEID, 184 | SNPRINTF, 185 | STRCHR, 186 | STRRCHR, 187 | STRCMP, 188 | STRLEN, 189 | STRNCMP, 190 | STRSTR, 191 | STRNSTR, 192 | THREAD_DEALLOCATE, 193 | THREAD_TERMINATE, 194 | VM_ALLOCATE_EXTERNAL, 195 | VM_DEALLOCATE, 196 | VM_MAP_DEALLOCATE, 197 | 198 | /* Identical to XNU's implementation */ 199 | VM_MAP_REFERENCE, 200 | 201 | /* Only valid for 13.x and 14.x. EINVAL will be returned otherwise. */ 202 | VM_MAP_UNWIRE, 203 | 204 | /* Only valid for 15.x. EINVAL will be returned otherwise. */ 205 | VM_MAP_UNWIRE_NESTED, 206 | 207 | /* Selects the correct way to unwire a vm_map based on the 208 | * kernel version. Parameters are the same as XNU's vm_map_unwire. */ 209 | VM_MAP_UNWIRE_WRAPPER, 210 | 211 | VM_MAP_WIRE_EXTERNAL, 212 | 213 | /* -------------------------------------------- 214 | * Everything above (with the exception of the small wrapper functions) 215 | * is from XNU, everything below are things from xnuspy you may 216 | * find useful 217 | * --------------------------------------------- 218 | */ 219 | 220 | /* uint64_t *el0_ptep(void *uaddr) 221 | * 222 | * Given a user virtual address, this function returns a pointer to its 223 | * page table entry. 224 | * 225 | * Parameters: 226 | * uaddr: user virtual address. 227 | * 228 | * Returns: 229 | * Kernel virtual address of page table entry for uaddr. 230 | */ 231 | EL0_PTEP, 232 | 233 | /* uint64_t *el1_ptep(void *kaddr) 234 | * 235 | * Given a kernel virtual address, this function returns a pointer to its 236 | * page table entry. 237 | * 238 | * Parameters: 239 | * kaddr: kernel virtual address. 240 | * 241 | * Returns: 242 | * Kernel virtual address of page table entry for kaddr. 243 | */ 244 | EL1_PTEP, 245 | 246 | /* void hookme(void *arg) 247 | * 248 | * This function is a stub for you to hook to easily gain kernel code 249 | * execution without having to hook an actual kernel function. You can 250 | * get xnuspy to call it by invoking xnuspy_ctl with the 251 | * XNUSPY_CALL_HOOKME flavor. 252 | */ 253 | HOOKME, 254 | 255 | /* uint64_t iOS_version 256 | * 257 | * This variable contains the major from the "Darwin Kernel Version" 258 | * string. On iOS 13.x, this is 19, on iOS 14.x, this is 20, and 259 | * on iOS 15.x, this is 21. */ 260 | IOS_VERSION, 261 | 262 | /* uint64_t kernel_slide 263 | * 264 | * KASLR slide */ 265 | KERNEL_SLIDE, 266 | 267 | /* uint64_t kern_version_minor 268 | * 269 | * This variable contains the minor from the "Darwin Kernel Version" 270 | * string. */ 271 | KERN_VERSION_MINOR, 272 | 273 | /* int kprotect(void *kaddr, uint64_t size, vm_prot_t prot) 274 | * 275 | * Change protections of kernel memory at the page table level. 276 | * You are allowed to make writable, executable memory. 277 | * 278 | * Parameters: 279 | * kaddr: kernel virtual address of target. 280 | * size: the number of bytes in the target region. 281 | * prot: protections to apply. Only VM_PROT_READ, VM_PROT_WRITE, and 282 | * VM_PROT_EXECUTE are respected. 283 | * 284 | * Returns: 285 | * Zero if successful, non-zero otherwise. 286 | */ 287 | KPROTECT, 288 | 289 | /* uint64_t kvtophys(uint64_t kaddr) 290 | * 291 | * Convert a kernel (EL1) virtual address to a physical address. 292 | * 293 | * Parameters: 294 | * kaddr: kernel virtual address. 295 | * 296 | * Returns: 297 | * Non-zero if address translation was successful, zero otherwise. 298 | */ 299 | KVTOPHYS, 300 | 301 | /* void kwrite_instr(uint64_t addr, uint32_t instr) 302 | * 303 | * Patch a single instruction of executable kernel code. This function 304 | * handles permissions, data cache cleaning, and instruction cache 305 | * invalidation. 306 | * 307 | * Parameters: 308 | * addr: kernel virtual address. 309 | * instr: new instruction for addr. 310 | */ 311 | KWRITE_INSTR, 312 | 313 | /* void kwrite_static(void *dst, void *buf, size_t sz) 314 | * 315 | * Write to static kernel memory, using bcopy_phys. 316 | * 317 | * Parameters: 318 | * dst: kernel virtual address of destination. 319 | * buf: kernel virtual address of data. 320 | * sz: how many bytes 'buf' is. 321 | */ 322 | KWRITE_STATIC, 323 | 324 | /* The next three functions deal with shared memory. KTOU ("kernel to 325 | * user") and UTOK ("user to kernel") specify the "direction". "a to b", 326 | * where and are both vm_map pointers, means pages from will 327 | * be mapped into as shared memory. Pages from must have been 328 | * allocated via vm_allocate for these functions to succeed. KTOU and UTOK 329 | * automatically select the and vm_map pointers for convenience. 330 | * The RAW variant allows you to specify the and vm_map pointers. 331 | * You would use mkshmem_raw when you are unsure of current_task()->map 332 | * or the current CPU's TTBR0 inside your kernel code. 333 | * 334 | * int mkshmem_ktou(uint64_t kaddr, uint64_t sz, vm_prot_t prot, 335 | * struct xnuspy_shmem *shmemp); 336 | * int mkshmem_utok(uint64_t uaddr, uint64_t sz, vm_prot_t prot, 337 | * struct xnuspy_shmem *shmemp); 338 | * int mkshmem_raw(uint64_t addr, uint64_t sz, vm_prot_t prot, 339 | * vm_map_t from, vm_map_t to, struct xnuspy_shmem *shmemp); 340 | * 341 | * Parameters (for all three): 342 | * kaddr/uaddr/addr: virtual address somewhere inside 343 | * sz: page aligned mapping size 344 | * prot: virtual protections to apply to the created 345 | * shared mapping 346 | * shmemp: returned shmem. The structure definition can 347 | * be found at the end of this file. 348 | * 349 | * Parameters specific to mkshmem_raw: 350 | * from: source map, aka 351 | * to: destination map, aka 352 | * 353 | * Returns (for all three): 354 | * Zero on success (and populated shmemp structure), non-zero BSD errno 355 | * on failure. 356 | * 357 | * Other notes: 358 | * These functions use kprotect to apply VM protections, so any 359 | * combination of those are allowed. VM protections are only applied 360 | * to the newly-created mapping, not the source pages that came 361 | * from . 362 | */ 363 | MKSHMEM_KTOU, 364 | MKSHMEM_UTOK, 365 | MKSHMEM_RAW, 366 | 367 | /* offsetof(struct thread, map), vm_map_t */ 368 | OFFSETOF_STRUCT_THREAD_MAP, 369 | 370 | /* offsetof(struct _vm_map, map_refcnt), int (yes, int) */ 371 | OFFSETOF_STRUCT_VM_MAP_REFCNT, 372 | 373 | /* int shmem_destroy(struct xnuspy_shmem *shmemp); 374 | * 375 | * Destory shared memory returned by mkshmem_ktou, mkshmem_utok, or 376 | * mkshmem_raw. 377 | * 378 | * Parameters: 379 | * shmemp: pointer to shmem structure 380 | * 381 | * Returns: 382 | * Zero on success, non-zero BSD errno on failure. 383 | */ 384 | SHMEM_DESTROY, 385 | 386 | /* void tlb_flush(void) 387 | * 388 | * After modifying a page table, call this function to invalidate 389 | * the TLB. 390 | */ 391 | TLB_FLUSH, 392 | 393 | /* The next two functions abstract away the different kalloc/kfree pairs 394 | * for different iOS versions and keeps track of allocation sizes. This 395 | * creates an API like malloc/free. Pointers returned from unified_kalloc 396 | * can only be freed with unified_kfree, and pointers returned by other 397 | * memory allocation functions cannot be freed with unified_kfree. 398 | * 399 | * uint8_t *buf = unified_kalloc(0x200); 400 | * 401 | * if(!buf) 402 | * 403 | * 404 | * buf[0] = '\0'; 405 | * 406 | * unified_kfree(buf); 407 | * 408 | * ------------------------------- 409 | * 410 | * void *unified_kalloc(size_t sz) 411 | * 412 | * Parameters: 413 | * sz: allocation size. 414 | * 415 | * Returns: 416 | * Upon success, a pointer to memory. If we are on 13.x, kalloc_canblock's 417 | * canblock parameter is false. Upon failure, NULL. 418 | * 419 | * ------------------------------- 420 | * 421 | * void unified_kfree(void *ptr) 422 | * 423 | * Parameters: 424 | * ptr: a pointer returned from unified_kalloc. 425 | */ 426 | UNIFIED_KALLOC, 427 | UNIFIED_KFREE, 428 | 429 | /* int uprotect(void *uaddr, uint64_t size, vm_prot_t prot) 430 | * 431 | * Change protections of user memory at the page table level. 432 | * You are allowed to make writable, executable memory. 433 | * 434 | * Parameters: 435 | * uaddr: user virtual address of target. 436 | * size: the number of bytes in the target region. 437 | * prot: protections to apply. Only VM_PROT_READ, VM_PROT_WRITE, and 438 | * VM_PROT_EXECUTE are respected. 439 | * 440 | * Returns: 441 | * Zero if successful, non-zero otherwise. 442 | */ 443 | UPROTECT, 444 | 445 | /* uint64_t uvtophys(uint64_t uaddr) 446 | * 447 | * Convert a user (EL0) virtual address to a physical address. 448 | * 449 | * Parameters: 450 | * uaddr: user virtual address. 451 | * 452 | * Returns: 453 | * Non-zero if address translation was successful, zero otherwise. 454 | */ 455 | UVTOPHYS, 456 | 457 | #ifdef XNUSPY_PRIVATE 458 | MAX_CACHE = UVTOPHYS, 459 | #endif 460 | }; 461 | 462 | #define iOS_13_x (19) 463 | #define iOS_14_x (20) 464 | #define iOS_15_x (21) 465 | 466 | /* Structures for locks that work in both kernelspace and userspace. 467 | * Any locks you declare must be declared globally so they 468 | * are mapped as shared memory when you install your kernel hooks */ 469 | /* kuslck_t: a simple spinlock */ 470 | typedef struct { 471 | uint32_t word; 472 | } kuslck_t; 473 | 474 | #define KUSLCK_UNLOCKED (0) 475 | #define KUSLCK_LOCKED (1) 476 | 477 | /* kuslck_t lck = KUSLCK_INITIALIZER; */ 478 | #define KUSLCK_INITIALIZER { .word = KUSLCK_UNLOCKED } 479 | 480 | #define kuslck_lock(lck) \ 481 | do { \ 482 | while(__atomic_exchange_n(&(lck).word, KUSLCK_LOCKED, \ 483 | __ATOMIC_ACQ_REL) == 0){} \ 484 | } while (0) \ 485 | 486 | #define kuslck_unlock(lck) \ 487 | do { \ 488 | __atomic_store_n(&(lck).word, KUSLCK_UNLOCKED, __ATOMIC_RELEASE); \ 489 | } while (0) \ 490 | 491 | struct xnuspy_shmem { 492 | /* Base of shared memory */ 493 | void *shm_base; 494 | /* Size of shared memory, page multiple */ 495 | uint64_t shm_sz; 496 | #ifdef XNUSPY_PRIVATE 497 | /* Memory entry for the shared memory, ipc_port_t */ 498 | void *shm_entry; 499 | /* The vm_map_t which the source pages belong to */ 500 | void *shm_map_from; 501 | /* The vm_map_t which the source pages were mapped into */ 502 | void *shm_map_to; 503 | #else 504 | void *opaque[3]; 505 | #endif 506 | }; 507 | 508 | #endif 509 | -------------------------------------------------------------------------------- /include/xnuspy/xnuspy_structs.h: -------------------------------------------------------------------------------- 1 | #ifndef XNUSPY_STRUCTS 2 | #define XNUSPY_STRUCTS 3 | 4 | #include 5 | 6 | struct stailq_entry { 7 | void *elem; 8 | STAILQ_ENTRY(stailq_entry) link; 9 | }; 10 | 11 | struct slist_entry { 12 | void *elem; 13 | SLIST_ENTRY(slist_entry) link; 14 | }; 15 | 16 | /* struct xnuspy_shmem { */ 17 | /* /1* Base of shared memory *1/ */ 18 | /* void *shm_base; */ 19 | /* /1* Size of shared memory, page multiple *1/ */ 20 | /* uint64_t shm_sz; */ 21 | /* /1* Memory entry for the shared memory, ipc_port_t *1/ */ 22 | /* void *shm_entry; */ 23 | /* /1* The vm_map_t which the source pages belong to *1/ */ 24 | /* void *shm_map_from; */ 25 | /* /1* The vm_map_t which the source pages were mapped into *1/ */ 26 | /* void *shm_map_to; */ 27 | /* }; */ 28 | 29 | #define MAX_MAPPING_REFERENCES (0x1000000) 30 | 31 | /* This structure represents a shared __TEXT and __DATA mapping. There could 32 | * be a number of these structures per-process because different dynamic 33 | * libraries loaded into the address space of one process can install 34 | * hooks. */ 35 | struct xnuspy_mapping { 36 | /* Reference count for this mapping, NOT the mapping metadata */ 37 | _Atomic int64_t refcnt; 38 | /* Pointer to caller's Mach-O header */ 39 | uint64_t mapping_addr_uva; 40 | /* Death callback to invoke when refcnt hits zero */ 41 | void (*death_callback)(void); 42 | /* Kernel's mapping of the shared __TEXT and __DATA. This has 43 | * to be a pointer so I can easily enqueue it onto the unmaplist */ 44 | struct xnuspy_shmem *segment_shmem; 45 | }; 46 | 47 | /* This structure maintains all shared mappings for a given process. There 48 | * is one of these per-process. This will be deallocated when the mappings 49 | * linked list is empty. */ 50 | struct xnuspy_mapping_metadata { 51 | /* Process which owns all of the mappings managed by this structure 52 | * (p_uniqueid) */ 53 | uint64_t owner; 54 | /* Linked list of all shared mappings we've created for this process. 55 | * Protected by xnuspy_rw_lck. */ 56 | SLIST_HEAD(, slist_entry) mappings; 57 | }; 58 | 59 | /* This structure contains information for an xnuspy_tramp that isn't 60 | * necessary to keep in the struct itself. I do this to save space. These are 61 | * not reference counted because they're per-hook. */ 62 | struct xnuspy_tramp_metadata { 63 | /* Hooked kernel function */ 64 | uint64_t hooked; 65 | /* Overwritten instruction */ 66 | uint32_t orig_instr; 67 | }; 68 | 69 | /* This structure represents a function hook. Every xnuspy_tramp struct resides 70 | * on writeable, executable memory. */ 71 | struct xnuspy_tramp { 72 | /* Kernel virtual address of userland replacement on shared mapping */ 73 | uint64_t replacement; 74 | /* The trampoline for a hooked function. When the user installs a hook 75 | * on a function, the first instruction of that function is replaced 76 | * with a branch to here. An xnuspy trampoline looks like this: 77 | * tramp[0] LDR X16, #-0x8 (replacement) 78 | * tramp[1] BR X16 79 | */ 80 | uint32_t tramp[2]; 81 | /* An abstraction that represents the original function. It's just another 82 | * trampoline, but it can take on one of seven forms. The most common 83 | * form is this: 84 | * orig[0] 85 | * orig[1] LDR X16, #0x8 86 | * orig[2] BR X16 87 | * orig[3]
[31:0] 88 | * orig[4]
[63:32] 89 | * 90 | * The above form is taken when the original first instruction of the hooked 91 | * function is not an immediate conditional branch (b.cond), an immediate 92 | * compare and branch (cbz/cbnz), an immediate test and branch (tbz/tbnz), 93 | * an immediate unconditional branch (b), an immediate unconditional 94 | * branch with link (bl), load register (literal), or an ADR. These are 95 | * special cases because the immediates do not contain enough bits for me 96 | * to just "fix up" or assume we'll always be in range once we do, so I 97 | * need to emit an equivalent sequence of instructions. 98 | * 99 | * If the first instruction was B.cond