Skip to content

Instantly share code, notes, and snippets.

@johnkhbaek
Last active October 9, 2024 23:18
Show Gist options
  • Save johnkhbaek/771a98212045f327cc1c86aaac63a4e3 to your computer and use it in GitHub Desktop.
Save johnkhbaek/771a98212045f327cc1c86aaac63a4e3 to your computer and use it in GitHub Desktop.
Load macho using NSLinkModule with arguments
/*
================================================================================
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