Mach-o 分析

组成

Mach-O通常有三部分组成
头部 (Header): Mach-O文件的架构 比如Mac的 PPC, PPC64, IA-32, x86-64,ios的arm系列.
加载命令(Load commands):
.
原始段数据(Raw segment data)*:可以拥有多个段(segment),每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。

官方给的图如下:


mach-o

准备工作

我们创建一个工程,mach-o

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

打开终端cd 到该工程的main所在目录执行下列命令gcc main.m -g -o test.out 会生成 test.out 文件。我们就分析这个文件

Mach-O 分析

通常一个iOS App应用会安装在/var/mobile/Applications,系统的原生App会安装在/Applications目录下,大部分情况下,xxx.app/xxx文件并不是Mach-O格式文件,由于现在需要支持不同CPU架构的iOS设备,所以我们编译打包出来的执行文件是一个Universal Binary格式文件(通用二进制文件,也称胖二进制文件),实际上Universal Binary只不过将支持不同架构的Mach-O打包在一起,再在文件起始位置加上Fat Header来说明所包含的Mach-O文件支持的架构和偏移地址信息。

Universal Binary

我们可以通过otool 工具查看头
otool - f test (上面的test.out 通过该命令是没有输出结果的,因为是no fat文件,这里的test必须是 多个结构体在一起的才可以)

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)

胖二进制文件定义在 /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字段就是我们常说的魔数(就是文件结构),加载器通过这个魔数值来判断这是什么样的文件。在32 位和64 位 上magic 不一样。
    32 位是0xcafebabe ,64位是0xcafebabf

  • nfat_arch字段是指当前的胖二进制文件包含了多少个不同架构的Mach-O文件;
    fat_header后会跟着fat_arch,有多少个不同架构的Mach-O文件,就有多少个fat_arch,用于说明对应Mach-O文件大小、支持的CPU架构、偏移地址等;

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) */

我们可以通过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,可以看到文件中的内容最开始部分,是以 cafe babe开头的对于一个 二进制文件 来讲,每个类型都可以在文件最初几个字节来标识出来,即“魔数”。不同类型的 二进制文件,都有自己独特的"魔数"。OS X上,可执行文件的标识有这样几个魔数(不同的魔数代表不同的可执行文件类型)是mach-o文件的魔数,0xfeedface代表的是32位,0xfeedfacf代表64位,cafebabe是跨处理器架构的通用格式。
2.cputype和cupsubtype代表的是cpu的类型和其子类型。
3.接着是filetype,2,代表可执行的文件

 * 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)的数量,例子中一共15个,编号0-14
5.sizeofcmds 表示load commands 的bytes, load commands区域是紧接着header区域的。

6.最后个flags,例子中是0x00218085,可以按文档分析之。
这个就是标志位。

#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结构的工具查看结构

image.png

Load commands

load commmand直接跟在 header 部分的后面,结构定义在文件/usr/include/mach-o/loader.h中。

结构定义如下

/*
 * 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.每个command 有cmd 和cmdsize 组成。
4.cmd 代表类型。每种类型都有常量标示
5.cmdsize 是大小
6.cmdSize 32位必须是4字节的倍数。 64位就是b字节的倍数

这些加载命令在Mach-O文件加载解析时,被内核加载器或者动态链接器调用,指导如何设置加载对应的二进制数据段。
cmd 类型很多都在 /usr/include/mach-o/loader.h中可以自己查看。
具体可以通过下面命令查看(test文件名)

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

段数据(Segments)

Segments包含了很多segment,每一个segment定义了一些Mach-O文件的数据、地址和内存保护属性,这些数据在动态链接器加载程序时被映射到了虚拟内存中。每个段都有不同的功能。

一般包含下列功能

  • 1). __PAGEZERO: 空指针陷阱段,映射到虚拟内存空间的第一页,用于捕捉对NULL指针的引用;
  • 2). __TEXT: 包含了执行代码以及其他只读数据。 为了让内核将它 直接从可执行文件映射到共享内存, 静态连接器设置该段的虚拟内存权限为不允许写。当这个段被映射到内存后,可以被所有进程共享。(这主要用在frameworks, bundles和共享库等程序中,也可以为同一个可执行文件的多个进程拷贝使用)
  • 3). __DATA: 包含了程序数据,该段可写;
  • 4). __LINKEDIT: 含有为动态链接库使用的原始数据,比如符号,字符串,重定位表条目等等。

一般的段又会按不同的功能划分为几个区(section),即段所有字母大小,加两个下横线作为前缀,而区则为小写,同样加两个下横线作为前缀

下面列出段中可能包含的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是实际上的代码部分;__DATA段的__data是实际的初始数据

可以通过otool –s查看某segment的某个section。(test是文件名)

otool -s __TEXT __text test.out
或者
otool -t test.out
想查看汇编
otool -t -v test.out

segment_command

接下来我们看 数据段 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 */
};

这里我们看看结构体的每个成员变量代表什么

1.cmd 32位是个常量 LC_SEGMENT 64 常量LC_SEGMENT_64
2.cmdsize 该区域大小
3.segname[16] 名字
4.vmaddr segment 的内存地址。
5.vmsize segment的内存大小
6.fileoff 文件偏移量

  1. filesize 在文件中的大小
    8.maxprot 页面所需要的最高内存保护(4=r,2=w,1=x)
    9.initprot 页面初始的内存保护
    10.nsects sections的多少
    11.flags 指示器。标记该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 充满
#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 */
};

结构体定义

1.sectname 第一个是__text ,就是主程序代码
2.segname 该section所属的 segment名,第一个是__TEXT
3.addr 该section在内存的启始位置
4.size 该section的大小
5.offset 该section的文件偏移
6.align 字节大小对齐 ,

  1. reloff 重定位入口的文件偏移,
    8.nreloc 需要重定位的入口数量,
    9.flags 包含section的type和attributes

这里还有点东西需要看看

/* 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 */

看到这里据我估计读者也很懵,其实我也很懵现在。现在需要把上面零散的至少汇总起来了。

汇总

这里我写的demo名字叫mach-o-parse。只解析出来了部分功能。不过这让我彻底搞懂了这个mach-o 的结构

源代码

#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 的长度
        length-=8;
      ///获取Mach_O_segment_command 的数据
        [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

测试结果截取了一部分

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
....

从测试代码终于看出了mach-o 的结构

mach-o结构

从测试代码看不出 loadCommand 与section 是不连续的
我们通过machoView 看出来的
截图如下


图1

图2

我们从图1 看loadCommand 的最后地址是0x00000f1c。
从图2 看section的首地址是0x00000F50。


图3

从图3 中我们发现0x00000F50又出现了。这个地址就是指向的需要连接的section地址。

到此为止,mach-o 的具体结构我们弄懂了。


图4

这里还有个知识点就是maxprot 和initprot 。看图就明白了。

今天终于把mach-o 文件分析完毕了。

注意 segment 有很多种。所有的定义都在 loader.h 文件中。是根据loadCommand 中的cmd 参数变化更改下面的segment 的。

推荐阅读更多精彩内容

  • Mach-O 概述 和 部分命令介绍 我们知道Windows下的文件都是PE文件,同样在OS X和iOS中可执行文...
    青花瓷的平方阅读 14,460评论 2 52
  • 熟悉Linux和windows开发的同学都知道,ELF是Linux下可执行文件的格式,PE32/PE32+是win...
    Klaus_J阅读 3,253评论 1 10
  • 在windows上可执行文件的格式是exe,在Linux上ELF是可执行文件,而在苹果系统上,Mac OS X和i...
    XX开发从开始到放弃阅读 616评论 0 3
  • 本文所读的源码,可以从这里找到,这是 Mach-O 系列的第一篇 我们的程序想要跑起来,肯定它的可执行文件格式要被...
    Joy___阅读 23,277评论 9 94
  • 原文地址 写在之前 之前工作中对Mach-O文件有一定的接触, 原本早就想写一篇文章分享一下,但是奈何只是不够深入...
    南栀倾寒阅读 4,368评论 3 22
  • 短信验证接口(API调用) 这里我们使用的短信接口:阿里大于 使用 SDK 1、官方下载2、npmjs ...
    Junting阅读 262评论 0 0
  • 静静的坐在床上冥想了有十几分钟之久,可是脑海里还是被这几天所发生的事情所牵绊。这是一个离别的季节,好些人都已经离开...
    飞城阅读 93评论 0 1
  • 今年元旦上初级班,赵燕程老师告诉我,应学会觉察,对自己心里的感受有觉知; 暑假上深度体验,孙淑文老师说,觉察的,不...
    辛夷观心阅读 163评论 0 1