Page 1 of 3

Pangu 9.1 Kernel Dump

PostPosted: Tue Mar 22, 2016 1:52 pm
by bob969
Hi Jonathan,

I was in your class last week and you mentioned that in the Pangu 9.1 jailbreak they add in a Mach host special port 4 for reading the kernel memory. I am trying to use this to get a kernel dump on an iPhone 6S. My first attempts resulted in instant reboots when I tried to read from kernel addresses that were not currently mapped due to ASLR. I then found the vm_region_64() function in the header and am currently using this to try and find the pages that are mapped and contain the kernel. After finding what I think are the right pages, I read them using mach_vm_read() and save them to disk. However, no matter what pages I read from the kernel the result is a file of the expected size where all the bytes are set to zero with the exception of the first 3 or 4 bytes. However, the phone no longer reboots immediately. Do I need to do anything special to actually read the kernel memory? To me it seems like I am at the right address but the kernel is refusing to actually provide its memory. I have attached the output of my program and the program itself below.

Got port e03
vm_region returned 0
kernel start
address = 0xffffff8002014000, size = 0xe0000, info_count = 9
protection = 3,max protect =7,inheritance=1,shared=0,reserved=0,offset=ffffff8002014000,behavior=0,wired=0
vm_region returned 0
kernel end
address = 0xffffff801a28c000, size = 0x3d78000, info_count = 9
protection = 3,max protect =7,inheritance=1,shared=0,reserved=1,offset=ffffff801a28c000,behavior=0,wired=0
Kernel size is about 0x18278000
read 0xe0000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x8000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x94000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x4000 bytes from the kernel mem
read 0x8000 bytes from the kernel mem
read 0x8000 bytes from the kernel mem


Code: Select all
#include <stdio.h>
#include <mach/mach.h>
#include <mach/vm_map.h>
#include <mach/vm_region.h>

#include <stdlib.h>

//From header
kern_return_t mach_vm_read(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, vm_offset_t *data, mach_msg_type_number_t *dataCnt);

int main(int argc, char** argv)
{
    mach_port_t kernMemPort;
    host_get_special_port(mach_host_self(),HOST_LOCAL_NODE,4,&kernMemPort); //host special port 4 was been added to get kernel memory?
    printf("Got port %x\n",kernMemPort);
   
    //try and find kernel start and end addresses
    vm_address_t address;
    vm_size_t size;
    kern_return_t kr;
    struct vm_region_basic_info_64 info;
    mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
    mach_port_t notUsed;
   
    for (address= VM_MIN_ADDRESS; ; address += size) {

        kr = vm_region_64(kernMemPort,&address,&size,VM_REGION_BASIC_INFO_64,(vm_region_info_t)&info,&info_count,&notUsed);
        if(info.protection == 3 && info.offset !=0){
            printf("vm_region returned %d\n",kr);
            printf("kernel start\n");
            printf("\taddress = 0x%lx, size = 0x%lx, info_count = %d\n",address,size,info_count);
            printf("\tprotection = %d,max protect =%d,inheritance=%d,shared=%d,reserved=%d,offset=%llx,behavior=%d,wired=%x\n",info.protection,info.max_protection,info.inheritance,info.shared,info.reserved,info.offset,info.behavior,info.user_wired_count);
            fflush(stdout);
            break;
        }
    }
   
    vm_address_t startAddress = address;
    vm_size_t startSize = size;
    vm_address_t endAddress = address;
    for (address = startAddress; ;address += size) {
       
        kr = vm_region_64(kernMemPort,&address,&size,VM_REGION_BASIC_INFO_64,(vm_region_info_t)&info,&info_count,&notUsed);
        if(info.protection == 0){
            break;
        }
        endAddress = address; //want the page before the first unmmaped page
    }

    //get info from the last mapped page
    kr = vm_region_64(kernMemPort,&endAddress,&size,VM_REGION_BASIC_INFO_64,(vm_region_info_t)&info,&info_count,&notUsed);
    printf("vm_region returned %d\n",kr);
    printf("kernel end\n");
    printf("\taddress = 0x%lx, size = 0x%lx, info_count = %d\n",endAddress,size,info_count);
    printf("\tprotection = %d,max protect =%d,inheritance=%d,shared=%d,reserved=%d,offset=%llx,behavior=%d,wired=%x\n",info.protection,info.max_protection,info.inheritance,info.shared,info.reserved,info.offset,info.behavior,info.user_wired_count);
    fflush(stdout);
    printf("Kernel size is about 0x%lx\n",endAddress-startAddress);
   
   
    //Save to disk and hope for the best?
    FILE* outFile;
    outFile = fopen("/var/root/kerndump.dump","w");

    for (address= startAddress; address < endAddress; address += size) {
       
        kr = vm_region_64(kernMemPort,&address,&size,VM_REGION_BASIC_INFO_64,(vm_region_info_t)&info,&info_count,&notUsed);
       
        vm_offset_t *kernData = malloc(size);
        mach_msg_type_number_t dataRead;
       
        kr = mach_vm_read(kernMemPort,address,size,kernData,&dataRead);
        //printf("vm_read returned %d\n",kr);
        printf("read 0x%x bytes from the kernel mem\n",dataRead);
        fwrite(kernData,size,1,outFile);
    }
    fclose(outFile);

}

Re: Pangu 9.1 Kernel Dump

PostPosted: Tue Mar 22, 2016 8:05 pm
by Siguza
Are you sure you're reading from the correct region?
I only know my way around iOS 8, but there seem to be too many differences.
From forking and tampering with saelo's kern-utils I learned that on iOS 8, the kernel maps over 1 GB of ram where it stores itself, that region has protection and max_protection both set to 0, and the reconstructed binary is about 20 MB.
Unless all of those things have changed, I suspect that you're reading from the wrong region.
Running kmap and grepping for 'M]' (region sizes in the megabytes) on iOS 8.4, I get:

Code: Select all
iPhone:~ mobile$ sudo kmap | fgrep 'M]'
ffffff8015800000-ffffff8097000000 [ 2072M] ---/---
ffffff8097000000-ffffff80adf7c000 [  367M] rw-/rwx

The kernel is in the 2072M region and I believe you're reading from the 367M one.

Re: Pangu 9.1 Kernel Dump

PostPosted: Tue Mar 22, 2016 8:51 pm
by Siguza
I added an experimental ios9 branch to my kern-utils fork.
I cannot test it though, because I have no device on 9.x.
Feel like testing kdump there? :P (if you do, make sure you clone the ios9-test branch and not master)

Re: Pangu 9.1 Kernel Dump

PostPosted: Wed Mar 23, 2016 2:03 pm
by bob969
Thanks for the tips! Turns out I was reading from the wrong section of the kernel memory as you suspected. After playing with your experimental branch for a bit I was able to get it to dump a 9.1 kernel that works with joker. The image offset for the Mach-O header was changed and the buffer for storing the kernel had to be increased.

Re: Pangu 9.1 Kernel Dump

PostPosted: Wed Mar 23, 2016 4:35 pm
by morpheus
Thanks for handling this one, Siguza. Bob - glad you got this working!

Re: Pangu 9.1 Kernel Dump

PostPosted: Wed Mar 23, 2016 6:25 pm
by Siguza
Happy I was able to help. :)

Re: Pangu 9.1 Kernel Dump

PostPosted: Fri Mar 25, 2016 5:16 pm
by backendbilly
I tried Siguza's latest test of ios-kern-utils on 9.1 but keep getting:

Code: Select all
iPhone:~ root# ./kdump3
[*] found kernel base at address 0xffffff8016002000
[*] reading kernel header...
[!] header has wrong magic, expected: 0xfeedfacf, found: 0x3af28282



I also tested it on 9.0.2. Getting different error:

Code: Select all
[!] could not find kernel base address

Re: Pangu 9.1 Kernel Dump

PostPosted: Fri Mar 25, 2016 7:17 pm
by Siguza
Could you give me the output of kmap on 9.0.2?

And could you run the following program on 9.1 and give me its output too?:
Code: Select all
// dump this into ios-kern-utils repo as test.c, then run:
// xcrun -sdk iphoneos gcc -arch armv7 -arch arm64 -Ilib/kernel lib/kernel/*.c -o test test.c
// codesign -s - --entitlements misc/ent.xml test
#include <stdio.h>
#include <mach-o/loader.h>
#include "libkern.h"

#if __LP64__
#define MACH_HEADER_MAGIC MH_MAGIC_64
#else
#define MACH_HEADER_MAGIC MH_MAGIC
#endif

int main()
{
    int magic = MACH_HEADER_MAGIC;
    vm_address_t base = get_kernel_base();
    printf("base: " ADDR "\n", base);
    if(base == 0)
    {
        return 1;
    }
    for(vm_address_t off = base - IMAGE_OFFSET; off < base + 0x10000; ++off)
    {
        off = find_bytes_kern(off, base + 0x10000, (unsigned char*)&magic, sizeof(int));
        if(off == 0)
            break;
        else
            printf(ADDR "\n", off);
    }
    return 0;
}

Re: Pangu 9.1 Kernel Dump

PostPosted: Fri Mar 25, 2016 7:43 pm
by backendbilly
I ran kmap on 9.0.2. Nothing comes back

Code: Select all
iPhone:~ root# ./kmap
iPhone:~ root#


Here is the output of test on 9.1:

Code: Select all
iPhone:~ root# ./test
base: ffffff8016002000
iPhone:~ root#

Re: Pangu 9.1 Kernel Dump

PostPosted: Fri Mar 25, 2016 8:12 pm
by Siguza
Ok, could you add two zeroes to both occurrences of 0x10000 in test.c and try again?

And it seems that vm_region_recurse_64 fails on 9.0.2... could you run this there?:
Code: Select all
#include <stdio.h>
#include <mach/vm_map.h>
#include "libkern.h"

int main()
{
    kern_return_t ret;
    task_t kernel_task;
    vm_region_submap_info_data_64_t info;
    vm_size_t size;
    mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
    unsigned int depth = 0;
    ret = get_kernel_task(&kernel_task);
    if(ret != KERN_SUCCESS)
    {
        printf("Failed to get kernel task\n");
        return 1;
    }
    for(vm_address_t addr = 0; 1; addr += size)
    {
        ret = vm_region_recurse_64(kernel_task, &addr, &size, &depth, (vm_region_info_t)&info, &info_count);
        if(ret != KERN_SUCCESS)
        {
            printf("return value: %u\n", ret);
            break;
        }
        printf(ADDR "-" ADDR "\n", addr, addr + size);
    }
    return 0;
}