Patching iOS kernel on jailbroken device (arm64).

Questions and Answers about all things *OS (macOS, iOS, tvOS, watchOS)

Patching iOS kernel on jailbroken device (arm64).

Postby jara » Thu Feb 11, 2016 8:51 pm

As far as I understood, patching kernel on already jailbroken device, must be much easier than jailbrake task. But I still do not understand how to do it. Looks like vm_write to code section is prohibited. On armv7 I found shadowmapping way. But pmap on arm64 according to reference much more complicated on one hand, and requires knowledge of physical kernel offset (which I do not know).

So what the suggested way for kernel patching on iOS arm64?
jara
 
Posts: 10
Joined: Wed Feb 25, 2015 9:59 am

Re: Patching iOS kernel on jailbroken device (arm64).

Postby Siguza » Sun Feb 14, 2016 10:15 pm

Did you just try and write to the __TEXT.__text section using vm_write?
Because I ran some tests, and it seems that nothing is stopping me from writing there, at least with TaiG on iOS 8.4.
User avatar
Siguza
Unicorn
 
Posts: 158
Joined: Thu Jan 28, 2016 10:38 am

Re: Patching iOS kernel on jailbroken device (arm64).

Postby jara » Thu Feb 18, 2016 8:55 am

Yes of course. And received reboot on Ipad mini 8.1.4 TaiG.
jara
 
Posts: 10
Joined: Wed Feb 25, 2015 9:59 am

Re: Patching iOS kernel on jailbroken device (arm64).

Postby morpheus » Thu Feb 18, 2016 1:06 pm

That's not necessarily saying it doesn't work. It could say you're patching incorrectly. There are oh so many things that could go wrong and lead to a reboot. Look at the panic log and see what it says - otherwise it's too generic a question for me, I'm afraid.
morpheus
Site Admin
 
Posts: 532
Joined: Thu Apr 11, 2013 6:24 pm

Re: Patching iOS kernel on jailbroken device (arm64).

Postby Siguza » Fri Feb 19, 2016 1:15 am

The tests I mentioned earlier were carried out with no reboot in between.
Yesterday I ran a similar test across 16 reboots.
On 7 boots, a call to vm_read_overwrite on kernel memory would cause a panic immediately, followed by a secure boot which would end in another panic, followed by a normal boot again.
On the other 9 boots, neither reading from nor writing to kernel memory would cause a panic, even if the program was run 50 times.
Today I wanted to run the same test on a bigger scale and collect the logs, but now I can't get a panic at all.

This is the code I'm using (crash.c):

Code: Select all
/*

Build with:

xcrun -sdk iphoneos gcc -arch armv7 -arch arm64 -Wall -o crash crash.c
tee >ent.plist <<'EOF'
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>task_for_pid-allow</key>
    <true/>
</dict>
</plist>
EOF
codesign -s - --entitlements ent.plist crash

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <mach/kern_return.h>
#include <mach/mach_init.h>
#include <mach/mach_traps.h>
#include <mach/vm_map.h>
#include <mach-o/loader.h>

#if __LP64__
#   define ADDR "0x%016lx"
#   define OFFSET 0x2000
#   define MAGIC MH_MAGIC_64
#   define SEGMENT LC_SEGMENT_64
    typedef struct mach_header_64 mach_hdr_t;
    typedef struct segment_command_64 seg_cmd_t;
    typedef struct section_64 sec_t;
#else
#   define ADDR "0x%08x"
#   define OFFSET 0x1000
#   define MAGIC MH_MAGIC
#   define SEGMENT LC_SEGMENT
    typedef struct mach_header mach_hdr_t;
    typedef struct segment_command seg_cmd_t;
    typedef struct section sec_t;
#endif

typedef struct load_command load_cmd_t;

// Apparently any read >= 0x1000 (2048) causes a KERN_PROTECTION_FAILURE
#define MAX_CHUNK 0xFFF

// Global variables
task_t kernel_task;
char buf[0x1000];

static void dump_mem(vm_address_t addr, size_t size)
{
    for(vm_size_t off = 0, chunk, count; off < size; off += count)
    {
        chunk = size - off;
        chunk = chunk > MAX_CHUNK ? MAX_CHUNK : chunk;
        if(vm_read_overwrite(kernel_task, addr + off, chunk, (vm_address_t)(buf + off), &count) != KERN_SUCCESS)
        {
            printf("Failed to read memory.");
            exit(-1);
        }
    }
}

int main()
{
    printf("Getting kernel task...\n");
    if(task_for_pid(mach_task_self(), 0, &kernel_task) != KERN_SUCCESS)
    {
        printf("Failed to get kernel task.\n");
        return -1;
    }

    printf("Locating kernel...\n");
    // I copied this method of locating the kernel from Samuel Groß
    // https://github.com/saelo/ios-kern-utils/blob/master/lib/kernel/base.c
    vm_address_t addr = 0;
    for(vm_size_t size; 1; addr += size)
    {
        natural_t depth = 0;
        vm_region_submap_info_data_64_t info;
        mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
        if(vm_region_recurse_64(kernel_task, &addr, &size, &depth, (vm_region_info_t)&info, &count) != KERN_SUCCESS)
        {
            printf("Failed to locate kernel.\n");
            return -1;
        }
        if(size > 0x40000000) // 1GB
        {
            addr += OFFSET;
            printf("Found kernel at " ADDR ".\n", addr);
            break;
        }
    }

    printf("Dumping mach header...\n");
    dump_mem(addr, 0x1000);
    mach_hdr_t *hdr = (mach_hdr_t*)buf;
    if(hdr->magic != MAGIC)
    {
        printf("Header looks broken, has magic 0x%08x.\n", hdr->magic);
        return -1;
    }

    printf("Locating text section...\n");
    addr = 0;
    for(load_cmd_t *cmd = (load_cmd_t*)(hdr + 1),
                   *end = (load_cmd_t*)((char*)cmd + hdr->sizeofcmds);
        cmd < end;
        cmd = (load_cmd_t*)((char*)cmd + cmd->cmdsize)
       )
    {
        if(cmd->cmd == SEGMENT)
        {
            seg_cmd_t *seg = (seg_cmd_t*)cmd;
            if(strcmp(seg->segname, "__TEXT") == 0)
                for(int i = 0; i < seg->nsects; ++i)
                {
                    sec_t *sec = (sec_t*)(seg + 1) + i;
                    if(strcmp(sec->sectname, "__text") == 0)
                    {
                        addr = sec->addr;
                        goto the_moon; // I want labelled breaks -.-
                    }
                }
        }
    }
    the_moon:
    if(addr == 0)
    {
        printf("Failed to locate text section.\n");
        return -1;
    }
    else
    {
        printf("Found text section at " ADDR ".\n", addr);
    }

    printf("Dumping %lu bytes of text section...\n", sizeof(unsigned long long));
    dump_mem(addr, sizeof(unsigned long long));
    printf("Read: 0x%llx\n", *((unsigned long long*)buf));

    // copy value to restore later
    unsigned long long have = *((unsigned long long*)buf);
    unsigned long long want = 0xdeadfacebeefcafe;

    printf("Overwriting text passage with 0x%llx...\n", want);
    if(vm_write(kernel_task, addr, (pointer_t)&want, sizeof(unsigned long long)) != KERN_SUCCESS)
    {
        printf("Failed to overwrite text passage.\n");
        return -1;
    }

    printf("Checking written passage...\n");
    dump_mem(addr, sizeof(unsigned long long));
    printf("New value: 0x%llx\n", *((unsigned long long*)buf));

    printf("Restoring text passage...\n");
    if(vm_write(kernel_task, addr, (pointer_t)&have, sizeof(unsigned long long)) != KERN_SUCCESS)
    {
        printf("Failed to restore text passage.\n");
        return -1;
    }

    printf("Checking restored passage...\n");
    dump_mem(addr, sizeof(unsigned long long));
    printf("Value: 0x%llx\n", *((unsigned long long*)buf));

    printf("Done.\n");
    return 0;
}


Would you be so kind as to test this on 8.1.4?
User avatar
Siguza
Unicorn
 
Posts: 158
Joined: Thu Jan 28, 2016 10:38 am

Re: Patching iOS kernel on jailbroken device (arm64).

Postby jara » Fri Feb 19, 2016 4:59 pm

Well, about doing it wrong, it was the first thing I thought. But, first, all is ok if I edit heap. So no problems with writing by itself. Second, I receive reboot even if I "patch" to the same value it was.

About test, I'll try to preform it tomorrow. Thanks for your attention to my problem.
jara
 
Posts: 10
Joined: Wed Feb 25, 2015 9:59 am

Re: Patching iOS kernel on jailbroken device (arm64).

Postby jara » Fri Feb 19, 2016 5:18 pm

I performed some tests.

I ran your code about four times. Each time I received reboot (I lose ping, see apple and uptime is zero). But in my perception reboot was fast.
The same I receive sometimes using saelo kdump. But sometimes it works well. So I decide that working kdump is sign of good device state.
So I tried to run kdump several times (about 5). And again received "fast" reboot all times except last. Last time reboot took much longer, but after that kdump worked well, and, what is most interesting, your code worked well also.

So I continue tests with my code, but if you have any idea what is going on, I would be glad to hear it.
Or if could help you to understand it passing some test, just write me.
jara
 
Posts: 10
Joined: Wed Feb 25, 2015 9:59 am

Re: Patching iOS kernel on jailbroken device (arm64).

Postby jara » Fri Feb 19, 2016 8:24 pm

I performed some test and still no success. Could you please try to overwrite (may be with the same code as in was) code of some function in kext?
jara
 
Posts: 10
Joined: Wed Feb 25, 2015 9:59 am

Re: Patching iOS kernel on jailbroken device (arm64).

Postby Siguza » Fri Feb 19, 2016 11:03 pm

So basically you're experiencing the same as I:
Sometimes, reading from kernel memory will cause the kernel to panic, be it from my crash.c, saelo's kernel tools or whatever else.
But if it doesn't panic the first time, then it won't panic at all.
Right?

That said, my crash.c is only meant to demonstrate that writing to kernel memory doesn't have to cause a panic.
Don't use it to debug the panics on reading from kernel memory, because it writes only garbage to the beginning of the text section, which might cause another panic.

Now, I managed to grab a panic log from a vm_read_overwrite.
I attached it together with the tool I used to cause it, in binary and source form (imports are from my fork of saelo's tools).

panic.zip
(90.87 KiB) Downloaded 78 times


As far as I understand, a "data abort" can be caused by a multitude of faults. How do we find the exact cause?
User avatar
Siguza
Unicorn
 
Posts: 158
Joined: Thu Jan 28, 2016 10:38 am

Re: Patching iOS kernel on jailbroken device (arm64).

Postby jara » Sat Feb 20, 2016 7:18 am

Not exactly.
I receive the same as you when use your tool.

And I receive reboot each time using mine, which does the same as your but with updating kext function part, with the same value as I read from that offset (I even change all after read to your code).

I agree that not any write to kernel cause panic. But I have no example which writes to kext function address, and do not cause panic.
jara
 
Posts: 10
Joined: Wed Feb 25, 2015 9:59 am

Next

Return to Questions and Answers

Who is online

Users browsing this forum: No registered users and 4 guests

cron