Skip to content

Instantly share code, notes, and snippets.

@bazad
Created November 21, 2019 02:46
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bazad/4636445f27df2086ca395790e9aca279 to your computer and use it in GitHub Desktop.
Save bazad/4636445f27df2086ca395790e9aca279 to your computer and use it in GitHub Desktop.
A demo of one way to find the kernel base on iOS 13.2.2 on an iPhone 8 using the kernel task port as exposed by checkra1n 0.9.5.
#include <assert.h>
#include <mach/mach.h>
#include <stdbool.h>
#include <stdio.h>
// ---- mach_vm.h ---------------------------------------------------------------------------------
extern
kern_return_t mach_vm_read_overwrite
(
vm_map_t target_task,
mach_vm_address_t address,
mach_vm_size_t size,
mach_vm_address_t data,
mach_vm_size_t *outsize
);
extern
kern_return_t mach_vm_region_recurse
(
vm_map_t target_task,
mach_vm_address_t *address,
mach_vm_size_t *size,
natural_t *nesting_depth,
vm_region_recurse_info_t info,
mach_msg_type_number_t *infoCnt
);
// ---- Kernel task -------------------------------------------------------------------------------
static mach_port_t kernel_task_port;
static void
kernel_task_init() {
task_for_pid(mach_task_self(), 0, &kernel_task_port);
assert(kernel_task_port != MACH_PORT_NULL);
printf("kernel task: 0x%x\n", kernel_task_port);
}
static bool
kernel_read(uint64_t address, void *data, size_t size) {
mach_vm_size_t size_out;
kern_return_t kr = mach_vm_read_overwrite(kernel_task_port, address, size,
(mach_vm_address_t) data, &size_out);
return (kr == KERN_SUCCESS);
}
static uint64_t
kernel_read64(uint64_t address) {
uint64_t value = 0;
bool ok = kernel_read(address, &value, sizeof(value));
if (!ok) {
printf("error: %s(0x%016llx)\n", __func__, address);
}
return value;
}
// ---- Kernel base -------------------------------------------------------------------------------
static uint64_t kernel_base;
static bool
is_kernel_base(uint64_t base) {
uint64_t header[2] = { 0x0100000cfeedfacf, 0x0000000200000000 };
uint64_t data[2] = {};
bool ok = kernel_read(base, &data, sizeof(data));
if (ok && memcmp(data, header, sizeof(data)) == 0) {
return true;
}
return false;
}
static bool
kernel_base_init_with_unsafe_heap_scan() {
uint64_t kernel_region_base = 0xfffffff000000000;
uint64_t kernel_region_end = 0xfffffffbffffc000;
// Try and find a pointer in the kernel heap to data in the kernel image. We'll take the
// smallest such pointer.
uint64_t kernel_ptr = (uint64_t)(-1);
mach_vm_address_t address = 0;
for (;;) {
// Get the next memory region.
mach_vm_size_t size = 0;
uint32_t depth = 2;
struct vm_region_submap_info_64 info;
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
kern_return_t kr = mach_vm_region_recurse(kernel_task_port, &address, &size,
&depth, (vm_region_recurse_info_t) &info, &count);
if (kr != KERN_SUCCESS) {
break;
}
// Skip any region that is not on the heap, not in a submap, not readable and
// writable, or not fully mapped.
int prot = VM_PROT_READ | VM_PROT_WRITE;
if (info.user_tag != 12
|| depth != 1
|| (info.protection & prot) != prot
|| info.pages_resident * 0x4000 != size) {
goto next;
}
// Read the first word of each page in this region.
for (size_t offset = 0; offset < size; offset += 0x4000) {
uint64_t value = 0;
bool ok = kernel_read(address + offset, &value, sizeof(value));
if (ok
&& kernel_region_base <= value
&& value < kernel_region_end
&& value < kernel_ptr) {
kernel_ptr = value;
}
}
next:
address += size;
}
// If we didn't find any such pointer, abort.
if (kernel_ptr == (uint64_t)(-1)) {
return false;
}
printf("found kernel pointer %p\n", (void *)kernel_ptr);
// Now that we have a pointer, we want to scan pages until we reach the kernel's Mach-O
// header.
uint64_t page = kernel_ptr & ~0x3fff;
for (;;) {
bool found = is_kernel_base(page);
if (found) {
kernel_base = page;
return true;
}
page -= 0x4000;
}
return false;
}
static void
kernel_base_init() {
bool ok = kernel_base_init_with_unsafe_heap_scan();
assert(ok);
printf("kernel base: %p\n", (void *)kernel_base);
}
// ---- Main --------------------------------------------------------------------------------------
int
main(int argc, const char *argv[]) {
kernel_task_init();
kernel_base_init();
for (size_t i = 0; i < 8; i++) {
printf("%016llx\n", kernel_read64(kernel_base + 8 * i));
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment