├── open1_hook.png ├── module ├── pf │ ├── 13 │ │ └── Makefile │ ├── 14 │ │ ├── Makefile │ │ └── pf.c │ ├── 15 │ │ ├── Makefile │ │ └── pf.c │ ├── README.md │ └── Makefile ├── common │ ├── Makefile │ ├── common.c │ └── asm.c ├── README.md ├── el3 │ ├── Makefile │ ├── kpp.s │ └── kpp.c ├── el1 │ ├── Makefile │ ├── xnuspy_ctl_tramp.h │ ├── hook_system_check_sysctlbyname_hook.h │ ├── xnuspy_ctl │ │ ├── Makefile │ │ ├── pte.c │ │ ├── utils.c │ │ ├── wrappers.c │ │ ├── libc.c │ │ ├── debug.c │ │ ├── tramp.c │ │ └── mem.c │ ├── xnuspy_ctl_tramp.s │ └── hook_system_check_sysctlbyname_hook.s ├── Makefile └── xnuspy.c ├── include ├── xnuspy │ ├── el3 │ │ └── kpp.h │ ├── el1 │ │ ├── tramp.h │ │ ├── wrappers.h │ │ ├── utils.h │ │ ├── libc.h │ │ ├── mem.h │ │ ├── debug.h │ │ ├── pte.h │ │ └── externs.h │ ├── xnuspy_cache.h │ ├── xnuspy_structs.h │ └── xnuspy_ctl.h ├── common │ ├── preboot_hook.h │ └── common.h ├── asm │ ├── asm_support.h │ └── asm.h └── pf │ ├── 13 │ └── pf.h │ ├── 14 │ └── pf.h │ ├── 15 │ └── pf.h │ ├── pf_common.h │ └── offsets.h ├── opdump ├── Makefile └── opdump.c ├── Makefile ├── loader ├── Makefile └── loader.c ├── klog ├── Makefile ├── README.md └── klog.c ├── .gitignore ├── ent.xml ├── LICENSE └── example ├── Makefile ├── README.md ├── kernel_thread.c ├── kaddr_of_port.c ├── open1_hook.c ├── shmem.c └── user_client_monitor.c /open1_hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsherman212/xnuspy/HEAD/open1_hook.png -------------------------------------------------------------------------------- /module/pf/13/Makefile: -------------------------------------------------------------------------------- 1 | pf.o : pf.c $(RP)/include/pf/13/pf.h 2 | $(CC) $(CFLAGS) pf.c -c 3 | -------------------------------------------------------------------------------- /module/pf/14/Makefile: -------------------------------------------------------------------------------- 1 | pf.o : pf.c $(RP)/include/pf/14/pf.h 2 | $(CC) $(CFLAGS) pf.c -c 3 | -------------------------------------------------------------------------------- /module/pf/15/Makefile: -------------------------------------------------------------------------------- 1 | pf.o : pf.c $(RP)/include/pf/15/pf.h 2 | $(CC) $(CFLAGS) pf.c -c 3 | -------------------------------------------------------------------------------- /include/xnuspy/el3/kpp.h: -------------------------------------------------------------------------------- 1 | #ifndef KPP 2 | #define KPP 3 | 4 | void patch_kpp(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /opdump/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | CFLAGS = -g 3 | 4 | opdump : opdump.c 5 | $(CC) $(CFLAGS) opdump.c -o opdump 6 | -------------------------------------------------------------------------------- /include/common/preboot_hook.h: -------------------------------------------------------------------------------- 1 | #ifndef PREBOOT_HOOK 2 | #define PREBOOT_HOOK 3 | 4 | void xnuspy_preboot_hook(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /module/pf/README.md: -------------------------------------------------------------------------------- 1 | Directory structure: 2 | 3 | 13/ 4 | - patchfinder code for iOS 13.x 5 | 6 | 14/ 7 | - patchfinder code for iOS 14.x 8 | 9 | 15/ 10 | - patchfinder code for iOS 15.x 11 | -------------------------------------------------------------------------------- /module/common/Makefile: -------------------------------------------------------------------------------- 1 | all : asm.o common.o 2 | 3 | asm.o : asm.c $(RP)/include/asm/asm.h 4 | $(CC) $(CFLAGS) asm.c -c 5 | 6 | common.o : common.c $(RP)/include/common/common.h 7 | $(CC) $(CFLAGS) common.c -c 8 | -------------------------------------------------------------------------------- /module/pf/Makefile: -------------------------------------------------------------------------------- 1 | TARGET_DIRS = 13 14 15 2 | 3 | all : $(TARGET_DIRS) 4 | 5 | target_dirs : $(TARGET_DIRS) 6 | 7 | .PHONY : target_dirs $(TARGET_DIRS) 8 | 9 | $(TARGET_DIRS) : 10 | $(MAKE) -C $@ 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /loader/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | CFLAGS = -g 3 | LDFLAGS = -lusb-1.0 4 | 5 | ifeq ($(XNUSPY_SERIAL), 1) 6 | CFLAGS += -DXNUSPY_SERIAL 7 | endif 8 | 9 | TARGET = loader 10 | 11 | SOURCES = loader.c 12 | 13 | $(TARGET) : $(SOURCES) 14 | $(CC) $(CFLAGS) $(LDFLAGS) $(SOURCES) -o $(TARGET) 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /klog/Makefile: -------------------------------------------------------------------------------- 1 | SDK = $(shell xcrun --sdk iphoneos --show-sdk-path) 2 | CC = $(shell xcrun --sdk $(SDK) --find clang) 3 | CFLAGS = -isysroot $(SDK) -arch arm64 4 | 5 | all : klog 6 | 7 | .PHONY : upload 8 | 9 | upload : klog 10 | rsync -sz -e 'ssh -p 2222' klog root@localhost:/var/root 11 | 12 | klog : klog.c 13 | $(CC) $(CFLAGS) klog.c -o klog 14 | ldid -S../ent.xml ./klog 15 | -------------------------------------------------------------------------------- /klog/README.md: -------------------------------------------------------------------------------- 1 | # klog 2 | 3 | klog will read from `/dev/klog` for incoming `kprintf` and `IOLog` messages. 4 | It depends on `atm_diagnostic_config=0x20000000` being present in 5 | XNU's boot arguments. 6 | 7 | Recommended usage: `stdbuf -o0 ./klog | grep ` 8 | 9 | Run `make` in this directory to build `klog`. `make upload` will 10 | upload it to your device, but you may have to swap out the port number. 11 | -------------------------------------------------------------------------------- /module/README.md: -------------------------------------------------------------------------------- 1 | Directory structure: 2 | 3 | common/ 4 | - miscellaneous functions and headers 5 | 6 | el1/ 7 | - kernel code 8 | 9 | el3/ 10 | - KPP patchfinder for A9 and its variants 11 | 12 | pf/ 13 | - patchfinder code for the kernel 14 | 15 | preboot_hook.c 16 | - sets everything up for xnuspy_ctl and installs it 17 | 18 | xnuspy.c 19 | - gets kernel version, either exploits SEPROM, patches KPP, or neither, and 20 | launches xnuspy's patchfinders 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /module/el3/Makefile: -------------------------------------------------------------------------------- 1 | DST = $(RP)/include/xnuspy/el3 2 | 3 | all : kpp kpp.o 4 | 5 | # kpp : kpp.s ../common/asm_support.h 6 | kpp : kpp.s $(RP)/include/asm/asm_support.h 7 | $(CC) -arch arm64 -isysroot $(SDK) -I$(RP)/include -e _kpp0 kpp.s -o kpp 8 | $(OPDUMP) -td -i ./kpp -a kpp_patches -o $(DST)/kpp_patches.h 9 | 10 | # kpp.o : ../common/common.h kpp.c kpp.h kpp_patches.h 11 | kpp.o : $(RP)/include/common/common.h kpp.c \ 12 | $(RP)/include/xnuspy/el3/kpp.h $(DST)/kpp_patches.h 13 | $(CC) $(CFLAGS) kpp.c -c 14 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /module/el1/Makefile: -------------------------------------------------------------------------------- 1 | ASM_INSTR_HDRS = $(patsubst %.s, $(RP)/include/xnuspy/el1/%_instrs.h, $(wildcard *.s)) 2 | TARGET_DIRS = xnuspy_ctl 3 | 4 | all : $(ASM_INSTR_HDRS) target_dirs 5 | 6 | $(RP)/include/xnuspy/el1/%_instrs.h : %.s %.h \ 7 | $(RP)/include/asm/asm_support.h $(RP)/include/xnuspy/xnuspy_cache.h 8 | $(eval OUTFILE = $(subst .s,,$<)) 9 | $(CC) -arch arm64 -isysroot $(SDK) -I$(RP)/include -e _$(OUTFILE) $< -o $(OUTFILE) 10 | $(OPDUMP) -td -i ./$(OUTFILE) -a $(OUTFILE) -o $(OUTFILE)_instrs.h 11 | mv $(OUTFILE)_instrs.h $(RP)/include/xnuspy/el1/ 12 | 13 | .PHONY : target_dirs $(TARGET_DIRS) 14 | 15 | target_dirs : $(TARGET_DIRS) 16 | 17 | $(TARGET_DIRS) : 18 | $(MAKE) -C $@ 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /klog/klog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char **argv){ 9 | int klog_fd = open("/dev/klog", O_RDONLY); 10 | 11 | if(klog_fd == -1){ 12 | printf("open: %s\n", strerror(errno)); 13 | return 1; 14 | } 15 | 16 | for(;;){ 17 | fd_set rfds; 18 | FD_ZERO(&rfds); 19 | FD_SET(klog_fd, &rfds); 20 | 21 | int res = select(FD_SETSIZE, &rfds, NULL, NULL, NULL); 22 | 23 | if(res < 0){ 24 | printf("select failed: %s\n", strerror(errno)); 25 | close(klog_fd); 26 | return 1; 27 | } 28 | 29 | char buf[1024]; 30 | memset(buf, 0, sizeof(buf)); 31 | ssize_t r = read(klog_fd, buf, sizeof(buf)); 32 | 33 | if(r < 0){ 34 | printf("read failed: %s\n", strerror(errno)); 35 | close(klog_fd); 36 | return 1; 37 | } 38 | 39 | buf[r] = '\0'; 40 | printf("%s", buf); 41 | } 42 | 43 | close(klog_fd); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /module/el3/kpp.s: -------------------------------------------------------------------------------- 1 | /* The code in this file replaces the function which is called by KPP's 2 | lower EL synchronous exception handler */ 3 | 4 | #include 5 | 6 | #define MONITOR_SET_ENTRY 0x800 7 | 8 | .align 2 9 | .global _kpp0 10 | 11 | _kpp0: 12 | stp x19, x20, [sp, #-0x10]! 13 | 14 | /* Are we here because of monitor_call? If so, the only case we have to 15 | handle is MONITOR_SET_ENTRY */ 16 | mrs x19, esr_el3 17 | mov x20, #0x11 18 | movk x20, #0x5e00, lsl 16 19 | cmp x19, x20 20 | b.eq Lsetentry 21 | 22 | /* Otherwise, we are here because the kernel touched CPACR_EL1, so we 23 | need to get off that instruction before we ERET */ 24 | mov x19, #0x6b3 25 | msr scr_el3, x19 26 | msr cptr_el3, xzr 27 | mov x19, #0x300000 28 | msr cpacr_el1, x19 29 | mrs x19, elr_el3 30 | add x19, x19, #0x4 31 | msr elr_el3, x19 32 | 33 | b Lreturn 34 | 35 | Lsetentry: 36 | cmp x0, MONITOR_SET_ENTRY 37 | b.ne Lreturn 38 | ldr x19, kernEntry 39 | str x1, [x19] 40 | 41 | Lreturn: 42 | ldp x19, x20, [sp], #0x10 43 | ret 44 | 45 | kernEntry: .dword QWORD_PLACEHOLDER 46 | -------------------------------------------------------------------------------- /module/el1/xnuspy_ctl_tramp.h: -------------------------------------------------------------------------------- 1 | #ifndef XNUSPY_CTL_TRAMP 2 | #define XNUSPY_CTL_TRAMP 3 | 4 | #define STACK (0x200) 5 | 6 | #define NEW_PTE_SPACE (STACK-0x80) 7 | 8 | /* mask for extracting pointer to the next table */ 9 | #define ARM_TTE_TABLE_MASK (0x0000ffffffffc000) 10 | 11 | #define ARM_16K_TT_L1_SHIFT (36) 12 | #define ARM_16K_TT_L2_SHIFT (25) 13 | #define ARM_16K_TT_L3_SHIFT (14) 14 | 15 | #define ARM_TT_L1_SHIFT ARM_16K_TT_L1_SHIFT 16 | #define ARM_TT_L2_SHIFT ARM_16K_TT_L2_SHIFT 17 | #define ARM_TT_L3_SHIFT ARM_16K_TT_L3_SHIFT 18 | 19 | #define ARM_16K_TT_L1_INDEX_MASK (0x00007ff000000000) 20 | #define ARM_16K_TT_L2_INDEX_MASK (0x0000000ffe000000) 21 | #define ARM_16K_TT_L3_INDEX_MASK (0x0000000001ffc000) 22 | 23 | #define ARM_TT_L1_INDEX_MASK ARM_16K_TT_L1_INDEX_MASK 24 | #define ARM_TT_L2_INDEX_MASK ARM_16K_TT_L2_INDEX_MASK 25 | #define ARM_TT_L3_INDEX_MASK ARM_16K_TT_L3_INDEX_MASK 26 | 27 | #define DAIFSC_DEBUGF (1 << 3) 28 | #define DAIFSC_ASYNCF (1 << 2) 29 | #define DAIFSC_IRQF (1 << 1) 30 | #define DAIFSC_FIQF (1 << 0) 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /module/el1/hook_system_check_sysctlbyname_hook.h: -------------------------------------------------------------------------------- 1 | #ifndef HOOK_SYSTEM_CHECK_SYSCTLBYNAME_HOOK 2 | #define HOOK_SYSTEM_CHECK_SYSCTLBYNAME_HOOK 3 | 4 | #define STACK (0x200) 5 | 6 | #define KALLOC_SZ (STACK-0xb0) 7 | #define SYSCTL_NAME_SPACE (STACK-0xf0) 8 | 9 | /* sysctl stuff */ 10 | #define CTL_MAXNAME (12) 11 | 12 | #define SIZEOF_STRUCT_SYSCTL_OID (0x50) 13 | 14 | #define OFFSETOF_OID_PARENT (0x0) 15 | #define OFFSETOF_OID_LINK (0x8) 16 | #define OFFSETOF_OID_NUMBER (0x10) 17 | #define OFFSETOF_OID_KIND (0x14) 18 | #define OFFSETOF_OID_ARG1 (0x18) 19 | #define OFFSETOF_OID_ARG2 (0x20) 20 | #define OFFSETOF_OID_NAME (0x28) 21 | #define OFFSETOF_OID_HANDLER (0x30) 22 | #define OFFSETOF_OID_FMT (0x38) 23 | #define OFFSETOF_OID_DESCR (0x40) 24 | #define OFFSETOF_OID_VERSION (0x48) 25 | #define OFFSETOF_OID_REFCNT (0x4c) 26 | 27 | #define OID_AUTO (-1) 28 | 29 | #define CTLTYPE_INT (2) 30 | #define CTLFLAG_OID2 (0x00400000) 31 | #define CTLFLAG_ANYBODY (0x10000000) 32 | #define CTLFLAG_RD (0x80000000) 33 | 34 | #define SYSCTL_OID_VERSION (1) 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /module/Makefile: -------------------------------------------------------------------------------- 1 | SDK = $(shell xcrun --sdk iphoneos --show-sdk-path) 2 | CC = $(shell xcrun --sdk $(SDK) --find clang) 3 | CFLAGS = -isysroot $(SDK) -arch arm64 -Wno-string-plus-int -fno-stack-protector 4 | CFLAGS += -Wno-shorten-64-to-32 -D_FORTIFY_SOURCE=0 -nostdlib 5 | CFLAGS += -DXNUSPY_PRIVATE -I$(RP)/include 6 | LDFLAGS = -Xlinker -kext 7 | OPDUMP = $(RP)/opdump/opdump 8 | 9 | export CC 10 | export CFLAGS 11 | export OPDUMP 12 | export SDK 13 | 14 | TARGET_DIRS = common el1 el3 pf 15 | 16 | all : $(TARGET_DIRS) preboot_hook.o xnuspy 17 | 18 | .PHONY : target_dirs $(TARGET_DIRS) 19 | 20 | target_dirs : $(TARGET_DIRS) 21 | 22 | $(TARGET_DIRS) : 23 | $(MAKE) -C $@ 24 | 25 | OBJECT_FILES = $(shell find ./common ./pf ./el3 -type f -name "*.o") 26 | INSTR_FILES = $(shell find $(RP)/include/xnuspy/el1 -type f -name "*_instrs.h") 27 | 28 | ifneq ($(XNUSPY_TRAMP_PAGES), ) 29 | preboot_hook.o : CFLAGS += -DXNUSPY_TRAMP_PAGES=$(XNUSPY_TRAMP_PAGES) 30 | endif 31 | 32 | preboot_hook.o : $(INSTR_FILES) preboot_hook.c \ 33 | $(RP)/include/common/preboot_hook.h \ 34 | $(RP)/include/xnuspy/xnuspy_structs.h 35 | $(CC) $(CFLAGS) preboot_hook.c -c 36 | 37 | xnuspy : $(OBJECT_FILES) xnuspy.c preboot_hook.o \ 38 | $(RP)/include/common/common.h \ 39 | $(RP)/include/pf/offsets.h \ 40 | $(RP)/include/pf/pfs.h \ 41 | $(RP)/include/xnuspy/xnuspy_ctl.h \ 42 | $(RP)/include/xnuspy/xnuspy_structs.h 43 | $(CC) $(CFLAGS) $(LDFLAGS) $(OBJECT_FILES) preboot_hook.o xnuspy.c -o xnuspy 44 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /module/el1/xnuspy_ctl/Makefile: -------------------------------------------------------------------------------- 1 | OBJECT_FILES = debug.o libc.o mem.o pte.o tramp.o utils.o wrappers.o 2 | 3 | ASM_DEP = ../../common/asm.o 4 | EXTERNAL_DEPS = $(RP)/include/xnuspy/el1/externs.h \ 5 | $(RP)/include/xnuspy/xnuspy_ctl.h \ 6 | $(RP)/include/xnuspy/xnuspy_structs.h 7 | 8 | CFLAGS += -mkernel -Wswitch-enum 9 | 10 | ifeq ($(XNUSPY_DEBUG), 1) 11 | CFLAGS += -DXNUSPY_DEBUG 12 | endif 13 | 14 | ifeq ($(XNUSPY_SERIAL), 1) 15 | CFLAGS += -DXNUSPY_SERIAL 16 | endif 17 | 18 | ifneq ($(XNUSPY_LEAKED_PAGE_LIMIT), ) 19 | CFLAGS += -DXNUSPY_LEAKED_PAGE_LIMIT=$(XNUSPY_LEAKED_PAGE_LIMIT) 20 | endif 21 | 22 | LDFLAGS = -Xlinker -kext 23 | 24 | all : $(OBJECT_FILES) xnuspy_ctl 25 | 26 | # %.o will match all object files, so in case someone's clang decides to 27 | # leave a leftover xnuspy_ctl.o I do it like this 28 | debug.o : debug.c $(RP)/include/xnuspy/el1/debug.h $(EXTERNAL_DEPS) 29 | $(CC) $(CFLAGS) debug.c -c 30 | 31 | libc.o : libc.c $(RP)/include/xnuspy/el1/libc.h $(EXTERNAL_DEPS) 32 | $(CC) $(CFLAGS) libc.c -c 33 | 34 | mem.o : mem.c $(RP)/include/xnuspy/el1/mem.h $(EXTERNAL_DEPS) 35 | $(CC) $(CFLAGS) mem.c -c 36 | 37 | pte.o : pte.c $(RP)/include/xnuspy/el1/pte.h $(EXTERNAL_DEPS) 38 | $(CC) $(CFLAGS) pte.c -c 39 | 40 | tramp.o : tramp.c $(RP)/include/xnuspy/el1/tramp.h $(ASM_DEP) $(EXTERNAL_DEPS) 41 | $(CC) $(CFLAGS) tramp.c -c 42 | 43 | utils.o : utils.c $(RP)/include/xnuspy/el1/utils.h $(EXTERNAL_DEPS) 44 | $(CC) $(CFLAGS) utils.c -c 45 | 46 | wrappers.o : wrappers.c $(RP)/include/xnuspy/el1/wrappers.h $(EXTERNAL_DEPS) 47 | $(CC) $(CFLAGS) wrappers.c -c 48 | 49 | xnuspy_ctl : $(OBJECT_FILES) $(ASM_DEP) $(EXTERNAL_DEPS) xnuspy_ctl.c 50 | $(CC) $(CFLAGS) $(LDFLAGS) $(OBJECT_FILES) $(ASM_DEP) xnuspy_ctl.c \ 51 | -o xnuspy_ctl 52 | -------------------------------------------------------------------------------- /module/el1/xnuspy_ctl/pte.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static pte_t *ptep(uint64_t ttbr, uint64_t addr){ 8 | uint64_t l1_table = phystokv(ttbr & 0xfffffffffffe); 9 | uint64_t l1_idx = (addr >> ARM_TT_L1_SHIFT) & 0x7; 10 | uint64_t *l1_ttep = (uint64_t *)(l1_table + (0x8 * l1_idx)); 11 | 12 | SPYDBG("%s: l1 table %#llx idx %#llx ttep %p tte %#llx\n", __func__, 13 | l1_table, l1_idx, l1_ttep, *l1_ttep); 14 | 15 | uint64_t l2_table = phystokv(*l1_ttep & ARM_TTE_TABLE_MASK); 16 | uint64_t l2_idx = (addr >> ARM_TT_L2_SHIFT) & 0x7ff; 17 | uint64_t *l2_ttep = (uint64_t *)(l2_table + (0x8 * l2_idx)); 18 | 19 | SPYDBG("%s: l2 table %#llx idx %#llx ttep %p tte %#llx\n", __func__, 20 | l2_table, l2_idx, l2_ttep, *l2_ttep); 21 | 22 | uint64_t l3_table = phystokv(*l2_ttep & ARM_TTE_TABLE_MASK); 23 | uint64_t l3_idx = (addr >> ARM_TT_L3_SHIFT) & 0x7ff; 24 | 25 | pte_t *l3_ptep = (pte_t *)(l3_table + (0x8 * l3_idx)); 26 | 27 | SPYDBG("%s: l3 table %#llx idx %#llx ptep %p pte %#llx\n", __func__, 28 | l3_table, l3_idx, l3_ptep, *l3_ptep); 29 | 30 | return l3_ptep; 31 | } 32 | 33 | pte_t *el0_ptep(void *uaddr){ 34 | uint64_t ttbr0_el1; 35 | asm volatile("mrs %0, ttbr0_el1" : "=r" (ttbr0_el1)); 36 | return ptep(ttbr0_el1, (uint64_t)uaddr); 37 | } 38 | 39 | pte_t *el1_ptep(void *kaddr){ 40 | uint64_t ttbr1_el1; 41 | asm volatile("mrs %0, ttbr1_el1" : "=r" (ttbr1_el1)); 42 | return ptep(ttbr1_el1, (uint64_t)kaddr); 43 | } 44 | 45 | __attribute__ ((naked)) void tlb_flush(void){ 46 | asm("" 47 | "isb sy\n" 48 | "dsb sy\n" 49 | "tlbi vmalle1\n" 50 | "dsb sy\n" 51 | "isb sy\n" 52 | "ret\n" 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /module/el1/xnuspy_ctl/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | __attribute__ ((naked)) uint64_t current_thread(void){ 10 | asm("" 11 | "mrs x0, tpidr_el1\n" 12 | "ret\n" 13 | ); 14 | } 15 | 16 | struct _vm_map *current_map(void){ 17 | return *(struct _vm_map **)(current_thread() + offsetof_struct_thread_map); 18 | } 19 | 20 | void vm_map_reference(void *map){ 21 | uint64_t off = offsetof_struct_vm_map_refcnt; 22 | _Atomic int *refcnt = (_Atomic int *)((uintptr_t)map + off); 23 | atomic_fetch_add_explicit(refcnt, 1, memory_order_relaxed); 24 | } 25 | 26 | bool is_15_x(void){ 27 | return iOS_version == iOS_15_x; 28 | } 29 | 30 | bool is_14_5_and_above(void){ 31 | if(iOS_version <= iOS_13_x) 32 | return false; 33 | 34 | if (iOS_version == iOS_14_x && 35 | kern_version_minor < 4) 36 | return false; 37 | 38 | return true; 39 | } 40 | 41 | bool is_14_x_and_above(void){ 42 | return iOS_version >= iOS_14_x; 43 | } 44 | 45 | bool is_14_x_and_below(void){ 46 | return iOS_version <= iOS_14_x; 47 | } 48 | 49 | bool is_14_x(void){ 50 | return iOS_version == iOS_14_x; 51 | } 52 | 53 | bool is_13_x(void){ 54 | return iOS_version == iOS_13_x; 55 | } 56 | 57 | /* On 14.5+, the patchfinder for proc_list_mlock yields a pointer 58 | * to it, not a pointer to a pointer to it like on 13.0 - 14.4.2 */ 59 | void *get_proc_list_mlock(void){ 60 | void *mtx = proc_list_mlockp; 61 | 62 | if(is_14_5_and_above()) 63 | return mtx; 64 | 65 | return *(void **)mtx; 66 | } 67 | 68 | void proc_list_lock(void){ 69 | lck_mtx_lock(get_proc_list_mlock()); 70 | } 71 | 72 | /* proc_list_unlock has been inlined so aggressively on all kernels that there 73 | * are no xrefs to the actual function so we need to do it like this */ 74 | void proc_list_unlock(void){ 75 | lck_mtx_unlock(get_proc_list_mlock()); 76 | } 77 | -------------------------------------------------------------------------------- /module/el1/xnuspy_ctl/wrappers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | void ipc_port_release_send_wrapper(void *port){ 8 | if(!is_14_5_and_above()){ 9 | ipc_port_release_send(port); 10 | return; 11 | } 12 | 13 | if(is_15_x()) 14 | ipc_object_lock(port); 15 | else 16 | io_lock(port); 17 | 18 | ipc_port_release_send_and_unlock(port); 19 | } 20 | 21 | kern_return_t vm_map_unwire_wrapper(void *map, uint64_t start, uint64_t end, 22 | int user){ 23 | if(is_15_x()) 24 | return vm_map_unwire_nested(map, start, end, user, 0, 0); 25 | 26 | return vm_map_unwire(map, start, end, user); 27 | } 28 | 29 | void *proc_ref_wrapper(void *proc, bool holding_proc_list_mlock){ 30 | /* For 13.x and 14.x, proc_ref_locked and proc_rele_locked expect 31 | * the proc_list_mlock to be held before they are called. For 32 | * 15.x, the second parameter to proc_ref indicates whether it is 33 | * held or not */ 34 | if(is_15_x()) 35 | return proc_ref(proc, holding_proc_list_mlock); 36 | 37 | void *proc_list_mlock = get_proc_list_mlock(); 38 | 39 | if(!holding_proc_list_mlock) 40 | lck_mtx_lock(proc_list_mlock); 41 | 42 | void *res = proc_ref_locked(proc); 43 | 44 | if(!holding_proc_list_mlock) 45 | lck_mtx_unlock(proc_list_mlock); 46 | 47 | return res; 48 | } 49 | 50 | int proc_rele_wrapper(void *proc, bool holding_proc_list_mlock){ 51 | /* On 15.x the second parameter is ignored, but we need to know 52 | * if we're on 13.x or 14.x */ 53 | if(is_15_x()) 54 | return proc_rele(proc); 55 | 56 | void *proc_list_mlock = get_proc_list_mlock(); 57 | 58 | if(!holding_proc_list_mlock) 59 | lck_mtx_lock(proc_list_mlock); 60 | 61 | proc_rele_locked(proc); 62 | 63 | if(!holding_proc_list_mlock) 64 | lck_mtx_unlock(proc_list_mlock); 65 | 66 | /* Just return 0 for 13.x and 14.x, proc_rele_locked has no 67 | * return value for those kernels */ 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /module/common/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | bool is_15_x__pongo(void){ 9 | return g_kern_version_major == iOS_15_x; 10 | } 11 | 12 | bool is_14_5_and_above__pongo(void){ 13 | if(g_kern_version_major <= iOS_13_x) 14 | return false; 15 | 16 | if (g_kern_version_major == iOS_14_x && 17 | g_kern_version_minor < 4) 18 | return false; 19 | 20 | return true; 21 | } 22 | 23 | bool is_14_x_and_above__pongo(void){ 24 | return g_kern_version_major >= iOS_14_x; 25 | } 26 | 27 | bool is_14_x_and_below__pongo(void){ 28 | return g_kern_version_major <= iOS_14_x; 29 | } 30 | 31 | bool is_14_x__pongo(void){ 32 | return g_kern_version_major == iOS_14_x; 33 | } 34 | 35 | bool is_13_x__pongo(void){ 36 | return g_kern_version_major == iOS_13_x; 37 | } 38 | 39 | /* no sign support */ 40 | int atoi(const char *s){ 41 | int res = 0; 42 | 43 | while(*s){ 44 | res = res * 10 + (*s - '0'); 45 | s++; 46 | } 47 | 48 | return res; 49 | } 50 | 51 | int isdigit(int c){ 52 | return c >= '0' && c <= '9'; 53 | } 54 | 55 | char *strcpy(char *dest, const char *src){ 56 | char *src0 = (char *)src; 57 | while((*dest++ = *src0++)); 58 | *dest = '\0'; 59 | /* who cares about strcpy return value */ 60 | return dest; 61 | } 62 | 63 | char *strstr(const char *haystack, const char *needle){ 64 | if(!*needle) 65 | return (char *)haystack; 66 | 67 | char *hay = (char *)haystack; 68 | char *n = (char *)needle; 69 | 70 | for(; *hay; hay++){ 71 | if(*hay != *n) 72 | continue; 73 | 74 | char *h = hay; 75 | 76 | for(;;){ 77 | if(!*n) 78 | return hay; 79 | 80 | if(*h++ != *n++) 81 | break; 82 | } 83 | 84 | n = (char *)needle; 85 | } 86 | 87 | return NULL; 88 | } 89 | 90 | struct mach_header_64 *mh_execute_header = NULL; 91 | uint64_t kernel_slide = 0; 92 | 93 | /* XXX do not panic so user can see what screen says */ 94 | __attribute__ ((noreturn)) void xnuspy_fatal_error(void){ 95 | puts("xnuspy: fatal error."); 96 | puts(" Please file an issue"); 97 | puts(" on Github. Include"); 98 | puts(" output up to this"); 99 | puts(" point and device/iOS"); 100 | puts(" version."); 101 | puts("Spinning forever."); 102 | 103 | for(;;); 104 | } 105 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /module/el1/xnuspy_ctl/libc.c: -------------------------------------------------------------------------------- 1 | /* libc functions that aren't hyper-optimized inside the kernel that I can 2 | * just reimplement here. This saves me the trouble of writing a ton of 3 | * patchfinders */ 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void bzero(void *p, size_t n){ 11 | uint8_t *p0 = p; 12 | uint8_t *p_end = p0 + n; 13 | 14 | while(p0 < p_end) 15 | *p0++ = '\0'; 16 | } 17 | 18 | void *memchr(const void *s, int c, size_t n){ 19 | if(!n) 20 | return NULL; 21 | 22 | uint8_t *sp = (uint8_t *)s; 23 | 24 | for(size_t i=0; i blen) 52 | return NULL; 53 | 54 | const char *bs = (const char *)big; 55 | const char *ls = (const char *)little; 56 | 57 | if(llen == 1) 58 | return memchr(big, (int)*ls, blen); 59 | 60 | char *limit = (char *)bs + (blen - llen); 61 | char *cursor; 62 | 63 | for(cursor = (char *)bs; cursor <= limit; cursor++){ 64 | if(*cursor != *ls) 65 | continue; 66 | 67 | if(memcmp(cursor, ls, llen) == 0) 68 | return cursor; 69 | } 70 | 71 | return NULL; 72 | } 73 | 74 | void *memrchr(const void *s, int c, size_t n){ 75 | if(!n) 76 | return NULL; 77 | 78 | uint8_t *sp = (uint8_t *)s + n; 79 | 80 | do { 81 | if(*(--sp) == (uint8_t)c) 82 | return sp; 83 | } while (--n != 0); 84 | 85 | return NULL; 86 | } 87 | 88 | char *strchr(const char *s, int c){ 89 | char *sp = (char *)s; 90 | 91 | for(; *sp != (char)c; ++sp){ 92 | if(!*sp) 93 | return NULL; 94 | } 95 | 96 | return sp; 97 | } 98 | 99 | char *strrchr(const char *s, int c){ 100 | char *lastp = NULL; 101 | char *sp = (char *)s; 102 | 103 | do { 104 | if(*sp == (char)c) 105 | lastp = sp; 106 | } while (*sp++); 107 | 108 | return lastp; 109 | } 110 | 111 | int strcmp(const char *s1, const char *s2){ 112 | while(*s1 && (*s1 == *s2)){ 113 | s1++; 114 | s2++; 115 | } 116 | 117 | return *(const unsigned char *)s1 - *(const unsigned char *)s2; 118 | } 119 | 120 | char *strstr(const char *big, const char *little){ 121 | size_t blen = _strlen(big); 122 | size_t llen = _strlen(little); 123 | 124 | if(!llen) 125 | return (char *)big; 126 | 127 | if(llen > blen) 128 | return NULL; 129 | 130 | char *limit = (char *)big + (blen - llen); 131 | char *cursor; 132 | 133 | for(cursor = (char *)big; cursor <= limit; cursor++){ 134 | if(memcmp(cursor, little, llen) == 0) 135 | return cursor; 136 | } 137 | 138 | return NULL; 139 | } 140 | 141 | char *strnstr(const char *big, const char *little, size_t n){ 142 | size_t llen = _strlen(little); 143 | 144 | if(!llen) 145 | return (char *)big; 146 | 147 | if(llen > n) 148 | return NULL; 149 | 150 | char *limit = (char *)big + (n - llen); 151 | char *cursor; 152 | 153 | for(cursor = (char *)big; cursor <= limit; cursor++){ 154 | if(memcmp(cursor, little, llen) == 0) 155 | return cursor; 156 | } 157 | 158 | return NULL; 159 | } 160 | -------------------------------------------------------------------------------- /module/el1/xnuspy_ctl_tramp.s: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "xnuspy_ctl_tramp.h" 5 | 6 | .align 2 7 | .global _xnuspy_ctl_tramp 8 | 9 | /* This exists to mark the __TEXT_EXEC segment of the xnuspy_ctl Mach-O as 10 | executable before we branch to it. We need to preserve x0, x1, and x2 11 | since this code is what an _enosys sysent was modified to point to. */ 12 | _xnuspy_ctl_tramp: 13 | sub sp, sp, STACK 14 | stp x0, x1, [sp, #(STACK-0x10)] 15 | stp x2, x19, [sp, #(STACK-0x20)] 16 | stp x20, x21, [sp, #(STACK-0x30)] 17 | stp x22, x23, [sp, #(STACK-0x40)] 18 | stp x24, x25, [sp, #(STACK-0x50)] 19 | stp x26, x27, [sp, #(STACK-0x60)] 20 | stp x29, x30, [sp, #(STACK-0x70)] 21 | add x29, sp, #(STACK-0x70) 22 | 23 | adr x27, addrof_xnuspy_cache 24 | ldr x27, [x27] 25 | 26 | ldr x19, [x27, XNUSPY_CTL_IS_RX] 27 | cbnz x19, Lhandoff 28 | 29 | ldr x19, [x27, XNUSPY_CTL_CODESTART] 30 | ldr x20, [x27, XNUSPY_CTL_CODESZ] 31 | /* We don't need to worry about this not being page aligned. Clang always 32 | page-aligns __TEXT_EXEC,__text for the xnuspy_ctl Mach-O */ 33 | add x20, x19, x20 34 | 35 | mrs x0, ttbr1_el1 36 | and x0, x0, #0xfffffffffffe 37 | ldr x22, [x27, PHYSTOKV] 38 | blr x22 39 | mov x21, x0 40 | 41 | /* For pteloop: 42 | X19: current page of xnuspy_ctl __TEXT_EXEC segment 43 | X20: end of last page of xnuspy_ctl __TEXT_EXEC segment 44 | X21: virtual address of TTBR1_EL1 translation table base 45 | X22 - X26: scratch registers 46 | X27: xnuspy cache pointer 47 | */ 48 | 49 | Lpteloop: 50 | lsr x22, x19, ARM_TT_L1_SHIFT 51 | and x22, x22, #0x7 52 | add x22, x21, x22, lsl #0x3 53 | ldr x22, [x22] 54 | and x0, x22, ARM_TTE_TABLE_MASK 55 | ldr x22, [x27, PHYSTOKV] 56 | blr x22 57 | mov x22, x0 58 | lsr x23, x19, ARM_TT_L2_SHIFT 59 | and x23, x23, #0x7ff 60 | add x23, x22, x23, lsl #0x3 61 | ldr x23, [x23] 62 | and x0, x23, ARM_TTE_TABLE_MASK 63 | ldr x22, [x27, PHYSTOKV] 64 | blr x22 65 | mov x22, x0 66 | lsr x23, x19, ARM_TT_L3_SHIFT 67 | and x23, x23, #0x7ff 68 | add x23, x22, x23, lsl #0x3 69 | /* X23 == pointer to PTE for this page */ 70 | ldr x24, [x23] 71 | /* ~(ARM_PTE_PNXMASK | ARM_PTE_NXMASK) */ 72 | mov x25, #0xff9fffffffffffff 73 | and x24, x24, x25 74 | str x24, [sp, NEW_PTE_SPACE] 75 | mov x0, x23 76 | bl _kvtophys 77 | mov x24, x0 78 | add x0, sp, NEW_PTE_SPACE 79 | bl _kvtophys 80 | mov x1, x24 81 | mov w2, #0x8 82 | ldr x23, [x27, BCOPY_PHYS] 83 | blr x23 84 | 85 | Lnextpage: 86 | mov w22, #0x1 87 | add x19, x19, x22, lsl #0xe 88 | cmp x20, x19 89 | b.ne Lpteloop 90 | 91 | isb sy 92 | dsb sy 93 | tlbi vmalle1 94 | dsb sy 95 | isb sy 96 | 97 | str x22, [x27, XNUSPY_CTL_IS_RX] 98 | 99 | Lhandoff: 100 | mov x7, x27 101 | ldp x0, x1, [sp, #(STACK-0x10)] 102 | ldp x2, x19, [sp, #(STACK-0x20)] 103 | ldp x20, x21, [sp, #(STACK-0x30)] 104 | ldp x22, x23, [sp, #(STACK-0x40)] 105 | ldp x24, x25, [sp, #(STACK-0x50)] 106 | ldp x26, x27, [sp, #(STACK-0x60)] 107 | ldp x29, x30, [sp, #(STACK-0x70)] 108 | add sp, sp, STACK 109 | ldr x7, [x7, XNUSPY_CTL_ENTRYPOINT] 110 | br x7 111 | /* Not reached */ 112 | 113 | /* All kvtophys calls were inlined on 14.x kernels :( */ 114 | _kvtophys: 115 | mrs x1, DAIF 116 | msr DAIFSet, #(DAIFSC_DEBUGF | DAIFSC_ASYNCF | DAIFSC_IRQF | DAIFSC_FIQF) 117 | at s1e1r, x0 118 | mrs x2, par_el1 119 | msr DAIF, x1 120 | tbnz x2, #0x0, 2f 121 | and x2, x2, #0xfffffffff000 122 | and x1, x0, #0x3fff 123 | orr x0, x2, x1 124 | 125 | b 1f 126 | 127 | 2: 128 | mov x0, xzr 129 | 130 | 1: 131 | ret 132 | 133 | addrof_xnuspy_cache: .dword QWORD_PLACEHOLDER 134 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /module/el1/xnuspy_ctl/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | void desc_freelist(void){ 12 | lck_rw_lock_shared(xnuspy_rw_lck); 13 | 14 | SPYDBG("[Freelist] "); 15 | 16 | if(STAILQ_EMPTY(&freelist)){ 17 | lck_rw_done(xnuspy_rw_lck); 18 | SPYDBG("Empty\n"); 19 | return; 20 | } 21 | 22 | SPYDBG("FRONT: "); 23 | 24 | struct stailq_entry *entry; 25 | 26 | STAILQ_FOREACH(entry, &freelist, link) 27 | SPYDBG("%#llx <- ", entry->elem); 28 | 29 | SPYDBG("\n"); 30 | 31 | lck_rw_done(xnuspy_rw_lck); 32 | } 33 | 34 | void desc_xnuspy_shmem(struct xnuspy_shmem *xs){ 35 | if(!xs){ 36 | SPYDBG("%s: NULL\n", __func__); 37 | return; 38 | } 39 | 40 | SPYDBG("This xnuspy_shmem object is at %p\n", xs); 41 | SPYDBG("\tRange: [%p, %p)\n", xs->shm_base, 42 | (uintptr_t)xs->shm_base + xs->shm_sz); 43 | SPYDBG("\tMemory entry: %p\n", xs->shm_entry); 44 | SPYDBG("\tOrigin map: %p\n", xs->shm_map_from); 45 | SPYDBG("\tDestination map: %p\n", xs->shm_map_to); 46 | } 47 | 48 | /* XXX ONLY meant to be called from xnuspy_gc_thread, hence the lack 49 | * of locking. */ 50 | void desc_unmaplist(void){ 51 | SPYDBG("[Unmaplist] "); 52 | 53 | if(STAILQ_EMPTY(&unmaplist)){ 54 | SPYDBG("Empty\n"); 55 | return; 56 | } 57 | 58 | SPYDBG("FRONT: "); 59 | 60 | struct stailq_entry *entry; 61 | 62 | STAILQ_FOREACH(entry, &unmaplist, link) 63 | SPYDBG("%#llx <- ", entry->elem); 64 | 65 | SPYDBG("\n"); 66 | } 67 | 68 | void desc_usedlist(void){ 69 | lck_rw_lock_shared(xnuspy_rw_lck); 70 | 71 | SPYDBG("[Usedlist] "); 72 | 73 | if(STAILQ_EMPTY(&usedlist)){ 74 | lck_rw_done(xnuspy_rw_lck); 75 | SPYDBG("Empty\n"); 76 | return; 77 | } 78 | 79 | struct stailq_entry *entry; 80 | 81 | STAILQ_FOREACH(entry, &usedlist, link) 82 | SPYDBG("%#llx -> ", entry->elem); 83 | 84 | SPYDBG("\n"); 85 | 86 | lck_rw_done(xnuspy_rw_lck); 87 | } 88 | 89 | static void _desc_xnuspy_mapping(struct xnuspy_mapping *m){ 90 | struct xnuspy_shmem *xs = m->segment_shmem; 91 | 92 | SPYDBG("\tMapping metadata refcnt: %lld\n", m->refcnt); 93 | SPYDBG("\tMemory entry: %p\n", xs->shm_entry); 94 | SPYDBG("\tUserspace Mach-O header source: %#llx\n", m->mapping_addr_uva); 95 | SPYDBG("\tShared mapping addr/size: %p/%#llx\n", xs->shm_base, 96 | (uintptr_t)xs->shm_base + xs->shm_sz); 97 | 98 | SPYDBG("\tDeath callback: "); 99 | 100 | if(m->death_callback) 101 | SPYDBG("%#llx\n", m->death_callback); 102 | else 103 | SPYDBG("none\n"); 104 | } 105 | 106 | void desc_xnuspy_mapping(struct xnuspy_mapping *m){ 107 | struct xnuspy_shmem *xs = m->segment_shmem; 108 | 109 | SPYDBG("Mapping metadata refcnt: %lld\n", m->refcnt); 110 | SPYDBG("Memory entry: %p\n", xs->shm_entry); 111 | SPYDBG("Userspace Mach-O header source: %#llx\n", m->mapping_addr_uva); 112 | SPYDBG("Shared mapping addr/size: %p/%#llx\n", xs->shm_base, 113 | (uintptr_t)xs->shm_base + xs->shm_sz); 114 | 115 | SPYDBG("Death callback: "); 116 | 117 | if(m->death_callback) 118 | SPYDBG("%#llx\n", m->death_callback); 119 | else 120 | SPYDBG("none\n"); 121 | } 122 | 123 | void desc_xnuspy_mapping_metadata(struct xnuspy_mapping_metadata *mm){ 124 | SPYDBG("Owner: %d\n", mm->owner); 125 | SPYDBG("Mappings:\n"); 126 | 127 | if(SLIST_EMPTY(&mm->mappings)){ 128 | SPYDBG("none\n"); 129 | return; 130 | } 131 | 132 | struct slist_entry *entry; 133 | 134 | SLIST_FOREACH(entry, &mm->mappings, link){ 135 | struct xnuspy_mapping *m = entry->elem; 136 | _desc_xnuspy_mapping(m); 137 | SPYDBG("\n"); 138 | } 139 | } 140 | 141 | void desc_xnuspy_tramp(struct xnuspy_tramp *t, uint32_t orig_tramp_len){ 142 | SPYDBG("This xnuspy_tramp is @ %#llx\n", (uint64_t)t); 143 | SPYDBG("Replacement: %#llx\n", t->replacement); 144 | 145 | SPYDBG("Replacement trampoline:\n"); 146 | 147 | for(int i=0; itramp)/sizeof(t->tramp[0]); i++) 148 | SPYDBG("\ttramp[%d] %#x\n", i, t->tramp[i]); 149 | 150 | SPYDBG("Original trampoline:\n"); 151 | 152 | for(int i=0; iorig[i]); 154 | 155 | if(!t->tramp_metadata) 156 | SPYDBG("NULL tramp metadata\n"); 157 | else{ 158 | SPYDBG("Hooked function: %#llx [unslid=%#llx]\n", 159 | t->tramp_metadata->hooked, 160 | t->tramp_metadata->hooked - kernel_slide); 161 | 162 | SPYDBG("Original instruction: %#x\n", t->tramp_metadata->orig_instr); 163 | } 164 | 165 | if(!t->mapping_metadata) 166 | SPYDBG("NULL mapping metadata\n"); 167 | else 168 | desc_xnuspy_mapping_metadata(t->mapping_metadata); 169 | } 170 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /opdump/opdump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void usage(void){ 12 | printf("Usage:\n" 13 | " -a Create a C uint8_t array out of what's dumped.\n" 14 | " -d Dump __DATA\n" 15 | " -i Input Mach-O file name\n" 16 | " -o Output file name\n" 17 | " -t Dump __TEXT\n" 18 | ); 19 | 20 | exit(1); 21 | } 22 | 23 | int main(int argc, char **argv){ 24 | char *input = NULL, *output = NULL; 25 | int c_array = 0; 26 | char *c_array_name = NULL; 27 | int dump_text = 0; 28 | int dump_data = 0; 29 | 30 | int c; 31 | 32 | opterr = 0; 33 | 34 | while((c = getopt(argc, argv, "a:di:o:t")) != -1){ 35 | switch(c){ 36 | case 'a': 37 | c_array = 1; 38 | c_array_name = optarg; 39 | break; 40 | case 'd': 41 | dump_data = 1; 42 | break; 43 | case 'i': 44 | input = optarg; 45 | break; 46 | case 'o': 47 | output = optarg; 48 | break; 49 | case 't': 50 | dump_text = 1; 51 | break; 52 | default: 53 | usage(); 54 | }; 55 | } 56 | 57 | if(!input){ 58 | printf("Expected input file name\n"); 59 | return 1; 60 | } 61 | 62 | if(!output){ 63 | printf("Expected output file name\n"); 64 | return 1; 65 | } 66 | 67 | if(c_array && !c_array_name){ 68 | printf("Need name for C array\n"); 69 | return 1; 70 | } 71 | 72 | if(!dump_data && !dump_text){ 73 | printf("Need either -d or -t\n"); 74 | return 1; 75 | } 76 | 77 | int fd = open(input, O_RDONLY); 78 | 79 | if(fd == -1){ 80 | printf("open: %s\n", strerror(errno)); 81 | return 1; 82 | } 83 | 84 | struct stat st; 85 | 86 | if(stat(input, &st)){ 87 | printf("stat: %s\n", strerror(errno)); 88 | close(fd); 89 | return 1; 90 | } 91 | 92 | size_t fsz = st.st_size; 93 | 94 | void *fdata = mmap(NULL, fsz, PROT_READ, MAP_PRIVATE, fd, 0); 95 | 96 | close(fd); 97 | 98 | if(fdata == MAP_FAILED){ 99 | printf("mmap: %s\n", strerror(errno)); 100 | return 1; 101 | } 102 | 103 | struct mach_header_64 *mh = fdata; 104 | 105 | if(mh->magic != MH_MAGIC_64){ 106 | printf("'%s' is not a mach-o file?\n", input); 107 | munmap(fdata, fsz); 108 | return 1; 109 | } 110 | 111 | struct load_command *lc = (struct load_command *)(mh + 1); 112 | 113 | /* __TEXT and __DATA are adjacent. But in case __DATA is 114 | * not present, we go until we hit the end of __TEXT */ 115 | uint8_t *raw_cursor = NULL, *raw_end = NULL; 116 | 117 | for(int i=0; incmds; i++){ 118 | if(lc->cmd != LC_SEGMENT_64) 119 | goto nextcmd; 120 | 121 | struct segment_command_64 *sc64 = (struct segment_command_64 *)lc; 122 | 123 | if(strcmp(sc64->segname, "__TEXT") == 0){ 124 | struct section_64 *sec64 = (struct section_64 *)(sc64 + 1); 125 | 126 | for(int k=0; knsects; k++){ 127 | if(dump_text && strcmp(sec64->sectname, "__text") == 0){ 128 | /* Make sure we start at the first function of __text and 129 | * not the Mach-O header */ 130 | raw_cursor = (uint8_t *)((uintptr_t)fdata + sec64->offset); 131 | raw_end = (uint8_t *)((uintptr_t)raw_cursor + sec64->size); 132 | 133 | break; 134 | } 135 | 136 | sec64++; 137 | } 138 | } 139 | else if(dump_data && strcmp(sc64->segname, "__DATA") == 0){ 140 | uint8_t *DATA_start = (uint8_t *)((uintptr_t)fdata + sc64->fileoff); 141 | 142 | if(!dump_text) 143 | raw_cursor = DATA_start; 144 | 145 | raw_end = (uint8_t *)(DATA_start + sc64->filesize); 146 | } 147 | 148 | nextcmd: 149 | lc = (struct load_command *)((uintptr_t)lc + lc->cmdsize); 150 | } 151 | 152 | if(!raw_cursor){ 153 | printf("Did not find start section\n"); 154 | munmap(fdata, fsz); 155 | return 1; 156 | } 157 | 158 | FILE *outp = fopen(output, "wb"); 159 | 160 | if(!outp){ 161 | printf("fopen: %s\n", strerror(errno)); 162 | munmap(fdata, fsz); 163 | return 1; 164 | } 165 | 166 | /* TODO: handle function starts */ 167 | uint64_t nbytes = 0; 168 | 169 | if(c_array){ 170 | fprintf(outp, 171 | "#ifndef %s_h\n" 172 | "#define %s_h\n" 173 | "static uint8_t g_%s[] = {\n", 174 | c_array_name, c_array_name, c_array_name); 175 | } 176 | 177 | while(raw_cursor < raw_end){ 178 | if(!c_array){ 179 | fwrite(raw_cursor, sizeof(uint8_t), 1, outp); 180 | } 181 | else{ 182 | fprintf(outp, "\t0x%02x,\n", *raw_cursor); 183 | nbytes++; 184 | } 185 | 186 | raw_cursor++; 187 | } 188 | 189 | if(c_array){ 190 | fprintf(outp, "};\n" 191 | "static const uint64_t g_%s_len = %lld;\n" 192 | "#endif", c_array_name, nbytes); 193 | } 194 | 195 | fflush(outp); 196 | fclose(outp); 197 | 198 | munmap(fdata, fsz); 199 | 200 | return 0; 201 | } 202 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /module/el3/kpp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | static uint64_t find_kpp(void){ 13 | dt_node_t *cpus = dt_find(gDeviceTree, "cpus"); 14 | 15 | if(!cpus){ 16 | printf("xnuspy: no cpus node?\n"); 17 | xnuspy_fatal_error(); 18 | } 19 | 20 | dt_node_t *cpu0 = dt_find(cpus, "cpu0"); 21 | 22 | if(!cpu0){ 23 | printf("xnuspy: no cpu0 node?\n"); 24 | xnuspy_fatal_error(); 25 | } 26 | 27 | uint32_t len; 28 | void *reg_private = dt_prop(cpu0, "reg-private", &len); 29 | 30 | if(!reg_private || len != 8){ 31 | printf("xnuspy: bad reg-private prop?\n"); 32 | xnuspy_fatal_error(); 33 | } 34 | 35 | uint64_t *IORVBARp = (uint64_t *)(*(uint64_t *)reg_private + 0x40000); 36 | 37 | if(!IORVBARp){ 38 | printf("xnuspy: no IORVBAR?\n"); 39 | xnuspy_fatal_error(); 40 | } 41 | 42 | return *IORVBARp & 0xfffffffff; 43 | } 44 | 45 | static void patchfind_kpp(uint32_t *kpp_stream, uint32_t *kpp_stream_end){ 46 | /* We're searching for KPP's handler for synchronous exceptions from EL1. 47 | * It'll be easy to find; it saves X0-X17, X29, and X30 to the stack 48 | * then calls the function that performs all the integrity checks. 49 | * We'll search for stp x16, x17, [sp, -0x10]! and then shortly after that 50 | * will be a BL to the integrity check/"KPP syscall" function. Once we've 51 | * got that function, we need to find the pointer to (what I call) 52 | * _kernEntry. It's set via an SMC with X0 == 0x800 (MONITOR_SET_ENTRY), 53 | * and KPP's _start routine depends on that being set so it can ERET back 54 | * to EL1 upon reset. We'll search for this, starting from the start of 55 | * the integrity check function: 56 | * 57 | * CMP X0, #0x802 58 | * B.EQ loc_410000647C 59 | * CMP X0, #0x801 60 | * B.EQ loc_41000064D0 61 | * CMP X0, #0x800 62 | * B.NE loc_4100005F90 63 | * 64 | * And once we've found that, the first ADRP or ADR,STR pair we see going 65 | * forward is for _kernEntry. We save its pointer and then loop back to 66 | * the start of the integrity check function and replace it with the code 67 | * from kpp.s. 68 | * 69 | * kpp_stream points to KPP's Mach-O header, so we can do a linear search. 70 | */ 71 | uint32_t stp_x16_x17_sp_pre = 0xa9bf47f0; 72 | 73 | while(*kpp_stream != stp_x16_x17_sp_pre){ 74 | if(kpp_stream >= kpp_stream_end){ 75 | printf("xnuspy: did not find\n" 76 | " stp x16, x17 in KPP?\n"); 77 | xnuspy_fatal_error(); 78 | } 79 | 80 | kpp_stream++; 81 | } 82 | 83 | kpp_stream = get_branch_dst_ptr(kpp_stream + 2); 84 | 85 | uint32_t *saved_prologue = kpp_stream; 86 | 87 | uint32_t cmp_matches[] = { 88 | 0xf120081f, /* cmp x0, 0x802 */ 89 | 0x54000000, /* b.eq n */ 90 | 0xf120041f, /* cmp x0, 0x801 */ 91 | 0x54000000, /* b.eq n */ 92 | 0xf120001f, /* cmp x0, 0x800 */ 93 | 0x54000001, /* b.ne n */ 94 | }; 95 | 96 | uint32_t cmp_masks[] = { 97 | 0xffffffff, /* match exactly */ 98 | 0xff00001f, /* ignore immediate */ 99 | 0xffffffff, /* match exactly */ 100 | 0xff00001f, /* ignore immediate */ 101 | 0xffffffff, /* match exactly */ 102 | 0xff00001f, /* ignore immediate */ 103 | }; 104 | 105 | for(;;){ 106 | if(kpp_stream >= kpp_stream_end){ 107 | printf("xnuspy: did not find\n" 108 | " X0 if statement?\n"); 109 | xnuspy_fatal_error(); 110 | } 111 | 112 | if((*kpp_stream & *cmp_masks) == *cmp_matches){ 113 | for(int i=1; i= kpp_stream_end){ 129 | printf("xnuspy: did not find\n" 130 | " _kernEntry for KPP\n"); 131 | xnuspy_fatal_error(); 132 | } 133 | 134 | kpp_stream++; 135 | } 136 | 137 | uint32_t adrp = *kpp_stream; 138 | uint32_t str = kpp_stream[1]; 139 | 140 | uint32_t immlo = bits(adrp, 29, 30); 141 | uint32_t immhi = bits(adrp, 5, 23); 142 | uint32_t imm12 = bits(str, 10, 21); 143 | uint32_t shift = bits(str, 30, 31); 144 | 145 | uint64_t page = sign_extend(((immhi << 2) | immlo) << 12, 32) + 146 | ((uintptr_t)kpp_stream & ~0xfffuLL); 147 | uint64_t pageoff = sign_extend(imm12, 12) << shift; 148 | 149 | uint64_t mapping_kernEntryp = page + pageoff; 150 | uint64_t kpp_kernEntryp = 0x4100000000 + (mapping_kernEntryp - 0xc10000000); 151 | 152 | uint64_t kpp_patches_len = g_kpp_patches_len / sizeof(uint32_t); 153 | uint32_t *kpp_patches_cursor = (uint32_t *)g_kpp_patches; 154 | uint32_t *kpp_patches_end = kpp_patches_cursor + kpp_patches_len; 155 | 156 | kpp_stream = saved_prologue; 157 | 158 | /* Finally, replace this function */ 159 | while(kpp_patches_cursor < kpp_patches_end){ 160 | if(*(uint64_t *)kpp_patches_cursor == QWORD_PLACEHOLDER) 161 | *(uint64_t *)kpp_patches_cursor = kpp_kernEntryp; 162 | 163 | *kpp_stream++ = *kpp_patches_cursor++; 164 | } 165 | } 166 | 167 | void patch_kpp(void){ 168 | uint64_t kppphys = find_kpp(); 169 | 170 | printf("xnuspy: found KPP at %#llx\n", kppphys); 171 | 172 | map_range(0xc10000000, kppphys, 0xc000, 3, 0, true); 173 | 174 | uint32_t *kppmapping = (uint32_t *)0xc10000000; 175 | 176 | patchfind_kpp(kppmapping, kppmapping + 0x3000); 177 | 178 | puts("xnuspy: patched KPP"); 179 | } 180 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /module/common/asm.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint64_t sign_extend(uint64_t number, uint32_t numbits /* signbit */){ 4 | if(number & ((uint64_t)1 << (numbits - 1))) 5 | return number | ~(((uint64_t)1 << numbits) - 1); 6 | 7 | return number; 8 | } 9 | 10 | uint64_t bits(uint64_t number, uint64_t start, uint64_t end){ 11 | uint64_t amount = (end - start) + 1; 12 | uint64_t mask = (((uint64_t)1 << amount) - 1) << start; 13 | 14 | return (number & mask) >> start; 15 | } 16 | 17 | uint32_t assemble_adrp(uint64_t label, uint64_t pc, uint32_t Rd){ 18 | label &= ~0xfffuLL; 19 | pc &= ~0xfffuLL; 20 | 21 | uint64_t dist = label - pc; 22 | 23 | return (1u << 31) | (1 << 28) | ((dist & 0x3000) << 17) | 24 | ((dist & 0x1ffffc000uLL) >> 9) | Rd; 25 | } 26 | 27 | uint32_t assemble_b(uint64_t from, uint64_t to){ 28 | uint32_t imm26 = ((to - from) >> 2) & 0x3ffffff; 29 | return (5 << 26) | imm26; 30 | } 31 | 32 | uint32_t assemble_bl(uint64_t from, uint64_t to){ 33 | uint32_t imm26 = ((to - from) >> 2) & 0x3ffffff; 34 | return (37u << 26) | imm26; 35 | } 36 | 37 | uint32_t assemble_csel(uint32_t sf, uint32_t Rm, uint32_t cond, 38 | uint32_t Rn, uint32_t Rd){ 39 | return (sf << 31) | (0xd4 << 21) | (Rm << 16) | (cond << 12) | (Rn << 5) | Rd; 40 | } 41 | 42 | uint32_t assemble_immediate_add(uint32_t sf, uint32_t sh, uint32_t imm12, 43 | uint32_t Rn, uint32_t Rd){ 44 | return (sf << 31) | (0x22 << 23) | (sh << 22) | (imm12 << 10) | (Rn << 5) | Rd; 45 | } 46 | 47 | /* this is really just SUBS RZR, Rn, #imm */ 48 | uint32_t assemble_immediate_cmp(uint32_t sf, uint32_t sh, uint32_t imm12, 49 | uint32_t Rn){ 50 | return (sf << 31) | (0xe2 << 23) | (sh << 22) | (imm12 << 10) | (Rn << 5) | 0x1f; 51 | } 52 | 53 | /* No offset */ 54 | uint32_t assemble_immediate_ldr(uint32_t size, uint32_t Rn, uint32_t Rt){ 55 | return (size << 30) | (0xe5 << 22) | (Rn << 5) | Rt; 56 | } 57 | 58 | /* No offset */ 59 | uint32_t assemble_immediate_prfm(uint32_t Rn, uint32_t Rt){ 60 | return (0x3e6u << 22) | (Rn << 5) | Rt; 61 | } 62 | 63 | /* No offset */ 64 | uint32_t assemble_ldrsw(uint32_t Rn, uint32_t Rt){ 65 | return (0x2e6u << 22) | (Rn << 5) | Rt; 66 | } 67 | 68 | /* No offset */ 69 | uint32_t assemble_simd_fp_ldr(uint32_t size, uint32_t opc, uint32_t Rn, 70 | uint32_t Rt){ 71 | return (size << 30) | (0x3d << 24) | (opc << 22) | (Rn << 5) | Rt; 72 | } 73 | 74 | uint32_t assemble_mov(uint32_t sf, uint32_t imm, uint32_t Rd){ 75 | uint32_t imm16 = imm & 0xffff; 76 | uint32_t hw = (imm & 0x30000); 77 | 78 | return (sf << 31) | (0xa5 << 23) | (hw << 21) | (imm16 << 5) | Rd; 79 | } 80 | 81 | /* resolves shift also */ 82 | uint64_t get_add_imm(uint32_t add){ 83 | uint64_t imm = 0; 84 | 85 | uint8_t sh = (add & 0x200000) >> 22; 86 | uint32_t imm12 = (add & 0x3ffc00) >> 10; 87 | 88 | if(sh) 89 | imm = imm12 << 12; 90 | else 91 | imm = imm12; 92 | 93 | return imm; 94 | } 95 | 96 | uint64_t get_adr_target(uint32_t *adrp){ 97 | uint32_t immlo = bits(*adrp, 29, 30); 98 | uint32_t immhi = bits(*adrp, 5, 23); 99 | 100 | return sign_extend((immhi << 2) | immlo, 21) + (uintptr_t)adrp; 101 | } 102 | 103 | uint64_t get_adrp_target(uint32_t *adrpp){ 104 | uint32_t adrp = *adrpp; 105 | 106 | uint32_t immlo = bits(adrp, 29, 30); 107 | uint32_t immhi = bits(adrp, 5, 23); 108 | 109 | return sign_extend(((immhi << 2) | immlo) << 12, 32) + 110 | ((uintptr_t)adrpp & ~0xfffuLL); 111 | } 112 | 113 | uint64_t get_adrp_add_target(uint32_t *adrpp){ 114 | uint32_t adrp = *adrpp; 115 | uint32_t add = *(adrpp + 1); 116 | 117 | int64_t addr = (int64_t)get_adrp_target(adrpp); 118 | 119 | return (uint64_t)(addr + (int64_t)bits(add, 10, 21)); 120 | } 121 | 122 | uint64_t get_adrp_ldr_target(uint32_t *adrpp){ 123 | uint32_t adrp = *adrpp; 124 | uint32_t ldr = *(adrpp + 1); 125 | 126 | int64_t addr = (int64_t)get_adrp_target(adrpp); 127 | 128 | /* for LDR, assuming unsigned immediate 129 | * 130 | * no shift on LDRB variants 131 | */ 132 | uint32_t shift = 0; 133 | 134 | uint32_t size = bits(ldr, 30, 31); 135 | uint32_t V = bits(ldr, 26, 26); 136 | uint32_t opc = bits(ldr, 22, 23); 137 | uint32_t imm12 = bits(ldr, 10, 21); 138 | 139 | uint32_t ldr_type = (size << 3) | (V << 2) | opc; 140 | 141 | /* floating point variant */ 142 | if(V) 143 | shift = ((opc >> 1) << 2) | size; 144 | /* LDRH || LDRSH (64 bit) || (LDRSH (32 bit) */ 145 | else if(ldr_type == 9 || ldr_type == 10 || ldr_type == 11) 146 | shift = 1; 147 | /* LDRSW */ 148 | else if(ldr_type == 18) 149 | shift = 2; 150 | /* LDR (32 bit) || LDR (64 bit) */ 151 | else if(ldr_type == 17 || ldr_type == 25) 152 | shift = size; 153 | 154 | /* takes care of LDR */ 155 | int64_t pimm = (int64_t)sign_extend(imm12, 12) << shift; 156 | 157 | return (uint64_t)(addr + pimm); 158 | } 159 | 160 | uint64_t get_pc_rel_target(uint32_t *adrpp){ 161 | if(((adrpp[1] >> 25) & 5) == 4) 162 | /* only ldr */ 163 | return get_adrp_ldr_target(adrpp); 164 | else if(*adrpp & 0x80000000) 165 | return get_adrp_add_target(adrpp); 166 | else 167 | return get_adr_target(adrpp); 168 | } 169 | 170 | uint64_t get_branch_dst(uint32_t branch, uint32_t *pc){ 171 | intptr_t signed_pc = (intptr_t)pc; 172 | int32_t imm26 = (int32_t)sign_extend(bits(branch, 0, 25) << 2, 28); 173 | 174 | return (uint64_t)(signed_pc + imm26); 175 | } 176 | 177 | uint32_t *get_branch_dst_ptr(uint32_t *pc){ 178 | uint32_t branch = *pc; 179 | intptr_t signed_pc = (intptr_t)pc; 180 | 181 | int32_t imm26 = (int32_t)sign_extend(bits(branch, 0, 25) << 2, 28); 182 | 183 | return (uint32_t *)(signed_pc + imm26); 184 | } 185 | 186 | uint64_t get_compare_and_branch_dst(uint32_t cab, uint32_t *pc){ 187 | intptr_t signed_pc = (intptr_t)pc; 188 | int32_t imm19 = (int32_t)sign_extend(bits(cab, 5, 23) << 2, 21); 189 | 190 | return (uint64_t)(signed_pc + imm19); 191 | } 192 | 193 | uint64_t get_cond_branch_dst(uint32_t branch, uint32_t *pc){ 194 | intptr_t signed_pc = (intptr_t)pc; 195 | int32_t imm19 = (int32_t)sign_extend(bits(branch, 5, 23) << 2, 21); 196 | 197 | return (uint64_t)(signed_pc + imm19); 198 | } 199 | 200 | uint64_t get_test_and_branch_dst(uint32_t tab, uint32_t *pc){ 201 | intptr_t signed_pc = (intptr_t)pc; 202 | int32_t imm14 = (int32_t)sign_extend(bits(tab, 5, 18) << 2, 16); 203 | 204 | return (uint64_t)(signed_pc + imm14); 205 | } 206 | 207 | void write_blr(uint32_t reg, uint32_t *from, uint64_t to){ 208 | /* movz */ 209 | *(from++) = (uint32_t)(0xd2800000 | ((to & 0xffff) << 5) | reg); 210 | /* movk */ 211 | *(from++) = (uint32_t)(0xf2800000 | (1 << 21) | (((to >> 16) & 0xffff) << 5) | reg); 212 | /* movk */ 213 | *(from++) = (uint32_t)(0xf2800000 | (2 << 21) | (((to >> 32) & 0xffff) << 5) | reg); 214 | /* movk */ 215 | *(from++) = (uint32_t)(0xf2800000 | (3 << 21) | (((to >> 48) & 0xffff) << 5) | reg); 216 | /* blr */ 217 | *(from++) = (uint32_t)(0xd63f0000 | (reg << 5)); 218 | } 219 | -------------------------------------------------------------------------------- /module/el1/hook_system_check_sysctlbyname_hook.s: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "hook_system_check_sysctlbyname_hook.h" 5 | 6 | .align 2 7 | .global _hook_system_check_sysctlbyname_hook 8 | 9 | _hook_system_check_sysctlbyname_hook: 10 | sub sp, sp, STACK 11 | /* We branched when parameters were being copied to callee-saved 12 | registers */ 13 | stp x7, x6, [sp, #(STACK-0xa0)] 14 | stp x5, x4, [sp, #(STACK-0x90)] 15 | stp x3, x2, [sp, #(STACK-0x80)] 16 | stp x1, x0, [sp, #(STACK-0x70)] 17 | stp x28, x27, [sp, #(STACK-0x60)] 18 | stp x26, x25, [sp, #(STACK-0x50)] 19 | stp x24, x23, [sp, #(STACK-0x40)] 20 | stp x22, x21, [sp, #(STACK-0x30)] 21 | stp x20, x19, [sp, #(STACK-0x20)] 22 | stp x29, x30, [sp, #(STACK-0x10)] 23 | add x29, sp, #(STACK-0x10) 24 | 25 | adr x19, addrof_xnuspy_cache 26 | ldr x28, [x19] 27 | 28 | /* MIB array */ 29 | mov x19, x2 30 | /* Length of MIB array */ 31 | mov w20, w3 32 | 33 | /* This function does not take sysctl_geometry_lock */ 34 | mov x0, x28 35 | bl _get_sysctl_geo_lck 36 | ldr x21, [x28, LCK_RW_LOCK_SHARED] 37 | blr x21 38 | /* If this sysctl hasn't been added yet, register it */ 39 | ldr x21, [x28, DID_REGISTER_SYSCTL] 40 | cbz x21, Lregister_xnuspy_ctl_callnum_sysctl 41 | ldr x21, [x28, XNUSPY_SYSCTL_MIB_COUNT_PTR] 42 | ldr w21, [x21] 43 | /* Not the same length? Definitely not ours */ 44 | cmp w21, w20 45 | b.ne Lnot_ours 46 | 47 | /* Same length, so compare MIB contents. Setting up for mib_check_loop: 48 | X21: pointer to kern.xnuspy_ctl_callnum MIB array 49 | X22: pointer to passed in MIB array 50 | X23: pointer to the end of our MIB array. If we're here, the MIB array 51 | parameter is the same length of ours. 52 | X24-X26: scratch registers 53 | */ 54 | ldr x21, [x28, XNUSPY_SYSCTL_MIB_PTR] 55 | mov x22, x19 56 | add x23, x21, x20, lsl #0x2 57 | 58 | Lmib_check_loop: 59 | ldr w24, [x21], #0x4 60 | ldr w25, [x22], #0x4 61 | /* One mismatched elem and we know it isn't ours */ 62 | cmp w24, w25 63 | b.ne Lnot_ours 64 | /* If we hit the end of our MIB array, it's ours */ 65 | subs x26, x23, x21 66 | cbnz x26, Lmib_check_loop 67 | 68 | Lours: 69 | mov x0, x28 70 | bl _get_sysctl_geo_lck 71 | ldr x19, [x28, LCK_RW_DONE] 72 | blr x19 73 | /* If it is ours, branch right to hook_system_check_sysctlbyname's 74 | epilogue, returning no error */ 75 | ldr x1, [x28, H_S_C_SBN_EPILOGUE_ADDR] 76 | add sp, sp, STACK 77 | mov x0, xzr 78 | br x1 79 | /* Not reached */ 80 | 81 | Lregister_xnuspy_ctl_callnum_sysctl: 82 | mov x0, x28 83 | bl _get_sysctl_geo_lck 84 | ldr x19, [x28, LCK_RW_DONE] 85 | blr x19 86 | 87 | mov x0, SIZEOF_STRUCT_SYSCTL_OID 88 | ldr x19, [x28, IOS_VERSION] 89 | cmp x19, iOS_13_x 90 | b.eq LiOS_13_x_kalloc 91 | 92 | ldr x19, [x28, KALLOC_EXTERNAL] 93 | blr x19 94 | 95 | b Lregister 96 | 97 | LiOS_13_x_kalloc: 98 | str x0, [sp, KALLOC_SZ] 99 | add x0, sp, KALLOC_SZ 100 | mov x1, xzr 101 | mov w2, wzr 102 | ldr x19, [x28, KALLOC_CANBLOCK] 103 | blr x19 104 | 105 | Lregister: 106 | cbz x0, Lnot_ours 107 | 108 | ldr x19, [x28, SYSCTL__KERN_CHILDREN_PTR] 109 | str x19, [x0, OFFSETOF_OID_PARENT] 110 | str xzr, [x0, OFFSETOF_OID_LINK] 111 | mov w19, OID_AUTO 112 | str w19, [x0, OFFSETOF_OID_NUMBER] 113 | mov w19, CTLTYPE_INT 114 | orr w19, w19, CTLFLAG_RD 115 | orr w19, w19, CTLFLAG_ANYBODY 116 | orr w19, w19, CTLFLAG_OID2 117 | str w19, [x0, OFFSETOF_OID_KIND] 118 | add x19, x28, XNUSPY_CTL_CALLNUM 119 | str x19, [x0, OFFSETOF_OID_ARG1] 120 | str wzr, [x0, OFFSETOF_OID_ARG2] 121 | adr x19, oid_name 122 | /* Skip "kern." */ 123 | add x19, x19, #0x5 124 | str x19, [x0, OFFSETOF_OID_NAME] 125 | ldr x19, [x28, SYSCTL_HANDLE_LONG] 126 | str x19, [x0, OFFSETOF_OID_HANDLER] 127 | adr x19, oid_fmt 128 | str x19, [x0, OFFSETOF_OID_FMT] 129 | adr x19, oid_descr 130 | str x19, [x0, OFFSETOF_OID_DESCR] 131 | mov w19, SYSCTL_OID_VERSION 132 | str w19, [x0, OFFSETOF_OID_VERSION] 133 | str wzr, [x0, OFFSETOF_OID_REFCNT] 134 | 135 | ldr x19, [x28, SYSCTL_REGISTER_OID] 136 | blr x19 137 | 138 | /* Figure out what the MIB array looks like for this new sysctl. 139 | Unfortunately I can't just reserve space for this because this 140 | page is r-x. name2oid expects sysctl_geometry_lock to be held */ 141 | mov x0, x28 142 | bl _get_sysctl_geo_lck 143 | ldr x19, [x28, LCK_RW_LOCK_SHARED] 144 | blr x19 145 | 146 | /* name2oid modifies the first parameter, so we need to deep copy */ 147 | adr x0, oid_name 148 | add x1, sp, SYSCTL_NAME_SPACE 149 | 150 | Lcopy_name: 151 | ldrb w2, [x0], #0x1 152 | strb w2, [x1], #0x1 153 | cmp w2, wzr 154 | b.ne Lcopy_name 155 | 156 | add x0, sp, SYSCTL_NAME_SPACE 157 | ldr x1, [x28, XNUSPY_SYSCTL_MIB_PTR] 158 | ldr x2, [x28, XNUSPY_SYSCTL_MIB_COUNT_PTR] 159 | ldr x19, [x28, NAME2OID] 160 | blr x19 161 | 162 | mov x19, #0x1 163 | str x19, [x28, DID_REGISTER_SYSCTL] 164 | 165 | Lnot_ours: 166 | mov x0, x28 167 | bl _get_sysctl_geo_lck 168 | ldr x19, [x28, LCK_RW_DONE] 169 | blr x19 170 | ldp x29, x30, [sp, #(STACK-0x10)] 171 | ldp x20, x19, [sp, #(STACK-0x20)] 172 | ldp x22, x21, [sp, #(STACK-0x30)] 173 | ldp x24, x23, [sp, #(STACK-0x40)] 174 | ldp x26, x25, [sp, #(STACK-0x50)] 175 | ldp x28, x27, [sp, #(STACK-0x60)] 176 | ldp x1, x0, [sp, #(STACK-0x70)] 177 | ldp x3, x2, [sp, #(STACK-0x80)] 178 | ldp x5, x4, [sp, #(STACK-0x90)] 179 | ldp x7, x6, [sp, #(STACK-0xa0)] 180 | add sp, sp, STACK 181 | .space (5*4), OPCODE_PLACEHOLDER_BYTE 182 | /* xnuspy will write back the instructions we overwrote in the space 183 | above inside install_h_s_c_sbn_hook (preboot_hook.c) */ 184 | ret 185 | 186 | /* These are still in __text, so clang treats them as code. Four byte align 187 | them so clang doesn't complain */ 188 | addrof_xnuspy_cache: .dword QWORD_PLACEHOLDER 189 | oid_name: .asciz "kern.xnuspy_ctl_callnum" 190 | .align 2 191 | oid_descr: .asciz "query for xnuspy_ctl's call number" 192 | .align 2 193 | oid_fmt: .asciz "L" 194 | /* Align so we can write four bytes every time and not have to worry about 195 | scratch_space being unaligned when we go to write other instructions */ 196 | .align 2 197 | 198 | /* Cursed case: are we on 14.5 or above? If we are, we get a pointer to 199 | sysctl_geometry_lock from *(xnuspy_cache+SYSCTL_GEOMETRY_LOCK_PTR), 200 | as opposed to a pointer to a pointer to sysctl_geometry_lock on 201 | 13.0 - 14.4.2. This is the case for both old and new 14.5 kernels. 202 | This is apparently also the case for 15.x. 203 | 204 | One parameter, a pointer to the xnuspy cache. Returns a pointer to 205 | sysctl_geometry_lock */ 206 | .align 2 207 | _get_sysctl_geo_lck: 208 | stp x19, x20, [sp, #-0x10]! 209 | stp x29, x30, [sp, #-0x10]! 210 | 211 | ldr x19, [x0, SYSCTL_GEOMETRY_LOCK_PTR] 212 | ldr x20, [x0, IOS_VERSION] 213 | cmp x20, iOS_13_x 214 | b.eq Lout_not_14_5 215 | cmp x20, iOS_15_x 216 | b.eq Lout_14_5_and_above_cursed_case 217 | ldr x20, [x0, KERN_VERSION_MINOR] 218 | cmp x20, #0x4 219 | /* ge in case a new version of 14 is released that does the 220 | same thing 14.5 does */ 221 | b.ge Lout_14_5_and_above_cursed_case 222 | 223 | Lout_not_14_5: 224 | ldr x0, [x19] 225 | b Lout 226 | 227 | Lout_14_5_and_above_cursed_case: 228 | mov x0, x19 229 | 230 | Lout: 231 | ldp x29, x30, [sp], #0x10 232 | ldp x19, x20, [sp], #0x10 233 | ret 234 | -------------------------------------------------------------------------------- /loader/loader.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 int pongo_send_command(libusb_device_handle *pongo_device, 14 | const char *command){ 15 | size_t command_len = 1; 16 | 17 | if(command) 18 | command_len += strlen(command); 19 | 20 | return libusb_control_transfer(pongo_device, 0x21, 3, 0, 0, 21 | (unsigned char *)command, command_len, 0); 22 | } 23 | 24 | static int pongo_init_bulk_upload(libusb_device_handle *pongo_device){ 25 | return libusb_control_transfer(pongo_device, 0x21, 1, 0, 0, NULL, 0, 0); 26 | } 27 | 28 | static int pongo_discard_bulk_upload(libusb_device_handle *pongo_device){ 29 | return libusb_control_transfer(pongo_device, 0x21, 2, 0, 0, NULL, 0, 0); 30 | } 31 | 32 | static int pongo_do_bulk_upload(libusb_device_handle *pongo_device, 33 | void *data, size_t len){ 34 | return libusb_bulk_transfer(pongo_device, 2, data, len, NULL, 0); 35 | } 36 | 37 | static int pongo_get_stdout(libusb_device_handle *pongo_device, char *outbuf){ 38 | return libusb_control_transfer(pongo_device, 0xa1, 1, 0, 0, 39 | (unsigned char *)outbuf, 512, 0); 40 | } 41 | 42 | static int hotplug_callback(libusb_context *ctx, libusb_device *device, 43 | libusb_hotplug_event event, void *user_data){ 44 | if(event != LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) 45 | return 0; 46 | 47 | libusb_device_handle **pongo_device = (libusb_device_handle **)user_data; 48 | int err = libusb_open(device, pongo_device); 49 | 50 | if(err){ 51 | printf("Couldn't open pongoOS device: %s\n", libusb_error_name(err)); 52 | libusb_exit(NULL); 53 | exit(1); 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | int main(int argc, char **argv, const char **envp){ 60 | if(argc < 2){ 61 | printf("usage: loader \n"); 62 | return 1; 63 | } 64 | 65 | int err = libusb_init(NULL); 66 | 67 | if(err < 0){ 68 | printf("libusb_init failed: %d\n", err); 69 | return 1; 70 | } 71 | 72 | printf("Waiting for pongoOS device...\n"); 73 | 74 | libusb_hotplug_callback_handle cbh = 0; 75 | libusb_device_handle *pongo_device = NULL; 76 | 77 | err = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, 78 | LIBUSB_HOTPLUG_ENUMERATE, 0x5ac, 0x4141, LIBUSB_HOTPLUG_MATCH_ANY, 79 | hotplug_callback, &pongo_device, &cbh); 80 | 81 | if(err < 0){ 82 | printf("libusb_hotplug_register_callback: %s\n", libusb_error_name(err)); 83 | return 1; 84 | } 85 | 86 | while(!pongo_device) 87 | libusb_handle_events_completed(NULL, NULL); 88 | 89 | libusb_hotplug_deregister_callback(NULL, cbh); 90 | 91 | printf("Got pongoOS device\n"); 92 | 93 | err = libusb_claim_interface(pongo_device, 0); 94 | 95 | if(err < 0){ 96 | printf("libusb_claim_interface: %s\n", libusb_error_name(err)); 97 | goto err0; 98 | } 99 | 100 | char *module_path = argv[1]; 101 | struct stat st = {0}; 102 | 103 | if(stat(module_path, &st)){ 104 | printf("Problem stat'ing '%s': %s\n", module_path, strerror(errno)); 105 | goto err0; 106 | } 107 | 108 | int module_fd = open(module_path, O_RDONLY); 109 | 110 | if(module_fd < 0){ 111 | printf("Problem open'ing '%s': %s\n", module_path, strerror(errno)); 112 | goto err0; 113 | } 114 | 115 | size_t module_size = st.st_size; 116 | printf("Module size %#lx\n", module_size); 117 | 118 | void *module_data = mmap(NULL, module_size, PROT_READ, MAP_PRIVATE, 119 | module_fd, 0); 120 | 121 | close(module_fd); 122 | 123 | if(module_data == MAP_FAILED){ 124 | printf("Problem mmap'ing '%s': %s\n", module_path, strerror(errno)); 125 | goto err0; 126 | } 127 | 128 | const char *xnuspy_ctl_path = "./module/el1/xnuspy_ctl/xnuspy_ctl"; 129 | 130 | memset(&st, 0, sizeof(st)); 131 | 132 | if(stat(xnuspy_ctl_path, &st)){ 133 | printf("Problem stat'ing '%s': %s\n", xnuspy_ctl_path, strerror(errno)); 134 | goto err1; 135 | } 136 | 137 | size_t xnuspy_ctl_imgsz = st.st_size; 138 | printf("xnuspy_ctl image size %#zx\n", xnuspy_ctl_imgsz); 139 | 140 | int xnuspy_ctl_fd = open(xnuspy_ctl_path, O_RDONLY); 141 | 142 | if(xnuspy_ctl_fd == -1){ 143 | printf("Problem open'ing '%s': %s\n", xnuspy_ctl_path, strerror(errno)); 144 | goto err1; 145 | } 146 | 147 | void *xnuspy_ctl_imgdata = mmap(NULL, xnuspy_ctl_imgsz, PROT_READ, 148 | MAP_PRIVATE, xnuspy_ctl_fd, 0); 149 | 150 | close(xnuspy_ctl_fd); 151 | 152 | if(xnuspy_ctl_imgdata == MAP_FAILED){ 153 | printf("Problem mmap'ing '%s': %s\n", xnuspy_ctl_path, strerror(errno)); 154 | goto err1; 155 | } 156 | 157 | err = pongo_init_bulk_upload(pongo_device); 158 | 159 | if(err < 0){ 160 | printf("pongo_init_bulk_upload: %s\n", libusb_error_name(err)); 161 | goto err2; 162 | } 163 | 164 | err = pongo_do_bulk_upload(pongo_device, module_data, module_size); 165 | 166 | if(err < 0){ 167 | printf("pongo_do_bulk_upload (module): %s\n", libusb_error_name(err)); 168 | goto err2; 169 | } 170 | 171 | err = pongo_send_command(pongo_device, "modload\n"); 172 | 173 | if(err < 0){ 174 | printf("pongo_send_command: %s\n", libusb_error_name(err)); 175 | goto err2; 176 | } 177 | 178 | usleep(200 * 1000); 179 | 180 | /* If you want to modify this string, don't remove rootdev=md0 and 181 | * use_contiguous_hint=0. Make sure to keep the newline. */ 182 | err = pongo_send_command(pongo_device, "xargs rootdev=md0" 183 | " use_contiguous_hint=0 msgbuf=0x3c000" 184 | #if defined(XNUSPY_SERIAL) 185 | " -v serial=3" 186 | #endif 187 | " atm_diagnostic_config=0x20000000\n"); 188 | 189 | if(err < 0){ 190 | printf("pongo_send_command: %s\n", libusb_error_name(err)); 191 | goto err2; 192 | } 193 | 194 | usleep(200 * 1000); 195 | 196 | err = pongo_send_command(pongo_device, "xnuspy-getkernelv\n"); 197 | 198 | if(err < 0){ 199 | printf("pongo_send_command: %s\n", libusb_error_name(err)); 200 | goto err2; 201 | } 202 | 203 | /* we may have had to pwn SEPROM or patch KPP, so wait a bit longer 204 | * before we continue */ 205 | sleep(3); 206 | 207 | /* send the compiled xnuspy_ctl image */ 208 | err = pongo_init_bulk_upload(pongo_device); 209 | 210 | if(err < 0){ 211 | printf("pongo_init_bulk_upload: %s\n", libusb_error_name(err)); 212 | goto err2; 213 | } 214 | 215 | err = pongo_do_bulk_upload(pongo_device, xnuspy_ctl_imgdata, 216 | xnuspy_ctl_imgsz); 217 | 218 | if(err < 0){ 219 | printf("pongo_do_bulk_upload (xnuspy_ctl): %s\n", 220 | libusb_error_name(err)); 221 | goto err2; 222 | } 223 | 224 | err = pongo_send_command(pongo_device, "xnuspy-prep\n"); 225 | 226 | if(err < 0){ 227 | printf("pongo_send_command: %s\n", libusb_error_name(err)); 228 | goto err2; 229 | } 230 | 231 | sleep(2); 232 | 233 | err = pongo_send_command(pongo_device, "bootx\n"); 234 | 235 | if(err < 0){ 236 | printf("pongo_send_command: %s\n", libusb_error_name(err)); 237 | goto err2; 238 | } 239 | 240 | err2: 241 | munmap(xnuspy_ctl_imgdata, xnuspy_ctl_imgsz); 242 | err1: 243 | munmap(module_data, module_size); 244 | err0:; 245 | libusb_release_interface(pongo_device, 0); 246 | libusb_close(pongo_device); 247 | libusb_exit(NULL); 248 | return 0; 249 | } 250 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /module/xnuspy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | uint64_t g_kern_version_major = 0; 15 | uint64_t g_kern_version_minor = 0; 16 | 17 | static uint32_t g_kern_version_revision = 0; 18 | 19 | static bool getkernelv_callback(xnu_pf_patch_t *patch, 20 | void *cacheable_stream){ 21 | xnu_pf_disable_patch(patch); 22 | 23 | char *version = cacheable_stream; 24 | 25 | /* on all kernels, major, minor, and version are no larger than 2 chars */ 26 | char major_s[3] = {0}; 27 | char minor_s[3] = {0}; 28 | char revision_s[3] = {0}; 29 | 30 | /* skip ahead until we get a digit */ 31 | while(!isdigit(*version)) 32 | version++; 33 | 34 | for(int i=0; *version != '.'; i++, version++) 35 | major_s[i] = *version; 36 | 37 | version++; 38 | 39 | for(int i=0; *version != '.'; i++, version++) 40 | minor_s[i] = *version; 41 | 42 | version++; 43 | 44 | for(int i=0; *version != ':'; i++, version++) 45 | revision_s[i] = *version; 46 | 47 | /* currently, I only use major and minor, but I get the rest in 48 | * case I need them in the future */ 49 | g_kern_version_major = atoi(major_s); 50 | g_kern_version_minor = atoi(minor_s); 51 | g_kern_version_revision = atoi(revision_s); 52 | 53 | /* iOS 14 or newer */ 54 | bool pwn_seprom = (g_kern_version_major >= iOS_14_x) && 55 | (socnum == 0x8010 || socnum == 0x8011 || socnum == 0x8015); 56 | 57 | if(g_kern_version_major == iOS_13_x) 58 | printf("xnuspy: iOS 13.x detected\n"); 59 | else if(g_kern_version_major == iOS_14_x) 60 | printf("xnuspy: iOS 14.x detected\n"); 61 | else if (g_kern_version_major == iOS_15_x) 62 | printf("xnuspy: iOS 15.x detected\n"); 63 | else{ 64 | printf("xnuspy: error: unknown\n" 65 | " major %lld\n", 66 | g_kern_version_major); 67 | 68 | xnuspy_fatal_error(); 69 | } 70 | 71 | /* No need to exploit SEPROM on 14.x A9(x) and below, which conveniently 72 | * is the only KPP chip that xnuspy supports */ 73 | if(pwn_seprom) 74 | queue_rx_string("sep auto\n"); 75 | else if(socnum == 0x8000 || socnum == 0x8001 || socnum == 0x8003) 76 | patch_kpp(); 77 | 78 | return true; 79 | } 80 | 81 | static void xnuspy_getkernelv(const char *cmd, char *args){ 82 | xnu_pf_patchset_t *patchset = xnu_pf_patchset_create(XNU_PF_ACCESS_8BIT); 83 | 84 | xnu_pf_range_t *__TEXT___const = xnu_pf_section(mh_execute_header, "__TEXT", 85 | "__const"); 86 | 87 | if(!__TEXT___const){ 88 | puts("xnuspy: xnu_pf_section"); 89 | puts(" returned NULL for"); 90 | puts(" __TEXT:__const?"); 91 | 92 | xnuspy_fatal_error(); 93 | } 94 | 95 | const char *vers = "Darwin Kernel Version "; 96 | 97 | /* hardcoded so clang does not generate ___chkstk_darwin calls */ 98 | uint64_t ver[21]; 99 | uint64_t masks[21]; 100 | 101 | for(int i=0; i<21; i++){ 102 | ver[i] = vers[i]; 103 | masks[i] = 0xff; 104 | } 105 | 106 | uint64_t count = sizeof(ver) / sizeof(*ver); 107 | 108 | xnu_pf_maskmatch(patchset, "kernel version finder", ver, masks, count, 109 | false, getkernelv_callback); 110 | xnu_pf_emit(patchset); 111 | xnu_pf_apply(__TEXT___const, patchset); 112 | xnu_pf_patchset_destroy(patchset); 113 | } 114 | 115 | #define MAXKEXTRANGE MAXPF 116 | 117 | struct kextrange { 118 | xnu_pf_range_t *range; 119 | char *kext; 120 | char *seg; 121 | char *sect; 122 | }; 123 | 124 | /* purpose of this function is to add patchfinder ranges for kexts in such 125 | * a way that there are no duplicates in `*ranges` */ 126 | static void add_kext_range(struct kextrange **ranges, const char *kext, 127 | const char *seg, const char *sect, size_t *nkextranges_out){ 128 | size_t nkextranges = *nkextranges_out; 129 | 130 | if(nkextranges == MAXKEXTRANGE) 131 | return; 132 | 133 | /* first, check if this kext is already present */ 134 | for(size_t i=0; ikext, kext) == 0){ 140 | /* same segment? It will be the same range even if the section differs */ 141 | if(seg && strcmp(kr->seg, seg) == 0) 142 | return; 143 | 144 | if(sect && strcmp(kr->sect, sect) == 0) 145 | return; 146 | } 147 | } 148 | 149 | /* new kext, make its range */ 150 | struct mach_header_64 *mh = xnu_pf_get_kext_header(mh_execute_header, kext); 151 | 152 | if(!mh){ 153 | printf( "xnuspy: could not\n" 154 | " get Mach header for\n" 155 | " %s\n", kext); 156 | 157 | xnuspy_fatal_error(); 158 | } 159 | 160 | struct kextrange *kr = malloc(sizeof(struct kextrange)); 161 | memset(kr, 0, sizeof(*kr)); 162 | 163 | if(sect) 164 | kr->range = xnu_pf_section(mh, (void *)seg, (char *)sect); 165 | else 166 | kr->range = xnu_pf_segment(mh, (void *)seg); 167 | 168 | size_t kextl = 0, segl = 0, sectl = 0; 169 | 170 | kextl = strlen(kext); 171 | 172 | char *kn = malloc(kextl + 1); 173 | strcpy(kn, kext); 174 | kn[kextl] = '\0'; 175 | kr->kext = kn; 176 | 177 | if(seg){ 178 | segl = strlen(seg); 179 | char *segn = malloc(segl + 1); 180 | strcpy(segn, seg); 181 | segn[segl] = '\0'; 182 | kr->seg = segn; 183 | } 184 | 185 | if(sect){ 186 | sectl = strlen(sect); 187 | char *sectn = malloc(sectl + 1); 188 | strcpy(sectn, sect); 189 | sectn[sectl] = '\0'; 190 | kr->sect = sectn; 191 | } 192 | 193 | ranges[nkextranges] = kr; 194 | *nkextranges_out = nkextranges + 1; 195 | } 196 | 197 | static void xnuspy_prep(const char *cmd, char *args){ 198 | /* all the patchfinders in pf/pfs.h currently do 32 bit */ 199 | xnu_pf_patchset_t *patchset = xnu_pf_patchset_create(XNU_PF_ACCESS_32BIT); 200 | 201 | size_t nkextranges = 0; 202 | struct kextrange **kextranges = malloc(sizeof(struct kextrange *) * MAXKEXTRANGE); 203 | 204 | for(int i=0; !PFS_END(g_all_pfs[i]); i++){ 205 | struct pf *pf = &g_all_pfs[i][g_kern_version_major - VERSION_BIAS]; 206 | 207 | if(IS_PF_UNUSED(pf)) 208 | continue; 209 | 210 | const char *pf_kext = pf->pf_kext; 211 | const char *pf_segment = pf->pf_segment; 212 | const char *pf_section = pf->pf_section; 213 | 214 | if(pf_kext){ 215 | add_kext_range(kextranges, pf_kext, pf_segment, pf_section, 216 | &nkextranges); 217 | } 218 | 219 | xnu_pf_maskmatch(patchset, (char *)pf->pf_name, pf->pf_matches, 220 | pf->pf_masks, pf->pf_mmcount, false, pf->pf_callback); 221 | } 222 | 223 | xnu_pf_emit(patchset); 224 | 225 | xnu_pf_range_t *__TEXT_EXEC = xnu_pf_segment(mh_execute_header, "__TEXT_EXEC"); 226 | xnu_pf_apply(__TEXT_EXEC, patchset); 227 | 228 | for(size_t i=0; irange; 230 | xnu_pf_apply(range, patchset); 231 | } 232 | 233 | xnu_pf_patchset_destroy(patchset); 234 | } 235 | 236 | void module_entry(void){ 237 | puts("xnuspy: loaded!"); 238 | 239 | mh_execute_header = xnu_header(); 240 | kernel_slide = xnu_slide_value(mh_execute_header); 241 | 242 | next_preboot_hook = preboot_hook; 243 | preboot_hook = xnuspy_preboot_hook; 244 | 245 | command_register("xnuspy-getkernelv", "get kernel version", xnuspy_getkernelv); 246 | command_register("xnuspy-prep", "get all offsets", xnuspy_prep); 247 | } 248 | 249 | const char *module_name = "xnuspy"; 250 | 251 | struct pongo_exports exported_symbols[] = { 252 | { .name = 0, .value = 0 } 253 | }; 254 | -------------------------------------------------------------------------------- /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