Last active
October 9, 2024 23:18
-
-
Save johnkhbaek/771a98212045f327cc1c86aaac63a4e3 to your computer and use it in GitHub Desktop.
Load macho using NSLinkModule with arguments
This file contains 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
/* | |
================================================================================ | |
modified from this: https://github.com/its-a-feature/macos_execute_from_memory (supports only bundle) | |
code injection : https://github.com/CylanceVulnResearch/osx_runbin by Stephanie Archibald (does not support m1 x64 emulation and FAT header) | |
added FAT header (universal Macho) parsing | |
script-kiddied, debugged, etc. by @exploitpreacher | |
================================================================================ | |
*/ | |
#include <mach-o/dyld.h> | |
#include <sys/stat.h> | |
#include <sys/mman.h> | |
#include <fcntl.h> | |
#include <unistd.h> // for close | |
#include <stdio.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <mach-o/fat.h> | |
int find_macho(unsigned long addr, unsigned long *base, unsigned int increment, unsigned int dereference) { | |
unsigned long ptr; | |
printf("in find_macho\n"); | |
*base = 0; | |
printf("set base pointer to 0\n"); | |
for (int i=0; i<100000000; i++) { | |
printf("i: %d\n", i); | |
ptr = addr; | |
printf("1\n"); | |
if(dereference) ptr = *(unsigned long *)ptr; | |
printf("2\n"); | |
// chmod returns EFAULT if the path pointer is | |
// outside the process's allocated address space; | |
// it returns ENOENT if the path doesn't exist... | |
// so it's WITHIN process's allocated addr space | |
chmod((char *)ptr, 0777); | |
if(errno == 2 /*ENOENT*/ && | |
((int *)ptr)[0] == 0xfeedfacf /*MH_MAGIC_64*/) { | |
*base = ptr; | |
return 0; | |
} | |
addr += increment; | |
} | |
return 1; | |
} | |
int find_epc(unsigned long base, struct entry_point_command **entry) { | |
// find the entry point command by searching through base's load commands | |
struct mach_header_64 *mh; | |
struct load_command *lc; | |
unsigned long text = 0; | |
*entry = NULL; | |
mh = (struct mach_header_64 *)base; | |
lc = (struct load_command *)(base + sizeof(struct mach_header_64)); | |
for(int i=0; i<mh->ncmds; i++) { | |
if(lc->cmd == LC_MAIN) { //0x80000028 | |
*entry = (struct entry_point_command *)lc; | |
return 0; | |
} | |
lc = (struct load_command *)((unsigned long)lc + lc->cmdsize); | |
} | |
return 1; | |
} | |
uint32_t swap_endian(uint32_t wrong_endian) { | |
uint32_t swapped = ((wrong_endian>>24)&0xff) | // move byte 3 to byte 0 | |
((wrong_endian<<8)&0xff0000) | // move byte 1 to byte 2 | |
((wrong_endian>>8)&0xff00) | // move byte 2 to byte 1 | |
((wrong_endian<<24)&0xff000000); // byte 0 to byte 3 | |
return swapped; | |
} | |
int main(int argc, char **argv) | |
{ | |
char filename[] = "/tmp/loadmacho"; | |
char *tokenBuf = NULL; | |
int numTokens = 0; | |
NSObjectFileImage fileImage = NULL; | |
NSModule module = NULL; | |
NSSymbol symbol = NULL; | |
struct stat stat_buf1, stat_buf2; | |
int fd1, fd2; | |
void *codeAddr = NULL; | |
void *machoAddr = NULL; | |
uint32_t type; | |
uint32_t offset = 0; | |
uint32_t machoBufSize; | |
// load the command file | |
if ((fd1 = open(filename, O_RDONLY, 0)) == -1) { | |
printf("Error opening the file %s\n", filename); | |
return 1; | |
} | |
if (fstat(fd1, &stat_buf1)){ | |
return 1; | |
} | |
tokenBuf = mmap(NULL, | |
stat_buf1.st_size, | |
PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, | |
fd1, | |
0); | |
close(fd1); | |
// remove newline | |
tokenBuf[strcspn(tokenBuf, "\n")] = 0; | |
char *tokenArray[256]; | |
int max_tokens = 256; | |
char *saveptr = NULL; | |
char *p = strtok_r(tokenBuf, " ", &saveptr); | |
while (p != NULL && numTokens < max_tokens) { | |
tokenArray[numTokens++] = p; | |
p = strtok_r(NULL, " ", &saveptr); | |
} | |
for(int j=0;j<numTokens;j++){ | |
printf("token: '%s'\n", tokenArray[j]); | |
} | |
if ((tokenArray[strlen(tokenArray)-1]) == '\\') { | |
printf("need to concatenate with the next"); | |
} | |
// load the binary | |
if ((fd2 = open(tokenArray[0], O_RDONLY, 0)) == -1) { | |
printf("Error opening the file %s\n", tokenArray[0]); | |
return 1; | |
} | |
if (fstat(fd2, &stat_buf2)){ | |
return 1; | |
} | |
codeAddr = mmap(NULL, | |
stat_buf2.st_size, | |
PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, | |
fd2, | |
0); | |
close(fd2); | |
// we loaded the file | |
// determine the type of the file we loaded | |
if (((int *)codeAddr)[0] == 0xbebafeca) /* MAGIC for FAT */ { | |
struct fat_arch *fa; | |
uint32_t num_arch = swap_endian(((uint32_t *)codeAddr)[1]); | |
printf("%x\n", num_arch); | |
for (int i=0;i<num_arch;i++) { | |
fa = (struct fat_arch *)(codeAddr + (sizeof(uint32_t) * 2) + ((sizeof (struct fat_arch))*i)); | |
printf("cpu type: %x\n", fa->cputype); | |
offset = swap_endian(fa->offset); | |
printf("offset: %x\n", offset); | |
machoAddr = codeAddr + offset; | |
printf("after offset: %x\n", ((uint32_t *)machoAddr)[0]); | |
if (((int *)codeAddr)[0] != 0xfeedfacf /* MAGIC for MACHO 64 */) { | |
printf("x64 macho found...\n"); | |
break; | |
} | |
} | |
if (!machoAddr) { | |
goto err; | |
} | |
} else if (((int *)codeAddr)[0] == 0xfeedfacf) /* MAGIC for MACHO x64 */ { | |
machoAddr = codeAddr; | |
} else { | |
printf("Not supported yet\n"); | |
goto err; | |
} | |
type = ((int *)machoAddr)[3]; | |
printf("type = %x\n", type); | |
machoBufSize = stat_buf2.st_size - offset; | |
if (type == 0x8) { // bundle - nothing to do | |
void (*function)(); | |
printf("bundle...no need to find the main function"); | |
NSCreateObjectFileImageFromMemory(machoAddr, machoBufSize, &fileImage); | |
module = NSLinkModule(fileImage, "module", NSLINKMODULE_OPTION_NONE); | |
symbol = NSLookupSymbolInModule(module, "_main"); | |
function = NSAddressOfSymbol(symbol); | |
function(); | |
} else { // we have to find the main function | |
unsigned long execute_base; | |
struct entry_point_command *epc; | |
((int *)machoAddr)[3] = 0x8; // first change to mh_bundle type | |
printf("type changed = %x\n", ((int *)machoAddr)[3]); | |
NSCreateObjectFileImageFromMemory(machoAddr, machoBufSize, &fileImage); | |
module = NSLinkModule(fileImage, "module", NSLINKMODULE_OPTION_NONE); | |
printf("got NSLinkModule ret: %p\n", module); | |
printf("got NSLinkModule ret: %p\n", (uint32_t)module); | |
module = ((uintptr_t)(module)) >> 1; | |
printf("got NSLinkModule ret: %p\n", module); | |
printf("searching for main\n"); | |
//if(find_macho((unsigned long)module, &execute_base, sizeof(uint32_t), 1)) { | |
if(find_macho((unsigned long)module, &execute_base, sizeof(uint32_t), 1)) { | |
printf("Could not find execute_base.\n"); | |
goto err; | |
} | |
printf("found base=%lx\n", execute_base); | |
if(find_epc(execute_base, &epc)) { | |
printf("Could not find epc.\n"); | |
goto err; | |
} | |
printf("found epc=%lx\n", epc); | |
int(*main)(int, char**, char**, char**) = (int(*)(int, char**, char**, char**))(execute_base + epc->entryoff); | |
char *env[] = {NULL}; | |
char *apple[] = {NULL}; | |
main(numTokens, tokenArray, env, apple); | |
} | |
err: | |
NSUnLinkModule(module, NSUNLINKMODULE_OPTION_NONE); | |
NSDestroyObjectFileImage(fileImage); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment