学习版本:class-dump-3.5.dmg
Build
上手打开工程先build一次(Xcode10)
报错缺少openssl的头文件
命令行指令 which openssl 打印 LibreSSL 2.6.5,说明苹果已经将openssl替换成了libressl
使用 brew install openssl安装openssl
安装完毕后会有这样的提示,应该是和现有有冲突,暂不做处理。
openssl@1.1 is keg-only, which means it was not symlinked into /usr/local,
because openssl/libressl is provided by macOS so don't link an incompatible version.
If you need to have openssl@1.1 first in your PATH run:
echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> ~/.bash_profile
For compilers to find openssl@1.1 you may need to set:
export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"
打开/usr/local/opt/openssl@1.1/1.1.1d/include,把里面的openssl拷贝一份到/Applications/Xcode.app/Contents/DeveloperPlatforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/
/usr/local/opt/openssl@1.1/1.1.1d/lib也拷贝一份放到对应目录下
再次build,success
直接从class-dump.m的main函数入手
处理参数的代码略过
从读取文件入手
CDFile *file = [CDFile fileWithContentsOfFile:executablePath searchPathState:classDump.searchPathState];
CDFile是基本的文件单位,子类有CDFatFiIe、CDMachOFile,对应fat文件(胖二进制文件,可以看作是多个mach-o文件的聚合体)、mach-o文件。
关注CDFile静态方法
+ (id)fileWithContentsOfFile:(NSString *)filename searchPathState:(CDSearchPathState *)searchPathState;
核心逻辑是分别调用两个子类的initWithData:filename:searchPathState:方法。
先看CDFatFile中的initWithData:filename:searchPathState:方法
- (id)initWithData:(NSData *)data filename:(NSString *)filename searchPathState:(CDSearchPathState *)searchPathState;
{
if ((self = [super initWithData:data filename:filename searchPathState:searchPathState])) {
CDDataCursor *cursor = [[CDDataCursor alloc] initWithData:data];
struct fat_header header;
header.magic = [cursor readBigInt32];
//NSLog(@"(testing fat) magic: 0x%x", header.magic);
if (header.magic != FAT_MAGIC) {
return nil;
}
_arches = [[NSMutableArray alloc] init];
header.nfat_arch = [cursor readBigInt32];
//NSLog(@"nfat_arch: %u", header.nfat_arch);
for (NSUInteger index = 0; index < header.nfat_arch; index++) {
CDFatArch *arch = [[CDFatArch alloc] initWithDataCursor:cursor];
arch.fatFile = self;
[_arches addObject:arch];
}
}
return self;
}
CDDataCursor很好理解,就是一个可以读取定长数据的指针。而fat_header是系统定义的fat文件头结构。
第一步读取魔数magic。加载器通过这个数值来判断当前文件是什么文件,主要区分32位和64位
#define FAT_MAGIC 0xcafebabe
#define FAT_MAGIC_64 0xcafebabf
不过能看到class-dump并没有处理FAT_MAGIC_64的情况(return nil;意味着会走到mach-o文件的逻辑),个人理解fat文件的魔数并不重要,不能作为加载器选择执行文件的参考。
同时这里有个点,如果使用machoview打开fat文件,一般会看到魔数的值为FAT_CIGAM,意味着魔数实际是以小端模式的形式存储的,而class-dump在读取时使用OSReadBigIn