在学习iOS逆向的过程中,发现在解密可执行文件dumpdecrypted砸壳原理时需要用到Mach-O相关知识,在动态库注入过程中也需要理解Mach-O可执行文件的文件结构,那么有必要系统学习记录Mach-O文件的组成结构。


       Mach-O,是Mach object文件格式的缩写,是一种可执行文件、目标代码、共享程序库、动态加载代码和核心dump。是a.out格式的一种替代。Mach-O提供更多的可扩展性和更快的符号表信息存取。Mach-O应用在基于Mach核心的系统上,目前NeXTSTEP、Darwin、Mac OS X(iPhone)都是使用这种可执行文件格式。熟悉Mach-O文件格式,有助于了解苹果底层软件运行机制,更好的掌握dyld加载Mach-O的步骤,为自己动手开发Mach-O相关的加解密工具注入工具打下基础。

Mach-O 没有类似于 XML、YAML、JSON 等诸如此类的特殊格式,它只是一个二进制字节流,被划分为了有意义的数据块。这些块包含元信息,比如,字节顺序、cpu 类型、块的大小,等等,简单期间这里我们仅讨论单架构模型。

典型的 Mach-O 文件包含三个区域:




ios操作系统的可执行文静_可执行文件


通过上图,可以看出Mach-O主要由以下三部分组成:

  • Mach-O头部(Mach Header)。描述了Mach-O的cpu架构、文件类型以及加载命令等信息。
  • 加载命令(load command)。描述了文件中数据的具体组织结构,不同的数据类型使用不同的加载命令表示。
  • Data。Data中的每个段(segment)的数据都保存在这里,段的概念与ELF文件中段的概念类似。每个段都有一个或多个Section,它们存放了具体的数据与代码,主要包含代码、数据,例如符号表,动态符号表等等。

Mach-O头部

与Mach-O文件格式有关的结构体,都可以直接或间接的在”mach-o/loader.h“文件中找到。

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

magic:字段与fat_header结构体中的magic字段一样,表示Mach-O文件的魔数值,对于32位架构的程序来说,它的取值是MH_MAGIC,固定为0xfeedface。这里主要关注MH_EXECUTE、MH_DYLIB与MH_DYLIB这3个文件格式。

ncmds:指明了Mach-O文件中加载命令(load commands)的数量。

sizeofcmds字段指明了Mach-O文件加载命令(load commands)所占的总字节大小。

flags字段表示文件标志,它是一个含有一组位标志的整数,指明了Mach-O文件的一些标志信息。

      我们通过xrun新建一个helloworld文件,构建成可执行文件,通过Mac系统的otool工具(或者MachOView)即可查看mach_header信息,如下图:

ios操作系统的可执行文静_加载_02

Load Command加载命令

在mach_header之后的是Load Command加载命令,这些加载命令在Mach-O文件加载解析时,被内核加载器或者动态链接器调用,基本的加载命令的数据结构如下:

struct load_command {
    uint32_t cmd;        /* type of load command */
    uint32_t cmdsize;    /* total size of command in bytes */
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 */
};

1.cmd 是load command的类型,本文中值=1就是LC_SEGMENT,,LC_SEGMENT的含义是(将文件中的段映射到进程地址空间)

ios操作系统的可执行文静_ios操作系统的可执行文静_03

2.cmdsize 代表load command的大小(0×58个字节)。

3.segname 16字节的段名字,当前是__PAGEZERO。

4.vmaddr 段的虚拟内存起始地址

5.vmsize 段的虚拟内存大小

6.fileoff 段在文件中的偏移量

7.filesize 段在文件中的大小

8.maxprot 段页面所需要的最高内存保护(4=r,2=w,1=x)

9.initprot 段页面初始的内存保护

10.nsects 段中包含section的数量

11.flags 其他杂项标志位

otool工具即可查看Load command信息,如下图:

ios操作系统的可执行文静_ios操作系统的可执行文静_04

Section数据

当一个段包含多个节区时,节区信息会以数组形式紧随着存储在段加载命令后面。节区使用结构体section表示(64位使用section_64表示),定义如下:

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

1.sectname 第一个是__text ,就是主程序代码

2.segname 该section所属的 segment名,第一个是__TEXT

3.addr 该section在内存的启始位置,0xa588。

4.size 该section的大小,0x84a

5.offset 该section的文件偏移,28116   0x6dd4

6.align 字节大小对齐 ,4

7. reloff 重定位入口的文件偏移,0

8.nreloc 需要重定位的入口数量,0

9.flags 包含section的type和attributes

S_REGULAR—This section has no particular type. The standard tools create a __TEXT,__text section of this type.

结构中的最后2项保留用。

段的命名规则是两个下划线紧跟着大写字母(如__TEXT),而section的命名则是两个下划线紧跟着小写字母(__text)。

下面列出段中可能包含的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是实际的初始数据。