diff --git a/Source/CDFatArch.m b/Source/CDFatArch.m index 0911a8bb..7b22464f 100644 --- a/Source/CDFatArch.m +++ b/Source/CDFatArch.m @@ -105,7 +105,7 @@ - (NSString *)archName; - (CDMachOFile *)machOFile; { if (machOFile == nil) { - machOFile = [CDFile fileWithData:self.fatFile.data archOffset:fatArch.offset archSize:fatArch.size filename:self.fatFile.filename searchPathState:self.fatFile.searchPathState]; + machOFile = [CDFile fileWithData:self.fatFile.data archOffset:fatArch.offset archSize:fatArch.size filename:self.fatFile.filename searchPathState:self.fatFile.searchPathState isCache:NO]; } return machOFile; diff --git a/Source/CDFatFile.m b/Source/CDFatFile.m index 6a64e710..be81800f 100644 --- a/Source/CDFatFile.m +++ b/Source/CDFatFile.m @@ -19,7 +19,7 @@ @implementation CDFatFile - (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; { - if ((self = [super initWithData:someData archOffset:anOffset archSize:aSize filename:aFilename searchPathState:aSearchPathState])) { + if ((self = [super initWithData:someData archOffset:anOffset archSize:aSize filename:aFilename searchPathState:aSearchPathState isCache:NO])) { CDDataCursor *cursor = [[CDDataCursor alloc] initWithData:someData offset:self.archOffset]; struct fat_header header; diff --git a/Source/CDFile.h b/Source/CDFile.h index c46667c0..6ea52a69 100644 --- a/Source/CDFile.h +++ b/Source/CDFile.h @@ -26,15 +26,16 @@ extern BOOL CDArchUses64BitABI(CDArch arch); // Returns CDFatFile or CDMachOFile. + (id)fileWithData:(NSData *)someData filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; -+ (id)fileWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; ++ (id)fileWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState isCache:(BOOL)aIsCache; -- (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; +- (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState isCache:(BOOL)aIsCache; @property (readonly) NSString *filename; @property (readonly) NSData *data; @property (readonly) NSUInteger archOffset; @property (readonly) NSUInteger archSize; @property (readonly) CDSearchPathState *searchPathState; +@property (readonly) BOOL isCache; - (BOOL)bestMatchForLocalArch:(CDArch *)archPtr; - (CDMachOFile *)machOFileWithArch:(CDArch)arch; diff --git a/Source/CDFile.m b/Source/CDFile.m index a7f27367..f12be5da 100644 --- a/Source/CDFile.m +++ b/Source/CDFile.m @@ -65,14 +65,15 @@ @implementation CDFile NSUInteger archOffset; NSUInteger archSize; CDSearchPathState *searchPathState; + BOOL isCache; } + (id)fileWithData:(NSData *)someData filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; { - return [self fileWithData:someData archOffset:0 archSize:[someData length] filename:aFilename searchPathState:aSearchPathState]; + return [self fileWithData:someData archOffset:0 archSize:[someData length] filename:aFilename searchPathState:aSearchPathState isCache:NO]; } -+ (id)fileWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; ++ (id)fileWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState isCache:(BOOL)aIsCache; { CDFatFile *aFatFile = nil; @@ -80,7 +81,7 @@ + (id)fileWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:( aFatFile = [[CDFatFile alloc] initWithData:someData archOffset:anOffset archSize:aSize filename:aFilename searchPathState:aSearchPathState]; if (aFatFile == nil) { - CDMachOFile *machOFile = [[CDMachOFile alloc] initWithData:someData archOffset:anOffset archSize:aSize filename:aFilename searchPathState:aSearchPathState]; + CDMachOFile *machOFile = [[CDMachOFile alloc] initWithData:someData archOffset:anOffset archSize:aSize filename:aFilename searchPathState:aSearchPathState isCache:aIsCache]; return machOFile; } @@ -93,7 +94,7 @@ - (id)init; return nil; } -- (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; +- (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState isCache:(BOOL)aIsCache; { if ((self = [super init])) { // Otherwise reading the magic number fails. @@ -106,6 +107,7 @@ - (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:( archOffset = anOffset; archSize = aSize; searchPathState = aSearchPathState; + isCache = aIsCache; } return self; @@ -118,6 +120,7 @@ - (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:( @synthesize archOffset; @synthesize archSize; @synthesize searchPathState; +@synthesize isCache; - (BOOL)bestMatchForLocalArch:(CDArch *)archPtr; { diff --git a/Source/CDMachOFile.h b/Source/CDMachOFile.h index 3e6d2c27..c6444c4b 100644 --- a/Source/CDMachOFile.h +++ b/Source/CDMachOFile.h @@ -23,7 +23,7 @@ typedef NSUInteger CDByteOrder; @interface CDMachOFile : CDFile -- (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; +- (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState isCache:(BOOL)aIsCache; - (NSString *)description; @@ -93,6 +93,10 @@ typedef NSUInteger CDByteOrder; - (BOOL)hasRelocationEntryForAddress2:(NSUInteger)address; - (NSString *)externalClassNameForAddress2:(NSUInteger)address; +- (BOOL) _loadCacheInfo; +- (NSUInteger) _cacheAddressForImage:(NSString *)fileName; +- (NSUInteger) _cacheOffsetForAddress:(NSUInteger)address; + @property (readonly) BOOL hasObjectiveC1Data; @property (readonly) BOOL hasObjectiveC2Data; @property (nonatomic, readonly) Class processorClass; diff --git a/Source/CDMachOFile.m b/Source/CDMachOFile.m index d08ccb64..f8943bad 100644 --- a/Source/CDMachOFile.m +++ b/Source/CDMachOFile.m @@ -29,6 +29,36 @@ #import "CDRelocationInfo.h" #import "CDSearchPathState.h" +struct dyld_cache_header +{ + char magic[16]; // e.g. "dyld_v0 ppc" + uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info + uint32_t mappingCount; // number of dyld_cache_mapping_info entries + uint32_t imagesOffset; // file offset to first dyld_cache_image_info + uint32_t imagesCount; // number of dyld_cache_image_info entries + uint64_t dyldBaseAddress; // base address of dyld when cache was built + uint64_t codeSignatureOffset; // file offset in of code signature blob + uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) +}; + +struct dyld_cache_mapping_info { + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint32_t maxProt; + uint32_t initProt; +}; + +struct dyld_cache_image_info +{ + uint64_t address; + uint64_t modTime; + uint64_t inode; + uint32_t pathFileOffset; + uint32_t pad; +}; + + NSString *CDMagicNumberString(uint32_t magic) { switch (magic) { @@ -61,11 +91,15 @@ @implementation CDMachOFile struct { unsigned int uses64BitABI:1; } _flags; + + struct dyld_cache_header cacheHeader; + struct dyld_cache_mapping_info *cacheMappingInfo; + struct dyld_cache_image_info *cacheImageInfo; } -- (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState; +- (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(NSUInteger)aSize filename:(NSString *)aFilename searchPathState:(CDSearchPathState *)aSearchPathState isCache:(BOOL)aIsCache; { - if ((self = [super initWithData:someData archOffset:anOffset archSize:aSize filename:aFilename searchPathState:aSearchPathState])) { + if ((self = [super initWithData:someData archOffset:anOffset archSize:aSize filename:aFilename searchPathState:aSearchPathState isCache:aIsCache])) { byteOrder = CDByteOrder_LittleEndian; loadCommands = nil; dylibLoadCommands = nil; @@ -79,7 +113,27 @@ - (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:( dyldEnvironment = nil; reExportedDylibs = nil; - CDDataCursor *cursor = [[CDDataCursor alloc] initWithData:someData offset:self.archOffset]; + if(self.isCache) { + if(![self _loadCacheInfo]) + return nil; + } + + NSUInteger magicOffset; + + if(self.isCache) { + NSUInteger address = [self _cacheAddressForImage:aFilename]; + if(address == 0) + { + NSLog(@"Could not find %@ in cache!", aFilename); + return nil; + } + + magicOffset = [self _cacheOffsetForAddress:address]; + } else { + magicOffset = self.archOffset; + } + + CDDataCursor *cursor = [[CDDataCursor alloc] initWithData:someData offset:magicOffset]; header.magic = [cursor readBigInt32]; if (header.magic == MH_MAGIC || header.magic == MH_MAGIC_64) { byteOrder = CDByteOrder_BigEndian; @@ -115,6 +169,8 @@ - (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:( header.cputype &= ~CPU_ARCH_MASK; NSUInteger headerOffset = _flags.uses64BitABI ? sizeof(struct mach_header_64) : sizeof(struct mach_header); + if(self.isCache) + headerOffset += magicOffset; CDMachOFileDataCursor *fileCursor = [[CDMachOFileDataCursor alloc] initWithFile:self offset:headerOffset]; [self _readLoadCommands:fileCursor count:header.ncmds]; } @@ -122,6 +178,65 @@ - (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:( return self; } +- (BOOL) _loadCacheInfo +{ + CDDataCursor *cursor = [[CDDataCursor alloc] initWithData:[self data] offset:0]; + [cursor readBytesOfLength:sizeof(cacheHeader.magic) intoBuffer:cacheHeader.magic]; + if(memcmp(cacheHeader.magic, "dyld_v1 armv7", sizeof("dyld_v1 armv7")) != 0) + return NO; + + cacheHeader.mappingOffset = [cursor readLittleInt32]; + cacheHeader.mappingCount = [cursor readLittleInt32]; + cacheHeader.imagesOffset = [cursor readLittleInt32]; + cacheHeader.imagesCount = [cursor readLittleInt32]; + cacheHeader.dyldBaseAddress = [cursor readLittleInt64]; + cacheHeader.codeSignatureOffset = [cursor readLittleInt64]; + cacheHeader.codeSignatureSize = [cursor readLittleInt64]; + + CDDataCursor *mappingCursor = [[CDDataCursor alloc] initWithData:[self data] offset:cacheHeader.mappingOffset]; + cacheMappingInfo = (struct dyld_cache_mapping_info*) malloc(sizeof(struct dyld_cache_mapping_info) * cacheHeader.mappingCount); + for(int i = 0; i < cacheHeader.mappingCount; ++i) { + cacheMappingInfo[i].address = [mappingCursor readLittleInt64]; + cacheMappingInfo[i].size = [mappingCursor readLittleInt64]; + cacheMappingInfo[i].fileOffset = [mappingCursor readLittleInt64]; + cacheMappingInfo[i].maxProt = [mappingCursor readLittleInt32]; + cacheMappingInfo[i].initProt = [mappingCursor readLittleInt32]; + } + + CDDataCursor *imageCursor = [[CDDataCursor alloc] initWithData:[self data] offset:cacheHeader.imagesOffset]; + cacheImageInfo = (struct dyld_cache_image_info*) malloc(sizeof(struct dyld_cache_image_info) * cacheHeader.imagesCount); + for(int i = 0; i < cacheHeader.imagesCount; ++i) { + cacheImageInfo[i].address = [imageCursor readLittleInt64]; + cacheImageInfo[i].modTime = [imageCursor readLittleInt64]; + cacheImageInfo[i].inode = [imageCursor readLittleInt64]; + cacheImageInfo[i].pathFileOffset = [imageCursor readLittleInt32]; + cacheImageInfo[i].pad = [imageCursor readLittleInt32]; + } + + return YES; +} + +- (NSUInteger) _cacheAddressForImage:(NSString *)fileName +{ + for(int i = 0; i < cacheHeader.imagesCount; ++i) { + if(strcmp(((char*)[[self data] bytes]) + cacheImageInfo[i].pathFileOffset, [fileName UTF8String]) == 0) { + return cacheImageInfo[i].address; + } + } + + return 0; +} + +- (NSUInteger) _cacheOffsetForAddress:(NSUInteger)address +{ + for(int i = 0; i < cacheHeader.mappingCount; ++i) { + if(cacheMappingInfo[i].address <= address && address < (cacheMappingInfo[i].address + cacheMappingInfo[i].size)) + return cacheMappingInfo[i].fileOffset + (address - cacheMappingInfo[i].address); + } + + return 0; +} + - (void)_readLoadCommands:(CDMachOFileDataCursor *)cursor count:(uint32_t)count; { NSMutableArray *_loadCommands = [[NSMutableArray alloc] init]; @@ -352,24 +467,32 @@ - (NSString *)stringAtAddress:(NSUInteger)address; if (address == 0) return nil; - CDLCSegment *segment = [self segmentContainingAddress:address]; - if (segment == nil) { - NSLog(@"Error: Cannot find offset for address 0x%08lx in stringAtAddress:", address); - exit(5); - return nil; - } - - if ([segment isProtected]) { - NSData *d2 = [segment decryptedData]; - NSUInteger d2Offset = [segment segmentOffsetForAddress:address]; - if (d2Offset == 0) + NSUInteger anOffset; + + if(self.isCache) { + // dylibs in the dyld shared cache sometimes reference things not technically "mapped in" by the mach-o, so it's counterproductive to try to map an address to a specific segment/section. + anOffset = [self _cacheOffsetForAddress:address]; + } else { + CDLCSegment *segment = [self segmentContainingAddress:address]; + if (segment == nil) { + NSLog(@"Error: Cannot find offset for address 0x%08lx in stringAtAddress:", address); + exit(5); return nil; + } + + if ([segment isProtected]) { + NSData *d2 = [segment decryptedData]; + NSUInteger d2Offset = [segment segmentOffsetForAddress:address]; + if (d2Offset == 0) + return nil; + + ptr = [d2 bytes] + d2Offset; + return [[NSString alloc] initWithBytes:ptr length:strlen(ptr) encoding:NSASCIIStringEncoding]; + } - ptr = [d2 bytes] + d2Offset; - return [[NSString alloc] initWithBytes:ptr length:strlen(ptr) encoding:NSASCIIStringEncoding]; + anOffset = self.archOffset + [self dataOffsetForAddress:address]; } - NSUInteger anOffset = self.archOffset + [self dataOffsetForAddress:address]; if (anOffset == 0) return nil; @@ -384,13 +507,17 @@ - (NSData *)machOData; } - (NSUInteger)dataOffsetForAddress:(NSUInteger)address; -{ +{ if (address == 0) return 0; + // dylibs in the dyld shared cache sometimes reference things not technically "mapped in" by the mach-o, so it's counterproductive to try to map an address to a specific segment/section. + if(self.isCache) + return [self _cacheOffsetForAddress:address]; + CDLCSegment *segment = [self segmentContainingAddress:address]; if (segment == nil) { - NSLog(@"Error: Cannot find offset for address 0x%08lx in dataOffsetForAddress:", address); + NSLog(@"Error: Cannot find offset for address 0x%08lx in dataOffsetForAddress: %@", address, [NSThread callStackSymbols]); exit(5); } @@ -617,4 +744,13 @@ - (Class)processorClass; return [CDObjectiveC1Processor class]; } +- (void) dealloc +{ + if(cacheImageInfo) + free(cacheImageInfo); + + if(cacheMappingInfo) + free(cacheMappingInfo); +} + @end diff --git a/class-dump.m b/class-dump.m index 174fcf65..e22768e5 100644 --- a/class-dump.m +++ b/class-dump.m @@ -67,6 +67,7 @@ int main(int argc, char *argv[]) CDArch targetArch; BOOL hasSpecifiedArch = NO; NSString *outputPath; + NSString *cacheName = nil; int ch; BOOL errorFlag = NO; @@ -89,6 +90,7 @@ int main(int argc, char *argv[]) { "sdk-ios", required_argument, NULL, CD_OPT_SDK_IOS }, { "sdk-mac", required_argument, NULL, CD_OPT_SDK_MAC }, { "sdk-root", required_argument, NULL, CD_OPT_SDK_ROOT }, + { "cache", required_argument, NULL, 'c' }, { NULL, 0, NULL, 0 }, }; @@ -99,7 +101,7 @@ int main(int argc, char *argv[]) CDClassDump *classDump = [[CDClassDump alloc] init]; - while ( (ch = getopt_long(argc, argv, "aAC:f:HIo:rRsSt", longopts, NULL)) != -1) { + while ( (ch = getopt_long(argc, argv, "aAC:f:HIo:rRsStc:", longopts, NULL)) != -1) { switch (ch) { case CD_OPT_ARCH: { NSString *name = [NSString stringWithUTF8String:optarg]; @@ -171,6 +173,11 @@ int main(int argc, char *argv[]) break; } + case 'c': { + cacheName = [NSString stringWithUTF8String:optarg]; + break; + } + case 'f': { searchString = [NSString stringWithUTF8String:optarg]; break; @@ -262,7 +269,14 @@ int main(int argc, char *argv[]) } classDump.searchPathState.executablePath = [executablePath stringByDeletingLastPathComponent]; - CDFile *file = [CDFile fileWithData:data filename:executablePath searchPathState:classDump.searchPathState]; + CDFile *file; + + if(cacheName) { + file = [CDFile fileWithData:data archOffset:0 archSize:[data length] filename:cacheName searchPathState:classDump.searchPathState isCache:YES]; + } else { + file = [CDFile fileWithData:data filename:executablePath searchPathState:classDump.searchPathState]; + } + if (file == nil) { fprintf(stderr, "class-dump: Input file (%s) is neither a Mach-O file nor a fat archive.\n", [executablePath UTF8String]); exit(1);