Mach-o 分析

組成

Mach-O通常有三部分組成
頭部 (Header): Mach-O文件的架構(gòu) 比如Mac的 PPC, PPC64, IA-32, x86-64,ios的arm系列.
加載命令(Load commands):
.
原始段數(shù)據(jù)(Raw segment data)*:可以擁有多個(gè)段(segment)顾翼,每個(gè)段可以擁有零個(gè)或多個(gè)區(qū)域(section)柠衅。每一個(gè)段(segment)都擁有一段虛擬地址映射到進(jìn)程的地址空間。

官方給的圖如下:


mach-o

準(zhǔn)備工作

我們創(chuàng)建一個(gè)工程,mach-o

#include <stdio.h>
int main(int argc, const char * argv[]) {
        printf("Hello, World!\n");
    return 0;
}

打開(kāi)終端cd 到該工程的main所在目錄執(zhí)行下列命令gcc main.m -g -o test.out 會(huì)生成 test.out 文件赵誓。我們就分析這個(gè)文件

Mach-O 分析

通常一個(gè)iOS App應(yīng)用會(huì)安裝在/var/mobile/Applications,系統(tǒng)的原生App會(huì)安裝在/Applications目錄下柿赊,大部分情況下俩功,xxx.app/xxx文件并不是Mach-O格式文件,由于現(xiàn)在需要支持不同CPU架構(gòu)的iOS設(shè)備碰声,所以我們編譯打包出來(lái)的執(zhí)行文件是一個(gè)Universal Binary格式文件(通用二進(jìn)制文件诡蜓,也稱(chēng)胖二進(jìn)制文件),實(shí)際上Universal Binary只不過(guò)將支持不同架構(gòu)的Mach-O打包在一起胰挑,再在文件起始位置加上Fat Header來(lái)說(shuō)明所包含的Mach-O文件支持的架構(gòu)和偏移地址信息蔓罚。

Universal Binary

我們可以通過(guò)otool 工具查看頭
otool - f test (上面的test.out 通過(guò)該命令是沒(méi)有輸出結(jié)果的,因?yàn)槭莕o fat文件瞻颂,這里的test必須是 多個(gè)結(jié)構(gòu)體在一起的才可以)

Fat headers
fat_magic 0xcafebabe
nfat_arch 2
architecture 0
    cputype 12
    cpusubtype 9
    capabilities 0x0
    offset 16384
    size 37933760
    align 2^14 (16384)
architecture 1
    cputype 16777228
    cpusubtype 0
    capabilities 0x0
    offset 37961728
    size 43561328
    align 2^14 (16384)

胖二進(jìn)制文件定義在 /usr/include/mach-o/fat.h豺谈,

#define FAT_MAGIC   0xcafebabe
#define FAT_CIGAM   0xbebafeca  /* NXSwapLong(FAT_MAGIC) */

struct fat_header {
    uint32_t    magic;      /* FAT_MAGIC or FAT_MAGIC_64 */
    uint32_t    nfat_arch;  /* number of structs that follow */
};

struct fat_arch {
    cpu_type_t  cputype;    /* cpu specifier (int) */
    cpu_subtype_t   cpusubtype; /* machine specifier (int) */
    uint32_t    offset;     /* file offset to this object file */
    uint32_t    size;       /* size of this object file */
    uint32_t    align;      /* alignment as a power of 2 */
};

/*
 * The support for the 64-bit fat file format described here is a work in
 * progress and not yet fully supported in all the Apple Developer Tools.
 *
 * When a slice is greater than 4mb or an offset to a slice is greater than 4mb
 * then the 64-bit fat file format is used.
 */
#define FAT_MAGIC_64    0xcafebabf
#define FAT_CIGAM_64    0xbfbafeca  /* NXSwapLong(FAT_MAGIC_64) */

struct fat_arch_64 {
    cpu_type_t  cputype;    /* cpu specifier (int) */
    cpu_subtype_t   cpusubtype; /* machine specifier (int) */
    uint64_t    offset;     /* file offset to this object file */
    uint64_t    size;       /* size of this object file */
    uint32_t    align;      /* alignment as a power of 2 */
    uint32_t    reserved;   /* reserved */
};
  • magic字段就是我們常說(shuō)的魔數(shù)(就是文件結(jié)構(gòu)),加載器通過(guò)這個(gè)魔數(shù)值來(lái)判斷這是什么樣的文件蘸朋。在32 位和64 位 上magic 不一樣核无。
    32 位是0xcafebabe ,64位是0xcafebabf

  • nfat_arch字段是指當(dāng)前的胖二進(jìn)制文件包含了多少個(gè)不同架構(gòu)的Mach-O文件藕坯;
    fat_header后會(huì)跟著fat_arch团南,有多少個(gè)不同架構(gòu)的Mach-O文件,就有多少個(gè)fat_arch炼彪,用于說(shuō)明對(duì)應(yīng)Mach-O文件大小吐根、支持的CPU架構(gòu)、偏移地址等辐马;

mach-o header

mach-o 的頭定義在在 /usr/include/mach-o/loader.h拷橘,

/*
 * The 32-bit mach header appears at the very beginning of the object file for
 * 32-bit architectures.
 */
struct mach_header {
    uint32_t    magic;      /* mach magic number identifier */
    cpu_type_t  cputype;    /* cpu specifier */
    cpu_subtype_t   cpusubtype; /* machine specifier */
    uint32_t    filetype;   /* type of file */
    uint32_t    ncmds;      /* number of load commands */
    uint32_t    sizeofcmds; /* the size of all the load commands */
    uint32_t    flags;      /* flags */
};

/* Constant for the magic field of the mach_header (32-bit architectures) */
#define MH_MAGIC    0xfeedface  /* the mach magic number */
#define MH_CIGAM    0xcefaedfe  /* NXSwapInt(MH_MAGIC) */

/*
 * The 64-bit mach header appears at the very beginning of object files for
 * 64-bit architectures.
 */
struct mach_header_64 {
    uint32_t    magic;      /* mach magic number identifier */
    cpu_type_t  cputype;    /* cpu specifier */
    cpu_subtype_t   cpusubtype; /* machine specifier */
    uint32_t    filetype;   /* type of file */
    uint32_t    ncmds;      /* number of load commands */
    uint32_t    sizeofcmds; /* the size of all the load commands */
    uint32_t    flags;      /* flags */
    uint32_t    reserved;   /* reserved */
};

/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */

我們可以通過(guò)otool -h test.out查看 mach-o header信息

  magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x80           2    15       1280 0x00200085

分析頭信息

1.magic,可以看到文件中的內(nèi)容最開(kāi)始部分喜爷,是以 cafe babe開(kāi)頭的對(duì)于一個(gè) 二進(jìn)制文件 來(lái)講冗疮,每個(gè)類(lèi)型都可以在文件最初幾個(gè)字節(jié)來(lái)標(biāo)識(shí)出來(lái),即“魔數(shù)”檩帐。不同類(lèi)型的 二進(jìn)制文件术幔,都有自己獨(dú)特的"魔數(shù)"。OS X上湃密,可執(zhí)行文件的標(biāo)識(shí)有這樣幾個(gè)魔數(shù)(不同的魔數(shù)代表不同的可執(zhí)行文件類(lèi)型)是mach-o文件的魔數(shù)诅挑,0xfeedface代表的是32位四敞,0xfeedfacf代表64位,cafebabe是跨處理器架構(gòu)的通用格式拔妥。
2.cputype和cupsubtype代表的是cpu的類(lèi)型和其子類(lèi)型忿危。
3.接著是filetype,2没龙,代表可執(zhí)行的文件

 * Constants for the filetype field of the mach_header
 */
#define MH_OBJECT   0x1     /* relocatable object file */
#define MH_EXECUTE  0x2     /* demand paged executable file */
#define MH_FVMLIB   0x3     /* fixed VM shared library file */
#define MH_CORE     0x4     /* core file */
#define MH_PRELOAD  0x5     /* preloaded executable file */
#define MH_DYLIB    0x6     /* dynamically bound shared library */
#define MH_DYLINKER 0x7     /* dynamic link editor */
#define MH_BUNDLE   0x8     /* dynamically bound bundle file */
#define MH_DYLIB_STUB   0x9     /* shared library stub for static */
                    /*  linking only, no section contents */
#define MH_DSYM     0xa     /* companion file with only debug */
                    /*  sections */
#define MH_KEXT_BUNDLE  0xb     /* x86_64 kexts */

4.ncmds 指的是加載命令(load commands)的數(shù)量铺厨,例子中一共15個(gè),編號(hào)0-14
5.sizeofcmds 表示load commands 的bytes兜畸, load commands區(qū)域是緊接著header區(qū)域的努释。

6.最后個(gè)flags,例子中是0x00218085咬摇,可以按文檔分析之。
這個(gè)就是標(biāo)志位煞躬。

#define MH_PIE 0x200000         /* When this bit is set, the OS will  load the main executable at a   random address.  Only used in MH_EXECUTE filetypes. */
#define MH_BINDS_TO_WEAK 0x10000    /* the final linked image uses weak symbols */
#define MH_WEAK_DEFINES 0x8000      /* the final linked image contains external weak symbols */
#define MH_TWOLEVEL 0x80        /* the image is using two-level name space bindings */
#define MH_DYLDLINK 0x4     /* the object file is input for the  dynamic linker and can't be staticly  link edited again */
#define MH_NOUNDEFS 0x1     /* the object file has no undefined references */

這里我們可以借助也UE程序MachOView,MachOView是Mac上查看Mach-O結(jié)構(gòu)的工具查看結(jié)構(gòu)

image.png

Load commands

load commmand直接跟在 header 部分的后面肛鹏,結(jié)構(gòu)定義在文件/usr/include/mach-o/loader.h中。

結(jié)構(gòu)定義如下

/*
 * The load commands directly follow the mach_header.  The total size of all
 * of the commands is given by the sizeofcmds field in the mach_header.  All
 * load commands must have as their first two fields cmd and cmdsize.  The cmd
 * field is filled in with a constant for that command type.  Each command type
 * has a structure specifically for it.  The cmdsize field is the size in bytes
 * of the particular load command structure plus anything that follows it that
 * is a part of the load command (i.e. section structures, strings, etc.).  To
 * advance to the next load command the cmdsize can be added to the offset or
 * pointer of the current load command.  The cmdsize for 32-bit architectures
 * MUST be a multiple of 4 bytes and for 64-bit architectures MUST be a multiple
 * of 8 bytes (these are forever the maximum alignment of any load commands).
 * The padded bytes must be zero.  All tables in the object file must also
 * follow these rules so the file can be memory mapped.  Otherwise the pointers
 * to these tables will not work well or at all on some machines.  With all
 * padding zeroed like objects will compare byte for byte.
 */
struct load_command {
    uint32_t cmd;       /* type of load command */
    uint32_t cmdsize;   /* total size of command in bytes */
};

這里的注釋很清楚

1.load commands 在mach_header 后面恩沛。
2.sizeofcmds 指定了load commands 的大小
3.每個(gè)command 有cmd 和cmdsize 組成在扰。
4.cmd 代表類(lèi)型。每種類(lèi)型都有常量標(biāo)示
5.cmdsize 是大小
6.cmdSize 32位必須是4字節(jié)的倍數(shù)雷客。 64位就是b字節(jié)的倍數(shù)

這些加載命令在Mach-O文件加載解析時(shí)芒珠,被內(nèi)核加載器或者動(dòng)態(tài)鏈接器調(diào)用,指導(dǎo)如何設(shè)置加載對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)段搅裙。
cmd 類(lèi)型很多都在 /usr/include/mach-o/loader.h中可以自己查看皱卓。
具體可以通過(guò)下面命令查看(test文件名)

otool -v -l test.out | open -f

段數(shù)據(jù)(Segments)

Segments包含了很多segment,每一個(gè)segment定義了一些Mach-O文件的數(shù)據(jù)部逮、地址和內(nèi)存保護(hù)屬性娜汁,這些數(shù)據(jù)在動(dòng)態(tài)鏈接器加載程序時(shí)被映射到了虛擬內(nèi)存中。每個(gè)段都有不同的功能兄朋。

一般包含下列功能

  • 1). __PAGEZERO: 空指針陷阱段掐禁,映射到虛擬內(nèi)存空間的第一頁(yè),用于捕捉對(duì)NULL指針的引用颅和;
  • 2). __TEXT: 包含了執(zhí)行代碼以及其他只讀數(shù)據(jù)傅事。 為了讓內(nèi)核將它 直接從可執(zhí)行文件映射到共享內(nèi)存, 靜態(tài)連接器設(shè)置該段的虛擬內(nèi)存權(quán)限為不允許寫(xiě)峡扩。當(dāng)這個(gè)段被映射到內(nèi)存后蹭越,可以被所有進(jìn)程共享。(這主要用在frameworks, bundles和共享庫(kù)等程序中有额,也可以為同一個(gè)可執(zhí)行文件的多個(gè)進(jìn)程拷貝使用)
  • 3). __DATA: 包含了程序數(shù)據(jù)般又,該段可寫(xiě)彼绷;
  • 4). __LINKEDIT: 含有為動(dòng)態(tài)鏈接庫(kù)使用的原始數(shù)據(jù),比如符號(hào)茴迁,字符串寄悯,重定位表?xiàng)l目等等。

一般的段又會(huì)按不同的功能劃分為幾個(gè)區(qū)(section)堕义,即段所有字母大小猜旬,加兩個(gè)下橫線(xiàn)作為前綴,而區(qū)則為小寫(xiě)倦卖,同樣加兩個(gè)下橫線(xiàn)作為前綴

下面列出段中可能包含的section:

__TEXT段:
__text, __cstring, __picsymbol_stub, __symbol_stub, __const, __litera14, __litera18;

__DATA段:
__data, __la_symbol_ptr, __nl_symbol_ptr, __dyld, __const, __mod_init_func, __mod_term_func, __bss, __commom;

__IMPORT段
__jump_table, __pointers;

其中__TEXT段中的__text是實(shí)際上的代碼部分洒擦;__DATA段的__data是實(shí)際的初始數(shù)據(jù)

可以通過(guò)otool –s查看某segment的某個(gè)section。(test是文件名)

otool -s __TEXT __text test.out
或者
otool -t test.out
想查看匯編
otool -t -v test.out

segment_command

接下來(lái)我們看 數(shù)據(jù)段 segment_command

/*
 * The segment load command indicates that a part of this file is to be
 * mapped into the task's address space.  The size of this segment in memory,
 * vmsize, maybe equal to or larger than the amount to map from this file,
 * filesize.  The file is mapped starting at fileoff to the beginning of
 * the segment in memory, vmaddr.  The rest of the memory of the segment,
 * if any, is allocated zero fill on demand.  The segment's maximum virtual
 * memory protection and initial virtual memory protection are specified
 * by the maxprot and initprot fields.  If the segment has sections then the
 * section structures directly follow the segment command and their size is
 * reflected in cmdsize.
 */
struct segment_command { /* for 32-bit architectures */
    uint32_t    cmd;        /* LC_SEGMENT */
    uint32_t    cmdsize;    /* includes sizeof section structs */
    char        segname[16];    /* segment name */
    uint32_t    vmaddr;     /* memory address of this segment */
    uint32_t    vmsize;     /* memory size of this segment */
    uint32_t    fileoff;    /* file offset of this segment */
    uint32_t    filesize;   /* amount to map from the file */
    vm_prot_t   maxprot;    /* maximum VM protection */
    vm_prot_t   initprot;   /* initial VM protection */
    uint32_t    nsects;     /* number of sections in segment */
    uint32_t    flags;      /* flags */
};
/*
 * The 64-bit segment load command indicates that a part of this file is to be
 * mapped into a 64-bit task's address space.  If the 64-bit segment has
 * sections then section_64 structures directly follow the 64-bit segment
 * command and their size is reflected in cmdsize.
 */
struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;        /* LC_SEGMENT_64 */
    uint32_t    cmdsize;    /* includes sizeof section_64 structs */
    char        segname[16];    /* segment name */
    uint64_t    vmaddr;     /* memory address of this segment */
    uint64_t    vmsize;     /* memory size of this segment */
    uint64_t    fileoff;    /* file offset of this segment */
    uint64_t    filesize;   /* amount to map from the file */
    vm_prot_t   maxprot;    /* maximum VM protection */
    vm_prot_t   initprot;   /* initial VM protection */
    uint32_t    nsects;     /* number of sections in segment */
    uint32_t    flags;      /* flags */
};

這里我們看看結(jié)構(gòu)體的每個(gè)成員變量代表什么

1.cmd 32位是個(gè)常量 LC_SEGMENT 64 常量LC_SEGMENT_64
2.cmdsize 該區(qū)域大小
3.segname[16] 名字
4.vmaddr segment 的內(nèi)存地址怕膛。
5.vmsize segment的內(nèi)存大小
6.fileoff 文件偏移量

  1. filesize 在文件中的大小
    8.maxprot 頁(yè)面所需要的最高內(nèi)存保護(hù)(4=r,2=w,1=x)
    9.initprot 頁(yè)面初始的內(nèi)存保護(hù)
    10.nsects sections的多少
    11.flags 指示器熟嫩。標(biāo)記該segment 如何操作
#define SG_HIGHVM   0x1 /* the file contents for this segment is forthe high part of the VM space, the low part  is zero filled (for stacks in core files) */   不足的部分用0 充滿(mǎn)
#define SG_FVMLIB   0x2 /* this segment is the VM that is allocated by a fixed VM library, for overlap checking in    the link editor */ 這部分重新連接vm library
#define SG_NORELOC  0x4 /* this segment has nothing that was relocated in it and nothing relocated to it, that is it maybe safely replaced without relocation*/
#define SG_PROTECTED_VERSION_1  0x8 /* This segment is protected.  If the=segment starts at file offset 0, the
                       first page of the segment is not
                       protected.  All other pages of the
                       segment are protected. */

下面是section 部分

/*
 * A segment is made up of zero or more sections.  Non-MH_OBJECT files have
 * all of their segments with the proper sections in each, and padded to the
 * specified segment alignment when produced by the link editor.  The first
 * segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header
 * and load commands of the object file before its first section.  The zero
 * fill sections are always last in their segment (in all formats).  This
 * allows the zeroed segment padding to be mapped into memory where zero fill
 * sections might be. The gigabyte zero fill sections, those with the section
 * type S_GB_ZEROFILL, can only be in a segment with sections of this type.
 * These segments are then placed after all other segments.
 *
 * The MH_OBJECT format has all of its sections in one segment for
 * compactness.  There is no padding to a specified segment boundary and the
 * mach_header and load commands are not part of the segment.
 *
 * Sections with the same section name, sectname, going into the same segment,
 * segname, are combined by the link editor.  The resulting section is aligned
 * to the maximum alignment of the combined sections and is the new section's
 * alignment.  The combined sections are aligned to their original alignment in
 * the combined section.  Any padded bytes to get the specified alignment are
 * zeroed.
 *
 * The format of the relocation entries referenced by the reloff and nreloc
 * fields of the section structure for mach object files is described in the
 * header file <reloc.h>.
 */
struct section { /* for 32-bit architectures */
    char        sectname[16];   /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint32_t    addr;       /* memory address of this section */
    uint32_t    size;       /* size in bytes of this section */
    uint32_t    offset;     /* file offset of this section */
    uint32_t    align;      /* section alignment (power of 2) */
    uint32_t    reloff;     /* file offset of relocation entries */
    uint32_t    nreloc;     /* number of relocation entries */
    uint32_t    flags;      /* flags (section type and attributes)*/
    uint32_t    reserved1;  /* reserved (for offset or index) */
    uint32_t    reserved2;  /* reserved (for count or sizeof) */
};

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint64_t    addr;       /* memory address of this section */
    uint64_t    size;       /* size in bytes of this section */
    uint32_t    offset;     /* file offset of this section */
    uint32_t    align;      /* section alignment (power of 2) */
    uint32_t    reloff;     /* file offset of relocation entries */
    uint32_t    nreloc;     /* number of relocation entries */
    uint32_t    flags;      /* flags (section type and attributes)*/
    uint32_t    reserved1;  /* reserved (for offset or index) */
    uint32_t    reserved2;  /* reserved (for count or sizeof) */
    uint32_t    reserved3;  /* reserved */
};

結(jié)構(gòu)體定義

1.sectname 第一個(gè)是__text ,就是主程序代碼
2.segname 該section所屬的 segment名,第一個(gè)是__TEXT
3.addr 該section在內(nèi)存的啟始位置
4.size 該section的大小
5.offset 該section的文件偏移
6.align 字節(jié)大小對(duì)齊 褐捻,

  1. reloff 重定位入口的文件偏移掸茅,
    8.nreloc 需要重定位的入口數(shù)量,
    9.flags 包含section的type和attributes

這里還有點(diǎn)東西需要看看

/* The currently known segment names and the section names in those segments */

#define SEG_PAGEZERO    "__PAGEZERO"    /* the pagezero segment which has no */
                    /* protections and catches NULL */
                    /* references for MH_EXECUTE files */


#define SEG_TEXT    "__TEXT"    /* the tradition UNIX text segment */
#define SECT_TEXT   "__text"    /* the real text part of the text */
                    /* section no headers, and no padding */
#define SECT_FVMLIB_INIT0 "__fvmlib_init0"  /* the fvmlib initialization */
                        /*  section */
#define SECT_FVMLIB_INIT1 "__fvmlib_init1"  /* the section following the */
                            /*  fvmlib initialization */
                        /*  section */

#define SEG_DATA    "__DATA"    /* the tradition UNIX data segment */
#define SECT_DATA   "__data"    /* the real initialized data section */
                    /* no padding, no bss overlap */
#define SECT_BSS    "__bss"     /* the real uninitialized data section*/
                    /* no padding */
#define SECT_COMMON "__common"  /* the section common symbols are */
                    /* allocated in by the link editor */

#define SEG_OBJC    "__OBJC"    /* objective-C runtime segment */
#define SECT_OBJC_SYMBOLS "__symbol_table"  /* symbol table */
#define SECT_OBJC_MODULES "__module_info"   /* module information */
#define SECT_OBJC_STRINGS "__selector_strs" /* string table */
#define SECT_OBJC_REFS "__selector_refs"    /* string table */

#define SEG_ICON     "__ICON"   /* the icon segment */
#define SECT_ICON_HEADER "__header" /* the icon headers */
#define SECT_ICON_TIFF   "__tiff"   /* the icons in tiff format */

#define SEG_LINKEDIT    "__LINKEDIT"    /* the segment containing all structs */
                    /* created and maintained by the link */
                    /* editor.  Created with -seglinkedit */
                    /* option to ld(1) for MH_EXECUTE and */
                    /* FVMLIB file types only */

#define SEG_UNIXSTACK   "__UNIXSTACK"   /* the unix stack segment */

#define SEG_IMPORT  "__IMPORT"  /* the segment for the self (dyld) */
                    /* modifing code stubs that has read, */
                    /* write and execute permissions */

看到這里據(jù)我估計(jì)讀者也很懵柠逞,其實(shí)我也很懵現(xiàn)在∶潦ǎ現(xiàn)在需要把上面零散的至少匯總起來(lái)了。

匯總

這里我寫(xiě)的demo名字叫mach-o-parse板壮。只解析出來(lái)了部分功能逗鸣。不過(guò)這讓我徹底搞懂了這個(gè)mach-o 的結(jié)構(gòu)

源代碼

#import "ViewController.h"

@interface ViewController ()

@end

typedef struct mach_new_header {
    uint32_t    magic;        /* mach magic number identifier */
    uint32_t    cputype;    /* cpu specifier */
    uint16_t    cpusubtype;    /* machine specifier */
    uint8_t     zhanwei;
    uint8_t     caps;
    uint32_t    filetype;    /* type of file */
    uint32_t    ncmds;        /* number of load commands */
    uint32_t    sizeofcmds;    /* the size of all the load commands */
    uint32_t    flags;        /* flags */
    uint32_t    reserved;
} Mach_O_Header;

void printHeader(Mach_O_Header header){
    NSLog(@"header magic 0x%0x",header.magic);
    NSLog(@"header cputype %d",header.cputype);
    NSLog(@"header cpusubtype %d",header.cpusubtype);
    NSLog(@"header caps 0x%x",header.caps);
    NSLog(@"header filetype %d",header.filetype);
    NSLog(@"header ncmds %d",header.ncmds);
    NSLog(@"header sizeofcmds %d",header.sizeofcmds);
    NSLog(@"header flags 0x%0x",header.flags);
}

typedef struct  {
    uint32_t cmd;       /* type of load command */
    uint32_t cmdsize;   /* total size of command in bytes */
}Mach_O_load_command;


void printLoadCommand(Mach_O_load_command loadCommand){
    NSLog(@"loadCommand cmd 0x%0x",loadCommand.cmd);
    NSLog(@"loadCommand cmdsize %0d",loadCommand.cmdsize);
}
typedef struct  { /* for 64-bit architectures */
    char        segname[16];    /* segment name */
    uint64_t    vmaddr;        /* memory address of this segment */
    uint64_t    vmsize;        /* memory size of this segment */
    uint64_t    fileoff;    /* file offset of this segment */
    uint64_t    filesize;    /* amount to map from the file */
    uint32_t    maxprot;    /* maximum VM protection */
    uint32_t    initprot;    /* initial VM protection */
    uint32_t    nsects;        /* number of sections in segment */
    uint32_t    flags;        /* flags */
}Mach_O_segment_command;

void printSegment_command(Mach_O_segment_command segmentCommand){
    NSLog(@"segmentCommand segname %s",segmentCommand.segname);
    NSLog(@"segmentCommand vmaddr 0x%llx",segmentCommand.vmaddr);
    NSLog(@"segmentCommand vmsize %llu",segmentCommand.vmsize);
    NSLog(@"segmentCommand fileoff 0x%llx",segmentCommand.fileoff);
    NSLog(@"segmentCommand filesize %llu",segmentCommand.filesize);
    NSLog(@"segmentCommand maxprot 0x%x",segmentCommand.maxprot);
    NSLog(@"segmentCommand initprot 0x%x",segmentCommand.initprot);
    NSLog(@"segmentCommand nsects %d",segmentCommand.nsects);
    NSLog(@"segmentCommand flags 0x%x",segmentCommand.flags);

}


typedef struct  { /* for 64-bit architectures */
    char        sectname[16];    /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint64_t    addr;        /* memory address of this section */
    uint64_t    size;        /* size in bytes of this section */
    uint32_t    offset;        /* file offset of this section */
    uint32_t    align;        /* section alignment (power of 2) */
    uint32_t    reloff;        /* file offset of relocation entries */
    uint32_t    nreloc;        /* number of relocation entries */
    uint32_t    flags;        /* flags (section type and attributes)*/
    uint32_t    reserved1;    /* reserved (for offset or index) */
    uint32_t    reserved2;    /* reserved (for count or sizeof) */
    uint32_t    reserved3;    /* reserved */
}Mach_O_section;

void printSection_command(Mach_O_section sectionCommand){
    NSLog(@"sectionCommand secname %s",sectionCommand.sectname);
    NSLog(@"sectionCommand segname %s",sectionCommand.segname);
    NSLog(@"sectionCommand addr 0x%llx",sectionCommand.addr);
    NSLog(@"sectionCommand size %llu",sectionCommand.size);
    NSLog(@"sectionCommand offset %d",sectionCommand.offset);
    NSLog(@"sectionCommand align %d",sectionCommand.align);
    NSLog(@"sectionCommand reloff %d",sectionCommand.reloff);
    NSLog(@"sectionCommand nreloc %d",sectionCommand.nreloc);
    NSLog(@"sectionCommand flags %d",sectionCommand.flags);
}


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
     NSData *mach = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"out"]];
    static int location =0;
    Mach_O_Header  header ;
    [mach getBytes:&header range:NSMakeRange(0, sizeof(Mach_O_Header))];
    location =sizeof(Mach_O_Header);
    printHeader(header);

    for (int i=0; i<header.ncmds; i++) {
        Mach_O_load_command loadCommand;
        Mach_O_segment_command segment;
        [mach getBytes:&loadCommand range:NSMakeRange(location, sizeof(Mach_O_load_command))];
        location+=sizeof(Mach_O_load_command);
        printLoadCommand(loadCommand);
       
        NSUInteger length = loadCommand.cmdsize;
  ///減去loadCommand 的長(zhǎng)度
        length-=8;
      ///獲取Mach_O_segment_command 的數(shù)據(jù)
        [mach getBytes:&segment range:NSMakeRange(location, sizeof(Mach_O_segment_command))];
        location+=sizeof(Mach_O_segment_command);
            printSegment_command(segment);
        length-=sizeof(Mach_O_segment_command);
        
        if (length>0) {
            Mach_O_section section ;
            [mach getBytes:&section range:NSMakeRange(location, sizeof(Mach_O_section))];
            location+=sizeof(Mach_O_section);
            printSection_command(section);
            length-=sizeof(Mach_O_section);

        }
    }
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

測(cè)試結(jié)果截取了一部分

2018-04-19 17:43:34.811420+0800 Mach-o-parse[58332:11931902] 64 8 8696
2018-04-19 17:43:34.811548+0800 Mach-o-parse[58332:11931902] header magic 0xfeedfacf
2018-04-19 17:43:34.811656+0800 Mach-o-parse[58332:11931902] header cputype 16777223
2018-04-19 17:43:34.811752+0800 Mach-o-parse[58332:11931902] header cpusubtype 3
2018-04-19 17:43:34.811886+0800 Mach-o-parse[58332:11931902] header caps 0x80
2018-04-19 17:43:34.811982+0800 Mach-o-parse[58332:11931902] header filetype 2
2018-04-19 17:43:34.812071+0800 Mach-o-parse[58332:11931902] header ncmds 15
2018-04-19 17:43:34.812128+0800 Mach-o-parse[58332:11931902] header sizeofcmds 1280
2018-04-19 17:43:34.812171+0800 Mach-o-parse[58332:11931902] header flags 0x200085
2018-04-19 17:43:34.812228+0800 Mach-o-parse[58332:11931902] loadCommand cmd 0x19
2018-04-19 17:43:34.812310+0800 Mach-o-parse[58332:11931902] loadCommand cmdsize 72
2018-04-19 17:43:44.179138+0800 Mach-o-parse[58332:11931902] segmentCommand segname __PAGEZERO
2018-04-19 17:43:44.179337+0800 Mach-o-parse[58332:11931902] segmentCommand vmaddr 0x0
2018-04-19 17:43:44.179672+0800 Mach-o-parse[58332:11931902] segmentCommand vmsize 4294967296
2018-04-19 17:43:44.180009+0800 Mach-o-parse[58332:11931902] segmentCommand fileoff 0x0
2018-04-19 17:43:44.180212+0800 Mach-o-parse[58332:11931902] segmentCommand filesize 0
2018-04-19 17:43:44.180323+0800 Mach-o-parse[58332:11931902] segmentCommand maxprot 0x0
2018-04-19 17:43:44.180489+0800 Mach-o-parse[58332:11931902] segmentCommand initprot 0x0
2018-04-19 17:43:44.180610+0800 Mach-o-parse[58332:11931902] segmentCommand nsects 0
2018-04-19 17:43:44.180709+0800 Mach-o-parse[58332:11931902] segmentCommand flags 0x0
2018-04-19 17:43:44.180803+0800 Mach-o-parse[58332:11931902] loadCommand cmd 0x19
2018-04-19 17:43:44.181155+0800 Mach-o-parse[58332:11931902] loadCommand cmdsize 472
2018-04-19 17:43:44.181445+0800 Mach-o-parse[58332:11931902] segmentCommand segname __TEXT
2018-04-19 17:43:44.181628+0800 Mach-o-parse[58332:11931902] segmentCommand vmaddr 0x100000000
2018-04-19 17:43:44.181898+0800 Mach-o-parse[58332:11931902] segmentCommand vmsize 4096
2018-04-19 17:43:44.182053+0800 Mach-o-parse[58332:11931902] segmentCommand fileoff 0x0
2018-04-19 17:43:44.182541+0800 Mach-o-parse[58332:11931902] segmentCommand filesize 4096
2018-04-19 17:43:44.183243+0800 Mach-o-parse[58332:11931902] segmentCommand maxprot 0x7
2018-04-19 17:43:44.183394+0800 Mach-o-parse[58332:11931902] segmentCommand initprot 0x5
2018-04-19 17:43:44.184954+0800 Mach-o-parse[58332:11931902] segmentCommand nsects 5
2018-04-19 17:43:44.185290+0800 Mach-o-parse[58332:11931902] segmentCommand flags 0x0
2018-04-19 17:43:44.185609+0800 Mach-o-parse[58332:11931902] sectionCommand secname __text
2018-04-19 17:43:44.185923+0800 Mach-o-parse[58332:11931902] sectionCommand segname __TEXT
2018-04-19 17:43:44.187436+0800 Mach-o-parse[58332:11931902] sectionCommand addr 0x100000f50
2018-04-19 17:43:44.187659+0800 Mach-o-parse[58332:11931902] sectionCommand size 52
2018-04-19 17:43:44.187749+0800 Mach-o-parse[58332:11931902] sectionCommand offset 3920
2018-04-19 17:43:44.187854+0800 Mach-o-parse[58332:11931902] sectionCommand align 4
2018-04-19 17:43:44.188435+0800 Mach-o-parse[58332:11931902] sectionCommand reloff 0
2018-04-19 17:43:44.207834+0800 Mach-o-parse[58332:11931902] sectionCommand nreloc 0
2018-04-19 17:43:44.208121+0800 Mach-o-parse[58332:11931902] sectionCommand flags -2147482624
2018-04-19 17:43:44.208212+0800 Mach-o-parse[58332:11931902] loadCommand cmd 0x74735f5f
2018-04-19 17:43:44.208903+0800 Mach-o-parse[58332:11931902] loadCommand cmdsize 7561845
2018-04-19 17:43:44.209102+0800 Mach-o-parse[58332:11931902] segmentCommand segname
2018-04-19 17:43:44.209186+0800 Mach-o-parse[58332:11931902] segmentCommand vmaddr 0x0
2018-04-19 17:43:44.209262+0800 Mach-o-parse[58332:11931902] segmentCommand vmsize 4294971268
2018-04-19 17:43:44.209351+0800 Mach-o-parse[58332:11931902] segmentCommand fileoff 0x6
2018-04-19 17:43:44.209532+0800 Mach-o-parse[58332:11931902] segmentCommand filesize 4294971268
2018-04-19 17:43:44.209662+0800 Mach-o-parse[58332:11931902] segmentCommand maxprot 0x0
2018-04-19 17:43:44.210690+0800 Mach-o-parse[58332:11931902] segmentCommand initprot 0x0
2018-04-19 17:43:44.211320+0800 Mach-o-parse[58332:11931902] segmentCommand nsects -2147482616
2018-04-19 17:43:44.214560+0800 Mach-o-parse[58332:11931902] segmentCommand flags 0x0
2018-04-19 17:43:44.214699+0800 Mach-o-parse[58332:11931902] sectionCommand secname \^F
2018-04-19 17:43:44.214795+0800 Mach-o-parse[58332:11931902] sectionCommand segname elper
2018-04-19 17:43:44.214890+0800 Mach-o-parse[58332:11931902] sectionCommand addr 0x0
2018-04-19 17:43:44.214979+0800 Mach-o-parse[58332:11931902] sectionCommand size 4294971276
2018-04-19 17:43:44.215061+0800 Mach-o-parse[58332:11931902] sectionCommand offset 26
2018-04-19 17:43:44.215160+0800 Mach-o-parse[58332:11931902] sectionCommand align 0
2018-04-19 17:43:44.215322+0800 Mach-o-parse[58332:11931902] sectionCommand reloff 3980
2018-04-19 17:43:44.215412+0800 Mach-o-parse[58332:11931902] sectionCommand nreloc 2
2018-04-19 17:43:44.215526+0800 Mach-o-parse[58332:11931902] sectionCommand flags 0
2018-04-19 17:43:44.215612+0800 Mach-o-parse[58332:11931902] loadCommand cmd 0x0
2018-04-19 17:43:44.251678+0800 Mach-o-parse[58332:11931902] loadCommand cmdsize 0
2018-04-19 17:43:44.252740+0800 Mach-o-parse[58332:11931902] segmentCommand segname __cstring
2018-04-19 17:43:44.253046+0800 Mach-o-parse[58332:11931902] segmentCommand vmaddr 0x545845545f5f
2018-04-19 17:43:44.253541+0800 Mach-o-parse[58332:11931902] segmentCommand vmsize 0
2018-04-19 17:43:44.254023+0800 Mach-o-parse[58332:11931902] segmentCommand fileoff 0x100000fa6
2018-04-19 17:43:44.254421+0800 Mach-o-parse[58332:11931902] segmentCommand filesize 15
2018-04-19 17:43:44.254554+0800 Mach-o-parse[58332:11931902] segmentCommand maxprot 0xfa6
2018-04-19 17:43:44.255075+0800 Mach-o-parse[58332:11931902] segmentCommand initprot 0x0
2018-04-19 17:43:44.255257+0800 Mach-o-parse[58332:11931902] segmentCommand nsects 0
2018-04-19 17:43:44.255360+0800 Mach-o-parse[58332:11931902] segmentCommand flags 0x0
2018-04-19 17:43:44.255604+0800 Mach-o-parse[58332:11931902] sectionCommand secname \^B
2018-04-19 17:43:44.255800+0800 Mach-o-parse[58332:11931902] sectionCommand segname __unwind_info
2018-04-19 17:43:44.255909+0800 Mach-o-parse[58332:11931902] sectionCommand addr 0x545845545f5f
2018-04-19 17:43:44.256104+0800 Mach-o-parse[58332:11931902] sectionCommand size 0
2018-04-19 17:43:44.256408+0800 Mach-o-parse[58332:11931902] sectionCommand offset 4024
2018-04-19 17:43:44.256492+0800 Mach-o-parse[58332:11931902] sectionCommand align 1
2018-04-19 17:43:44.256590+0800 Mach-o-parse[58332:11931902] sectionCommand reloff 72
2018-04-19 17:43:44.256680+0800 Mach-o-parse[58332:11931902] sectionCommand nreloc 0
2018-04-19 17:43:44.256771+0800 Mach-o-parse[58332:11931902] sectionCommand flags 4024
....

從測(cè)試代碼終于看出了mach-o 的結(jié)構(gòu)

mach-o結(jié)構(gòu)

從測(cè)試代碼看不出 loadCommand 與section 是不連續(xù)的
我們通過(guò)machoView 看出來(lái)的
截圖如下


圖1

圖2

我們從圖1 看loadCommand 的最后地址是0x00000f1c。
從圖2 看section的首地址是0x00000F50绰精。


圖3

從圖3 中我們發(fā)現(xiàn)0x00000F50又出現(xiàn)了撒璧。這個(gè)地址就是指向的需要連接的section地址。

到此為止茬底,mach-o 的具體結(jié)構(gòu)我們弄懂了沪悲。


圖4

這里還有個(gè)知識(shí)點(diǎn)就是maxprot 和initprot ≮灞恚看圖就明白了殿如。

今天終于把mach-o 文件分析完畢了。

注意 segment 有很多種最爬。所有的定義都在 loader.h 文件中涉馁。是根據(jù)loadCommand 中的cmd 參數(shù)變化更改下面的segment 的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末爱致,一起剝皮案震驚了整個(gè)濱河市烤送,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糠悯,老刑警劉巖帮坚,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妻往,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡试和,警方通過(guò)查閱死者的電腦和手機(jī)讯泣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)阅悍,“玉大人好渠,你說(shuō)我怎么就攤上這事〗谑樱” “怎么了拳锚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)寻行。 經(jīng)常有香客問(wèn)我霍掺,道長(zhǎng),這世上最難降的妖魔是什么拌蜘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任抗楔,我火速辦了婚禮,結(jié)果婚禮上拦坠,老公的妹妹穿的比我還像新娘。我一直安慰自己剩岳,他們只是感情好贞滨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著拍棕,像睡著了一般晓铆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绰播,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天骄噪,我揣著相機(jī)與錄音,去河邊找鬼蠢箩。 笑死链蕊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谬泌。 我是一名探鬼主播滔韵,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掌实!你這毒婦竟也來(lái)了陪蜻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贱鼻,失蹤者是張志新(化名)和其女友劉穎宴卖,沒(méi)想到半個(gè)月后滋将,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡症昏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年随闽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片齿兔。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡橱脸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出分苇,到底是詐尸還是另有隱情添诉,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布医寿,位于F島的核電站栏赴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏靖秩。R本人自食惡果不足惜须眷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沟突。 院中可真熱鬧花颗,春花似錦、人聲如沸惠拭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)职辅。三九已至棒呛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間域携,已是汗流浹背簇秒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秀鞭,地道東北人趋观。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像气筋,于是被迫代替她去往敵國(guó)和親拆内。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Mach-O 概述 和 部分命令介紹 我們知道Windows下的文件都是PE文件宠默,同樣在OS X和iOS中可執(zhí)行文...
    青花瓷的平方閱讀 14,904評(píng)論 2 52
  • 熟悉Linux和windows開(kāi)發(fā)的同學(xué)都知道麸恍,ELF是Linux下可執(zhí)行文件的格式,PE32/PE32+是win...
    Klaus_J閱讀 3,945評(píng)論 1 10
  • 在windows上可執(zhí)行文件的格式是exe,在Linux上ELF是可執(zhí)行文件抹沪,而在蘋(píng)果系統(tǒng)上刻肄,Mac OS X和i...
    XX開(kāi)發(fā)從開(kāi)始到放棄閱讀 799評(píng)論 0 3
  • 本文所讀的源碼,可以從這里找到融欧,這是 Mach-O 系列的第一篇 我們的程序想要跑起來(lái)敏弃,肯定它的可執(zhí)行文件格式要被...
    Joy___閱讀 24,175評(píng)論 9 97
  • 原文地址 寫(xiě)在之前 之前工作中對(duì)Mach-O文件有一定的接觸, 原本早就想寫(xiě)一篇文章分享一下,但是奈何只是不夠深入...
    南梔傾寒閱讀 4,789評(píng)論 3 22