此文章是直接手动解析Mach-O , 可以根据Mach-O解析出类名列表, 方法列表, 符号表等你需要的信息
参考文章:
个人博客Mach-O格式分析
官网xnu文件
MachOView源码
贴一个特别经典的图解
废话不多说, 直接上代码
main.m代码:
#import <Foundation/Foundation.h>
#import <mach-o/loader.h>
#import "Mach_Header_Parser.h"
#import "Load_Command_Parser.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString * machoPath =@"/Users/dinghao/Documents/Demo/MachO/MachO/MachO1";
NSData * data = [NSData dataWithContentsOfFile:machoPath];
//用堆来保存整个MachO
void * file_buf = malloc(data.length);
[data getBytes:file_buf length:data.length];
//把macho指针交给Mach_Header_Parser解析类,去解析Header
struct mach_header_64 *mh = [Mach_Header_Parser mach_header_parserMachO:file_buf];
//把macho指针以及header信息交给Load_Command_Parser解析类,去解析Load_command
[Load_Command_Parser load_Command_ParserMacho:file_buf mach_header:mh];
free(file_buf);
}
return 0;
}
Mach_Header_Parser 用来解析Mach-O的header部分
Mach_Header_Parser.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Mach_Header_Parser : NSObject
+ (struct mach_header_64 *)mach_header_parserMachO:(void*)data;
@end
Mach_Header_Parser.m
#import "Mach_Header_Parser.h"
#import <mach-o/loader.h>
@implementation Mach_Header_Parser
+ (struct mach_header_64 *)mach_header_parserMachO:(void*)data
{
struct mach_header_64 *mh = (struct mach_header_64*)data;
uint32_t magic = mh->magic;
cpu_type_t cputype = mh->cputype;
uint32_t filetype = mh->filetype;
uint32_t ncmds = mh->ncmds;
uint32_t sizeofcmds = mh->sizeofcmds;
NSLog(@"MAGIC类型:%@:",[self getMAGIC:magic] );
NSLog(@"cpu类型:%@:",[self getCPUType:cputype] );
NSLog(@"Mach-O类型:%@",[self getFileType:filetype]);
NSLog(@"Load Commands 数量:%d",ncmds);
NSLog(@"Load Commands 大小:%d",sizeofcmds);
return mh;
}
+ (NSString *)getMAGIC:(uint32_t)magic
{
switch (magic) {
case MH_MAGIC:
return @"MH_MAGIC";
case MH_CIGAM:
return @"MH_CIGAM";
break;
case MH_MAGIC_64:
return @"MH_MAGIC_64";
break;
case MH_CIGAM_64:
return @"MH_CIGAM_64";
default:
return @"magic error";
}
}
+ (NSString *)getCPUType:(cpu_type_t)cputype
{
switch (cputype)
{
case CPU_TYPE_I386: return @"CPU_TYPE_I386";
case CPU_TYPE_POWERPC: return @"CPU_TYPE_POWERPC" ;
case CPU_TYPE_X86_64: return @"CPU_TYPE_X86_64" ;
case CPU_TYPE_POWERPC64: return @"CPU_TYPE_POWERPC64" ;
case CPU_TYPE_ARM: return @"CPU_TYPE_ARM" ;
case CPU_TYPE_ARM64: return @"CPU_TYPE_ARM64" ;
default: return @"cputype error";
}
}
+ (NSString *)getFileType:(uint32_t)filetype
{
switch (filetype) {
case MH_OBJECT : return @"MH_OBJECT" ;
case MH_EXECUTE : return @"MH_EXECUTE" ;
case MH_FVMLIB : return @"MH_FVMLIB" ;
case MH_CORE : return @"MH_CORE" ;
case MH_PRELOAD : return @"MH_PRELOAD" ;
case MH_DYLIB : return @"MH_DYLIB" ;
case MH_DYLINKER : return @"MH_DYLINKER" ;
case MH_BUNDLE : return @"MH_BUNDLE" ;
case MH_DYLIB_STUB : return @"MH_DYLIB_STUB" ;
case MH_DSYM : return @"MH_DSYM" ;
case MH_KEXT_BUNDLE : return @"MH_KEXT_BUNDLE" ;
default: return @"filetype error";
}
}
Load_Command_Parser 用来解析Mach-O的Load Command部分
Load_Command_Parser.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Load_Command_Parser : NSObject
+ (void)load_Command_ParserMacho:(void*)data mach_header:(struct mach_header_64*)mh;
@end
NS_ASSUME_NONNULL_END
Load_Command_Parser.m
#import "Load_Command_Parser.h"
#import <mach-o/loader.h>
#import "Section_Header_Parser.h"
@implementation Load_Command_Parser
+ (void)load_Command_ParserMacho:(void*)data mach_header:(struct mach_header_64*)mh
{
// Load Commands 里面包含的都是一个一个的SEGMENT
struct load_command *load_commands =(struct load_command *)(data + sizeof(struct mach_header_64));
struct segment_command_64 *currentSegment =(struct segment_command_64 *)load_commands;
for (int i = 0; i < mh->ncmds; i++) {
uint32_t currentSegment_Size = currentSegment->cmdsize;
struct segment_command_64* nextCmd = (struct segment_command_64 *)(((void*)currentSegment)+currentSegment_Size);
struct segment_command_64 * segment_com = (struct segment_command_64 *)currentSegment;
// 这里我们只看LC_SEGMENT_64(_TEXT) 和 LC_SEGMENT_64(DATA)
switch (segment_com->cmd) {
case LC_SEGMENT_64:
{
NSLog(@"cmd = %@" ,[self commandName:segment_com->cmd] );
NSLog(@"cmdsize = %x" , segment_com->cmdsize);
NSLog(@"segName = %s",segment_com->segname);
if (strcmp(segment_com->segname , "__TEXT") == 0) {
//将MachO指针和Section Header信息交给Section_Header_Parser去解析
[Section_Header_Parser section_Header_Parser:segment_com MachO:data];
}
else if (strcmp(segment_com->segname , "__DATA") == 0)
{
//处理方法同上
}
}
break;
default:
break;
}
currentSegment = nextCmd;
}
}
+ (NSString *)commandName:(uint32_t)cmd;
{
switch (cmd) {
case LC_SEGMENT: return @"LC_SEGMENT";
case LC_SYMTAB: return @"LC_SYMTAB";
case LC_SYMSEG: return @"LC_SYMSEG";
case LC_THREAD: return @"LC_THREAD";
case LC_UNIXTHREAD: return @"LC_UNIXTHREAD";
case LC_LOADFVMLIB: return @"LC_LOADFVMLIB";
case LC_IDFVMLIB: return @"LC_IDFVMLIB";
case LC_IDENT: return @"LC_IDENT";
case LC_FVMFILE: return @"LC_FVMFILE";
case LC_PREPAGE: return @"LC_PREPAGE";
case LC_DYSYMTAB: return @"LC_DYSYMTAB";
case LC_LOAD_DYLIB: return @"LC_LOAD_DYLIB";
case LC_ID_DYLIB: return @"LC_ID_DYLIB";
case LC_LOAD_DYLINKER: return @"LC_LOAD_DYLINKER";
case LC_ID_DYLINKER: return @"LC_ID_DYLINKER";
case LC_PREBOUND_DYLIB: return @"LC_PREBOUND_DYLIB";
case LC_ROUTINES: return @"LC_ROUTINES";
case LC_SUB_FRAMEWORK: return @"LC_SUB_FRAMEWORK";
case LC_SUB_UMBRELLA: return @"LC_SUB_UMBRELLA";
case LC_SUB_CLIENT: return @"LC_SUB_CLIENT";
case LC_SUB_LIBRARY: return @"LC_SUB_LIBRARY";
case LC_TWOLEVEL_HINTS: return @"LC_TWOLEVEL_HINTS";
case LC_PREBIND_CKSUM: return @"LC_PREBIND_CKSUM";
case LC_LOAD_WEAK_DYLIB: return @"LC_LOAD_WEAK_DYLIB";
case LC_SEGMENT_64: return @"LC_SEGMENT_64";
case LC_ROUTINES_64: return @"LC_ROUTINES_64";
case LC_UUID: return @"LC_UUID";
case LC_RPATH: return @"LC_RPATH";
case LC_CODE_SIGNATURE: return @"LC_CODE_SIGNATURE";
case LC_SEGMENT_SPLIT_INFO: return @"LC_SEGMENT_SPLIT_INFO";
case LC_REEXPORT_DYLIB: return @"LC_REEXPORT_DYLIB";
case LC_LAZY_LOAD_DYLIB: return @"LC_LAZY_LOAD_DYLIB";
case LC_ENCRYPTION_INFO: return @"LC_ENCRYPTION_INFO";
case LC_DYLD_INFO: return @"LC_DYLD_INFO";
case LC_DYLD_INFO_ONLY: return @"LC_DYLD_INFO_ONLY";
case LC_LOAD_UPWARD_DYLIB: return @"LC_LOAD_UPWARD_DYLIB";
case LC_VERSION_MIN_MACOSX: return @"LC_VERSION_MIN_MACOSX";
case LC_VERSION_MIN_IPHONEOS: return @"LC_VERSION_MIN_IPHONEOS";
case LC_FUNCTION_STARTS: return @"LC_FUNCTION_STARTS";
case LC_DYLD_ENVIRONMENT: return @"LC_DYLD_ENVIRONMENT";
case LC_LINKER_OPTION: return @"LC_LINKER_OPTION";
case LC_LINKER_OPTIMIZATION_HINT: return @"LC_LINKER_OPTIMIZATION_HINT";
case LC_VERSION_MIN_TVOS: return @"LC_VERSION_MIN_TVOS";
case LC_VERSION_MIN_WATCHOS: return @"LC_VERSION_MIN_WATCHOS";
case LC_NOTE: return @"LC_NOTE";
case LC_BUILD_VERSION: return @"LC_BUILD_VERSION";
default:
break;
}
return [NSString stringWithFormat:@"0x%08x", cmd];
}
@end
Section_Header_Parser 用来解析Load Commmand中的section的信息, 位置, 长度, 偏移, 名字等, 具体的内容下面的类来解析
Section_Header_Parser.h
#import <Foundation/Foundation.h>
#import <mach-o/loader.h>
NS_ASSUME_NONNULL_BEGIN
@interface Section_Header_Parser : NSObject
+ (void)section_Header_Parser:(struct segment_command_64 *)lc_segment MachO:(void*)data;
@end
NS_ASSUME_NONNULL_END
Section_Header_Parser.m
#import "Section_Header_Parser.h"
#import "Section_Parser.h"
@implementation Section_Header_Parser
+ (void)section_Header_Parser:(struct segment_command_64 *)lc_segment MachO:(void*)data
{
struct segment_command_64 * segment_com = (struct segment_command_64 *)lc_segment;
if (segment_com->nsects >0) {
// 必须先转成void* 或者uint8_t来计算, 要不然算的不对
struct section_64 * section_header = (struct section_64 *)((void *)lc_segment+sizeof(struct segment_command_64));
for (int i = 0; i< segment_com->nsects; i++) {
uint32_t section_size = sizeof(struct section_64);
struct section_64 * next_section_header = (struct section_64 *)((void*)section_header+section_size);
NSLog(@"Section ---- name = %s",section_header->sectname);
// 用strncmp是因为__objc_classname__TEXT这个classname后面没有\0, 所以会把下一句__TEXT给带上, 可以限制下输出长度16也行
// if (strncmp(section_header->sectname, "__objc_methname", strlen("__objc_methname"))==0) {
// [Section_Parser section_parser:section_header MachO:data];
// }
//
// 获取className , 也可以自己写获取其他的信息
if (strncmp(section_header->sectname, "__objc_classname", strlen("__objc_classname"))==0) {
[Section_Parser section_parser:section_header MachO:data];
}
section_header = next_section_header;
}
}
}
@end
Section_Parser 用来解析section 具体的内容
Section_Parser.h
#import <Foundation/Foundation.h>
#import <mach-o/loader.h>
NS_ASSUME_NONNULL_BEGIN
@interface Section_Parser : NSObject
+ (void)section_parser:(struct section_64 *)section_header MachO:(void*)data;
@end
NS_ASSUME_NONNULL_END
Section_Parser.m
#import "Section_Parser.h"
@implementation Section_Parser
+ (void)section_parser:(struct section_64 *)section_header MachO:(void*)data
{
uint32_t offset = section_header->offset;
int size = (int)section_header->size;
NSData *dataClassName = [NSData dataWithBytesNoCopy:data+offset length:size freeWhenDone:NO];
NSString * strClassName = [[NSString alloc]initWithData:dataClassName encoding:NSUTF8StringEncoding];
NSLog(@"strClassName = %@",strClassName);
NSArray * arrayClassName = [strClassName componentsSeparatedByString:@"\00"];
[arrayClassName enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![obj isEqualToString:@""]) {
NSLog(@"list [%lu] = %@ ",(unsigned long)idx,obj);
}
}];
}
@end