MachO文件

MachO文件

Mach-O其实就是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式,类似于windows上的PE格式(Portable Executable),linux上的elf格式(Executable and Linking Format)

Mach-O 文件格式

Mach-O为Mach Object文件格式的缩写,它是一种用于可执行文件,目标代码,动态库的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性

属于MachO格式的常见文件

  • 目标文件.o
  • 库文件
    • .a
    • .dylib
    • .Framework
  • 可执行文件
  • dyld
  • .dsym

File指令

  • 通过$file 文件路径查看文件类型

通用二进制文件(Universal binary)

  • 苹果公司提出的一种程序代码,能同时使用多种架构的二进制文件
  • 同一个程序包中同时为多种架构提供最理想的性能
  • 因为需要存储多种代码,通用二进制的程序通常会比单一平台的二进制程序要大
  • 但是由于两种架构有共通的非执行资源,所以并不会达到单一版本的两倍之多
  • 而且由于执行中只调用一部分代码,运行起来也不需要额外的内存。

lipo命令

  • 使用lifo -info 可以查看MachO文件包含的架构
$lipo -info MachO 文件
  • 使用lifo -thin拆分某种架构
$lipo MachO 文件 -thin架构 -output 输出文件路径
  • 使用lipo -create 合并多种架构
$lipo -create MachO1 MachO2 -output 输出文件路径

MachO 文件结构

因为MachO文件本身是一种文件格式,所以我们一定需要了解其文件内部结构
image.png

Mach-O 的组成机构如图所示包括了

  • Header 包含该二进制文件的一般信息
    • 字节顺序,架构类型,加载指令的数量等
    • 使得可以快速确认一些信息,比如当前文件用于32还是64位,对应的处理器是什么,文件类型是什么
  • Load commands 一张包含很多内容的表
    • 内容包含区域的位置,符号表,动态符号表
  • Data 通常是对象文件中最大的部分
    • 包含Segment的具体数据

Header的数据结构

struct mach_header_64 {
 unit32_t magic; /*魔数,快速定位属于64还是32位*/
cpu_type_t cputype; /*CPU类型,比如ARM*/
cpu_subtype_t cpusubtype;/*CPU的具体类型arm64\arm7*/
uint32_t filetype;/*文件类型,比如可执行文件*/
unit32_t ncmds;/*loadCommands条数*/
uint32_t sizeofcmds;/*LoadCommands的大小*/
uint32_t flags;/*标志位标识二进制文件支持的功能,主要是和系统加载,链接有关*/
uint32_t reserved;/*reserved*/
}

LoadCommands

image.png

Mach-O Data

Data区域是由Segment段和Section节组成。先来看看Segment的组成,代码来自loader.h

Segment 的组成段

#define    SEG_PAGEZERO    "__PAGEZERO" /* 当时 MH_EXECUTE 文件时,捕获到空指针 */
#define    SEG_TEXT    "__TEXT" /* 代码/只读数据段 */
#define    SEG_DATA    "__DATA" /* 数据段 */
#define    SEG_OBJC    "__OBJC" /* Objective-C runtime 段 */
#define    SEG_LINKEDIT    "__LINKEDIT" /* 包含需要被动态链接器使用的符号和其他表,包括符号表、字符串表等 */

Segment 的数据结构

struct segment_command_64 { 
    uint32_t    cmd;        /* LC_SEGMENT_64 */
    uint32_t    cmdsize;    /* section_64 结构体所需要的空间 */
    char        segname[16];    /* segment 名字,上述宏中的定义 */
    uint64_t    vmaddr;        /* 所描述段的虚拟内存地址 */
    uint64_t    vmsize;        /* 为当前段分配的虚拟内存大小 */
    uint64_t    fileoff;    /* 当前段在文件中的偏移量 */
    uint64_t    filesize;    /* 当前段在文件中占用的字节 */
    vm_prot_t    maxprot;    /* 段所在页所需要的最高内存保护,用八进制表示 */
    vm_prot_t    initprot;    /* 段所在页原始内存保护 */
    uint32_t    nsects;        /* 段中 Section 数量 */
    uint32_t    flags;        /* 标识符 */
};

Segment可以说是由多个Section组成的,下面是Section具体的数据结构

struct section_64 { 
    char        sectname[16];    /* Section 名字 */
    char        segname[16];    /* Section 所在的 Segment 名称 */
    uint64_t    addr;        /* Section 所在的内存地址 */
    uint64_t    size;        /* Section 的大小 */
    uint32_t    offset;        /* Section 所在的文件偏移 */
    uint32_t    align;        /* Section 的内存对齐边界 (2 的次幂) */
    uint32_t    reloff;        /* 重定位信息的文件偏移 */
    uint32_t    nreloc;        /* 重定位条目的数目 */
    uint32_t    flags;        /* 标志属性 */
    uint32_t    reserved1;    /* 保留字段1 (for offset or index) */
    uint32_t    reserved2;    /* 保留字段2 (for count or sizeof) */
    uint32_t    reserved3;    /* 保留字段3 */
};

一下是一些常见的Section

Section 账号
__TEXT.__text 主程序代码
__TEXT.__cstring C 语言字符串
__TEXT.__const const 关键字修饰的常量
__TEXT.__stubs 用于 Stub 的占位代码,很多地方称之为桩代码。
__TEXT.__stubs_helper 当 Stub 无法找到真正的符号地址后的最终指向
__TEXT.__objc_methname Objective-C 方法名称
__TEXT.__objc_methtype Objective-C 方法类型
__TEXT.__objc_classname Objective-C 类名称
__DATA.__data 初始化过的可变数据
__DATA.__la_symbol_ptr lazy binding 的指针表,表中的指针一开始都指向 __stub_helper
__DATA.nl_symbol_ptr 非 lazy binding 的指针表,每个表项中的指针都指向一个在装载过程中,被动态链机器搜索完成的符号
__DATA.__const 没有初始化过的常量
__DATA.__cfstring 程序中使用的 Core Foundation 字符串(CFStringRefs)
__DATA.__bss BSS,存放为初始化的全局变量,即常说的静态内存分配
__DATA.__common 没有初始化过的符号声明
__DATA.__objc_classlist Objective-C 类列表
__DATA.__objc_protolist Objective-C 原型
__DATA.__objc_imginfo Objective-C 镜像信息
__DATA.__objc_selfrefs Objective-C self 引用
__DATA.__objc_protorefs Objective-C 原型引用
__DATA.__objc_superrefs Objective-C 超类引用

DYLD

dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作

参数解释

  • LC_SEGMENT_64:将该段(64位)隐射到进程地址空间中
    -LC_DYLD_INFO_ONLY:加载动态链接库信息(重定向地址、弱引用绑定、懒加载绑定、开放函数等的偏移值等信息)
    -LC_SYMTAB:载入符号表地址
  • LC_DYSYMTAB:载入动态符号表地址
  • LC_LOAD_DYLINKER:加载动态加载库
  • LC_UUID:确定文件的唯一标识,crash解析中也会有这个,去检测dysm文件和crash文件是否匹配
  • LC_VERSION_MIN_MACOSX/LC_VERSION_MIN_IPHONEOS:确定二进制文件要求的最低操作系统版本
  • LC_SOURCE_VERSION:构建该二进制文件使用的源代码版本
  • LC_MAIN:设置程序主线程的入口地址和栈大小
  • LC_ENCRYPTION_INFO_64:获取加密信息
  • LC_LOAD_DYLIB:加载额外的动态库
  • LC_FUNCTION_STARTS:定义一个函数起始地址表,使调试器和其他程序易于看到一个地址是否在函数内
    -LC_DATA_IN_CODE:定义在代码段内的非指令的表
    -LC_CODE_SIGNATURE:获取应用签名信息

源码地址

opensource.apple.com
Mach-O文件格式探索

你可能感兴趣的:(MachO文件)