From 525f9168679392920da51d2487cc5fdba3c68e2c Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Sun, 26 Oct 2008 14:34:59 +0000 Subject: [PATCH] Mach-O parsing from Dan Williams --- hfwget.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 165 insertions(+), 9 deletions(-) diff --git a/hfwget.c b/hfwget.c index 1c3a95b..6ccea31 100644 --- a/hfwget.c +++ b/hfwget.c @@ -78,6 +78,14 @@ static int host_words_in_dword_be = 0; : swap_bytes_32(value)) : \ (host_bytes_in_word_be ? swap_words_32(value) : (value)) +#define be16_to_host(value) \ + host_bytes_in_word_be ? (value) : swap_bytes_16(value) +#define be32_to_host(value) \ + host_words_in_dword_be ? \ + (host_bytes_in_word_be ? (value) : reverse_bytes_32(value)) : \ + (host_bytes_in_word_be ? swap_words_32(value) : \ + swap_bytes_32(value)) + /* Structures to read image data */ struct _segarray { __le32 offset; @@ -315,10 +323,133 @@ static unsigned int imagebase(const u8* data) return le32_to_host(*((u32*)(pehdr + 0x30))); } +struct mach_header { + u32 magic; + int cputype; + int cpusubtype; + u32 filetype; + u32 ncmds; + u32 sizeofcmds; + u32 flags; +}; + +struct mach_load_command +{ + u32 cmd; + u32 cmdsize; +}; + +struct mach_segment_command +{ + u32 cmd; + u32 cmdsize; + char segname[16]; + u32 vmaddr; + u32 vmsize; + u32 fileoff; + u32 filesize; + int maxprot; + int initprot; + u32 nsects; + u32 flags; +}; + +struct mach_section +{ + char sectname[16]; + char segname[16]; + u32 addr; + u32 size; + u32 offset; + u32 align; + u32 reloff; + u32 nreloc; + u32 flags; + u32 reserved1; + u32 reserved2; +}; + +/* returns the start of the segment that contains the firmware */ +static unsigned int macho_imagebase(const u8 *data) +{ + struct mach_header *hdr = (struct mach_header *) data; + int i, j; + const u8 *p = data + sizeof(struct mach_header); + + for (i = 0; i < be32_to_host(hdr->ncmds); i++) { + struct mach_load_command *load_cmd = (struct mach_load_command *) p; + + if (be32_to_host(load_cmd->cmd) == 0x0001) { /* LC_SEGMENT */ + struct mach_segment_command *seg_cmd = (struct mach_segment_command *) p; + + p += sizeof(struct mach_segment_command); + for (j = 0; j < be32_to_host(seg_cmd->nsects); j++) { + struct mach_section *sect = (struct mach_section *) p; + + if (!strcmp (sect->sectname, "__data") && !strcmp (sect->segname, "__DATA")) { + u32 *imgbase = (u32 *) (data + (be32_to_host(sect->addr) - be32_to_host(seg_cmd->vmaddr))); + return *imgbase; + } + + p += sizeof (struct mach_section); + } + } + + /* advance to past the load command */ + p += sizeof (struct mach_load_command); + p += be32_to_host(load_cmd->cmdsize); + } + + printf("Couldn't find Mach-O __data/__DATA section\n"); + return 0; +} + +#define MH_MAGIC 0xfeedface /* BE Mach-O magic number */ +#define MH_CIGAM 0xcefaedfe /* LE Mach-O magic number */ +#define MH_MAGIC_64 0xfeedfacf /* BE Mach-O 64-bit magic number */ +#define MH_CIGAM_64 0xcffaedfe /* LE Mach-O 64-bit magic number */ + +/* Validates the Mach-O object file; only accepts 32-bit BE + * PPC Mach-O object files because classic AirPort was only + * ever used on 32-bit PPC machines. + * + * Returns: + * 0 = success + * -1 = Not a 32-bit PPC Mach-O object file + * -2 = Not a Mach-O object file + */ +static int macho_validate(const u8 *data) +{ + struct mach_header *hdr = (struct mach_header *) data; + + switch (hdr->magic) { + case MH_MAGIC: + /* Yay, what we need */ + break; + case MH_MAGIC_64: + case MH_CIGAM_64: + case MH_CIGAM: + /* 64-bit or LE 32-bit, can't use it */ + return -1; + default: + /* Not a Mach-O file at all */ + return -2; + } + + if (hdr->cputype != 0x12) /* PPC */ + return -1; + + if (hdr->filetype != 0x0001) /* MH_OBJECT */ + return -1; + + return 0; +} + /* Returns the virtual location of the firmware block */ static unsigned int find_fwblock_entry(const u8* data, unsigned int flen, - u32 vfwoffs) + u32 vfwoffs, + const u32 ibase) { u32 *p = (u32*) ((unsigned int)(data + flen) & 0xFFFFFFFCu); u8 *q; @@ -349,7 +480,7 @@ static unsigned int find_fwblock_entry(const u8* data, */ q = (u8*)(p - 1); - fwblock = (unsigned int)(q - data) + imagebase(data); + fwblock = (unsigned int)(q - data) + ibase; if (found == false) { printf("Firmware block entry not found - contact Mark!\n"); return 0; @@ -394,9 +525,10 @@ static struct _firmwareblock* find_fwtable_entry(const u8* data, */ static void copy_fw_data(struct firmwareblock* firmware, struct _firmwareblock *fw_image, - u8 *data) + const u8 *data, + const u32 ibase) { - u32 delta = (u32)data - imagebase(data); + u32 delta = (u32)data - ibase; unsigned int i; /* Deal with pointers in firmwareblock */ @@ -698,13 +830,35 @@ static int dump_fw(const char *basename, const char hexchar, u32 vfwoffs; FILE* output; size_t len; + int found = 0; if (memcmp(data, "MZ", 2) == 0) { printf ("Driver looks like Microsoft PE format\n"); ibase = imagebase(data); + found = 1; + } + + if (!found) + { + int ret; + + ret = macho_validate(data); + if (ret == 0) + { + printf ("Driver looks like Apple Mach-O format\n"); + ibase = macho_imagebase(data); + found = 1; + } + else if (ret == -1) + { + printf ("Driver looks like Apple Mach-O format\n" + "But only a 32-bit PPC Mach-O format driver is supported.\n"); + return -5; + } } - else if (memcmp(data, "Joy!", 4) == 0) + + if (!found && memcmp(data, "Joy!", 4) == 0) { printf ("Driver looks like Apple PEF format\n"); printf ("I don't know how to extract for this format.\n" @@ -724,7 +878,8 @@ static int dump_fw(const char *basename, const char hexchar, /* ibase = pef_imagebase(data); */ return -5; } - else + + if (!found) { printf ("Unknown object file format\n"); return -5; @@ -751,12 +906,13 @@ static int dump_fw(const char *basename, const char hexchar, vfwoffs = (u32)(fw - data) + ibase; - printf("PE imagebase is 0x%08x, therefore virtual offset of firmware is 0x%08x\n", + printf("Image base is 0x%08x, therefore virtual offset of firmware is 0x%08x\n", ibase, vfwoffs); fwblock = find_fwblock_entry(data, flen, - vfwoffs); + vfwoffs, + ibase); if (!fwblock) return -2; @@ -766,7 +922,7 @@ static int dump_fw(const char *basename, const char hexchar, if (!fw_image) return -3; - copy_fw_data(&firmware, fw_image, data); + copy_fw_data(&firmware, fw_image, data, ibase); /* Print FW ident information */ printf("Entry point at 0x%08x\n", firmware.halfentry * 2); -- 2.11.4.GIT