格式
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类型都有哪些作用。
LC-SEGMENT;LC-SEGMENT-64 在内核中由load-segment 函数处理(将segment中的数据加载并映射到进程的内存空间去)
LC-LOAD-DYLINKER 在内核中由load-dylinker 函数处理(调用/usr/lib/dyld程序)
LC-UUID 在内核中由load-uuid 函数处理 (加载128-bit的唯一ID)
LC-THREAD 在内核中由load-thread 函数处理 (开启一个MACH线程,但是不分配栈空间)
LC-UNIXTHREAD 在内核中由load-unixthread 函数处理 (开启一个UNIX posix线程)
LC-CODE-SIGNATURE 在内核中由load-code-signature 函数处理 (进行数字签名)
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位于符号表的索引数组。