Last active
November 2, 2023 14:47
-
-
Save C0deH4cker/80b53de22012146ea9d8 to your computer and use it in GitHub Desktop.
Program that will extract a segment from a mach-o file. Should even work on Linux/BSD/UNIX?
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
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/mman.h> | |
/* For supporting Linux and other systems that don't have mach-o headers */ | |
#if defined(__has_include) && __has_include(<mach-o/loader.h>) | |
#include <mach-o/loader.h> | |
#else | |
#include "stub_loader.h" | |
#endif | |
/* Get the next load command from the current one */ | |
#define NEXTCMD(cmd) (struct load_command*)((char*)(cmd) + (cmd)->cmdsize) | |
/* Iterate through all load commands */ | |
#define ITERCMDS(i, cmd, cmds, ncmds) for(i = 0, cmd = (cmds); i < (ncmds); i++, cmd = NEXTCMD(cmd)) | |
/* Searches for the specified section. If found, its data will be written to a new file | |
at the path specified by outfile. | |
*/ | |
int extract_section(struct mach_header* mh, size_t filesize, const char* segname, | |
const char* sectname, const char* outfile) { | |
bool is64bit = false; | |
uint32_t i, ncmds; | |
struct load_command* cmd, *cmds; | |
/* Parse mach_header to get the first load command and the number of commands */ | |
if(mh->magic != MH_MAGIC) { | |
if(mh->magic == MH_MAGIC_64) { | |
is64bit = true; | |
struct mach_header_64* mh64 = (struct mach_header_64*)mh; | |
cmds = (struct load_command*)&mh64[1]; | |
ncmds = mh64->ncmds; | |
} | |
else { | |
fprintf(stderr, "Invalid magic number: %08X\n", mh->magic); | |
return -1; | |
} | |
} | |
else { | |
cmds = (struct load_command*)&mh[1]; | |
ncmds = mh->ncmds; | |
} | |
/* Keep track of the section if found. */ | |
bool foundsect = false; | |
uint32_t sectoff = 0; | |
uint64_t sectsize = 0; | |
/* Iterate through the mach-o's load commands */ | |
ITERCMDS(i, cmd, cmds, ncmds) { | |
/* Make sure we don't loop infinitely */ | |
if(cmd->cmdsize == 0) { | |
break; | |
} | |
/* Make sure the load command is completely contained in the file */ | |
if((uintptr_t)cmd + cmd->cmdsize - (uintptr_t)mh > filesize) { | |
break; | |
} | |
/* Process the load command */ | |
switch(cmd->cmd) { | |
case LC_SEGMENT: { | |
struct segment_command* seg = (struct segment_command*)cmd; | |
if(strncmp(seg->segname, segname, 16) == 0) { | |
struct section* sects = (struct section*)&seg[1]; | |
for(int j = 0; j < seg->nsects; j++) { | |
if(strncmp(sects[j].sectname, sectname, 16) == 0) { | |
sectoff = sects[j].offset; | |
sectsize = sects[j].size; | |
foundsect = true; | |
break; | |
} | |
} | |
} | |
break; | |
} | |
case LC_SEGMENT_64: { | |
struct segment_command_64* seg = (struct segment_command_64*)cmd; | |
if(strncmp(seg->segname, segname, 16) == 0) { | |
struct section_64* sects = (struct section_64*)&seg[1]; | |
for(int j = 0; j < seg->nsects; j++) { | |
if(strncmp(sects[j].sectname, sectname, 16) == 0) { | |
sectoff = sects[j].offset; | |
sectsize = sects[j].size; | |
foundsect = true; | |
break; | |
} | |
} | |
} | |
break; | |
} | |
} | |
/* Found the section we were looking for */ | |
if(foundsect) { | |
break; | |
} | |
} | |
if(!foundsect) { | |
fprintf(stderr, "Unable to find section %s,%s!\n", segname, sectname); | |
return -1; | |
} | |
/* Open the output file for writing. It must not already exist */ | |
void* sectdata = (char*)mh + sectoff; | |
int outfd = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0644); | |
if(outfd == -1) { | |
perror(outfile); | |
return -1; | |
} | |
/* Write the section's data to the output file */ | |
int ret = 0; | |
ssize_t written = write(outfd, sectdata, sectsize); | |
if(written == -1) { | |
perror(outfile); | |
ret = -1; | |
} | |
close(outfd); | |
return ret; | |
} | |
int main(int argc, char* argv[]) { | |
if(argc != 5) { | |
fprintf(stderr, "Usage: %s mach-o segment-name section-name output\n", argv[0]); | |
return -1; | |
} | |
/* Get an open file descriptor for mmap */ | |
int fd = open(argv[1], O_RDONLY); | |
if(fd == -1) { | |
perror(argv[1]); | |
return -1; | |
} | |
/* Get filesize for mmap */ | |
size_t filesize = lseek(fd, 0, SEEK_END); | |
lseek(fd, 0, SEEK_SET); | |
/* Map the file */ | |
void* map = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0); | |
if(map == MAP_FAILED) { | |
perror("mmap"); | |
close(fd); | |
return -1; | |
} | |
/* Attempt to print its segment names */ | |
int ret = extract_section(map, filesize, argv[2], argv[3], argv[4]); | |
/* Clean up */ | |
munmap(map, filesize); | |
close(fd); | |
return ret; | |
} |
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
#if !defined(_MACHO_LOADER_H_) && !defined(_STUB_LOADER_H_) | |
#define _STUB_LOADER_H_ | |
typedef int cpu_type_t; | |
typedef int cpu_subtype_t; | |
struct mach_header { | |
uint32_t magic; | |
cpu_type_t cputype; | |
cpu_subtype_t cpusubtype; | |
uint32_t filetype; | |
uint32_t ncmds; | |
uint32_t sizeofcmds; | |
uint32_t flags; | |
}; | |
#define MH_MAGIC 0xfeedface | |
struct mach_header_64 { | |
uint32_t magic; | |
cpu_type_t cputype; | |
cpu_subtype_t cpusubtype; | |
uint32_t filetype; | |
uint32_t ncmds; | |
uint32_t sizeofcmds; | |
uint32_t flags; | |
uint32_t reserved; | |
}; | |
#define MH_MAGIC_64 0xfeedfacf | |
struct load_command { | |
uint32_t cmd; | |
uint32_t cmdsize; | |
}; | |
#define LC_SEGMENT 0x1 | |
#define LC_SEGMENT_64 0x19 | |
typedef int vm_prot_t; | |
struct segment_command { | |
uint32_t cmd; | |
uint32_t cmdsize; | |
char segname[16]; | |
uint32_t vmaddr; | |
uint32_t vmsize; | |
uint32_t fileoff; | |
uint32_t filesize; | |
vm_prot_t maxprot; | |
vm_prot_t initprot; | |
uint32_t nsects; | |
uint32_t flags; | |
}; | |
struct segment_command_64 { | |
uint32_t cmd; | |
uint32_t cmdsize; | |
char segname[16]; | |
uint64_t vmaddr; | |
uint64_t vmsize; | |
uint64_t fileoff; | |
uint64_t filesize; | |
vm_prot_t maxprot; | |
vm_prot_t initprot; | |
uint32_t nsects; | |
uint32_t flags; | |
}; | |
struct section { | |
char sectname[16]; | |
char segname[16]; | |
uint32_t addr; | |
uint32_t size; | |
uint32_t offset; | |
uint32_t align; | |
uint32_t reloff; | |
uint32_t nreloc; | |
uint32_t flags; | |
uint32_t reserved1; | |
uint32_t reserved2; | |
}; | |
struct section_64 { | |
char sectname[16]; | |
char segname[16]; | |
uint64_t addr; | |
uint64_t size; | |
uint32_t offset; | |
uint32_t align; | |
uint32_t reloff; | |
uint32_t nreloc; | |
uint32_t flags; | |
uint32_t reserved1; | |
uint32_t reserved2; | |
uint32_t reserved3; | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment