Last active
April 26, 2020 16:49
-
-
Save sophiawisdom/31cd09656d81beb024c3eb4f06edccba to your computer and use it in GitHub Desktop.
thread_set_state does not set gs register
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <mach/mach_init.h> | |
#include <mach/thread_act.h> | |
#include <mach/task.h> | |
#include <mach/port.h> | |
#include <mach-o/dyld_images.h> | |
#include <mach/mach_vm.h> | |
#include <dlfcn.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include "dylib_stuff.h" | |
// Simple macro to check failure for mach system calls. | |
#define MACH_CALL(kret, critical) if (kret != 0) {\ | |
printf("Mach call on line %d failed with error \"%s\".\n", __LINE__, mach_error_string(kret));\ | |
if (critical) {exit(1);}\ | |
} | |
// What this does is inject into a task, create a thread, and then attempt to call a function with that thread. | |
int execute_symbol_with_args(task_t task, const char *dylib, const char *symbol, unsigned long long arg1, unsigned long long arg2, unsigned long long arg3) { | |
// First, we have to get the location of the symbol in the other process' address space. | |
// To do that, we get all the dylibs here | |
int size = 0; | |
struct dyld_image_info * dylibs = get_dylibs(task, &size); | |
if (dylibs == NULL) { | |
printf("Getting dylibs failed.\n"); | |
return -1; | |
} | |
// Then we find the address the dylib we want was loaded at | |
mach_vm_address_t dylib_address = find_dylib(dylibs, size, dylib); | |
if (dylib_address == -1) { | |
printf("Getting address of dylib %s failed\n", dylib); | |
return -1; | |
} | |
free(dylibs); | |
// Then we get the offset of the symbol we want in that dylib | |
long offset = get_symbol_offset(dylib, symbol); | |
mach_vm_address_t dylib_symbol_address = dylib_address + offset; | |
thread_act_t thread_port; | |
// Then, we create a bare mach thread | |
MACH_CALL(thread_create(task, &thread_port), TRUE); | |
vm_address_t stack_bottom; | |
// Allocate stack for it | |
MACH_CALL(vm_allocate(task, &stack_bottom, 2*1024*1024, TRUE), TRUE); // 2MB of stack | |
vm_address_t stack_top = stack_bottom + 2*1024*1024; | |
vm_address_t stack_middle = (stack_top - stack_bottom)/2 + stack_bottom; // just a random place for tsd | |
x86_thread_state64_t *thread_state = calloc(1, x86_THREAD_STATE64_COUNT); | |
// Set stack registers | |
thread_state -> __rsp = stack_middle; | |
thread_state -> __rbp = stack_middle; | |
// Set the thread up to call the function specified | |
thread_state -> __rip = dylib_symbol_address; | |
// Emulate C calling convention | |
thread_state -> __rdi = arg1; | |
thread_state -> __rsi = arg2; | |
thread_state -> __rdx = arg3; | |
// THIS IS A CRUCIAL LINE | |
// This won't be set correctly, and the process injected into will crash because it | |
// will try to access data at an offset from gs and it will get 0. | |
thread_state -> __gs = (stack_middle - stack_bottom)/2 + stack_bottom; | |
// Set the thread's state (but gs will not be set) | |
MACH_CALL(thread_set_state(thread_port, x86_THREAD_STATE64, (thread_state_t) thread_state, x86_THREAD_STATE64_COUNT), TRUE); | |
thread_resume(thread_port); | |
return 0; | |
} | |
int main(int argc, char **argv) { | |
int pid = 0; | |
if (argc > 1) { | |
pid = atoi(argv[1]); | |
} | |
if (pid == 0) { | |
printf("Input PID to pause: "); | |
scanf("%d", &pid); | |
} | |
mach_port_name_t task = MACH_PORT_NULL; | |
int kr = task_for_pid(mach_task_self(), pid, &task); | |
if (kr != KERN_SUCCESS) { | |
if (geteuid() != 0) { | |
printf("You must be root in order to run task_for_pid. your euid is %d\n", geteuid()); | |
} | |
printf("task_for_pid failed with error %s.", mach_error_string(kr)); | |
return 1; | |
} | |
vm_address_t play_memory; | |
MACH_CALL(vm_allocate(task, &play_memory, 0x800, TRUE), TRUE); // 2KB of scratch space | |
printf("Allocated 2KB of play memory at address %p\n", (void *)play_memory); | |
// Test library that does some logging when injected, but any library is fine -- dlopen() will still crash. | |
char *library = "/usr/lib/injected/libinjected_library.dylib"; | |
unsigned int s_len = (unsigned int)strlen(library); | |
MACH_CALL(vm_write(task, play_memory, (vm_offset_t) library, s_len), TRUE); | |
execute_symbol_with_args(task, "/usr/lib/system/libdyld.dylib", "dlopen", play_memory, RTLD_NOW, 0); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Process: Activity Monitor [28371] | |
Path: /System/Applications/Utilities/Activity Monitor.app/Contents/MacOS/Activity Monitor | |
Identifier: com.apple.ActivityMonitor | |
Version: 10.14 (1076) | |
Build Info: ActivityMonitor-1076000000000000~128 | |
Code Type: X86-64 (Native) | |
Parent Process: ??? [1] | |
Responsible: Activity Monitor [28371] | |
User ID: 501 | |
Date/Time: 2020-04-26 09:22:52.018 -0700 | |
OS Version: Mac OS X 10.15.2 (19C57) | |
Report Version: 12 | |
Bridge OS Version: 4.2 (17P2551) | |
Anonymous UUID: F5805F1D-7F47-A37D-164F-864D03CBA810 | |
Sleep/Wake UUID: 7AB827D1-4FFE-4423-BCEB-FEEEE3B96274 | |
Time Awake Since Boot: 280000 seconds | |
Time Since Wake: 26000 seconds | |
System Integrity Protection: disabled | |
Crashed Thread: 11 | |
Exception Type: EXC_BAD_ACCESS (SIGSEGV) | |
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000000 | |
Exception Note: EXC_CORPSE_NOTIFY | |
Termination Signal: Segmentation fault: 11 | |
Termination Reason: Namespace SIGNAL, Code 0xb | |
Terminating Process: exc handler [28371] | |
External Modification Warnings: | |
Thread creation by external task. | |
Debugger attached to process. | |
VM Regions Near 0: | |
--> | |
__TEXT 00000001002c8000-0000000100341000 [ 484K] r-x/r-x SM=COW /System/Applications/Utilities/Activity Monitor.app/Contents/MacOS/Activity Monitor | |
Application Specific Information: | |
dyld3 mode | |
Thread 0:: Dispatch queue: com.apple.main-thread | |
0 libsystem_kernel.dylib 0x00007fff70fff25a mach_msg_trap + 10 | |
1 libsystem_kernel.dylib 0x00007fff70fff5d0 mach_msg + 60 | |
2 com.apple.CoreFoundation 0x00007fff399fcd0b __CFRunLoopServiceMachPort + 322 | |
3 com.apple.CoreFoundation 0x00007fff399fb8e7 __CFRunLoopRun + 1695 | |
4 com.apple.CoreFoundation 0x00007fff399fabd3 CFRunLoopRunSpecific + 499 | |
5 com.apple.HIToolbox 0x00007fff3855165d RunCurrentEventLoopInMode + 292 | |
6 com.apple.HIToolbox 0x00007fff3855139d ReceiveNextEventCommon + 600 | |
7 com.apple.HIToolbox 0x00007fff38551127 _BlockUntilNextEventMatchingListInModeWithFilter + 64 | |
8 com.apple.AppKit 0x00007fff36bc2eb4 _DPSNextEvent + 990 | |
9 com.apple.AppKit 0x00007fff36bc1690 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1352 | |
10 com.apple.AppKit 0x00007fff36bb33ae -[NSApplication run] + 658 | |
11 com.apple.AppKit 0x00007fff36b85775 NSApplicationMain + 777 | |
12 libdyld.dylib 0x00007fff70ebe7fd start + 1 | |
Thread 1:: com.apple.NSEventThread | |
0 libsystem_kernel.dylib 0x00007fff70fff25a mach_msg_trap + 10 | |
1 libsystem_kernel.dylib 0x00007fff70fff5d0 mach_msg + 60 | |
2 com.apple.CoreFoundation 0x00007fff399fcd0b __CFRunLoopServiceMachPort + 322 | |
3 com.apple.CoreFoundation 0x00007fff399fb8e7 __CFRunLoopRun + 1695 | |
4 com.apple.CoreFoundation 0x00007fff399fabd3 CFRunLoopRunSpecific + 499 | |
5 com.apple.AppKit 0x00007fff36d65a72 _NSEventThread + 132 | |
6 libsystem_pthread.dylib 0x00007fff710c2e65 _pthread_start + 148 | |
7 libsystem_pthread.dylib 0x00007fff710be83b thread_start + 15 | |
Thread 2: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 3: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 4: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 5: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 6: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 7: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 8: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 9: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 10: | |
0 libsystem_pthread.dylib 0x00007fff710be818 start_wqthread + 0 | |
Thread 11 Crashed: | |
0 libsystem_pthread.dylib 0x00007fff710be528 _pthread_mutex_firstfit_lock_slow + 33 | |
1 libdyld.dylib 0x00007fff70ea9ad0 dyld3::dlopen_internal(char const*, int, void*) + 38 | |
2 libdyld.dylib 0x00007fff70ea9a48 dlopen + 116 | |
Thread 11 crashed with X86 Thread State (64-bit): | |
rax: 0x0000000000000000 rbx: 0x0000000000000000 rcx: 0x0000000000000002 rdx: 0x0000000000000000 | |
rdi: 0x00007fff9ae9c5f8 rsi: 0x0000000000000000 rbp: 0x0000000105624f08 rsp: 0x0000000105624ee8 | |
r8: 0x0000000000000000 r9: 0x00007fff9ae9c610 r10: 0x00007fff9ae9c618 r11: 0x0000000000000000 | |
r12: 0x0000000000000000 r13: 0x00000001020fe000 r14: 0x00007fff9ae9c5f8 r15: 0x0000000000000000 | |
rip: 0x00007fff710be528 rfl: 0x0000000000010202 cr2: 0x0000000000000000 | |
Logical CPU: 6 | |
Error Code: 0x00000004 (no mapping for user data write) | |
Trap Number: 14 | |
binary images and memory summary eliminated for brevity |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sophiawisdom@Sophias-MacBook-Pro ~ % objdump --disassemble --dis-symname=__pthread_mutex_firstfit_lock_slow -macho /usr/lib/system/libsystem_pthread.dylib | |
/usr/lib/system/libsystem_pthread.dylib: | |
(__TEXT,__text) section | |
__pthread_mutex_firstfit_lock_slow: | |
1507: 55 pushq %rbp | |
1508: 48 89 e5 movq %rsp, %rbp | |
150b: 41 56 pushq %r14 | |
150d: 53 pushq %rbx | |
150e: 48 83 ec 10 subq $16, %rsp | |
1512: 49 89 fe movq %rdi, %r14 | |
1515: 4c 8d 57 27 leaq 39(%rdi), %r10 | |
1519: 49 83 e2 f8 andq $-8, %r10 | |
151d: 49 8b 02 movq (%r10), %rax | |
1520: 4c 8d 4f 1f leaq 31(%rdi), %r9 | |
1524: 49 83 e1 f8 andq $-8, %r9 | |
1528: 65 48 8b 0c 25 00 00 00 00 movq %gs:0, %rcx <-- CRASHING INSTRUCTION | |
1531: 4c 8b 81 d8 00 00 00 movq 216(%rcx), %r8 | |
1538: 8b 4f 0c movl 12(%rdi), %ecx | |
153b: 89 ca movl %ecx, %edx | |
153d: 83 e2 0c andl $12, %edx | |
1540: 74 42 je 0x1584 | |
1542: 49 8b 19 movq (%r9), %rbx | |
1545: 4c 39 c3 cmpq %r8, %rbx | |
1548: 75 3a jne 0x1584 | |
154a: 83 fa 08 cmpl $8, %edx | |
154d: 0f 85 b0 00 00 00 jne 0x1603 | |
1553: 89 cb movl %ecx, %ebx | |
1555: c1 eb 10 shrl $16, %ebx | |
1558: ba 23 00 00 00 movl $35, %edx | |
155d: 81 fb ff ff 00 00 cmpl $65535, %ebx | |
1563: 0f 84 a2 00 00 00 je 0x160b | |
1569: ff c3 incl %ebx | |
156b: 89 da movl %ebx, %edx | |
156d: c1 e2 10 shll $16, %edx | |
1570: 0f b7 c9 movzwl %cx, %ecx | |
1573: 09 d1 orl %edx, %ecx | |
1575: 41 89 4e 0c movl %ecx, 12(%r14) | |
1579: 31 d2 xorl %edx, %edx | |
157b: 66 85 db testw %bx, %bx | |
157e: 0f 85 87 00 00 00 jne 0x160b | |
1584: 89 f3 movl %esi, %ebx | |
1586: 80 f3 01 xorb $1, %bl | |
1589: 80 f3 01 xorb $1, %bl | |
158c: 48 89 c2 movq %rax, %rdx | |
158f: 48 89 45 e8 movq %rax, -24(%rbp) | |
1593: 49 8b 09 movq (%r9), %rcx | |
1596: d0 e8 shrb %al | |
1598: 84 d8 testb %bl, %al | |
159a: 75 14 jne 0x15b0 | |
159c: 8d 82 00 01 00 00 leal 256(%rdx), %eax | |
15a2: 89 d7 movl %edx, %edi | |
15a4: 83 cf 02 orl $2, %edi | |
15a7: f6 c2 02 testb $2, %dl | |
15aa: 0f 45 f8 cmovnel %eax, %edi | |
15ad: 89 7d e8 movl %edi, -24(%rbp) | |
15b0: 48 8b 7d e8 movq -24(%rbp), %rdi | |
15b4: 48 89 d0 movq %rdx, %rax | |
15b7: f0 lock | |
15b8: 49 0f b1 3a cmpxchgq %rdi, (%r10) | |
15bc: 75 ce jne 0x158c | |
15be: f6 c2 02 testb $2, %dl | |
15c1: 75 05 jne 0x15c8 | |
15c3: 4d 89 01 movq %r8, (%r9) | |
15c6: eb 1d jmp 0x15e5 | |
15c8: ba 10 00 00 00 movl $16, %edx | |
15cd: 40 84 f6 testb %sil, %sil | |
15d0: 75 39 jne 0x160b | |
15d2: 48 8b 45 e8 movq -24(%rbp), %rax | |
15d6: 48 89 04 24 movq %rax, (%rsp) | |
15da: 4c 89 f7 movq %r14, %rdi | |
15dd: 48 89 ce movq %rcx, %rsi | |
15e0: e8 e5 1f 00 00 callq __pthread_mutex_firstfit_lock_wait | |
15e5: 41 8b 46 0c movl 12(%r14), %eax | |
15e9: 89 c1 movl %eax, %ecx | |
15eb: 83 e1 0c andl $12, %ecx | |
15ee: 31 d2 xorl %edx, %edx | |
15f0: 83 f9 08 cmpl $8, %ecx | |
15f3: 75 16 jne 0x160b | |
15f5: 0f b7 c0 movzwl %ax, %eax | |
15f8: 0d 00 00 01 00 orl $65536, %eax | |
15fd: 41 89 46 0c movl %eax, 12(%r14) | |
1601: eb 08 jmp 0x160b | |
1603: 40 0f b6 c6 movzbl %sil, %eax | |
1607: 8d 54 80 0b leal 11(%rax,%rax,4), %edx | |
160b: 89 d0 movl %edx, %eax | |
160d: 48 83 c4 10 addq $16, %rsp | |
1611: 5b popq %rbx | |
1612: 41 5e popq %r14 | |
1614: 5d popq %rbp | |
1615: c3 retq | |
sophiawisdom@Sophias-MacBook-Pro ~ % |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file is included for completeness and so if you wish you can compile call_one_function.c | |
// but it isn't helpful in understanding what's happening | |
#include <mach/mach_vm.h> | |
#include <mach/task.h> | |
#include <dlfcn.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include "dylib_stuff.h" | |
#define MACH_CALL(kret, critical) if (kret != 0) {\ | |
printf("Mach call on line %d failed with error \"%s\".\n", __LINE__, mach_error_string(kret));\ | |
if (critical) {exit(1);}\ | |
} | |
unsigned char * readProcessMemory (task_t task, mach_vm_address_t addr, mach_msg_type_number_t *size) { | |
vm_offset_t readMem; | |
// Use vm_read, rather than mach_vm_read, since the latter is different | |
// in iOS. | |
MACH_CALL(vm_read(task, // vm_map_t target_task, | |
addr, // mach_vm_address_t address, | |
*size, // mach_vm_size_t size | |
&readMem, // vm_offset_t *data, | |
size), TRUE); // mach_msg_type_number_t *dataCnt | |
return ( (unsigned char *) readMem); | |
} | |
/** | |
* Get all the dylibs in a task. dyld_image_info has a | |
*/ | |
struct dyld_image_info * get_dylibs(task_t task, int *size) { | |
task_dyld_info_data_t task_dyld_info; | |
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; | |
MACH_CALL(task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count), FALSE); | |
// If you call task_info with the TASK_DYLD_INFO flavor, it'll give you information about dyld - specifically, where is the struct | |
// that lists out the location of all the dylibs in the other process' memory. I think this can eventually be painfully discovered | |
// using mmap, but this way is much easier. | |
unsigned int dyld_all_image_infos_size = sizeof(struct dyld_all_image_infos); | |
// Every time there's a pointer, we have to read out the resulting data structure. | |
struct dyld_all_image_infos *dyldaii = (struct dyld_all_image_infos *) readProcessMemory(task, task_dyld_info.all_image_info_addr, &dyld_all_image_infos_size); | |
int imageCount = dyldaii->infoArrayCount; | |
mach_msg_type_number_t dataCnt = imageCount * sizeof(struct dyld_image_info); | |
struct dyld_image_info * dii = (struct dyld_image_info *) readProcessMemory(task, (mach_vm_address_t) dyldaii->infoArray, &dataCnt); | |
if (!dii) { return NULL;} | |
// This one will only have images with a name | |
struct dyld_image_info *images = (struct dyld_image_info *) malloc(dataCnt); | |
int images_index = 0; | |
for (int i = 0; i < imageCount; i++) { | |
dataCnt = 1024; | |
char *imageName = (char *) readProcessMemory(task, (mach_vm_address_t) dii[i].imageFilePath, &dataCnt); | |
if (imageName) { | |
images[images_index].imageFilePath = imageName; | |
images[images_index].imageLoadAddress = dii[i].imageLoadAddress; | |
images_index++; | |
} | |
} | |
// In theory we should be freeing dii and dyldaii, but it's not malloc'd, so I'd need to use mach_vm_deallocate or something, which I don't care about. | |
// This function probably leaks memory, but I'm not super sure. | |
*size = images_index; | |
return images; | |
} | |
mach_vm_address_t find_dylib(struct dyld_image_info * dyld_image_info, int size, const char *image_name) { | |
for (int i = 0; i < size; i++) { | |
if (strcmp(image_name, dyld_image_info[i].imageFilePath) == 0) { | |
return (mach_vm_address_t) dyld_image_info[i].imageLoadAddress; | |
} | |
} | |
return -1; | |
} | |
long get_symbol_offset(const char *dylib_path, const char *symbol_name) { | |
void *handle = dlopen(dylib_path, RTLD_LAZY); | |
void *sym_loc = dlsym(handle, symbol_name); | |
Dl_info info; | |
int result = dladdr(sym_loc, &info); | |
if (result == 0) { | |
printf("dladdr call failed: %d\n", result); | |
return -1; | |
} | |
dlclose(handle); | |
return sym_loc - info.dli_fbase; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <mach/task.h> | |
#include <mach-o/dyld_images.h> | |
struct dyld_image_info * get_dylibs(task_t task, int *size); | |
mach_vm_address_t find_dylib(struct dyld_image_info * dyld_image_info, int size, const char *image_name); | |
long get_symbol_offset(const char *dylib_path, const char *symbol_name); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment