Mach-O简介:
Mach-O是Mach object的缩写,是Mac\iOS上用于存储程序、库的标准格式.常见的Mach-O文件比如iOS开发好的代码打包好后就是Mach-O格式的文件.
Apple中定义的Mach-O文件包含图一中几种:
常见的几种Mach-O文件:
a、目标文件(.o)
b、静态库文件(.a),静态库其实就是N个.o合并在一起
MH_EXECUTE:可执行文件
a、.app/xx
MH_DYLIB:动态库文件
a、.dylib
b、.framework/xx
MH_DYLINKER:动态链接编辑器
a、/usr/lib/dyld
MH_DSYM:存储着二进制文件符号信息的文件
a、.dSYM/Contents/Resources/DWARF/xx(常用于分析APP的崩溃信息)
Mach-O的基本结构:
主要包含三个部分:
Header部分:保存了该文件的一些基本信息,如平台,文件类型,加载命令的个数等
loadCommends部分:根据这里的数据来确定内存的分布
Data部分:存放具体的代码和数据,data部分是以段来划分的,loadCommends部分的Segment command对应Data中的Segment
Header:
Header的第一行的file offset为0,Data为4个字节(2个16进制代表一个字节),第二行file offset为4,Data为4个字节
Load Commands:
loadCommand是用于加载指令的,它的大小和数目在header中已经被提供,在Mach.h下以loadCommand结构体展示
load_commands紧跟mach_header
VM Address:虚拟内存的地址
VM Size:虚拟内存的size,这里转为16进制为100000000
File Offset:相对于mach-o文件Data数据的偏移量
File Size:就是数据大小,就__PAGEZERO有些特殊,File Size为0代表实际占用mach-o为0,但是它描述了占用虚拟内存的大小,就是上面的4294967296
VM Address:虚拟内存的地址
VM Size:虚拟内存的size
File Offset:相对于mach-o文件Data数据的偏移量
File Size:File Size代表实际占用mach-o的大小
Address:该段在文件中的实际地址+adsr
Size:段大小
Offset:该段在文件中的实际地址
segment段类型如下:
1:__PAGEZERO段: 空指针陷阱段,映射到虚拟内存空间的第一页,用于捕捉对NULL指针的引用;
2: __TEXT 段: 包含了执行代码以及其他只读数据。 为了让内核将它 直接从可执行文件映射到共享内存, 静态连接器设置该段的虚拟内存权限为不允许写。当这个段被映射到内存后,可以被所有进程共享。(这主要用在frameworks, bundles和共享库等程序中,也可以为同一个可执行文件的多个进程拷贝使用)
3: __DATA段: 包含了程序数据,该段可写;
4: __OBJC段: Objective-C运行时支持库;
5: __LINKEDIT段: 含有为动态链接库使用的原始数据,比如符号,字符串,重定位表条目等等。
每种类型的段又会按不同的功能划分为几个区(section, 名称小写,加两个下横线作为前缀)如下:
TEXT 段中的section具体类型和作用:
_text:只有可执行机器码(主程序代码)
_cstring: 去重后的c字符串
_const: 初始化的常量
_stubs: 符号桩,本质上就是一小段会直接跳入到lazybinding的表的对应项指针指向的地址的代码(???)
_stubs_helper: 辅助函数,上述lazybinding表中没有找到符号地址都指向这
_unwind_info:用于存储异常请况信息>
_eh_frame 调试辅助信息
DATA 段中section的具体类型和作用
_data :初始化过得可变的数据,即全局变量和静态变量的存储是放在一块的,都放在全局区(静态区),初始化的全局变量和静态变量在一块区域
_const: 没有初始化过得常量
_bss: 没有初始化的静态变量
_common: 没有初始化过的符号声明
_mod_init_func : 初始化函数:在main之前调用
_mod_term_func: 终止函数,在main返回之后调用
_nl_symbol_ptr: 在非lazy-binding的指针表中 的每个表项中的指针都指向一个在装载过程中,被动态链机器搜索完成的符号(符号的指针)
__la_symbol_ptr:lazy-binding的指针表,每个表项中的指针一开始指向stub_helper(没有找到的符号指针)
注意: 虽然段类型是不一样的,但是加载都是使用LC_SEGMENT_64 这个命令, 只是其中加载的段的信息不同
__text: 主程序代码
__stubs, __stub_helper: 用于动态链接的桩
__cstring: 程序中c语言字符串
__const: 常量
__TEXT,__objc_methname:OC方法名称
__TEXT__objc_methtype:OC方法类型
__TEXT__objc_classname:OC类名
__DATA,__objc_classlist:OC类列表
__DATA,__objc_protollist:OC原型列表
__DATA,__objc_imageinfo:OC镜像信息
__DATA,__objc_const:OC常量
__DATA,__objc_selfrefs:OC类自引用(self)
__DATA,__objc_superrefs:OC类超类引用(super)
__DATA,__objc_protolrefs:OC原型引用
__DATA, __bss: 没有初始化和初始化为0 的全局变量
Dynamic Loader Info:动态链接器所需要使用的信息(重定向,符号绑定,懒加载绑定等..)
后续的信息就是函数起始位置,符号表,字符表,代码签名等.
查看Mach-O信息:
1.查看Mach-O的文件类型
$ file Mach-O文件路径
例如查看APP支持的架构(arm64或者32)如图
2.查看Mach-O的特定部分如图四(头信息或者段信息)
$ otool -h Mach-O文件路径//查看头信息
$ otool -l Mach-O文件路径 | grep cryptid//查看Mach-O文件路径文件是否加密
3.多架构Mach-O文件的处理,可以将arm64和32分开,合并
$ lipo -info 文件路径//看架构信息
$ lipo 文件路径 -thin 架构类型 -output 输出文件路径//导出某种特定架构
$ lipo 文件路径1文件路径2-output 输出文件路径//合并多种架构
Mach-o文件详解:
ASLR:
地址空间布局随机化,是一种避免app被攻击的有效保护;进程每次启动时,地址空间都会被简单地随机化——只是偏移,不是搅乱。实现方式是通过内核将Mach-O的段“平移”某个随机数;
真正的内存地址则是vmaddr + ALSR。
https://www.jianshu.com/p/37f10bb70c50
https://www.exchen.net/mach-o-文件格式解析.html
相关工具:
Mach-OGUI查看工具Mach-OView
MachOView下载编译即可.