From 121abf41a28aba6cd2b18fb65c9c21d39c1c4af3 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 21 Jul 2024 01:57:19 +0800 Subject: [PATCH] real box --- checkra1n/kpf/main.c | 224 ++++++++++++++++---------------------- checkra1n/kpf/shellcode.S | 45 +++++++- 2 files changed, 134 insertions(+), 135 deletions(-) diff --git a/checkra1n/kpf/main.c b/checkra1n/kpf/main.c index 04da650a..ca1b3562 100644 --- a/checkra1n/kpf/main.c +++ b/checkra1n/kpf/main.c @@ -169,6 +169,7 @@ static void kpf_kernel_version_init(xnu_pf_range_t *text_const_range) // Imports from shellcode.S extern uint32_t sandbox_shellcode[], sandbox_shellcode_setuid_patch[], sandbox_shellcode_ptrs[], sandbox_shellcode_end[]; extern uint32_t launchd_execve_hook[], launchd_execve_hook_ptr[], launchd_execve_hook_offset[], launchd_execve_hook_pagesize[], launchd_execve_hook_mach_vm_allocate_kernel[]; +extern uint32_t proc_set_syscall_filter_mask_shc[], proc_set_syscall_filter_mask_shc_target[], zalloc_ro_mut[]; uint32_t* _mac_mount = NULL; bool kpf_has_done_mac_mount = false; @@ -798,82 +799,45 @@ bool vnode_lookup_callback(struct xnu_pf_patch* patch, uint32_t* opcode_stream) return true; } -extern uint32_t one_area[]; -uint64_t* repatch_one_area_ptr; +uint32_t* _proc_set_syscall_filter_mask = NULL; +uint32_t* protobox_patchpoint = NULL; -bool kpf_autobox_callback(struct xnu_pf_patch *patch, uint32_t *opcode_stream) +bool kpf_protobox_callback(struct xnu_pf_patch *patch, uint32_t *opcode_stream) { - void* proc_syscall_filter_mask_size = (void*)((const char *)(((uint64_t)(opcode_stream) & ~0xfffULL) - + adrp_off(opcode_stream[0]) + ((opcode_stream[1] >> 10) & 0xfff))); - uint32_t* b = find_next_insn(opcode_stream, 0x10, 0x14000000, 0xfc000000); // b proc_set_syscall_filter_mask if (!b) { - panic_at(opcode_stream, "kpf_autobox: Failed to find b proc_set_syscall_filter_mask"); + panic_at(opcode_stream, "kpf_protobox: Failed to find b proc_set_syscall_filter_mask"); } uint32_t* proc_set_syscall_filter_mask = follow_call(b); uint32_t *stackframe = find_prev_insn(opcode_stream - 1, 0x20, 0xa9007bfd, 0xffc07fff); // stp x29, x30, [sp, ...] if(!stackframe) { - panic_at(opcode_stream, "kpf_autobox: Failed to find stack frame"); + panic_at(opcode_stream, "kpf_protobox: Failed to find stack frame"); } uint32_t *start = find_prev_insn(stackframe - 1, 8, 0xd10003ff, 0xffc003ff); // sub sp, sp, ... - if(!start) - { - panic_at(stackframe, "kpf_autobox: Failed to find start of function"); + if(!start) { + start = find_prev_insn(stackframe, 10, 0xa9a003e0, 0xffe003e0); // stp xN, xM, [sp, -0x...]! } - /* Function logic - * int proc_set_syscall_filter_mask_shorthand(proc_t p, int which, void* maskptr, size_t masksize) { - * return repatch_proc_set_syscall_filter_mask( - * p, which, repatch_one_area_ptr, - * *repatch_proc_set_syscall_filter_size[which] - * ); - * } - */ - start[0] = 0x10000148; // adr x8, _proc_set_syscall_filter_mask - start[1] = 0x10000169; // adr x9, _proc_syscall_filter_mask_size - start[2] = 0x10000182; // adr x2, _one_area_ptr - start[3] = 0xf9400108; // ldr x8, [x8] - start[4] = 0xf9400129; // ldr x9, [x9] - start[5] = 0xf9400042; // ldr x2, [x2] - start[6] = 0xf861d923; // ldr x3, [x9, w1, sxtw #3] - start[7] = 0xb9400063; // ldr w3, [x3] - start[8] = 0xd61f0100; // br x8 - start[9] = 0xd503201f; // nop - - uint64_t* repatch_proc_set_syscall_filter_mask = (uint64_t*)&start[10]; - uint64_t* repatch_proc_set_syscall_filter_size = (repatch_proc_set_syscall_filter_mask + 1); - repatch_one_area_ptr = (repatch_proc_set_syscall_filter_mask + 2); - - *repatch_proc_set_syscall_filter_mask = xnu_ptr_to_va(proc_set_syscall_filter_mask); - *repatch_proc_set_syscall_filter_size = xnu_ptr_to_va(proc_syscall_filter_mask_size); - *repatch_one_area_ptr = 0x4141414141414141; - - printf("KPF: found autobox\n"); - return true; -} + if (!start) { + panic_at(stackframe, "kpf_protobox: Failed to find start of function"); + } -bool kpf_protobox_callback(struct xnu_pf_patch *patch, uint32_t *opcode_stream) -{ - uint32_t adrp1 = opcode_stream[0], - add1 = opcode_stream[1]; - const char *str1 = (const char *)(((uint64_t)(opcode_stream) & ~0xfffULL) + adrp_off(adrp1) + ((add1 >> 10) & 0xfff)); - - uint32_t adrp2 = opcode_stream[4], - add2 = opcode_stream[5]; - const char *str2 = (const char *)(((uint64_t)(opcode_stream) & ~0xfffULL) + adrp_off(adrp2) + ((add2 >> 10) & 0xfff)); - - if (!strcmp(str1, "Restore") && !strcmp(str2, "Darwin")) { - // Make protobox think this device is in "Restore" mode - // This will disable protobox - opcode_stream[2] = 0xD2800020; // mov x0, #1 - printf("KPF: Found and patched protobox check @ 0x%" PRIx64 "\n", xnu_ptr_to_va(opcode_stream)); - return true; + uint32_t* bl = find_prev_insn(opcode_stream, 6, 0x94000000, 0xfc000000); // bl zone_require_ro + if (!bl) { + panic_at(opcode_stream, "kpf_protobox: Failed to find zone_require_ro"); } - return false; + *bl = 0xaa0003f1; // mov x17, x0 + + _proc_set_syscall_filter_mask = proc_set_syscall_filter_mask; + + protobox_patchpoint = b; + + printf("KPF: found protobox\n"); + return true; } bool kpf_filter_mismatch_callback(struct xnu_pf_patch *patch, uint32_t *opcode_stream) { @@ -882,6 +846,18 @@ bool kpf_filter_mismatch_callback(struct xnu_pf_patch *patch, uint32_t *opcode_s return true; } +uint32_t* _zalloc_ro_mut = NULL; +bool kpf_zalloc_ro_mut_callback(struct xnu_pf_patch *patch, uint32_t *opcode_stream) { + uint32_t* _zalloc_ro_mut_candidate = follow_call(&opcode_stream[6]); + if (!_zalloc_ro_mut) _zalloc_ro_mut = _zalloc_ro_mut_candidate; + if (_zalloc_ro_mut != _zalloc_ro_mut_candidate) { + panic("kpf_zalloc_ro_mut: Found multiple zalloc_ro_mut candidates"); + } + + puts("KPF: Found zalloc_ro_mut"); + return true; +} + void kpf_find_shellcode_funcs(xnu_pf_patchset_t* xnu_text_exec_patchset) { // to find this with r2 run: // /x 00008192007fbef2:00ffffff00ffffff @@ -931,6 +907,29 @@ void kpf_find_shellcode_funcs(xnu_pf_patchset_t* xnu_text_exec_patchset) { 0xffffffff }; xnu_pf_maskmatch(xnu_text_exec_patchset, "ret0_gadget", iiii_matches, iiii_masks, sizeof(iiii_masks)/sizeof(uint64_t), true, (void*)ret0_gadget_callback); + + // find mac label related calls to zalloc_ro_mut + uint64_t zalloc_ro_mut_matches[] = { + 0x90000003, // adrp x3, ... + 0x91000063, // add x3, x3, ... + 0x52800080, // mov w0, #0x4 + 0xaa1003e1, // mov x1, x{16-31} + 0xd2800002, // mov x2, #0x0 + 0x52800404, // mov w4, #0x20 + 0x94000000 // bl zalloc_ro_mut + }; + + uint64_t zalloc_ro_mut_masks[] = { + 0x9f00001f, + 0xffc003ff, + 0xffffffff, + 0xfff0ffff, + 0xffffffff, + 0xffffffff, + 0xfc000000 + }; + + xnu_pf_maskmatch(xnu_text_exec_patchset, "zalloc_ro_mut", zalloc_ro_mut_matches, zalloc_ro_mut_masks, sizeof(zalloc_ro_mut_matches) / sizeof(uint64_t), false, (void *)kpf_zalloc_ro_mut_callback); } static bool found_mach_traps = false; @@ -1779,7 +1778,7 @@ void kpf_amfi_kext_patches(xnu_pf_patchset_t* patchset) { xnu_pf_maskmatch(patchset, "amfi_mac_syscall_low", iiii_matches, iiii_masks, sizeof(iiii_matches)/sizeof(uint64_t), false, (void*)kpf_amfi_mac_syscall_low); } -void kpf_sandbox_kext_patches(xnu_pf_patchset_t* patchset, bool protobox_used, bool has_autobox) { +void kpf_sandbox_kext_patches(xnu_pf_patchset_t* patchset, bool protobox_used) { uint64_t matches[] = { 0x35000000, // CBNZ 0x94000000, // BL _vfs_context_current @@ -1804,79 +1803,34 @@ void kpf_sandbox_kext_patches(xnu_pf_patchset_t* patchset, bool protobox_used, b }; xnu_pf_maskmatch(patchset, "vnode_lookup", matches, masks, sizeof(masks)/sizeof(uint64_t), true, (void*)vnode_lookup_callback); - if (has_autobox) { - uint64_t autobox_matches[] = { + // /x 0800009008010091081970f8030140b9e00300aae10300aae20300aa:1f00009fff03c0ffff1ff0ffffffffffffffe0ffffffe0ffffffe0ff + // iOS 15.4+ + if (protobox_used) { + uint64_t protobox_matches[] = { 0x90000008, // adrp x8, ... 0x91000108, // add, x8, x8 - 0xf860c808, // ldr x8, ... + 0xf8701908, // ldr x8, [x8, w{16-31}, ... #0x3] 0xb9400103, // ldr w3, [x8] 0xaa0003e0, // mov x0, x{16-31} 0xaa0003e1, // mov x1, x{16-31} 0xaa0003e2 // mov x2, x{16-31} }; - uint64_t autobox_masks[] = { + uint64_t protobox_masks[] = { 0x9f00001f, 0xffc003ff, - 0xffe0ec1f, + 0xfff01fff, 0xffffffff, 0xffe0ffff, 0xffe0ffff, 0xffe0ffff }; - xnu_pf_maskmatch(patchset, "autobox", autobox_matches, autobox_masks, sizeof(autobox_masks) / sizeof(uint64_t), true, (void *)kpf_autobox_callback); - - // syscall mask mismatch: %s\" @%s:% - uint64_t mismatch_matches[] = { - 0xb4000000, // cbz - 0xf9400000, // ldr - 0xaa0003e0, // mov x0, ... - 0x52800001, // mov w1, #0x0 - 0x94000000, // bl proc_get_syscall_filter_mask - 0xeb00001f, // cmp x0, ... - 0x54000001, // b.ne Lmismatch_panic - 0xf9400000, // ldr - 0xaa0003e0, // mov x0, ... - 0x52800021, // mov w1, #0x0 - 0x94000000, // bl proc_get_syscall_filter_mask - 0xeb00001f, // cmp x0, ... - 0x54000001, // b.ne Lmismatch_panic - 0xf9400000, // ldr - 0xaa0003e0, // mov x0, ... - 0x52800041, // mov w1, #0x0 - 0x94000000, // bl proc_get_syscall_filter_mask - 0xeb00001f, // cmp x0, ... - 0x54000001 // b.ne Lmismatch_panic - }; - - uint64_t mismatch_masks[] = { - 0xff000000, - 0xffc00000, - 0xffe0ffff, - 0xffffffff, - 0xfc000000, - 0xffe0ffff, - 0xff00001f, - 0xffc00000, - 0xffe0ffff, - 0xffffffff, - 0xfc000000, - 0xffe0ffff, - 0xff00001f, - 0xffc00000, - 0xffe0ffff, - 0xffffffff, - 0xfc000000, - 0xffe0ffff, - 0xff00001f - }; - xnu_pf_maskmatch(patchset, "filter_mismatch", mismatch_matches, mismatch_masks, sizeof(mismatch_masks) / sizeof(uint64_t), true, (void *)kpf_filter_mismatch_callback); - + xnu_pf_maskmatch(patchset, "protobox", protobox_matches, protobox_masks, sizeof(protobox_masks) / sizeof(uint64_t), true, (void *)kpf_protobox_callback); } - if (protobox_used) { + /*if (protobox_used) { // Protobox on is an additional sandbox mechanism in iOS 16+ that introduces syscall masks, which is used to have syscall whitelists on some system processes // When injecting into them or using something like Frida, it can prevent certain functionality // Additionally it makes these processes crash on sandbox violations, meaning that calling even something simple like mach_thread_self in watchdogd will crash the process @@ -1903,7 +1857,7 @@ void kpf_sandbox_kext_patches(xnu_pf_patchset_t* patchset, bool protobox_used, b 0xfe00001f, }; xnu_pf_maskmatch(patchset, "protobox", protobox_matches, protobox_masks, sizeof(protobox_masks)/sizeof(uint64_t), true, (void *)kpf_protobox_callback); - } + }*/ } bool vnop_rootvp_auth_callback(struct xnu_pf_patch *patch, uint32_t *opcode_stream) { @@ -2601,29 +2555,18 @@ static void kpf_cmd(const char *cmd, char *args) xnu_pf_range_t* protobox_string_range = xnu_pf_section(sandbox_header, "__TEXT", "__cstring"); if (!protobox_string_range) protobox_string_range = text_cstring_range; - const char protobox_string[] = "failed to initialize protobox collection"; + const char protobox_string[] = "(apply-protobox)"; const char *protobox_string_match = memmem(protobox_string_range->cacheable_base, protobox_string_range->size, protobox_string, sizeof(protobox_string)-1); - const char autobox_string[] = "failed to initialize autobox collection"; - const char* autobox_string_match = memmem(protobox_string_range->cacheable_base, protobox_string_range->size, autobox_string, sizeof(autobox_string)-1); - #ifdef DEV_BUILD // 15.0 beta 3 and later, except bridgeOS - if ((gKernelVersion.xnuMajor >= 8019 && (gKernelVersion.xnuMajor <= 10063) && (xnu_platform() != PLATFORM_BRIDGEOS)) != (protobox_string_match != NULL)) { + if ((gKernelVersion.xnuMajor >= 8019 && (xnu_platform() != PLATFORM_BRIDGEOS)) != (protobox_string_match != NULL)) { panic("Protobox string doesn't match expected Darwin version"); } - - // 18.0 beta 1 and later, except bridgeOS - if ((gKernelVersion.xnuMajor >= 11215) != (autobox_string_match != NULL) && (xnu_platform() != PLATFORM_BRIDGEOS)) { - panic("Autobox string doesn't match expected Darwin version"); - } #endif + bool protobox_used = (protobox_string_match != NULL && gKernelVersion.xnuMajor >= 8792); - // 16.0 beta 1 and later - const char constraints_string[] = "mac_proc_check_launch_constraints"; - const char *constraints_string_match = memmem(text_cstring_range->cacheable_base, text_cstring_range->size, constraints_string, sizeof(constraints_string)); - - kpf_sandbox_kext_patches(sandbox_patchset, (protobox_string_match != NULL && gKernelVersion.darwinMajor >= 8792 && autobox_string_match == NULL), autobox_string_match != NULL); + kpf_sandbox_kext_patches(sandbox_patchset, protobox_used); xnu_pf_emit(sandbox_patchset); xnu_pf_apply(sandbox_text_exec_range, sandbox_patchset); xnu_pf_patchset_destroy(sandbox_patchset); @@ -2833,8 +2776,27 @@ static void kpf_cmd(const char *cmd, char *args) *mac_execve_hook = delta; } - if (autobox_string_match != NULL) { - *repatch_one_area_ptr = xnu_ptr_to_va(one_area - shellcode_from + shellcode_to); + if (protobox_used) { + if (!_zalloc_ro_mut) panic("Missing patch: zalloc_ro_mut"); + + uint32_t* repatch_proc_set_syscall_filter_mask_shc = (uint32_t*)(proc_set_syscall_filter_mask_shc - shellcode_from + shellcode_to); + uint32_t* repatch_proc_set_syscall_filter_mask_shc_target = (uint32_t*)(proc_set_syscall_filter_mask_shc_target - shellcode_from + shellcode_to); + uint32_t* repatch_zalloc_ro_mut = (uint32_t*)(zalloc_ro_mut - shellcode_from + shellcode_to); + + uint32_t delta = (&repatch_proc_set_syscall_filter_mask_shc[0]) - protobox_patchpoint; + delta &= 0x03ffffff; + delta |= 0x14000000; + *protobox_patchpoint = delta; + + delta = (&_proc_set_syscall_filter_mask[0]) - repatch_proc_set_syscall_filter_mask_shc_target; + delta &= 0x03ffffff; + delta |= 0x14000000; + *repatch_proc_set_syscall_filter_mask_shc_target = delta; + + delta = (&_zalloc_ro_mut[0]) - repatch_zalloc_ro_mut; + delta &= 0x03ffffff; + delta |= 0x14000000; + *repatch_zalloc_ro_mut = delta; } if(!livefs_string_match) // Only disable snapshot when we can remount realfs diff --git a/checkra1n/kpf/shellcode.S b/checkra1n/kpf/shellcode.S index 86cdfe74..6e36f3c8 100644 --- a/checkra1n/kpf/shellcode.S +++ b/checkra1n/kpf/shellcode.S @@ -223,6 +223,47 @@ proc_selfname: .quad 0x5151515151515151 vnode_check_open_orig: .quad 0x5252525252525252 + +.globl _proc_set_syscall_filter_mask_shc +.globl _proc_set_syscall_filter_mask_shc_target +.globl _zalloc_ro_mut +.globl one_area +one_area: +.balign 0x100, 0xff + +_proc_set_syscall_filter_mask_shc: +cbz x2, _proc_set_syscall_filter_mask_shc_target +sub sp, sp, 0x40 +stp x19, x20, [sp, 0x10] +stp x21, x22, [sp, 0x20] +stp x29, x30, [sp, 0x30] +mov x19, x0 +mov x20, x1 +mov x21, x2 +mov x22, x3 +mov x8, #8 +mov x0, x17 +mov x1, x21 +mov x2, #0 +adr x3, one_area +udiv x4, x22, x8 +msub x10, x4, x8, x22 +cbz x10, Lbl_zalloc_ro_mut +add x4, x4, #1 +Lbl_zalloc_ro_mut: +bl _zalloc_ro_mut +mov x0, x19 +mov x1, x20 +mov x2, x21 +mov x3, x22 +ldp x19, x20, [sp, 0x10] +ldp x21, x22, [sp, 0x20] +ldp x29, x30, [sp, 0x30] +add sp, sp, 0x40 +_proc_set_syscall_filter_mask_shc_target: +b . +_zalloc_ro_mut: +b . .align 2 .globl _launchd_execve_hook @@ -433,10 +474,6 @@ _nvram_shc: ret _nvram_shc_end: -.globl _one_area -_one_area: -.balign 0x100, 0xff - .globl _kdi_shc .globl _kdi_shc_orig .globl _kdi_shc_get