Skip to content

Instantly share code, notes, and snippets.

@sophiawisdom
Last active April 26, 2020 16:49
Show Gist options
  • Save sophiawisdom/31cd09656d81beb024c3eb4f06edccba to your computer and use it in GitHub Desktop.
Save sophiawisdom/31cd09656d81beb024c3eb4f06edccba to your computer and use it in GitHub Desktop.
thread_set_state does not set gs register
#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);
}
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
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 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;
}
#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