Mach-O 介绍

一、Mach-O 相关概念简介

1.概念描述

Mach-O 为 Mach Object 文件格式的缩写,它是一种用于可执行文件、目标代码、动态库、内核转储的文件格式。作为 a.out 格式的替代,Mach-O 提供了更强的扩展性,并提升了 符号表 中信息的访问速度。

通用二进制
通用二进制代码有两种基本类型:

  • 一种类型就是简单提供两种独立的二进制代码,一个用来对应x86架构,一个用来对应PowerPC架构。
  • 另外一种类型就是只编写一个架构的代码,当另外一种处理环境时让系统自动调用模拟器运行,这会导致运行速度下降。

多重架构二进制(胖二进制)
在 NeXTSTEP ,OPENSTEP 和 Mac OS X 中,可以将多个Mach-O文件组合进一个多重架构二进制(胖二进制)文件中,以用一个单独的二进制文件支持多种架构的指令集。例如,一个Mac OS X中的多重架构二进制可以包含32位和64位的PowerPC代码,或PowerPC和x86的32位代码,甚至包含32位的PowerPC代码,64位PowerPC代码,32位x86代码和64位x86代码。

2.对 Mach-O 文件进行操作

  • 使用 file Mach-O 命令查看 Mach-O 文件类型

    查看文件架构.png
    从上图可以看出该可执行文件是一个通用二进制文件,且包含2种架构:arm_v7 和 arm64。

  • 使用 lifo -info <Mach-O> 命令查看文件架构

    文件架构.png

  • 使用 lipo 命令拆分某种架构

lipo <Mach-O> -thin <架构名> -output <输出文件路径>
  • 使用 lipo 命令合并多种架构
lipo -create <Mach-O1> <Mach-O2> -output <输出文件路径>

二、查看可执行文件

1.使用 otool 命令查看 Mach-O 文件

  • 查看可执行文件的动态链接库
otool -L WeChat
  • 查看头信息
otool -h WeChat
  • 查看是否加密
otool -l WeChat | grep crypt

查看是否加密.png

cryptid 为 0 时表示无加密,即已砸壳;
cryptid 为 1 时表示有加密,即未砸壳。

  • 查看头信息
otool -h DingTalk

2.使用 MachOView 软件查看

用 MachOView 打开可执行文件可以看到有胖二进制文件的结构如下图:


胖二进制.png

可以看到可执行文件包括三个部分:

  • Fat Header:包含架构数量及不同架构指令集的简单信息
  • Executable(ARM_V7):arm_v7 架构对应的指令集
  • Executable(ARM64_ALL):arm64 架构对应的指令集

三、Mach-O 文件结构

Mach-O.png

Mach-O主要分为三个部分:HeaderLoad commandsData

  • Header:包含字节顺序、架构类型、加载指令的数量等,使得系统可以快速确认一些信息,比如当前文件用于32位还是64位,对应的处理器是什么、文件类型是什么。
  • Load commands:它是一张包含很多内容的表,内容包括区域的位置、符号表、动态符号表等。每个加载指令都包含一个元信息,比如指令类型、名称、在二进制文件中的位置等等。
  • Data:通常是对象文件中最大的部分。主要包含代码、数据,例如符号表,动态符号表等等。Data 中包含若干个 segment (段),每个 segment 下又有若干个 section(节)。

1. Header

头文件就是该可执行文件的信息概要

头部.png

该部分结构可以 打开 <macho-o/loader.h> 查看

/*
 * 64位结构头
 */
struct mach_header_64 {
    uint32_t    magic;      // Mach-O 文件的
    cpu_type_t  cputype;    // CPU 架构
    cpu_subtype_t   cpusubtype; // CPU 架构子版本
    uint32_t    filetype;   // 文件类型。常见的有 MH_OBJECT(目标文件)、MH_EXECUTE(可执行文件)、MH_DYLIB(动态库)、MH_DYLINKER(动态链接器)
    uint32_t    ncmds;      // 加载指令数量
    uint32_t    sizeofcmds; // 加载指令大小
    uint32_t    flags;      // dyld 加载需要的一些标记
    uint32_t    reserved;   // 64 位的保留字段
};

2. Load commands

Load commands 作用是让系统知道如何加载文件中的信息,对系统内核加载器和动态链接器起引导作用。

部分加载指令.png

从上图可以看到,Load command 包含以下部分:

  • LC_SEGMENT_64:定义一个段,加载后被映射到内存中,包括里面的节。相当与一个数据索引,指明了不同类型数据的地址和大小
  • LC_DYLD_INFO_ONLY:记录了有关链接的重要信息,包括 __LINKEDIT 中动态链接相关信息的具体偏移和大小
  • LC_SYMTAB:为文件定义符号表和字符串表,在链接文件时被连接器使用,同时也用于调试器映射符号到源文件。
  • LC_DYSYMTAB:将符号表中给出符号的额外符号信息提供给动态链接器
  • LC_LOAD_DYLINKER:默认的加载器路径
  • LC_UUID:用于标识 Mach-O 文件的 ID,也用于奔溃堆栈和符号文件的对应解析
  • LC_VERSION_MIN_IPHONEOS:系统要求的最低版本
  • LC_SOURCE_VERSION:构建二进制文件的源代码版本号
  • LC_MAIN:程序的入口。dyld 获取改地址,然后跳转到该处执行
  • LC_ENCRYPTION_INFO_64:文件是否加密标志,加密内容的偏移和大小
  • LC_LOAD_DYLIB:依赖的动态库
  • LC_RPATH:Runpath Search Paths, @rpatch 搜索的路径
  • LC_FUNCTION_STARTS:函数起始地址表,使用调试器和其他程序能很容易看到一个地址是否在函数内
  • LC_DATA_IN_CODE:定义在代码段内的非指令的表
  • LC_CODE_SIGNATURE:代码签名信息

3. Data

LC_SEGMENT_64 加载指令映射的就是 Data 中的数据偏移和大小,该文件组要包含四个段:

  • __PAGEZERO:空指针陷阱段,映射到虚拟内容控件的第一页,用于捕捉对 NULL 指针的引用
  • __TEXT:代码段/只读数据段
  • __DATA:读取和写入数据的段
  • __LINKEDIT:动态链接器需要使用的信息,包括重定位信息、绑定信息、懒加载信息等

下面是 64 位 segment 段的数据结构

struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;        // 指令类型
    uint32_t    cmdsize;    // 指令大小
    char        segname[16];// 段的名字
    uint64_t    vmaddr;     // 映射到虚拟地址的偏移
    uint64_t    vmsize;     // 映射到虚拟地址的大小
    uint64_t    fileoff;    // 对应当前架构文件的偏移
    uint64_t    filesize;   // 文件的大小
    vm_prot_t   maxprot;    // 段页面的最高内存保护
    vm_prot_t   initprot;   // 初始内存保护
    uint32_t    nsects;     // 包含的节的个数
    uint32_t    flags;      // 段页面的标志
};

段中包含的节的数据结构

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   // 节的名字
    char        segname[16];    // 所属段的名字
    uint64_t    addr;       // 映射到虚拟地址的偏移
    uint64_t    size;       // 节的大小
    uint32_t    offset;     // 节在当前架构文件中的偏移
    uint32_t    align;      // 节的字节对齐大小
    uint32_t    reloff;     // 重定位入口的文件偏移
    uint32_t    nreloc;     // 重定位入口的个性
    uint32_t    flags;      // 节的类型和属性
    uint32_t    reserved1;  // 保留位
    uint32_t    reserved2;  // 保留位
    uint32_t    reserved3;  // 保留位
};

__Text 段中包含的节

  • __text:程序可执行的代码区域
  • __stubs:间接符号存根,跳转到懒加载指针表
  • __stub_helper:帮助解决懒加载符号加载的辅助函数
  • __objc_methname:方法名
  • __objc_classname:类名
  • __objc_methtype:方法签名
  • cstring:只读的 C 风格字符串,包含 OC 的部分字符串和属性名

__Data 段中包含的节

  • __nl_symbol_ptr:非懒加载指针表,在 dylib 加载时立即绑定值
  • __la_symbol_ptr:懒加载指针表,第一次调用是才会绑定值
  • __got:非懒加载全局指针表
  • __mod_init_func:constructor 函数
  • __mod_term_func:destructor 函数
  • __cfstring:OC 字符串
  • __objc_classlist:程序中类的列表
  • __objc_nlclslist:程序中自己实现了+load 方法的类
  • __objc_protolist:协议列表
  • __objc_classrefs:被引用的类列表
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 227,533评论 6 531
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,055评论 3 414
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 175,365评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,561评论 1 307
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,346评论 6 404
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,889评论 1 321
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,978评论 3 439
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,118评论 0 286
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,637评论 1 333
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,558评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,739评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,246评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,980评论 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,362评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,619评论 1 280
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,347评论 3 390
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,702评论 2 370

推荐阅读更多精彩内容