Mach-o 文件
Mach-O格式全称为Mach Object文件格式的缩写,是mac上可执行文件的格式
Mach-O 文件构成如下
Headers
保存了一些基本信息,包括了该文件运行的平台、文件类型、LoadCommands的个数等等。Headers的主要作用就是帮助系统迅速的定位Mach-O文件的运行环境,文件类型,保存了一些dyld重要的加载参数。 Mach-O 文件的头部定义如下:
- magic 标志符 0xfeedface 是 32 位, 0xfeedfacf 是 64 位。
- cputype 和 cpusubtype 确定 cpu 类型、平台
- filetype 文件类型,可执行文件、符号文件(DSYM)、内核扩展等
- ncmds 加载 Load Commands 的数量
- flags dyld 加载的标志
MH_NOUNDEFS
目标文件没有未定义的符号,-
MH_DYLDLINK
目标文件是动态链接输入文件,不能被再次静态链接 MH_SPLIT_SEGS
只读 segments 和 可读写 segments 分离MH_NO_HEAP_EXECUTION
堆内存不可执行
header的作用是保存Mach-O可执行文件需要的环境和合法性。
Load Commands
可以理解为加载命令,在加载Mach-O文件时会使用这里的数据来确定内存的分布以及相关的加载命令。比如我们的main函数的加载地址,程序所需的dyld的文件路径,以及相关依赖库的文件路径。
cmd
字段表示command 类型cmdsize
字段标识command占用地址长度,即到下一个command的偏移量
Data & Segment
每一个segment的具体数据都保存在这里,这里包含了具体的代码、数据等
数据页的概念,
segname
是数据页类型
- #define SEG_PAGEZERO “__PAGEZERO” // 可执行文件捕获空指针的段
- #define SEG_TEXT “__TEXT” // 代码段,只读数据段
- #define SEG_DATA “__DATA” // 数据段
- #define SEG_LINKEDIT “__LINKEDIT” // 包含动态链接器所需的符号、字符串表等数据
Section
数据页中的子分区。
sectname
表示自己所在分区名称
- Text.__text 主程序代码
- Text.__cstring c 字符串
- Text.__stubs 桩代码
- Text.__stub_helper
- Data.__data 初始化可变的数据
- Data.__objc_imageinfo 镜像信息 ,在运行时初始化时 objc_init,调用 load_images 加载新的镜像到 infolist 中
- Data.__la_symbol_ptr
- Data.__nl_symbol_ptr
- Data.__objc_classlist 类列表
- Data.__objc_classrefs 引用的类
了解Mach-O的作用
分析可执行文件结构,对于逆向砸壳、调取堆栈信息、分析代码组成等有重要帮助。这里介绍分析代码组成减少安装包大小。
由于 OC 的动态特性,编译器会对所有的源文件进行编译,并连接进可执行文件中,增大了安装包的大小。 上文中提到了 __objc_classlist 和 __objc_classrefs,它们分别表示项目中全部类列表和项目中被引用的类列表,那么取两者之差,就能删除一些项目中没使用的类文件。但是在删除过程中记住要在项目中全局搜索确认下,看看有没有通过字符串调用无引用的类的方法,原因还是 OC 是动态语言。
详细过程见iOS删除无用的类、iOS项目优化、iOS删除无用的方法
查看工程中代码量大的库或类
通过linkMap文件,可分析出各个类和库占用的大小,用来优化库或代码的数量。 在build setting中打开 link map的开关,在drivedata中找到编译产生的link map文件。
更多详情见对Mach-O文件的初步探索