blob: 6511d84476d5abc8258cde431340e6d27c280771 [file] [log] [blame]
// -*- mode: ObjC -*-
// This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
// Copyright (C) 1997-1998, 2000-2001, 2004-2012 Steve Nygard.
#import "CDLCSegment.h"
#import "CDMachOFile.h"
#import "CDSection.h"
#include <openssl/aes.h>
#include <openssl/blowfish.h>
NSString *CDSegmentEncryptionTypeName(CDSegmentEncryptionType type)
{
switch (type) {
case CDSegmentEncryptionType_None: return @"None";
case CDSegmentEncryptionType_AES: return @"Protected Segment Type 1 (prior to 10.6)";
case CDSegmentEncryptionType_Blowfish: return @"Protected Segment Type 2 (10.6)";
case CDSegmentEncryptionType_Unknown: return @"Unknown";
}
}
@implementation CDLCSegment
{
NSString *_name;
NSArray *_sections;
NSMutableData *_decryptedData;
}
- (id)initWithDataCursor:(CDMachOFileDataCursor *)cursor;
{
if ((self = [super initWithDataCursor:cursor])) {
_name = nil;
_sections = nil;
_decryptedData = nil;
}
return self;
}
#pragma mark - Debugging
- (NSString *)description;
{
NSString *extra = [self extraDescription];
if (extra == nil) {
return [NSString stringWithFormat:@"<%@:%p> name: %@",
NSStringFromClass([self class]), self,
self.name];
}
return [NSString stringWithFormat:@"<%@:%p> name: %@, %@",
NSStringFromClass([self class]), self,
self.name, extra];
}
- (NSString *)extraDescription;
{
// Implement in subclasses
return nil;
}
#pragma mark -
- (NSUInteger)vmaddr;
{
// Implement in subclasses.
return 0;
}
- (NSUInteger)fileoff;
{
// Implement in subclasses.
return 0;
}
- (NSUInteger)filesize;
{
// Implement in subclasses.
return 0;
}
- (vm_prot_t)initprot;
{
// Implement in subclsses.
return 0;
}
- (uint32_t)flags;
{
// Implement in subclsses.
return 0;
}
- (BOOL)isProtected;
{
return (self.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1;
}
- (CDSegmentEncryptionType)encryptionType;
{
//NSLog(@"%s, isProtected? %u, filesize: %lu, fileoff: %lu", __cmd, [self isProtected], [self filesize], [self fileoff]);
if (self.isProtected) {
if (self.filesize <= 3 * PAGE_SIZE) {
// First three pages aren't encrypted, so we can't tell. Let's pretent it's something we can decrypt.
return CDSegmentEncryptionType_AES;
} else {
const void *src = (uint8_t *)[self.machOFile.data bytes] + self.fileoff + 3 * PAGE_SIZE;
uint32_t magic = OSReadLittleInt32(src, 0);
//NSLog(@"%s, magic= 0x%08x", __cmd, magic);
switch (magic) {
case CDSegmentProtectedMagic_None: return CDSegmentEncryptionType_None;
case CDSegmentProtectedMagic_AES: return CDSegmentEncryptionType_AES;
case CDSegmentProtectedMagic_Blowfish: return CDSegmentEncryptionType_Blowfish;
}
return CDSegmentEncryptionType_Unknown;
}
}
return CDSegmentEncryptionType_None;
}
- (BOOL)canDecrypt;
{
CDSegmentEncryptionType encryptionType = self.encryptionType;
return (encryptionType == CDSegmentEncryptionType_None)
|| (encryptionType == CDSegmentEncryptionType_AES)
|| (encryptionType == CDSegmentEncryptionType_Blowfish);
}
- (NSString *)flagDescription;
{
NSMutableArray *setFlags = [NSMutableArray array];
uint32_t flags = self.flags;
if (flags & SG_HIGHVM) [setFlags addObject:@"HIGHVM"];
if (flags & SG_FVMLIB) [setFlags addObject:@"FVMLIB"];
if (flags & SG_NORELOC) [setFlags addObject:@"NORELOC"];
if (flags & SG_PROTECTED_VERSION_1) [setFlags addObject:@"PROTECTED_VERSION_1"];
if ([setFlags count] == 0)
return @"(none)";
return [setFlags componentsJoinedByString:@" "];
}
- (BOOL)containsAddress:(NSUInteger)address;
{
// Implement in subclasses
return NO;
}
- (CDSection *)sectionContainingAddress:(NSUInteger)address;
{
for (CDSection *section in self.sections) {
if ([section containsAddress:address])
return section;
}
return nil;
}
- (CDSection *)sectionWithName:(NSString *)name;
{
for (CDSection *section in self.sections) {
if ([[section sectionName] isEqual:name])
return section;
}
return nil;
}
- (NSUInteger)fileOffsetForAddress:(NSUInteger)address;
{
return [[self sectionContainingAddress:address] fileOffsetForAddress:address];
}
- (NSUInteger)segmentOffsetForAddress:(NSUInteger)address;
{
return [self fileOffsetForAddress:address] - self.fileoff;
}
- (void)appendToString:(NSMutableString *)resultString verbose:(BOOL)isVerbose;
{
[super appendToString:resultString verbose:isVerbose];
#if 0
[resultString appendFormat:@" segname %@\n", self.name];
[resultString appendFormat:@" vmaddr 0x%08x\n", segmentCommand.vmaddr];
[resultString appendFormat:@" vmsize 0x%08x\n", segmentCommand.vmsize];
[resultString appendFormat:@" fileoff %d\n", segmentCommand.fileoff];
[resultString appendFormat:@" filesize %d\n", segmentCommand.filesize];
[resultString appendFormat:@" maxprot 0x%08x\n", segmentCommand.maxprot];
[resultString appendFormat:@" initprot 0x%08x\n", segmentCommand.initprot];
[resultString appendFormat:@" nsects %d\n", segmentCommand.nsects];
if (isVerbose)
[resultString appendFormat:@" flags %@\n", [self flagDescription]];
else
[resultString appendFormat:@" flags 0x%x\n", segmentCommand.flags];
#endif
// Implement in subclasses
}
- (void)writeSectionData;
{
[self.sections enumerateObjectsUsingBlock:^(CDSection *section, NSUInteger index, BOOL *stop){
[[section data] writeToFile:[NSString stringWithFormat:@"/tmp/%02ld-%@", index, section.sectionName] atomically:NO];
}];
}
- (NSData *)decryptedData;
{
if (self.isProtected == NO)
return nil;
if (_decryptedData == nil) {
//NSLog(@"filesize: %08x, pagesize: %04x", [self filesize], PAGE_SIZE);
NSParameterAssert((self.filesize % PAGE_SIZE) == 0);
_decryptedData = [[NSMutableData alloc] initWithLength:self.filesize];
const uint8_t *src = (uint8_t *)[self.machOFile.data bytes] + self.fileoff;
uint8_t *dest = [_decryptedData mutableBytes];
if (self.filesize <= PAGE_SIZE * 3) {
memcpy(dest, src, [self filesize]);
} else {
uint8_t keyData[64] = { 0x6f, 0x75, 0x72, 0x68, 0x61, 0x72, 0x64, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x79, 0x74, 0x68, 0x65,
0x73, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x67, 0x75, 0x61, 0x72, 0x64, 0x65, 0x64, 0x70, 0x6c,
0x65, 0x61, 0x73, 0x65, 0x64, 0x6f, 0x6e, 0x74, 0x73, 0x74, 0x65, 0x61, 0x6c, 0x28, 0x63, 0x29,
0x41, 0x70, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x63, };
// First three pages are encrypted, just copy
memcpy(dest, src, PAGE_SIZE * 3);
src += PAGE_SIZE * 3;
dest += PAGE_SIZE * 3;
NSUInteger count = (self.filesize / PAGE_SIZE) - 3;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
uint32_t magic = OSReadLittleInt32(src, 0);
if (magic == CDSegmentProtectedMagic_None) {
memcpy(dest, src, [self filesize] - PAGE_SIZE * 3);
} else if (magic == CDSegmentProtectedMagic_Blowfish) {
// 10.6 decryption
unsigned char ivec[8];
BF_KEY key;
BF_set_key(&key, 64, keyData);
for (NSUInteger index = 0; index < count; index++) {
memset(ivec, 0, 8);
BF_cbc_encrypt(src, dest, PAGE_SIZE, &key, ivec, BF_DECRYPT);
src += PAGE_SIZE;
dest += PAGE_SIZE;
}
} else if (magic == CDSegmentProtectedMagic_AES) {
AES_KEY key1, key2;
// 10.5 decryption
AES_set_decrypt_key(keyData, 256, &key1);
AES_set_decrypt_key(keyData + 32, 256, &key2);
for (NSUInteger index = 0; index < count; index++) {
unsigned char iv1[AES_BLOCK_SIZE];
unsigned char iv2[AES_BLOCK_SIZE];
//NSLog(@"src = %08x, encrypted", src);
memset(iv1, 0, AES_BLOCK_SIZE);
memset(iv2, 0, AES_BLOCK_SIZE);
AES_cbc_encrypt(src, dest, PAGE_SIZE / 2, &key1, iv1, AES_DECRYPT);
AES_cbc_encrypt(src + PAGE_SIZE / 2, dest + PAGE_SIZE / 2, PAGE_SIZE / 2, &key2, iv2, AES_DECRYPT);
src += PAGE_SIZE;
dest += PAGE_SIZE;
}
} else {
NSLog(@"Unknown encryption type: 0x%08x", magic);
exit(99);
}
#pragma clang diagnostic pop
}
}
return _decryptedData;
}
@end