Mach-O文件结构
Mach-O(Mach Object)是一种基于Mach内核的文件格式。iOS系统生成的可执行程序或者动态库文件的存储布局格式被称之为mach-o格式。Mach-O可以有一个或多个架构。多个架构的称为FAT文件。Mach-O文件结构包括
- Mach-O header 文件的目标基本信息,包含架构例如PPC,PPC64,IA-32或x86-64等信息
- Load Command 文件的逻辑结构和文件在虚拟内存中的布局。可以根据他找到相关的Section 原始数据。mach-o文件由诸多的load command组成,,每个load command所代表的是一种数据类型。每种load command都是结构体struct load_command的扩展结构体。
- Section 节:每个段则由多个节(Section)组成。节是内容分类的最小管理单元。每个节的描述信息是一个称之为:struct section的结构体。每个节有一个唯一的名称用来标识这个节。
ASLR技术
ASLR,全称是Address Spce Layout Randomization,地址空间布局随机化,是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,增加了攻击者预测目的地址的难度,防止攻击者直接定位代码位置,阻止溢出攻击。这种技术会使得每个程序或者库每次运行加载到内存中时的基地址都不是固定而是随机的,这种机制会增加黑客的破解难度。
iOS中,Mach-O文件 load Commonds 中LC_DYLD_INFO或者LC_DYLD_INFO_ONLY 就是用来记录所有需要进行地址调整的位置。这样当程序被加载到内存时,加载器就会将需要调整的地址分别进行调整处理,以便转化为真实的内存地址。这个过程称之为基地址重定向(rebase)。
Header
mach-o/loader.h 。使用otool -h -v 命令查看Mach-O文件头。
$ otool -h -v TestMachO
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 X86_64 ALL 0x00 EXECUTE 40 5256 NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE
对于FAT文件。Fat文件的header,最前面时FatHeader, 接着是各架构的arch
struct fat_header {
unsigned long magic; /* FAT_MAGIC */
unsigned long nfat_arch; /* number of structs that follow */
};
struct fat_arch {
cpu_type_t cputype; /* cpu specifier (int) */
cpu_subtype_t cpusubtype; /* machine specifier (int) */
unsigned long offset; /* file offset to this object file */
unsigned long size; /* size of this object file */
unsigned long align; /* alignment as a power of 2 */
};
单架构的 Mach-O header的定义
struct mach_header_64 {
uint32_t magic; /* mach magic number identifier */
cpu_type_t cputype; /* cup 架构 cpu specifier */
cpu_subtype_t cpusubtype; /* machine specifier */
// 文件类型常见有的MH_OBJECT(目标文件)、MH_EXECUTABLE(可执行二进制文件)、MH_DYLIB (动态库)。
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 */
};
Load Command
Load Command告诉操作系统应当如何加载文件中的数据,对系统内核加载器和动态链接器起指导作用。使用MachOView查看LoadCommand
常见的Segment
LC_SEGMENT_64: 64 位segment 的映射。64-bit segment of this file to be mapped.
LC_DYLD_INF0_0NLY:记录了有关链接的重要信息,包括在_LINKEDIT中动态链接相关信息的具体偏移和大小。ONLY表示这个加载指令是程序运行所必需的。
LC_SYMTAB:为文件定义符号表和字符串表。在链接文件时被链接器使用,同时也用于调试器映射符号到源文件。符号表定义的本地符号仅用于调试,而已定义和未定义的 external符号被链接器使用。
LC_DYSYMTAB:将符号表中给出符号的额外符号信息提供给动态链接器。
LC_LOAD_DYLINKER:默认的加载器路径。
LC.UUID:用于标识Mach-0文件的ID,也用于崩溃堆栈和符号文件的对应解析。
LC_VERSION_MIN_IPHONEOS:系统要求的最低版本。
LC.SOURCE.VERSION:构建二进制文件的源代码版本号。
LC.MAIN:程序的入口。dykl获取该地址,然后跳转到该处执行。
LC_ENCRYPTION_INFO_64:文件是否加密的标志,加密内容的偏移和大小。
LC_LOADJDYLIB:依赖的动态库,包括动态库名称、当前版本号、兼容版本号。可以使用 “otool-Lxxx”命令查看。
$ otool -L TestMachO
SDSAD:
/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1751.108.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.0.0)
...
- LC_RPATH: Runpath Search Paths, @rpath
- LC_FUNCTION_STARTS:函数起始地址表,使调试器和其他程序能很容易地看到一个地
址是否在函数内。 - LC_DATA_IN_CODE:定义在代码段内的非指令的表。 • LC_CODE_SIGNATURE:代码签名信息。
LC_Segment_64
segment 的结构定义:
struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LoadCommand类型 */
uint32_t cmdsize; /* LoadCommand结构的大小 */
char segname[16]; /* segment name */
uint64_t vmaddr; /* 对应section的起始地址 。映射到虚拟地址的偏移 */
uint64_t vmsize; /* 映射到虚拟地址的大小 */
uint64_t fileoff; /* 对应于当前架构文件的偏移(注意:是当前架构文件,不是整个FAT文件) */
uint64_t filesize; /* 文件的大小 */
vm_prot_t maxprot; /* 段页面的最高内存保护 */
vm_prot_t initprot; /* 初始内存保护。 */
uint32_t nsects; /* 包含section 的个数 */
uint32_t flags; /* flags 页面标志 */
};
系统将fileoff偏移处filesize大小的内容加载到虚拟内存的vmaddr处,大小为vmsize,Segment页面的权限由initprot进行初始化。它的权限可以动态改变,但是不能超过maxprot的值,例如 _TEXT初始化和最大权限都是可读/可执行/不可写。
Segment 的分类
- _PAGEZERO:空指针陷阱段,映射到虚拟内存空间的第1页,用于捕捉对 的引用。
- _TEXT:代码段/只读数据段。
- _DATA_CONST:常量数据的段
- _DATA:读取和写入数据的段。
- _LINKEDIT:动态链接器需要使用的信息,包括重定位信息、绑定信息、懒加载信息等。
每一个 Segment 对应着0到多个Section 的数据。LC_Senment_64 的后面位置则存储着 Section64 数据(找到对应的section 原始数据的偏移,大小等)。
Section64
struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* name of this section */
char segname[16]; /* 对应Segment 的名称 */
uint64_t addr; /* 映射到虚拟地址的偏移 */
uint64_t size; /* size in bytes of this section */
uint32_t offset; /* file offset of this section */
uint32_t align; /* 字节对其大小 (power of 2) */
uint32_t reloff; /* file offset of relocation entries */
uint32_t nreloc; /* 重定位入口的个数。 */
uint32_t flags; /* flags (section type and attributes)*/
uint32_t reserved1; /* 保留位 (for offset or index) */
uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */
};
Segment _TEXT
- 程序可执行的代码区域。
- __stubs:间接符号存根,跳转到懒加载指针表。
- __stub_helper:帮助解决懒加载符号加载的辅助函数。
- __objc一methname:方法名。
- __objc_classname:类名。
- __objc_methtype:方法签名。
- __cstring:只读的C风格字符串,包含0C的部分字符串和属性名。
Segment _DATA
- __nl_symboLptr:非懒加载指针表,在dyld加载时会立即绑定值。
- _la_symbol_ptr:懒加载指针表,第1次调用时才会绑定值。
- __got:非懒加载全局指针表。
- __objc_classrefs:被引用的类列表。
- __mod一 init一 func: constructor函数
- __mod_term_func: destructor函数。
- __cfstring: 0C字符串。
- __objc_classlist:程序中类的列表。
- __objc_nlclslist:程序中自己实现了+load方法的类。
- __objc_protolist:协议的列表。
用于分析Mach-O的工具
- /usr/bin/lipo :可以创建和分析包含用于多个体系结构的映像的二进制文件。这种二进制文件的一个示例是通用二进制文件。通用二进制文件可以在基于PowerPC和基于Intel的Macintosh计算机中使用。另一个示例是PPC / PPC64二进制文件,可以在基于32位PowerPC和基于64位PowerPC的Macintosh计算机中使用。
- /usr/bin/file:显示文件的类型。对于多体系结构文件,它显示构成档案的每个图像的类型。
- /usr/bin/otool:可以列出了Mach-O文件中特定节和段的内容。它包括每种受支持体系结构的符号反汇编程序,并且知道如何格式化许多常见节类型的内容。
- /usr/bin/pagestuff:显示组成图像的每个逻辑页面上的信息,包括各部分的名称和每个页面中包含的符号。该工具不适用于包含多个架构的图像的二进制文件。
- /usr/bin/nm:可以查看目标文件的符号表的内容。
- MachoView:可以查看mach-o文件内容
// 通过file命令查看文件信息
$ file TestMachO
SDSAD: Mach-O 64-bit executable x86_64
相关链接
- mach-o 开源文件
- Apple文档 Mach-O Programming Topics
- Apple文档 Launch Services Programming Guide
- Apple文档
- 深入iOS系统底层之程序映像