Mach-O类型的文件
-
Mach-O是一种文件的格式; 是iOS/Mac OS上存储程序以及库的标准格式Mach Object
-
Mach-O格式的文件-
EXTERNAL_HEADERS/mach-o/fat.h loader.h
-
#define MH_OBJECT 0x1 /* 目标文件*/
#define MH_EXECUTE 0x2 /* 可执行文件 */
#define MH_FVMLIB 0x3 /* fixed VM shared library file */
#define MH_CORE 0x4 /*核心转储文件 */
#define MH_PRELOAD 0x5 /* preloaded executable file */
#define MH_DYLIB 0x6 /* dynamically bound shared library */
#define MH_DYLINKER 0x7 /* dynamic link editor */
#define MH_BUNDLE 0x8 /* dynamically bound bundle file */
#define MH_DYLIB_STUB 0x9 /* shared library stub for static */
/* linking only, no section contents */
#define MH_DSYM 0xa /* companion file with only debug */
/* sections */
#define MH_KEXT_BUNDLE 0xb /* x86_64 kexts */
-
常见的
Mach-O格式的文件-
MH_OBJECT目标文件-
.o -
.a/ .framework静态库- 静态库即多个
.o文件存放在一起实现特定的功能
- 静态库即多个
-
-
MH_EXECUTE可执行文件-
.app/MyApp -
.out
-
-
MH_DYLIB动态库-
.framework/xxx -
/dylib
-
-
MH_DYLINKER动态链接器usr/lib/dyld
-
MH_DSYM存储二进制文件符号信息的文件.dYSM/Contents/Resources/DWARF/MyApp
image.png
-
-
查看项目
target的Mach-O文件的类型-
MH_EXECUTE类型
image.png
-
Mach-O文件的基本结构
-
Mach-O包含三个主要区域-
Header: 文件类型, 目标架构 -
Load command: 描述文件在虚拟内存中的逻辑与布局 -
Raw segment date:Load command中定义的原始数据
-
image.png
- 使用
otool查看Mach-O文件
➜ otool -h DingTalk
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedface 12 9 0x00 2 79 7860 0x00218085
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedfacf 16777228 0 0x00 2 79 8672 0x00218085
- 使用
file查看Mach-O文件
# 该Mach-O文件类型是 executable 只有armv7指令集
➜ HomeDesign3D China.app file HomeDesign3D\ China
HomeDesign3D China: Mach-O executable arm_v7
# 该Mach-O文件 类型是 executable; 有armv7, arm64指令集是一个通用二进制文件
➜ DingTalk.app file DingTalk
DingTalk: Mach-O universal binary with 2 architectures: [arm_v7: Mach-O executable arm_v7] [arm64]
DingTalk (for architecture armv7): Mach-O executable arm_v7
DingTalk (for architecture arm64): Mach-O 64-bit executable arm64
-
通用二进制文件
-
universal binary或者Fat binary -
含有多个不同架构的独立二进制文件; 故体积较大
-
执行时, 只会选择一种架构的二进制文件
image.png
-
-
使用
MachOView查看Mach-O文件- 以查看
DingTalk为例
- 以查看
image.png
image.png
image.png
-
RAW与RVA-
RV: 虚拟地址 -
RAW: 文件偏移地址(物理地址) -
RVA: 相对虚拟地址的偏移
-
Mach-O结构详解
-
以分析
DingTalkarm64指令集为例子
Mach Header(arm64)
-
Magic Number: 魔数, 表示支持设备的CPU位数-
oxFEEDFACE: 表示32位二进制 -
oxFEEDFACF: 表示64位二进制
-
-
cputype和cpusubtype:CPU类型和子类型 -
filetype:Mach-O文件类型 -
ncmds和sizeofcmds: 用于加载器的加载命令的条数和大小 -
flags: 动态链接器dyld的标志
image.png
LC_SEGMENT / LC_SEGMENT_64段的详解
-
常见段
-
__PAGEZERO: 空指针陷阱段 -
_TEXT: 程序代码段 -
__DATA: 程序数据段 -
__RODATA:read only程序只读数据段 -
__LINKEDIT: 链接器使用段
-
image.png
-
section段常见字段-
Segment Name: 该Segment的名称, 用于load_segment -
VM Address: 该段的虚拟物理地址 -
VM Size: 该段所需要分配的虚拟内存大小(字节) -
File Offset: 该段在文件中的偏移量 -
File Size: 该段在文件中占据的字节数 -
Maximum VM Protection: 段的页面所需要的最高内存保护-
ox1: x 执行 -
ox2: w 写 -
0x4: r 读
-
-
Initial VM Protection: 段页面初始化的内存保护 -
Number of Sections: 段中section区的数量 -
Flags: 其他标志位
-
tips: 小结:
根据
LC_SEGMENT命令 设置进程虚拟内存对于每一个段, 将其内容从
Mach-O文件加载到内存中即从
Mach-O文件中的偏移量为File Offset处加载File Size字节内容到虚拟内存地址VM Address处VM Size字节空间内
image.png
段中区section详解
-
常见区
section-
__text: 主程序代码 -
__stubs, __stub_helper: 用于动态链接的桩 -
__cstring: 程序中c语言字符串 -
__const: 常量 -
__RODATA,__objc_methname:OC方法名称 -
__RODATA,__objc_methntype:OC方法类型 -
__RODATA,__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 的全局变量
-
image.png
Load Commmands加载命令中其他信息
-
LC_MAIN- 设置程序主线程
入口地址和栈大小
- 设置程序主线程
image.png
-
LC_CODE_SIGNATURE-
包含
Mach-O文件的代码签名 -
没有签名或签名不正确, 该进程会被kill, 程序崩溃
-
image.png
Mach-O中动态库的加载
-
动态库来源
-
系统提供的动态库
-
第三方动态库
-
-
如图:
DingTalk使用大量的系统动态库-
即
Mach-O镜像中有很多对外部库以及符号的引用 -
这些引用将在程序启动时, 由
动态链接器 /usr/lib/dyld来执行符号绑定
-
image.png
-
加载动态链接器
-
LC_LOAD_DYLINKER: 内核执行该命令时, 启动dyld
-
image.png
-
获取符号表
-
LC_SYMTAB: 符号地址表 -
LC_DYSYMTAB: 动态符号地址表
-
-
加载动态库
-
LC_LOAD_WEAK_DYLIB -
LC_LOAD_DYLIB
-
动态库加载流程小结
1.0 首先启动
dyld动态链接器; 内核根据LC_LOAD_DYLINKER启动/usr/lib/dyld2.0 如果
Mach-O文件中使用了外部定义的符号或函数, 则会在文本段__TEXT有__stubs, __stub_helper区; 区内放着本地未被定义的符号; 编译器在编译源码时会创建对这些未定义符号桩区的调用3.0
dyld运行时, 会在符号桩区调用地址上; 添加JMP 到 真实函数地址的指令4.0 至于
dyld怎么找到指定的动态库中指定的函数地址? 此时dyld将加载Load Command中的LC_LOAD_DYLIB命令5.0
LC_LOAD_DYLIB(动态库),dyld将加载每一个指定的库且搜寻匹配的符号6.0 当符号匹配时, 将在符号表(由
dyld加载LC_SYMTAB,LC_DYSYMTAB获取)查找对应的函数/符号地址














网友评论