Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
272 lines (248 sloc) 9.75 KB
#ifdef IMG3_SUPPORT
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>
#include <unistd.h>
#include <CommonCrypto/CommonCryptor.h>
#include <mach-o/fat.h>
#include "common.h"
#include "lzss.h"
// this is sort of irrelevant, but I'd like to use it for OS X kernelcaches which are sometimes compressed within fat
#ifndef __arm__ // copy and paste from libstuff
static const struct arch_flag {
const char *name;
cpu_type_t type;
cpu_subtype_t subtype;
} arch_flags[] = {
{ "any", CPU_TYPE_ANY, CPU_SUBTYPE_MULTIPLE },
{ "little", CPU_TYPE_ANY, CPU_SUBTYPE_LITTLE_ENDIAN },
{ "big", CPU_TYPE_ANY, CPU_SUBTYPE_BIG_ENDIAN },
/* 64-bit Mach-O architectures */
/* architecture families */
{ "ppc64", CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL },
{ "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
/* specific architecture implementations */
{ "ppc970-64", CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_970 },
/* 32-bit Mach-O architectures */
/* architecture families */
{ "ppc", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL },
{ "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL },
{ "m68k", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC680x0_ALL },
{ "hppa", CPU_TYPE_HPPA, CPU_SUBTYPE_HPPA_ALL },
{ "sparc", CPU_TYPE_SPARC, CPU_SUBTYPE_SPARC_ALL },
{ "m88k", CPU_TYPE_MC88000, CPU_SUBTYPE_MC88000_ALL },
{ "i860", CPU_TYPE_I860, CPU_SUBTYPE_I860_ALL },
{ "arm", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_ALL },
/* specific architecture implementations */
{ "ppc601", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_601 },
{ "ppc603", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603 },
{ "ppc603e",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603e },
{ "ppc603ev",CPU_TYPE_POWERPC,CPU_SUBTYPE_POWERPC_603ev },
{ "ppc604", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604 },
{ "ppc604e",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604e },
{ "ppc750", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_750 },
{ "ppc7400",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 },
{ "ppc7450",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7450 },
{ "ppc970", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_970 },
{ "i486", CPU_TYPE_I386, CPU_SUBTYPE_486 },
{ "i486SX", CPU_TYPE_I386, CPU_SUBTYPE_486SX },
{ "pentium",CPU_TYPE_I386, CPU_SUBTYPE_PENT }, /* same as i586 */
{ "i586", CPU_TYPE_I386, CPU_SUBTYPE_586 },
{ "pentpro", CPU_TYPE_I386, CPU_SUBTYPE_PENTPRO }, /* same as i686 */
{ "i686", CPU_TYPE_I386, CPU_SUBTYPE_PENTPRO },
{ "pentIIm3",CPU_TYPE_I386, CPU_SUBTYPE_PENTII_M3 },
{ "pentIIm5",CPU_TYPE_I386, CPU_SUBTYPE_PENTII_M5 },
{ "pentium4",CPU_TYPE_I386, CPU_SUBTYPE_PENTIUM_4 },
{ "m68030", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC68030_ONLY },
{ "m68040", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC68040 },
{ "hppa7100LC", CPU_TYPE_HPPA, CPU_SUBTYPE_HPPA_7100LC },
{ "armv4t", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T},
{ "armv5", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ},
{ "xscale", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_XSCALE},
{ "armv6", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 },
{ "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 },
{ NULL, 0, 0 }
};
#endif
static prange_t parse_fat(prange_t input, const char *arch) {
#ifdef __arm__
(void) arch;
return input;
#else
if(input.size < sizeof(struct fat_header)) return input;
struct fat_header *fh = input.start;
if(SWAP32(fh->magic) != FAT_MAGIC) return input;
if(!arch) die("arch not specified for fat file");
cpu_type_t type;
cpu_subtype_t subtype;
for(const struct arch_flag *p = arch_flags; p < (struct arch_flag *) (&arch_flags + 1); p++) {
if(!strcmp(arch, p->name)) {
type = p->type;
subtype = p->subtype;
goto ok;
}
}
die("unknown arch %s", arch);
ok:;
struct fat_arch *fa = (void *) (fh + 1);
uint32_t nfat_arch = SWAP32(fh->nfat_arch);
if((input.size - sizeof(struct fat_header)) / sizeof(struct fat_arch) < nfat_arch) die("nfat_arch overflow");
for(uint32_t i = 0; i < nfat_arch; i++) {
cpu_type_t mytype = SWAP32(fa[i].cputype);
cpu_subtype_t mysubtype = SWAP32(fa[i].cpusubtype);
uint32_t offset = SWAP32(fa[i].offset);
uint32_t size = SWAP32(fa[i].size);
if(type == mytype && subtype == mysubtype) {
if(offset > input.size || size > input.size - offset) die("fat overflow");
return (prange_t) {input.start + offset, size};
}
}
die("arch %s not present in fat file", arch);
#endif
}
static prange_t decrypt(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;
autofree 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);
}
return (prange_t) {outbuf, outbuf_len};
}
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));
static prange_t decompress(prange_t buffer) {
// is it really compressed?
if(buffer.size < sizeof(struct comp_header)) return buffer;
struct comp_header *ch = buffer.start;
if(!(ch->signature == 0x706d6f63 && ch->compression_type == 0x73737a6c)) {
return buffer;
}
uint32_t length_compressed = swap32(ch->length_compressed);
uint32_t length_uncompressed = swap32(ch->length_uncompressed);
uint32_t checksum = swap32(ch->checksum);
if((buffer.size - sizeof(struct comp_header)) < length_compressed) {
die("too large length_compressed %x > %lx", length_compressed, buffer.size - 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 < 0 || (unsigned int) actual_length_uncompressed != length_uncompressed) {
die("invalid complzss thing");
}
#ifndef __arm__
uint32_t actual_checksum = lzadler32(decbuf, actual_length_uncompressed);
if(actual_checksum != checksum) {
die("bad checksum (%x, %x)", actual_checksum, checksum);
}
#else
(void) checksum;
#endif
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));
static prange_t parse_img3(prange_t img3, const char *key, const char *iv) {
if(img3.size < sizeof(struct img3_header)) return img3;
struct img3_header *hdr = img3.start;
if(hdr->magic != (uint32_t) 'Img3') return 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;
memset(&result, 0, sizeof(result)); // not actually necessary, >:( gcc
bool have_data = false, have_kbag = false;
uint32_t key_bits = 0;
while(!(have_data && have_kbag)) {
if((void *)tag->data >= end) {
// out of tags
break;
}
//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;
}
if(!have_data) {
die("didn't find DATA");
}
if(have_kbag) {
if(!key || !iv) die("key/iv not specified for encrypted img3");
return decrypt(key_bits, parse_hex_string(key), parse_hex_string(iv), result);
} else {
// unencrypted like iOS 4.3.1
return result;
}
}
prange_t unpack(prange_t input, const char *key, const char *iv) {
input = parse_img3(input, key, iv);
input = parse_fat(input, key);
input = decompress(input);
return input;
}
#endif
Jump to Line
Something went wrong with that request. Please try again.