summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Bingner <sam@bingner.com>2018-11-27 09:16:27 -1000
committerSam Bingner <sam@bingner.com>2018-11-27 09:16:27 -1000
commitdd6856043a7cb9814a4aeab48e8cf4222438647c (patch)
tree1a8a5325bfbb9bf966a5a1610e334d43b0821190
Initial commitv0.0.1
-rw-r--r--.gitmodules3
-rw-r--r--Makefile10
m---------async_wake_ios0
-rw-r--r--control12
-rw-r--r--entitlements.xml12
-rw-r--r--include/kmem.h72
-rw-r--r--include/trav.h5
-rw-r--r--inject.c245
-rw-r--r--patchfinder64.c1721
-rw-r--r--patchfinder64.h55
10 files changed, 2135 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9ee3a12
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "async_wake_ios"]
+ path = async_wake_ios
+ url = git@github.com:xerub/async_wake_ios.git
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..17cb1cd
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+ARCHS ?= arm64
+include $(THEOS)/makefiles/common.mk
+
+TOOL_NAME = inject
+inject_CODESIGN_FLAGS = -Hsha256 -Sentitlements.xml
+inject_FRAMEWORKS = IOKit
+inject_CFLAGS = -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=missing-braces -Iinclude
+inject_FILES = inject.c patchfinder64.c async_wake_ios/async_wake_ios/libjb/trav.c
+
+include $(THEOS_MAKE_PATH)/tool.mk
diff --git a/async_wake_ios b/async_wake_ios
new file mode 160000
+Subproject 76592d9b1628accdc4d3f832e0eed6788a13f99
diff --git a/control b/control
new file mode 100644
index 0000000..bc7d266
--- /dev/null
+++ b/control
@@ -0,0 +1,12 @@
+Package: trustinjector
+Name: Trust Cache Injector
+Version: 0.0.1
+Architecture: iphoneos-arm
+Description: Inject files to kernel trust cache
+Maintainer: Sam Bingner <maintainer@sbdhi.com>
+Author: Sam Bingner <maintainer@sbdhi.com>
+Provides: trustinjector, science.xnu.injector
+Conflicts: science.xnu.injector
+Replaces: science.xnu.injector
+Section: System
+Tag: role::hacker
diff --git a/entitlements.xml b/entitlements.xml
new file mode 100644
index 0000000..54d4832
--- /dev/null
+++ b/entitlements.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.system-task-ports</key>
+ <true/>
+ <key>task_for_pid-allow</key>
+ <true/>
+ <key>platform-application</key>
+ <true/>
+</dict>
+</plist>
diff --git a/include/kmem.h b/include/kmem.h
new file mode 100644
index 0000000..698bccc
--- /dev/null
+++ b/include/kmem.h
@@ -0,0 +1,72 @@
+#ifndef kmem_h
+#define kmem_h
+
+#include <mach/mach.h>
+
+/***** mach_vm.h *****/
+kern_return_t mach_vm_read(
+ vm_map_t target_task,
+ mach_vm_address_t address,
+ mach_vm_size_t size,
+ vm_offset_t *data,
+ mach_msg_type_number_t *dataCnt);
+
+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);
+
+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);
+
+kern_return_t mach_vm_allocate(
+ vm_map_t target,
+ mach_vm_address_t *address,
+ mach_vm_size_t size,
+ int flags);
+
+kern_return_t mach_vm_deallocate (
+ vm_map_t target,
+ mach_vm_address_t address,
+ mach_vm_size_t size);
+
+kern_return_t mach_vm_protect (
+ vm_map_t target_task,
+ mach_vm_address_t address,
+ mach_vm_size_t size,
+ boolean_t set_maximum,
+ vm_prot_t new_protection);
+
+extern mach_port_t tfp0;
+
+uint32_t rk32(uint64_t kaddr);
+uint64_t rk64(uint64_t kaddr);
+
+void wk32(uint64_t kaddr, uint32_t val);
+void wk64(uint64_t kaddr, uint64_t val);
+
+void wkbuffer(uint64_t kaddr, void* buffer, uint32_t length);
+void rkbuffer(uint64_t kaddr, void* buffer, uint32_t length);
+
+void kmemcpy(uint64_t dest, uint64_t src, uint32_t length);
+
+void kmem_protect(uint64_t kaddr, uint32_t size, int prot);
+
+uint64_t kmem_alloc(uint64_t size);
+uint64_t kmem_alloc_wired(uint64_t size);
+void kmem_free(uint64_t kaddr, uint64_t size);
+
+void prepare_rk_via_kmem_read_port(mach_port_t port);
+void prepare_rwk_via_tfp0(mach_port_t port);
+void prepare_for_rw_with_fake_tfp0(mach_port_t fake_tfp0);
+
+// query whether kmem read or write is present
+int have_kmem_read(void);
+int have_kmem_write(void);
+
+#endif
diff --git a/include/trav.h b/include/trav.h
new file mode 100644
index 0000000..47aeec3
--- /dev/null
+++ b/include/trav.h
@@ -0,0 +1,5 @@
+#ifndef _TRAV_H
+#define _TRAV_H
+#include <sys/time.h>
+#include "../async_wake_ios/async_wake_ios/libjb.h"
+#endif
diff --git a/inject.c b/inject.c
new file mode 100644
index 0000000..82a0c57
--- /dev/null
+++ b/inject.c
@@ -0,0 +1,245 @@
+#include <sys/snapshot.h>
+#include <dlfcn.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <copyfile.h>
+#include <spawn.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <dirent.h>
+#include <sys/sysctl.h>
+#include <mach-o/dyld.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <mach/mach.h>
+#include "async_wake_ios/async_wake_ios/libjb.h"
+#include "patchfinder64.h"
+#include <kmem.h>
+
+mach_port_t tfp0 = MACH_PORT_NULL;
+
+void wk32(uint64_t kaddr, uint32_t val) {
+ if (tfp0 == MACH_PORT_NULL) {
+ printf("attempt to write to kernel memory before any kernel memory write primitives available\n");
+ sleep(3);
+ return;
+ }
+
+ kern_return_t err;
+ err = mach_vm_write(tfp0,
+ (mach_vm_address_t)kaddr,
+ (vm_offset_t)&val,
+ (mach_msg_type_number_t)sizeof(uint32_t));
+
+ if (err != KERN_SUCCESS) {
+ printf("tfp0 write failed: %s %x\n", mach_error_string(err), err);
+ return;
+ }
+}
+
+void wk64(uint64_t kaddr, uint64_t val) {
+ uint32_t lower = (uint32_t)(val & 0xffffffff);
+ uint32_t higher = (uint32_t)(val >> 32);
+ wk32(kaddr, lower);
+ wk32(kaddr+4, higher);
+}
+
+uint32_t rk32(uint64_t kaddr) {
+ kern_return_t err;
+ uint32_t val = 0;
+ mach_vm_size_t outsize = 0;
+ err = mach_vm_read_overwrite(tfp0,
+ (mach_vm_address_t)kaddr,
+ (mach_vm_size_t)sizeof(uint32_t),
+ (mach_vm_address_t)&val,
+ &outsize);
+ if (err != KERN_SUCCESS){
+ printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp0);
+ sleep(3);
+ return 0;
+ }
+
+ if (outsize != sizeof(uint32_t)){
+ printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize);
+ sleep(3);
+ return 0;
+ }
+ return val;
+}
+
+uint64_t rk64(uint64_t kaddr) {
+ uint64_t lower = rk32(kaddr);
+ uint64_t higher = rk32(kaddr+4);
+ uint64_t full = ((higher<<32) | lower);
+ return full;
+}
+
+uint64_t kmem_alloc(uint64_t size) {
+ if (tfp0 == MACH_PORT_NULL) {
+ printf("attempt to allocate kernel memory before any kernel memory write primitives available\n");
+ sleep(3);
+ return 0;
+ }
+
+ kern_return_t err;
+ mach_vm_address_t addr = 0;
+ mach_vm_size_t ksize = round_page_kernel(size);
+ err = mach_vm_allocate(tfp0, &addr, ksize, VM_FLAGS_ANYWHERE);
+ if (err != KERN_SUCCESS) {
+ printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
+ sleep(3);
+ return 0;
+ }
+ return addr;
+}
+
+// https://github.com/JonathanSeals/kernelversionhacker/blob/3dcbf59f316047a34737f393ff946175164bf03f/kernelversionhacker.c#L92
+
+#define IMAGE_OFFSET 0x2000
+#define MACHO_HEADER_MAGIC 0xfeedfacf
+#define MAX_KASLR_SLIDE 0x21000000
+#define KERNEL_SEARCH_ADDRESS 0xfffffff007004000
+
+#define ptrSize sizeof(uintptr_t)
+
+static vm_address_t get_kernel_base(mach_port_t tfp0)
+{
+ uint64_t addr = 0;
+ addr = KERNEL_SEARCH_ADDRESS+MAX_KASLR_SLIDE;
+
+ while (1) {
+ char *buf;
+ mach_msg_type_number_t sz = 0;
+ kern_return_t ret = vm_read(tfp0, addr, 0x200, (vm_offset_t*)&buf, &sz);
+
+ if (ret) {
+ goto next;
+ }
+
+ if (*((uint32_t *)buf) == MACHO_HEADER_MAGIC) {
+ int ret = vm_read(tfp0, addr, 0x1000, (vm_offset_t*)&buf, &sz);
+ if (ret != KERN_SUCCESS) {
+ printf("Failed vm_read %i\n", ret);
+ goto next;
+ }
+
+ for (uintptr_t i=addr; i < (addr+0x2000); i+=(ptrSize)) {
+ mach_msg_type_number_t sz;
+ int ret = vm_read(tfp0, i, 0x120, (vm_offset_t*)&buf, &sz);
+
+ if (ret != KERN_SUCCESS) {
+ printf("Failed vm_read %i\n", ret);
+ exit(-1);
+ }
+ if (!strcmp(buf, "__text") && !strcmp(buf+0x10, "__PRELINK_TEXT")) {
+ return addr;
+ }
+ }
+ }
+
+ next:
+ addr -= 0x200000;
+ }
+}
+
+size_t
+kread(uint64_t where, void *p, size_t size)
+{
+ int rv;
+ size_t offset = 0;
+ while (offset < size) {
+ mach_vm_size_t sz, chunk = 2048;
+ if (chunk > size - offset) {
+ chunk = size - offset;
+ }
+ rv = mach_vm_read_overwrite(tfp0, where + offset, chunk, (mach_vm_address_t)p + offset, &sz);
+ if (rv || sz == 0) {
+ fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where));
+ break;
+ }
+ offset += sz;
+ }
+ return offset;
+}
+
+size_t
+kwrite(uint64_t where, const void *p, size_t size)
+{
+ int rv;
+ size_t offset = 0;
+ while (offset < size) {
+ size_t chunk = 2048;
+ if (chunk > size - offset) {
+ chunk = size - offset;
+ }
+ rv = mach_vm_write(tfp0, where + offset, (mach_vm_offset_t)p + offset, (mach_msg_type_number_t)chunk);
+ if (rv) {
+ fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where));
+ break;
+ }
+ offset += chunk;
+ }
+ return offset;
+}
+
+mach_port_t try_restore_port() {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t err;
+ err = host_get_special_port(mach_host_self(), 0, 4, &port);
+ if (err == KERN_SUCCESS && port != MACH_PORT_NULL) {
+ printf("got persisted port!\n");
+ // make sure rk64 etc use this port
+ return port;
+ }
+ printf("unable to retrieve persisted port\n");
+ return MACH_PORT_NULL;
+}
+
+int injectTrustCache(int argc, char* argv[], uint64_t trust_chain, uint64_t amficache) {
+ printf("Injecting to trust cache...\n");
+ struct trust_mem mem;
+ size_t length = 0;
+ uint64_t kernel_trust = 0;
+
+ mem.next = rk64(trust_chain);
+ *(uint64_t *)&mem.uuid[0] = 0xabadbabeabadbabe;
+ *(uint64_t *)&mem.uuid[8] = 0xabadbabeabadbabe;
+
+ for (int i = 1; i < argc; i++) {
+ int rv = grab_hashes(argv[i], kread, amficache, mem.next);
+ if (rv) {
+ printf("Failed to inject to trust cache.\n");
+ return -1;
+ }
+ }
+
+ length = (sizeof(mem) + numhash * 20 + 0xFFFF) & ~0xFFFF;
+ kernel_trust = kmem_alloc(length);
+ printf("alloced: 0x%zx => 0x%llx\n", length, kernel_trust);
+
+ mem.count = numhash;
+ kwrite(kernel_trust, &mem, sizeof(mem));
+ kwrite(kernel_trust + sizeof(mem), allhash, numhash * 20);
+ wk64(trust_chain, kernel_trust);
+ printf("Successfully injected to trust cache.\n");
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc < 2) {
+ fprintf(stderr,"Usage: inject /full/path/to/executable\n");
+ fprintf(stderr,"Inject executables to trust cache\n");
+ return -1;
+ }
+ tfp0 = try_restore_port();
+ uint64_t kernel_base = get_kernel_base(tfp0);
+ init_kernel(kernel_base, NULL);
+ uint64_t trust_chain = find_trustcache();
+ uint64_t amficache = find_amficache();
+ term_kernel();
+ return injectTrustCache(argc, argv, trust_chain, amficache);
+}
diff --git a/patchfinder64.c b/patchfinder64.c
new file mode 100644
index 0000000..dfc6264
--- /dev/null
+++ b/patchfinder64.c
@@ -0,0 +1,1721 @@
+//
+// patchfinder64.c
+// extra_recipe
+//
+// Created by xerub on 06/06/2017.
+// Copyright © 2017 xerub. All rights reserved.
+//
+
+#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)
+
+/* 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 **************************************************************/
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <mach-o/loader.h>
+
+#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;
+static addr_t kernel_delta = 0;
+
+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);
+}
+
+/* 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 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;
+}
+
+/* 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, call4, call5, call6, call7;
+ 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, 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);
+ 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, call4, call5, call6, 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, call2, call3, call4, call5, call6, call7;
+ 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, call7;
+ addr_t func1;
+
+ 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
new file mode 100644
index 0000000..bd805b5
--- /dev/null
+++ b/patchfinder64.h
@@ -0,0 +1,55 @@
+#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
+