Mach-O文件结构

主要内容:

  1. 理解可执行文件
  2. 理解Mach-O文件
  3. Mach-O文件结构
  4. Mach Header
  5. Load Commands
  6. Data
  7. 理解大小端模式
  8. 理解通用二进制文件

一、理解可执行文件

1.可执行文件
  1. 进程,其实就是可执行文件在内存中加载得到的结果;
  2. 可执行文件必须是操作系统可理解的格式,而且不同系统的可执行文件的格式也是不同的;
2.不同平台的可执行文件
  • Linux:ELF文件
  • WindowsPE32/PE32+文件
  • OS和iOSMach-O(Mach Object)文件

二、理解Mach-O文件

作为iOSiPadOSmacOS平台的可执行文件格式,Mach-O文件涉及App启动运行、bitcode分析、 crash符号化等诸多多个功能:

1. Mach-O文件
  1. Mach-O文件是iOSiPadOSmacOS平台的可执行文件格式。对应系统通过应用二进制接口(application binary interface,缩写为ABI)来运行该格式的文件;
  2. Mach-O格式用来替代BSD系统中的a.out格式,保存了在编译和链接过程中产生的机器代码和数据,从而为静态链接和动态链接的代码提供单一文件格式。
  3. Mach-O提供了更强的扩展性,以及更快的符号表信息访问速度;
2.Mach-O格式的常见文件类型
  1. Executable:可执行文件(.out .o);
  2. Dylib:动态链接库;
  3. Bundle:不能被链接,只能在运行时使用dlopen()加载;
  4. Image:包含ExecutableDylibBundle
  5. Framework:包含Dylib、资源文件和头文件的文件夹;

三、Mach-O文件结构

1.查看Mach-O的两种方法
  1. 使用MachOView软件,可直接查看MachO文件的结构;
  2. 使用终端命令objdump
2.查看Mach-O文件结构

使用MachOView查看Mach-O,效果如下:

image

Mach-O文件中包含三个主要的部分:

  1. Header:头部,描述CPU类型、文件类型、加载命令的条数大小等信息;
  2. Load Commands:加载命令,其条数和大小已经在header中被提供;
  3. Data:数据段;

其他的信息还有:

  1. Dynamic Loader Info:动态库加载信息
  2. Function Starts:入口函数
  3. Symbol Table:符号表
  4. Dynamic Symbol Table: 动态库符号表
  5. String Table:字符串表

四、Mach Header(可执行文件头)

1.功能总结
  1. Header是链接器加载时最先读取的内容,因为它决定了一些基础架构系统类型等信息;
  2. Header包含整个Mach-O文件的关键信息,如CPU类型文件类型加载命令的条数大小等信息,使得系统能够迅速定位Mach-O文件的运行环境;
  3. Header针对32位和64位架构的CPU,分别对应mach_headermach_header_64的结构体;
2.源码分析

Header被定义在loader.h文件中,具体代码如下:

struct mach_header_64 {
    uint32_t    magic;          // 32位或者64位,系统内核用来判断是否是mach-o格式
    cpu_type_t  cputype;        // CPU架构类型,比如ARM
    cpu_subtype_t   cpusubtype; // CPU的具体类型,例如arm64、armv7
    uint32_t    filetype;       // mach-o文件类型, 可执行文件、目标文件或者静态库和动态库
    uint32_t    ncmds;          // LoadCommands加载命令的条数(加载命令紧跟header之后)
    uint32_t    sizeofcmds;     // 全部LoadCommands加载命令的大小
    uint32_t    flags;          // 标志位标识二进制文件支持的功能,主要是和系统加载、链接有关
    uint32_t    reserved;       // 保留字段(相比于32位多出的字段)
    };

由于可执行文件目标文件或者静态库动态库等都是Mach-O格式,所以才需要filetype来说明。常用的文件类型有以下几种:

#define MH_OBJECT   0x1     /* 目标文件*/
#define MH_EXECUTE  0x2     /* 可执行文件*/
#define MH_DYLIB    0x6     /* 动态库*/
#define MH_DYLINKER 0x7     /* 动态链接器*/
#define MH_DSYM     0xa     /* 存储二进制文件符号信息,用于debug分析*/
3.MachOView演示
image

五、分析Load Commands

1.功能总结
  1. Load Commands是加载命令的列表,用于描述Data在二进制文件和虚拟内存中的布局信息;
  2. Load Commands记录了很多信息,例如动态链接器的位置、程序的入口、依赖库的信息、代码的位置、符号表的位置等;
  3. Load commands由内核定义,不同版本的command数量不同,其条数和大小记录在header中;
  4. Load commandstype是以LC_为前缀常量,譬如LC_SEGMENTLC_SYMTAB等;
2..代码分析

Load Command被定义在loader.h文件中,具体代码如下:

struct load_command {
    uint32_t cmd;       /* 加载命令的类型 */
    uint32_t cmdsize;   /* 加载命令的大小 */
};

每个Load Command都有独立的结构,但是所有结构的前两个字段是固定的。比如LC_SEGMENT_64,这是一个读取segmentsection有关命令,具体代码如下:

struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;          // 表示加载命令类型
    uint32_t    cmdsize;      // 表示加载命令大小(还包括了紧跟其后的nsects个section的大小)
    char        segname[16];  // 16个字节的段名字
    uint64_t    vmaddr;       // 段的虚拟内存起始地址
    uint64_t    vmsize;       // 段的虚拟内存大小
    uint64_t    fileoff;      // 段在文件中的偏移量
    uint64_t    filesize;     // 段在文件中的大小
    vm_prot_t   maxprot;      // 段页面所需要的最高内存保护(4 = r,2 = w,1 = x)
    vm_prot_t   initprot;     // 段页面初始的内存保护
    uint32_t    nsects;       // 段中section数量
    uint32_t    flags;        // 标志位
};

六、Data

1.功能总结
  1. Data中存储了实际的数据与代码,主要包含方法、符号表、动态符号表、动态库加载信息(重定向、符号绑定等)等;
  2. Data中的排布完全按照Load Command中的描述;
  3. DataSegment(段)和 Section (节)的方式来组成,通常,Data拥有多个segment,每个segment可以有零到多个section节;
  4. 不同的segment都有一段虚拟地址映射到进程的地址空间;

几乎所有的Mach-O文件都包含3segment

  1. __TEXT:代码段,只读可执行,存储函数的二进制代码(__text)常量字符串(__cstring)OC的类/方法名等信息
  2. __DATA:数据段, 可读可写,存储OC的字符串(__cfstring),以及运行时的元数据:class/protocol/method,以及全局变量,静态变量等;
  3. __LINKEDIT:只读,存储启动App需要的信息,如 bind & rebase 的地址、函数的名称和地址等信息;
2.源码分析

Data区中,Section占了很大的比例,而且在Mach-O中集中体现在__TEXT__DATA两段里。

Section被定义在loader.h文件中,具体代码如下:

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   // 当前section的名称
    char        segname[16];    // section所在的segment名称
    uint64_t    addr;       // 内存中起始位置
    uint64_t    size;       // section大小
    uint32_t    offset;     // section的文件偏移
    uint32_t    align;    // 字节大小对齐
    uint32_t    reloff;     // 重定位入口的文件偏移
    uint32_t    nreloc;   // 重定位入口数量
    uint32_t    flags;      // 标志,section的类型和属性
    uint32_t    reserved1;  // 保留(用于偏移量或索引)
    uint32_t    reserved2;  // 保留(用于count或sizeof)
    uint32_t    reserved3;  // 保留
};

七、理解大小端模式

分析Mach-O文件时,经常会看到内存地址相关的内容,这里就涉及到了大小端模式的概念;

  1. 小端模式:数据的低字节,保存在内存的低地址;
  2. 大端模式:数据的低字节,保存在内存的高地址;

iOS设备的处理器是基于ARM架构的,默认是采用小端模式(低字节放低位)读取数据的,而网络和蓝牙传输数据通常是用的大端模式(低字节放高位):

下面以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value

Little-Endian: 低地址存放低位,如下:
低地址 ------------------> 高地址
0x78  |  0x56  |  0x34  |  0x12

Big-Endian: 低地址存放高位,如下:
低地址 -----------------> 高地址
0x12  |  0x34  |  0x56  |  0x78
内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78

八、理解通用二进制文件

1.基本概念
  1. 通用二进制文件的存储结构,是将多种架构的Mach-O文件打包在一起,CPU在读取该二进制文件时可以自动检测并选用合适的架构;
  2. 通用二进制文件会同时存储多种架构,所以比单一架构的二进制文件大很多,会占用大量的磁盘空间。但由于系统运行时会自动选择最合适的,不相关的架构代码,不会占用内存空间,所以执行效率提高了;
  3. 通用二进制格式也被称为胖二进制格式;
2.通用二进制格式分析

通用二进制格式的定义在<mach-o/fat.h>中:

  1. 下载xnu后,依次在 xnu -> EXTERNAL_HEADERS ->mach-o中找到该文件。
  2. 通用二进制文件有两个重要结构体:fat_headerfat_arch

两个结构体的定义如下:

/*
 - magic:可以让系统内核读取该文件时知道是通用二进制文件
 - nfat_arch:表明下面有多个fat_arch结构体,即通用二进制文件包含多少个Mach-O
 */
struct fat_header {
    uint32_t    magic;      /* FAT_MAGIC */
    uint32_t    nfat_arch;  /* number of structs that follow */
};

/*
 fat_arch是描述Mach-O
 - cputype 和 cpusubtype:说明Mach-O适用的平台
 - offset(偏移)、size(大小)、align(页对齐)描述了Mach-O二进制位于通用二进制文件的位置
 */
struct fat_arch {
    cpu_type_t  cputype;    /* cpu specifier (int) */
    cpu_subtype_t   cpusubtype; /* machine specifier (int) */
    uint32_t    offset;     /* file offset to this object file */
    uint32_t    size;       /* size of this object file */
    uint32_t    align;      /* alignment as a power of 2 */
};

参考链接

  1. xnu
  2. Mach-O官方源码
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 上一篇说到源码经过预处理、编译、汇编之后生成目标文件,这一章介绍一下iOS、Mac OS中目标文件的格式Mach-...
    Tenloy阅读 2,007评论 2 9
  • 前言 在学习iOS逆向的过程中,发现在解密可执行文件 dumpdecrypted 砸壳原理时需要用到 Mach-O...
    云霄_云霄阅读 5,275评论 0 3
  • Mach-O类型的文件 Mach-O是一种文件的格式; 是iOS/Mac OS上存储程序以及库的标准格式Mach ...
    其字德安阅读 5,374评论 0 13
  • 本文源码从苹果开源官网获得 什么是Mach-O Mach-O 为 Mach Object文件格式的缩写,是用于 i...
    Rimson阅读 498评论 0 0
  • 转载自 http://www.blogfshare.com/ioss-mach-o.html[http://www...
    厨子阅读 375评论 0 1