Mach-O解析Header , LoadCommand, Section

\color{red}{轉(zhuǎn)載請(qǐng)注明出處}

此文章是直接手動(dòng)解析Mach-O , 可以根據(jù)Mach-O解析出類名列表, 方法列表, 符號(hào)表等你需要的信息

參考文章:

個(gè)人博客Mach-O格式分析
官網(wǎng)xnu文件
MachOView源碼

貼一個(gè)特別經(jīng)典的圖解

image.png

廢話不多說, 直接上代碼

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];

        //用堆來保存整個(gè)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 數(shù)量:%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 里面包含的都是一個(gè)一個(gè)的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的信息, 位置, 長度, 偏移, 名字等, 具體的內(nèi)容下面的類來解析

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) {
        // 必須先轉(zhuǎn)成void* 或者uint8_t來計(jì)算, 要不然算的不對(duì)
        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是因?yàn)開_objc_classname__TEXT這個(gè)classname后面沒有\(zhòng)0, 所以會(huì)把下一句__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 具體的內(nèi)容

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

結(jié)果:

image.png
打印name 出問題的句子 __objc_classname__TEXT , 看圖是因?yàn)閯e的都有00來結(jié)尾, 結(jié)果__objc_classname沒有00 , 也就是沒有\(zhòng)0, 說以我用%s輸出就連在一起了
image.png

解析Mach-O的方式就在上面的代碼中, 已經(jīng)調(diào)試過了 , 全拷貝是可以直接運(yùn)行的, 想解析其他的也可以按照上面的格式 ,再參考我提到的文章, 就可以順利解析Mach-O , 符號(hào)表, 之類的都可以通過以上方式解析

\color{red}{轉(zhuǎn)載請(qǐng)注明出處}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乙帮,一起剝皮案震驚了整個(gè)濱河市碎节,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌圾浅,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件憾朴,死亡現(xiàn)場(chǎng)離奇詭異狸捕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)众雷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門灸拍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人报腔,你說我怎么就攤上這事株搔。” “怎么了纯蛾?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵纤房,是天一觀的道長。 經(jīng)常有香客問我翻诉,道長炮姨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任碰煌,我火速辦了婚禮舒岸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芦圾。我一直安慰自己蛾派,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著洪乍,像睡著了一般眯杏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上壳澳,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天岂贩,我揣著相機(jī)與錄音,去河邊找鬼巷波。 笑死萎津,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的抹镊。 我是一名探鬼主播锉屈,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼髓考!你這毒婦竟也來了部念?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤氨菇,失蹤者是張志新(化名)和其女友劉穎儡炼,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體查蓉,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乌询,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了豌研。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妹田。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鹃共,靈堂內(nèi)的尸體忽然破棺而出鬼佣,到底是詐尸還是另有隱情,我是刑警寧澤霜浴,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布晶衷,位于F島的核電站,受9級(jí)特大地震影響阴孟,放射性物質(zhì)發(fā)生泄漏晌纫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一永丝、第九天 我趴在偏房一處隱蔽的房頂上張望锹漱。 院中可真熱鬧,春花似錦慕嚷、人聲如沸哥牍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽砂心。三九已至懈词,卻和暖如春辩诞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背译暂。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撩炊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓拧咳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骆膝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祭衩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355