Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dyld cache support #7

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Source/CDFatArch.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Source/CDFatFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions Source/CDFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 7 additions & 4 deletions Source/CDFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,23 @@ @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;

if (anOffset == 0)
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;
}

Expand All @@ -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.
Expand All @@ -106,6 +107,7 @@ - (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(
archOffset = anOffset;
archSize = aSize;
searchPathState = aSearchPathState;
isCache = aIsCache;
}

return self;
Expand All @@ -118,6 +120,7 @@ - (id)initWithData:(NSData *)someData archOffset:(NSUInteger)anOffset archSize:(
@synthesize archOffset;
@synthesize archSize;
@synthesize searchPathState;
@synthesize isCache;

- (BOOL)bestMatchForLocalArch:(CDArch *)archPtr;
{
Expand Down
6 changes: 5 additions & 1 deletion Source/CDMachOFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
174 changes: 155 additions & 19 deletions Source/CDMachOFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -115,13 +169,74 @@ - (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];
}

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];
Expand Down Expand Up @@ -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;

Expand All @@ -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);
}

Expand Down Expand Up @@ -617,4 +744,13 @@ - (Class)processorClass;
return [CDObjectiveC1Processor class];
}

- (void) dealloc
{
if(cacheImageInfo)
free(cacheImageInfo);

if(cacheMappingInfo)
free(cacheMappingInfo);
}

@end
Loading