Permalink
Browse files

img3 parsing

  • Loading branch information...
1 parent 5c820a8 commit ed5f894a66b99dc668a6ede0c015fcb36db83a57 @comex committed Oct 25, 2010
Showing with 397 additions and 15 deletions.
  1. +63 −10 binary.c
  2. +4 −0 binary.h
  3. +161 −0 cc.c
  4. +4 −0 cc.h
  5. +29 −0 common.c
  6. +1 −0 common.h
  7. +29 −5 data.c
  8. +103 −0 lzss.c
  9. +3 −0 lzss.h
View
@@ -200,14 +200,13 @@ DEFINE_RANGECONV(rangeconv_off, sfm_file_offset, fileoff, "offset range", \
(char *)b_addrconv_unsafe(range.binary, sfm->sfm_address) + range.start - sfm->sfm_file_offset, \
(char *)b_addrconv_unsafe(range.binary, seg->vmaddr) + range.start - seg->fileoff)
-void b_load_macho(struct binary *binary, const char *path, bool rw) {
+typedef void *(*almost_mmap_func)(void *, size_t, int, int, uintptr_t, off_t);
+typedef int (*munmap_func)(void *, size_t);
+
+static void b_load_macho_m(struct binary *binary, const char *path, uintptr_t fd, bool rw, almost_mmap_func mm, munmap_func mum) {
#define _arg path
struct mach_header *mach_hdr;
- int fd = open(path, O_RDONLY);
- if(fd == -1) {
- edie("could not open");
- }
- void *fhdr = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ void *fhdr = (*mm)(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if(fhdr == MAP_FAILED) {
edie("could not map file header");
}
@@ -232,9 +231,9 @@ void b_load_macho(struct binary *binary, const char *path, bool rw) {
}
while(nfat_arch--) {
if(arch->cputype == desired_cputype && (arch->cpusubtype == 0 || arch->cpusubtype == desired_cpusubtype)) {
- munmap(fhdr, 0x1000);
+ (*mum)(fhdr, 0x1000);
fat_offset = arch->offset;
- mach_hdr = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, fat_offset);
+ mach_hdr = (*mm)(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, fat_offset);
if(mach_hdr == MAP_FAILED) {
edie("could not map mach-o header from fat file", path);
}
@@ -269,7 +268,7 @@ void b_load_macho(struct binary *binary, const char *path, bool rw) {
struct segment_command *scmd = (void *) cmd;
if(scmd->vmsize == 0) scmd->filesize = 0; // __CTF
if(scmd->filesize != 0) {
- if(mmap(b_addrconv_unsafe(binary, scmd->vmaddr), scmd->filesize, rw ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, fat_offset + scmd->fileoff) == MAP_FAILED) {
+ if((*mm)(b_addrconv_unsafe(binary, scmd->vmaddr), scmd->filesize, rw ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, fat_offset + scmd->fileoff) == MAP_FAILED) {
edie("could not map segment %.16s at %u+%u,%u", scmd->segname, scmd->fileoff, fat_offset, scmd->filesize);
}
}
@@ -279,12 +278,66 @@ void b_load_macho(struct binary *binary, const char *path, bool rw) {
}
}
- munmap(mach_hdr, 0x1000);
+ (*mum)(mach_hdr, 0x1000);
b_macho_load_symbols(binary);
#undef _arg
}
+static void *almost_mmap(void *addr, size_t len, int prot, int flags, uintptr_t fd, off_t offset) {
+ return mmap(addr, len, prot, flags, (int) fd, offset);
+}
+
+void b_load_macho(struct binary *binary, const char *path, bool rw) {
+ int fd = open(path, O_RDONLY);
+ if(fd == -1) {
+ edie("could not open");
+ }
+ b_load_macho_m(binary, path, fd, rw, &almost_mmap, &munmap);
+}
+
+#ifdef IMG3_SUPPORT
+#include <mach/mach.h>
+static void *fake_mmap(void *addr, size_t len, int prot, int flags, uintptr_t fd, off_t offset) {
+ vm_address_t address = (vm_address_t) addr;
+ vm_prot_t c, m;
+ prange_t *pr = (void *) fd;
+
+ if(flags & MAP_FIXED) {
+ munmap((void *)addr, len);
+ }
+
+ kern_return_t kr = vm_remap(mach_task_self(), &address, (vm_size_t) ((len + 0xfff) & ~0xfff), 0xfff, !(flags & MAP_FIXED), mach_task_self(), (vm_address_t)pr->start + offset, true, &c, &m, VM_INHERIT_NONE);
+ if(kr) {
+ switch(kr) {
+ case KERN_INVALID_ADDRESS: errno = EINVAL; break;
+ case KERN_NO_SPACE: errno = ENOMEM; break;
+ case KERN_PROTECTION_FAILURE: errno = EACCES; break;
+ }
+ return MAP_FAILED;
+ } else if(!(c & VM_PROT_READ)) {
+ errno = EACCES;
+ return MAP_FAILED;
+ } else {
+ return (void *) address;
+ }
+}
+
+static int fake_munmap(void *addr, size_t len) {
+ kern_return_t kr = vm_deallocate(mach_task_self(), (mach_vm_address_t) addr, (mach_vm_size_t) len);
+ if(!kr) {
+ return 0;
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+void b_prange_load_macho(struct binary *binary, prange_t range, bool rw) {
+ b_load_macho_m(binary, "(buffer)", (uintptr_t) &range, rw, &fake_mmap, &fake_munmap);
+}
+#endif
+
void b_running_kernel_load_macho(struct binary *binary) {
#ifdef __APPLE__
kern_return_t kr;
View
@@ -51,6 +51,10 @@ void b_dyldcache_load_macho(struct binary *binary, const char *filename);
void b_running_kernel_load_macho(struct binary *binary);
void b_macho_load_symbols(struct binary *binary);
void b_load_macho(struct binary *binary, const char *path, bool rw);
+#ifdef IMG3_SUPPORT
+void b_prange_load_macho(struct binary *binary, prange_t range, bool rw);
+#endif
+
__attribute__((pure)) range_t b_macho_segrange(const struct binary *binary, const char *segname);
void b_macho_store(struct binary *binary, const char *path);
View
161 cc.c
@@ -0,0 +1,161 @@
+#ifndef IMG3_SUPPORT
+#error cc.c and lzss.c are for IMG3_SUPPORT builds
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <CommonCrypto/CommonCryptor.h>
+#include "common.h"
+#include "lzss.h"
+
+struct comp_header {
+ uint32_t signature;
+ uint32_t compression_type;
+ uint32_t checksum;
+ uint32_t length_uncompressed;
+ uint32_t length_compressed;
+ uint8_t padding[0x16C];
+} __attribute__((packed));
+
+prange_t decrypt_and_decompress(uint32_t key_bits, prange_t key, prange_t iv, prange_t buffer) {
+ size_t size;
+ switch(key_bits) {
+ case 128: size = kCCKeySizeAES128; break;
+ case 192: size = kCCKeySizeAES192; break;
+ case 256: size = kCCKeySizeAES256; break;
+ default: abort();
+ }
+ if(key.size != size) {
+ die("bad key_len %zu", key.size);
+ }
+ if(iv.size != 16) {
+ die("bad iv_len %zu", iv.size);
+ }
+ size_t outbuf_len = buffer.size + 32;
+ void *outbuf = malloc(outbuf_len);
+ assert(outbuf);
+ CCCryptorStatus result = CCCrypt(kCCDecrypt,
+ kCCAlgorithmAES128,
+ 0,
+ key.start,
+ size,
+ iv.start,
+ buffer.start,
+ buffer.size & ~0xf,
+ outbuf,
+ outbuf_len,
+ &outbuf_len);
+ if(result != kCCSuccess) {
+ die("decryption failed: %d", (unsigned int) result);
+ }
+ if(outbuf_len < sizeof(struct comp_header)) {
+ die("too small decrypted result");
+ }
+ struct comp_header *ch = outbuf;
+ if(!(ch->signature == 0x706d6f63 && ch->compression_type == 0x73737a6c)) {
+ die("nonsense decrypted result is not complzss (%x %x)", ch->signature, ch->compression_type);
+ }
+ uint32_t length_compressed = ntohl(ch->length_compressed);
+ uint32_t length_uncompressed = ntohl(ch->length_uncompressed);
+ uint32_t checksum = ntohl(ch->checksum);
+ if((outbuf_len - sizeof(struct comp_header)) < length_compressed) {
+ die("too big length_compressed %x > %lx", length_compressed, outbuf_len - sizeof(struct comp_header));
+ }
+ // not a fan of buffer overflows
+ size_t decbuf_len = (length_uncompressed + 0x1fff) & ~0xfff;
+ void *decbuf = mmap(NULL, decbuf_len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+ assert(decbuf != MAP_FAILED);
+ assert(!mprotect((char *)decbuf + decbuf_len - 0x1000, PROT_NONE, 0x1000));
+
+ int actual_length_uncompressed = decompress_lzss(decbuf, (void *) (ch + 1), length_compressed);
+ if(actual_length_uncompressed != length_uncompressed) {
+ die("invalid complzss thing");
+ }
+
+#if 0
+ uint32_t actual_checksum = lzadler32(decbuf, actual_length_uncompressed);
+ if(actual_checksum != checksum) {
+ die("bad checksum (%x, %x)", actual_checksum, checksum);
+ }
+#endif
+
+ free(outbuf);
+ return (prange_t) {decbuf, actual_length_uncompressed};
+}
+
+struct img3_header {
+ uint32_t magic;
+ uint32_t size;
+ uint32_t data_size;
+ uint32_t shsh_offset;
+ uint32_t name;
+} __attribute__((packed));
+
+struct img3_tag {
+ uint32_t magic;
+ uint32_t size;
+ uint32_t data_size;
+ char data[0];
+ union {
+ struct {
+ uint32_t key_modifier;
+ uint32_t key_bits;
+ } __attribute__((packed)) kbag;
+ };
+} __attribute__((packed));
+
+prange_t parse_img3(prange_t img3, uint32_t *key_bits) {
+ assert(img3.size >= sizeof(struct img3_header));
+ struct img3_header *hdr = img3.start;
+ assert(hdr->magic == (uint32_t) 'Img3');
+ assert(hdr->size <= img3.size);
+ void *end = (char *)(img3.start) + hdr->size;
+ assert(hdr->name == (uint32_t) 'krnl');
+ struct img3_tag *tag = (void *) (hdr + 1);
+ struct img3_tag *tag2;
+ prange_t result;
+ bool have_data = false, have_kbag = false;
+ while(!(have_data && have_kbag)) {
+ if((void *)tag->data >= end) {
+ // out of tags
+ die("didn't find DATA and KBAG");
+ }
+ printf("%.4s %p %x\n", (char *) &tag->magic, &tag->data[0], tag->data_size);
+ tag2 = (void *) ((char *)tag + tag->size);
+ if((void *)tag2 > end || tag2 <= tag) {
+ die("tag cut off");
+ }
+ if(tag->magic == (uint32_t) 'DATA') {
+ result = (prange_t) {tag->data, tag->size - 3 * sizeof(uint32_t)};
+ have_data = true;
+ } else if(tag->magic == (uint32_t) 'KBAG') {
+ assert(tag->size >= 5 * sizeof(uint32_t));
+ if(tag->kbag.key_modifier) {
+ *key_bits = tag->kbag.key_bits;
+ have_kbag = true;
+ }
+ }
+ tag = tag2;
+ }
+ return result;
+}
+
+prange_t parse_img3_file(char *filename, uint32_t *key_bits) {
+#define _arg filename
+ int fd = open(filename, O_RDONLY);
+ if(fd == -1) {
+ edie("could not open");
+ }
+ off_t end = lseek(fd, 0, SEEK_END);
+ if(end > SIZE_MAX) {
+ die("too big");
+ }
+ void *img3 = mmap(NULL, (size_t) end, PROT_READ, MAP_SHARED, fd, 0);
+ if(img3 == MAP_FAILED) {
+ edie("could not mmap img3");
+ }
+ return parse_img3((prange_t) {img3, (size_t) end}, key_bits);
+#undef _arg
+}
View
4 cc.h
@@ -0,0 +1,4 @@
+#include "common.h"
+prange_t decrypt_and_decompress(uint32_t key_bits, prange_t key, prange_t iv, prange_t output);
+prange_t parse_img3(prange_t img3, uint32_t *key_bits);
+prange_t parse_img3_file(char *filename, uint32_t *key_bits);
View
@@ -23,3 +23,32 @@ void check_range_has_addr(range_t range, addr_t addr) {
}
}
+static inline bool parse_hex_digit(char digit, uint8_t *result) {
+ if(digit >= '0' && digit <= '9') {
+ *result = digit - '0';
+ return true;
+ } else if(digit >= 'a' && digit <= 'f') {
+ *result = 10 + (digit - 'a');
+ return true;
+ }
+ return false;
+}
+
+prange_t parse_hex_string(char *string) {
+ size_t len = strlen(string);
+ if(len % 2) goto bad;
+ len /= 2;
+ uint8_t *buf = malloc(len);
+ prange_t result = (prange_t) {buf, len};
+ while(len--) {
+ char first = *string++;
+ char second = *string++;
+ uint8_t a, b;
+ if(!parse_hex_digit(first, &a)) goto bad;
+ if(!parse_hex_digit(second, &b)) goto bad;
+ *buf++ = (a * 0x10) + b;
+ }
+ return result;
+ bad:
+ die("bad hex string %s", string);
+}
View
@@ -39,3 +39,4 @@ static inline bool is_valid_range(prange_t range) {
return !mincore(range.start, range.size, &c);
}
+prange_t parse_hex_string(char *string);
View
34 data.c
@@ -1,6 +1,7 @@
#include "common.h"
#include "find.h"
#include "binary.h"
+#include "cc.h"
extern unsigned char pf2_bin[], one_bin[];
extern unsigned int pf2_bin_len, one_bin_len;
@@ -144,24 +145,47 @@ int main(int argc, char **argv) {
struct binary binary;
b_init(&binary);
switch(argv[1][1]) {
- case 'C':
+ case 'C': {
b_load_running_dyldcache(&binary, (void *) 0x30000000);
write_range(bar(&binary), "libgmalloc.dylib", 0644);
return 0;
- case 'c':
+ }
+ case 'c': {
+ if(argc <= 2) goto usage;
b_load_dyldcache(&binary, argv[2]);
write_range(bar(&binary), "libgmalloc.dylib", 0644);
return 0;
- case 'k':
+ }
+ case 'k': {
+ if(argc <= 2) goto usage;
b_load_macho(&binary, argv[2], false);
write_range(foo(&binary), "pf2", 0755);
return 0;
- case 'K':
+ }
+ case 'K': {
b_running_kernel_load_macho(&binary);
write_range(foo(&binary), "pf2", 0755);
return 0;
}
+#ifdef IMG3_SUPPORT
+ case 'i': {
+ if(argc <= 4) goto usage;
+ uint32_t key_bits;
+ prange_t key = parse_hex_string(argv[3]);
+ prange_t iv = parse_hex_string(argv[4]);
+ prange_t data = parse_img3_file(argv[2], &key_bits);
+ prange_t kern = decrypt_and_decompress(key_bits, key, iv, data);
+ b_prange_load_macho(&binary, kern, false);
+ write_range(foo(&binary), "pf2", 0755);
+ return 0;
+ }
+#endif
+ }
usage:
- fprintf(stderr, "Usage: data -c cache | -k kernel | -C cache | -K\n");
+ fprintf(stderr, "Usage: data -c cache | -k kernel | -C cache | -K"
+#ifdef IMG3_SUPPORT
+ " | -i kernel_img3 key iv"
+#endif
+ "\n");
return 1;
}
Oops, something went wrong.

0 comments on commit ed5f894

Please sign in to comment.