Skip to content

Instantly share code, notes, and snippets.

@C0deH4cker
Last active November 2, 2023 14:47
Show Gist options
  • Save C0deH4cker/80b53de22012146ea9d8 to your computer and use it in GitHub Desktop.
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?
#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;
}
#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