KTRW 커널 디버거 6s 포팅 실패 (Failed porting KTRW kernel debugger to iPhone 6s)

    https://github.com/jsherman212/ktrw

    아이폰8 iOS 14에서만 작동하는 커널 디버거를 6s 14.5.1에 호환되도록 몇가지 삽질을 했지만, 실패했다.
    I’ve just tried to make working kernel debugger which only supported A11 devices to iPhone 6s/iOS 14.5.1, but I failed.

    정확히 말하자면, kext_insert 함수의 __PRELINK_INFO 세그먼트에 있는 역직렬화된 kext dictionary를 넣는데에서 막혔는데,
    Especially, got stucked to get working static void kext_insert(struct kext_load_info *info) function which insert unserialized kext dictionary.

    __PRELINK_INFO 세그먼트 마지막에 아이폰8에서는 </array></dict>로 끝나지만, 6s의 경우 </data></dict>로 끝난다는 점이었다.
    In last part of __PRELINK_INFO segments, It ends with </array></dict> on iPhone 8,
    However, on 6s, it ends with </data></dict>.

    • iPhone 8 Kernel
    • iPhone 6s Kernel


    아이폰8 기기에는 0xFFFFFFF0095A437A지점에 삽입하기에
    6s에서 최대한 비슷한 지점을 찾아 0xFFFFFFF007B69F25 지점에 kext dictionary를 삽입하려고 시도하였다.

    Actually kext dictionary inserts in 0xFFFFFFF0095A437A on iPhone 8,
    so finding as close a spot as possible, tried to insert dict in 0xFFFFFFF007B69F25 on iPhone 6s.

    • iPhone 8 Kernel/14.5.1
    • iPhone 6s Kernel/14.5.1
    • pongo_kextload/source/pongo_kextload.c
    static void
    kext_insert(struct kext_load_info *info) {
    	// Get the kernelcache's __PRELINK_INFO.__info section.
    	struct segment_command_64 *prelink_info_segment
    		= macho_get_segment(mh_execute_header, "__PRELINK_INFO");
    	struct section_64 *prelink_info_section
    		= macho_get_section(prelink_info_segment, "__info");
    	// Insert the plist before the "</array></dict>" at the end.
    	char *p = ptr_for_va(prelink_info_section->addr);
    	printf("prelink_info_section->addr: 0x%llx\n", prelink_info_section->addr - kernel_slide);
    	printf("prelink_info_section->size: 0x%llx\n", prelink_info_section->size);
    	char *begin = p;
    	p += prelink_info_section->size;
    	while (p[-1] == 0) {
    		p--;
    	}
    	//while (strcmp(p, "</array></dict>") != 0) {
    	// 3C 2F 61 72 72 61 79 3E 3C 6B 65 79 3E 5F 50 72 65
    	// </array><key>_Pre
    	while (memcmp(p, "\x3C\x2F\x61\x72\x72\x61\x79\x3E\x3C\x6B\x65\x79\x3E\x5F\x50\x72\x65", 17) != 0) {
    		if (p <= begin) {
    			puts("Could not insert kernel extension into __PRELINK_INFO.__info");
    			sleep(15);
    			return;
    		}
    		p--;
    	}
    	puts("Inserting kernel extension into __PRELINK_INFO.__info");
    
    	puts("Copying last bytes");
    
    	size_t info_size = strlen(prelink_info_str);
    	// Re-insert the "</array></dict>" at the end.
    	char *end = p + info_size;
    	//strcpy(end, "</array></dict>");
    	const char* aaaa = "AAAA";
    	memmove(end, p, 1638503);
    	memmove(p, prelink_info_str, strlen(prelink_info_str));
    	//printf("%s", p-14);
    	// Patch up the info dict fields. _PrelinkKmodInfo must be unslid.
    	char *nnn0 = memmem(p, info_size, "NNN0", 4);
    	char *nnn1 = memmem(p, info_size, "NNN1", 4);
    	char *address = memmem(p, info_size, "ADDRESS", 7);
    	char *size = memmem(p, info_size, "SIZE", 4);
    	char *kmodinfo = memmem(p, info_size, "KMODINFO", 8);
    	printf("nnn0: %p, nnn1: %p, address: %p, size: %p, kmodinfo: %p\n", nnn0, nnn1, address, size, kmodinfo);
    	//printf("sa_for_ptr(info->kext): 0x%llx, info->vm_size: %zu, sa_for_ptr(info->kmod_info): 0x%llx, kext_id: %u\n", sa_for_ptr(info->kext), info->vm_size, sa_for_ptr(info->kmod_info), kext_id);
    	//sleep(120);
    	format_hex(address, 16, sa_for_ptr(info->kext));
    	format_hex(size, 16, info->vm_size);
    	format_hex(kmodinfo, 16, sa_for_ptr(info->kmod_info));
    	format_hex(nnn0, 4, kext_id);
    	format_hex(nnn1, 4, kext_id);
    	// Adjust the __PRELINK_INFO metadata.
    	prelink_info_section->size += info_size;
    	// Increment the kext ID for the next kext.
    	kext_id++;
    }

    삽입된 kext dictionary 내용을 확인해봤을때, 제대로 된 것 삽입되었지만
    When I check about inserted kext dictionary, It inserted as well, but

    무슨 이유에선지 위와 같이 “ml_static_protect(): 0 < 0xfffffffe000000000” 패닉 메시지가 나타난다.
    due to unknown reason, It panic messages with “ml_static_protect(): 0 < 0xfffffffe000000000” as above.

    사실 이뿐만이 아니라, 여러가지를 손댈 필요가 있었는데,
    Not only this, but there was also needed to be fix another things,

    패치파인더가 완전히 작동하지는 않아
    아이폰8 커널과 비교하면서 footprint를 찾아 아래 6가지의 6s 커널 오프셋을 찾아낼 필요가 있었다.
    Since kernel patchfinder not fully works,
    I had to find these kernel offsets and fixed with hardcoded offsets and finding footprint.

    • g_paniclog_append_noflush_addr
    • g_kernel_thread_start_addr
    • g_thread_deallocate_addr
    • g__enable_preemption_addr
    • g__disable_preemption_addr
    • g_kernel_memory_allocate_addr

    …..

    • pongo_kextload/source/pf/14/pf.c
    #include <stdio.h>
    #include <stdint.h>
    
    #include "../offsets.h"
    
    #include "../../asm.h"
    #include "../../common.h"
    
    #include "../../third_party/pongo.h"
    
    /* XXX Start original KTRW patchfinders */
    bool OSKext_init_patcher_14(xnu_pf_patch_t *patch, void *cacheable_stream) {
    	const int MAX_SEARCH = 300;
    	uint32_t *insn = cacheable_stream;
    	// First we need to resolve the ADRP/ADD target at [2].
    	void *target = RESOLVE_ADRP_ADD(&insn[2]);
    	if (target == NULL) {
    		return false;
    	}
    	// Check if the target is "_PrelinkBundlePath", which indicates that this function is
    	// OSKext::initWithPrelinkedInfoDict(). Bailing here is the most common path.
    	if (strcmp(target, "_PrelinkBundlePath") != 0) {
    		return false;
    	}
    	/* puts("Patching OSKext::initWithPrelinkedInfoDict()"); */
    	// Search backwards until we get the prologue. Record the instruction that MOVs from X2.
    	uint32_t *x2_insn = NULL;
    	for (int i = 0;; i--) {
    		if (i < -MAX_SEARCH) {
    			return false;
    		}
    		// Check for either of the following instructions, signaling we hit the prologue:
    		// 	SUB  SP, SP, #0xNNN		;; 0xNNN < 0x400
    		// 	STP  X28, X27, [SP,#0xNNN]	;; 0xNNN < 0x100
    		bool prologue = MATCH(insn[i], 0xD10003FF, 0xFFF01FFF)
    			|| MATCH(insn[i], 0xA9006FFC, 0xFFC0FFFF);
    		if (prologue) {
    			break;
    		}
    		// Check for the instruction that saves argument X2, doCoalesedSlides:
    		// 	MOV  Xn, X2
    		bool mov_xn_x2 = MATCH(insn[i], 0xAA0203E0, 0xFFFFFFE0);
    		if (mov_xn_x2) {
    			x2_insn = &insn[i];
    		}
    	}
    	// Check that we found the target instruction.
    	if (x2_insn == NULL) {
    		return false;
    	}
    	// Patch the instruction to zero out doCoalesedSlides:
    	// 	MOV  Xn, XZR
    	*x2_insn |= 0x001F0000;
        puts("KTRW: Patched OSKext::initWithPrelinkedInfoDict");
    	// We no longer need to match this. Disabling the patch speeds up execution time, since the
    	// pattern is pretty frequent.
    	xnu_pf_disable_patch(patch);
    	return true;
    }
    
    /* XXX End original KTRW patchfinders */
    
    /* confirmed working on all KTRR kernels 14.0-14.5 */
    bool ktrr_lockdown_patcher_14(xnu_pf_patch_t *patch, void *cacheable_stream){
        /* This also hits rorgn_lockdown, where the AMCC CTRR patches are,
         * but it's easier for me to separate them since the instruction
         * sequences are so different */
        static int count = 1;
        uint32_t *opcode_stream = cacheable_stream;
    
        *opcode_stream = 0xd503201f;
        opcode_stream[1] = 0xd503201f;
        opcode_stream[3] = 0xd503201f;
    
        if(count == 2){
            xnu_pf_disable_patch(patch);
            puts("KTRW: disabled KTRR MMU lockdown");
        }
    
        count++;
    
        return true;
    }
    
    /* confirmed working on all KTRR kernels 14.0-14.5 */
    bool amcc_ctrr_lockdown_patcher_14(xnu_pf_patch_t *patch,
            void *cacheable_stream){
        /* On 14.x A10+ there doesn't seem to be a specific lock for
         * RoRgn, instead we've got these AMCC CTRR registers. We are
         * patching three of them: lock, enable, and write-disable. See
         * find_lock_group_data and rorgn_lockdown for more info. */
        static int count = 1;
        uint32_t *opcode_stream = cacheable_stream;
    
        /* str w0, [x16, x17] --> str wzr, [x16, x17] */
        opcode_stream[5] = 0xb8316a1f;
    
        if(count == 3){
            xnu_pf_disable_patch(patch);
            puts("KTRW: disabled AMCC CTRR MMU lockdown");
        }
    
        count++;
    
        return true;
    }
    
    /* confirmed working on all kernels 13.0-14.5 */
    bool IOSleep_finder_14(xnu_pf_patch_t *patch, void *cacheable_stream){
        xnu_pf_disable_patch(patch);
    
        g_IOSleep_addr = xnu_ptr_to_va(cacheable_stream);
    
        puts("KTRW: found IOSleep");
        printf("g_IOSleep_addr: 0x%llx\n", g_IOSleep_addr - kernel_slide);
    
        return true;
    }
    
    /* confirmed working on all kernels 13.0-14.5 */
    bool kernel_map_finder_14(xnu_pf_patch_t *patch, void *cacheable_stream){
        xnu_pf_disable_patch(patch);
    
        /* If we're 13.x, we've landed inside profile_release, if we're 14.x,
         * we've landed inside _profile_destroy. For vm_map_unwire, it'll be the
         * branch we're currently sitting at. */
        uint32_t *opcode_stream = cacheable_stream;
    
        /* Finally, we can find kernel_map by searching up for the first ADRP
         * or ADR from where we initially landed */
        uint32_t instr_limit = 150;
    
        while((*opcode_stream & 0x1f000000) != 0x10000000){
            if(instr_limit-- == 0)
                return false;
    
            opcode_stream--;
        }
    
        /* The ADRP,LDR pairs require another level of indirection for this */
        if(((opcode_stream[1] >> 25) & 5) == 4){
            g_kernel_map_addr = *(uint64_t *)get_adrp_ldr_target(opcode_stream);
            g_kernel_map_addr |= ((uint64_t)0xffff << 48);
            g_kernel_map_addr = kext_rebase_va(g_kernel_map_addr);
        }
        else{
            uint64_t kernel_map_addr;
    
            if(*opcode_stream & 0x80000000)
                kernel_map_addr = get_adrp_add_target(opcode_stream);
            else
                kernel_map_addr = get_adr_target(opcode_stream);
    
            g_kernel_map_addr = xnu_ptr_to_va((void *)kernel_map_addr);
        }
    
        puts("KTRW: found kernel_map");
        printf("g_kernel_map_addr: 0x%llx\n", g_kernel_map_addr - kernel_slide);
    
        return true;
    }
    
    /* confirmed working on all kernels 13.0-14.5 */
    bool kernel_thread_start_thread_deallocate_finder_14(xnu_pf_patch_t *patch,
            void *cacheable_stream){
        /* There's two hits for this, but they're identical, so whatever is
         * matched first will do */
        xnu_pf_disable_patch(patch);
    
        // uint32_t *opcode_stream = cacheable_stream;
    
        // uint32_t *kernel_thread_start = get_branch_dst_ptr(opcode_stream);
        // uint32_t *thread_deallocate = get_branch_dst_ptr(opcode_stream + 8);
    
        // g_kernel_thread_start_addr = xnu_ptr_to_va(kernel_thread_start);
        // g_thread_deallocate_addr = xnu_ptr_to_va(thread_deallocate);
    
        //6s 14.5.1 offset
        g_kernel_thread_start_addr = 0xFFFFFFF00719A308 + kernel_slide;
        g_thread_deallocate_addr = 0xFFFFFFF007198538 + kernel_slide;
    
        puts("KTRW: found kernel_thread_start");
        puts("KTRW: found thread_deallocate");
    
        printf("g_kernel_thread_start_addr: 0x%llx\n", g_kernel_thread_start_addr - kernel_slide);
        printf("g_thread_deallocate_addr: 0x%llx\n", g_thread_deallocate_addr - kernel_slide);
    
        return true;
    }
    
    /* confirmed working on all kernels 13.0-14.5 */
    bool panic_finder_14(xnu_pf_patch_t *patch, void *cacheable_stream){
        xnu_pf_disable_patch(patch);
    
        uint32_t *opcode_stream = cacheable_stream;
    
        /* Look for the start of panic's prologue, trying to match
         * sub sp, sp, n */
        uint32_t instr_limit = 50;
    
        while((*opcode_stream & 0xffc003ff) != 0xd10003ff){
            if(instr_limit-- == 0)
                return false;
    
            opcode_stream--;
        }
    
        g_panic_addr = xnu_ptr_to_va(opcode_stream);
    
        puts("KTRW: found panic");
        printf("g_panic_addr: 0x%llx\n", g_panic_addr - kernel_slide);
    
        return true;
    }
    
    bool const_boot_args_finder_14(xnu_pf_patch_t *patch,
            void *cacheable_stream){
        /* I don't know where we landed but const_boot_args is the target
         * of the ADRP/ADD one instruction down */
        xnu_pf_disable_patch(patch);
    
        // uint32_t *opcode_stream = cacheable_stream;
    
        // uint64_t const_boot_args = get_pc_rel_target(opcode_stream + 1);
        // g_const_boot_args_addr = xnu_ptr_to_va(const_boot_args);
    
        //6s 14.5.1 offset
        g_const_boot_args_addr = 0xFFFFFFF0070B6380 + kernel_slide;
    
        puts("KTRW: found const_boot_args");
        printf("g_const_boot_args_addr: 0x%llx\n", g_const_boot_args_addr - kernel_slide);
    
        return true;
    }
    
    bool _disable_enable_preemption_finder_14(xnu_pf_patch_t *patch,
            void *cacheable_stream){
        /* We may have landed inside _disable_preemption. The only way
         * we can tell is if there's a clrex #0xf less than ten instructions up,
         * so look for that. */
        // uint32_t *opcode_stream = cacheable_stream;
        // uint32_t instr_limit = 10;
    
        // while(*opcode_stream != 0xd5033f5f){
        //     if(instr_limit-- == 0)
        //         return false;
    
        //     opcode_stream--;
        // }
    
        xnu_pf_disable_patch(patch);
    
        // instr_limit = 10;
    
        /* The clrex #0xf is there, go forward for the start of
         * _disable_preemption. Looking for stp x29, x30, [sp, #-0x10]! */
        // while(*opcode_stream != 0xa9bf7bfd){
        //     if(instr_limit-- == 0)
        //         return false;
    
        //     opcode_stream++;
        // }
    
        // g__disable_preemption_addr = xnu_ptr_to_va(opcode_stream);
    
        /* _enable_preemption is right under _disable_preemption, so
         * we grab that also */
        // instr_limit = 25;
    
        /* The clrex #0xf is there, go forward for the start of
         * _disable_preemption. Looking for stp x29, x30, [sp, #-0x10]! */
        // while(*opcode_stream != 0xa9bf7bfd){
        //     if(instr_limit-- == 0)
        //         return false;
    
        //     opcode_stream++;
        // }
    
        // g__enable_preemption_addr = xnu_ptr_to_va(opcode_stream);
    
        //6s 14.5.1 offset
        g__enable_preemption_addr = 0xFFFFFFF007268C0C + kernel_slide;
        g__disable_preemption_addr = 0xFFFFFFF007268BE4 + kernel_slide;
    
        puts("KTRW: found _disable_preemption");
        puts("KTRW: found _enable_preemption");
    
        printf("g__enable_preemption_addr: 0x%llx\n", g__enable_preemption_addr - kernel_slide);
        printf("g__disable_preemption_addr: 0x%llx\n", g__disable_preemption_addr - kernel_slide);
    
        return true;
    }
    
    bool vsnprintf_finder_14(xnu_pf_patch_t *patch, void *cacheable_stream){
        /* We landed in vsnprintf so we need to find its prolouge. Searching
         * for sub sp, sp, n */
        xnu_pf_disable_patch(patch);
    
        uint32_t *opcode_stream = cacheable_stream;
        uint32_t instr_limit = 50;
    
        while((*opcode_stream & 0xffc003ff) != 0xd10003ff){
            if(instr_limit-- == 0)
                return false;
    
            opcode_stream--;
        }
    
        g_vsnprintf_addr = xnu_ptr_to_va(opcode_stream);
    
        puts("KTRW: found vsnprintf");
        printf("g_vsnprintf_addr: 0x%llx\n", g_vsnprintf_addr - kernel_slide);
    
        return true;
    }
    
    bool ml_nofault_copy_finder_14(xnu_pf_patch_t *patch,
            void *cacheable_stream){
        /* We landed inside ml_nofault_copy so we need to find its
         * prologue. Searching for stp x28, x27, [sp, #-0x60]! */
        xnu_pf_disable_patch(patch);
    
        uint32_t *opcode_stream = cacheable_stream;
        uint32_t instr_limit = 200;
    
        while(*opcode_stream != 0xa9ba6ffc){
            if(instr_limit-- == 0)
                return false;
    
            opcode_stream--;
        }
    
        g_ml_nofault_copy_addr = xnu_ptr_to_va(opcode_stream);
    
        puts("KTRW: found ml_nofault_copy");
        printf("g_ml_nofault_copy_addr: 0x%llx\n", g_ml_nofault_copy_addr - kernel_slide);
    
        return true;
    }
    
    bool kernel_memory_allocate_finder_14(xnu_pf_patch_t *patch,
            void *cacheable_stream){
        /* kernel_memory_allocate is the dst of the branch three
         * instructions down */
        xnu_pf_disable_patch(patch);
    
        // uint32_t *opcode_stream = cacheable_stream;
        // uint32_t *kernel_memory_allocate = get_branch_dst_ptr(opcode_stream + 3);
    
        // g_kernel_memory_allocate_addr = xnu_ptr_to_va(kernel_memory_allocate);
    
        //6s 14.5.1 offset
        g_kernel_memory_allocate_addr = 0xFFFFFFF0071F10D0 + kernel_slide;
    
        puts("KTRW: found kernel_memory_allocate");
        printf("g_kernel_memory_allocate_addr: 0x%llx\n", g_kernel_memory_allocate_addr - kernel_slide);
    
        return true;
    }
    
    bool paniclog_append_noflush_finder_14(xnu_pf_patch_t *patch,
            void *cacheable_stream){
        // uint32_t *opcode_stream = cacheable_stream;
    
    	// void *target = RESOLVE_ADRP_ADD(opcode_stream + 2);
        // target = target ? target : RESOLVE_ADR(opcode_stream + 2);
    
        // if(!target)
        //     return false;
    
        // if(strcmp(target, "\nPlease go to https://panic.apple.com to report this panic\n"))
        //     return false;
    
        xnu_pf_disable_patch(patch);
    
        // uint32_t *paniclog_append_noflush = get_branch_dst_ptr(opcode_stream + 3);
    
        // g_paniclog_append_noflush_addr = xnu_ptr_to_va(paniclog_append_noflush);
    
        //6s 14.5.1
        g_paniclog_append_noflush_addr = 0xFFFFFFF007173C84+ kernel_slide;
        //add anyway
        g_const_boot_args_addr = 0xFFFFFFF0070B6380 + kernel_slide;
        g_kernel_memory_allocate_addr = 0xFFFFFFF0071F10D0 + kernel_slide;
    
        puts("KTRW: found paniclog_append_noflush");
        printf("g_paniclog_append_noflush_addr: 0x%llx\n", g_paniclog_append_noflush_addr - kernel_slide);
    
        return true;
    }
    
    bool OSKext_slidePrelinkedExecutable_patcher_14(xnu_pf_patch_t *patch,
            void *cacheable_stream){
        /* The STR we matched zeroes vmaddr of all the segments in the
         * KTRW kext and I don't know why, but patching it out works */
        xnu_pf_disable_patch(patch);
    
        // uint32_t *opcode_stream = cacheable_stream;
        // opcode_stream[2] = 0xd503201f;
    
        g_did_patch_slidePrelinkedExecutable = true;
    
        puts("KTRW: patched OSKext::slidePrelinkedExecutable");
    
        return true;
    }
    

    결국 고치는데 시간이 많이 들거라 생각되어 그냥 iOS 14.4.2 버전의 아이폰 8 중고를 사기로 했다.
    I ended up buying used iPhone 8/iOS 14.4.2, because It takes a lot of time to fix it.

    답글 남기기

    이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다