Mach-O

格式

Mash-O格式.gif
1. header

header 包含了,cpu类型,加载command的数量,文件类型等信息

/*
* The 32-bit mach header appears at the very beginning of the object file for
* 32-bit architectures.
32位 架构数据结构类型
*/
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.
64位架构数据结构类型
*/
struct mach_header_64 {
  uint32_t    magic;      /* mach magic number identifier */
// 识CPU的架构 arm x86, i386
  cpu_type_t  cputype;    /* cpu specifier */
// 体的CPU类型,区分不同版本的处理器
  cpu_subtype_t   cpusubtype; /* machine specifier */
// 文件类型
  uint32_t    filetype;   /* type of file */
//加载了多少command,每个LoadCommands代表了一种Segment的加载方式
  uint32_t    ncmds;      /* number of load commands */
//LoadCommand的大小,主要用于划分Mach-O文件的‘区域’
  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 /* 

经常遇见的Mach-O文件类型:

  • MH_OBJECT,这种类型的文件有目标文件(.o)、静态库文件(.a) (静态库文件就是N个.o文件合并在一起的)

  • MH_EXECUTE,可执行文件,例如上面说的Super文件

  • MH_DYLIB,动态库文件,包括.dylib、.framework

  • MH_DYLINKER,动态链接编辑器,例如:位于手机这里的Device/usr/lib/的dyld程序

MH_DSYM,存储二进制符号信息的文件,dsym文件常用于分析APP的崩溃信息

loadCommands

用来描述文件在虚拟地址中的布局结构,就是存储着各段数据的大小,分段,地址等信息

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

这些加载指令清晰地告诉加载器如何处理二进制数据,有些命令是由内核处理的,有些是由动态链接器处理的。在源码中有明显的注释来说明这些是动态连接器处理的。
根据cmd字段的类型不同,使用了不同的函数来加载.看一看在内核代码中不同的command类型都有哪些作用。

  1. LC-SEGMENT;LC-SEGMENT-64 在内核中由load-segment 函数处理(将segment中的数据加载并映射到进程的内存空间去)

  2. LC-LOAD-DYLINKER 在内核中由load-dylinker 函数处理(调用/usr/lib/dyld程序)

  3. LC-UUID 在内核中由load-uuid 函数处理 (加载128-bit的唯一ID)

  4. LC-THREAD 在内核中由load-thread 函数处理 (开启一个MACH线程,但是不分配栈空间)

  5. LC-UNIXTHREAD 在内核中由load-unixthread 函数处理 (开启一个UNIX posix线程)

  6. LC-CODE-SIGNATURE 在内核中由load-code-signature 函数处理 (进行数字签名)

  7. LC-ENCRYPTION-INFO 在内核中由 set-code-unprotect 函数处理 (加密二进制文件)

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

section段

存放着各段的原始数据,就是Load commands区域描述的地址所指向的数据

注入dylib整体思路

1、读取Mach-O文件信息到内存中;

2、定义一个mach_header,将原来的mach_header写到新定义的mach_header中;

3、 在新定义的mach_header中依需将ncmds加1,sizeofcmds加上要注入的dylib库的大小;

4、将新定义并修改好的mach_header从Mach-O最开始部分覆盖原文件的mach_header;

5、指针跳过sizeofcmds大小内存;

6、定义一个dylib结构体并赋值,即注入的 dylib 信息;

7、回退(新mach_header中sizeofcmds已包含要注入dylib的大小)并覆盖、写入 path 信息。

命令 数据结构 用途
LC_UUID uuid_command(page 20) 指定图像或其对应的dSYM文件的128位UUID
LC_SEGMENT segment_command 加载此文件时,定义映射到进程地址空间中所需的文件段。而且每个段中包含了所有的节
LC_SYMTAB symtab_command 指定了文件的符号表。静态链接器和动态连接器连接文件的时候都需要用到这些信息,还可以通过调试器将符号映射到生成符号的原始源代码文件。
LC_DYSYMTAB dysymtab_command 指定了动态连接器用到的附带符号表信息
LC_THREAD LC_UNIXTHREAD thread_command 对于可执行文件,LC_UNIXTHREAD命令定义了进程主线程的线程状态。LC_THREAD和LC_UNIXTHREAD一样,但是LC_THREAD不会引起内核分配堆栈
LC_LOAD_DYLIB dylib_command 定义此文件链接的动态共享库的名称。
LC_ID_DYLIB dylib_command 定义了动态共享库安装名称
LC_PREBOUND_DYLIB prebound_dylib_command 对于此可执行文件链接预绑定的共享库,指定使用的共享库中的模块。
LC_LOAD_DYLINKER dylinker_command 指定内核执行加载文件所需的动态连接器
LC_ID_DYLINKER dylinker_command 标志这个文件可以作为动态连接器
LC_ROUTINES routines_command 包含共享库初始化例行程序的地址(由链接器的-init选项指定)。
LC_ROUTINES_64 routines_command_64 包含共享库64位初始化例行程序的地址(由链接器的-init选项指定)。
LC_TWOLEVEL_HINTS twolevel_hints_command 包含两级命名空间查询提示表。
LC_SUB_FRAMEWORK sub_framework_command 将此文件标识为伞形框架的子框架的实现。伞形框架的名称存储在字符串参数中。(伞形框架可以包含多个子框架,苹果不推荐这样使用)
LC_SUB_UMBRELLA sub_umbrella_command 指定此文件作为伞框架的子伞
LC_SUB_LIBRARY sub_library_command 标志这个文件可以作为伞框架的一个字库的实现。请注意,Apple尚未为子库定义受支持的位置。
LC_SUB_CLIENT sub_client_command 子框架可以明确地允许另一个框架或包链接到它,方法是包含一个LC_SUB_CLIENT load命令,该命令包含框架的名称或包的客户端名称。

1、(__TEXT,__text)

这里存放的是汇编后的代码,当我们进行编译时,每个.m文件会经过预编译->编译->汇编形成.o文件,称之为目标文件。汇编后,所有的代码会形成汇编指令存储在.o文件的(__TEXT,__text)区((__DATA,__data)也是类似)。链接后,所有的.o文件会合并成一个文件,所有.o文件的(__TEXT,__text)数据都会按链接顺序存放到应用文件的(__TEXT,__text)中。

2、(__DATA,__data)

存储数据的section,static在进行非零赋值后会存储在这里,如果static 变量没有赋值或者赋值为0,那么它会存储在(__DATA,__bss)中。

3、Symbol Table

符号表,这个是重点中的重点,符号表是将地址和符号联系起来的桥梁。符号表并不能直接存储符号,而是存储符号位于字符串表的位置。

4、String Table

字符串表所有的变量名、函数名等,都以字符串的形式存储在字符串表中。

5、动态符号表

动态符号表存储的是动态库函数位于符号表的偏移信息。(__DATA,__la_symbol_ptr) section 可以从动态符号表中获取到该section位于符号表的索引数组。

你可能感兴趣的:(Mach-O)