MachO文件

1、MachO文件的概念

Mach-O 其实是Mach Object文件格式的缩写,是 mac 以及 iOS 上可执行文件的格式, 类似于 windows 上的 PE 格式 (Portable Executable), linux 上的 elf 格式 (Executable and Linking Format) 。常⻅的 .o.a .dylib Frameworkdyld .dsym

2、整体结构如下图

MachO结构图.png

2.1、Header(头部)

Header表明该文件是 Mach-O 格式,指定目标架构,还有一些其他的文件属性信 息,文件头信息影响后续的文件结构安排。
与 Mach-O 文件格式有关的结构体定义都可以从xnu源码loader.h中找到。

32位和64位的mach_header的数据结构如下

/*
 * The 32-bit mach header appears at the very beginning of the object file for
 * 32-bit architectures.
 */
struct mach_header {
    uint32_t    magic;      /* mach magic number identifier */
    cpu_type_t  cputype;    /* cpu specifier */
    cpu_subtype_t   cpusubtype; /* machine specifier */
    uint32_t    filetype;   /* type of file */
    uint32_t    ncmds;      /* number of load commands */
    uint32_t    sizeofcmds; /* the size of all the load commands */
    uint32_t    flags;      /* flags */
};

/* Constant for the magic field of the mach_header (32-bit architectures) */
#define MH_MAGIC    0xfeedface  /* the mach magic number */
#define MH_CIGAM    0xcefaedfe  /* NXSwapInt(MH_MAGIC) */

/*
 * The 64-bit mach header appears at the very beginning of object files for
 * 64-bit architectures.
 */
struct mach_header_64 {
    uint32_t    magic;      /* mach magic number identifier */
    cpu_type_t  cputype;    /* cpu specifier */
    cpu_subtype_t   cpusubtype; /* machine specifier */
    uint32_t    filetype;   /* type of file */
    uint32_t    ncmds;      /* number of load commands */
    uint32_t    sizeofcmds; /* the size of all the load commands */
    uint32_t    flags;      /* flags */
    uint32_t    reserved;   /* reserved */
};
  • magic 魔数(特征数字),用来标记当前设备是大端序还是小端序。
  • cputype 标识 CPU 的架构,例如 ARM、ARM64、X86_64 等。
  • cpusubtype 标识 CPU 的具体类型,区分不同版本的处理器。
  • filetype 由于 Mach-O 支持多种类型文件,所以此处引入了 filetype 字段来标明( .o.a .dylib Frameworkdyld .dsym等)
  • ncmds Mach-O 文件中加载命令(load commands)的条数。
  • sizeofcmds Mach-O文件中加载命令(load commands)的总大小。
  • flags 标识着 Mach-O 文件的一些重要信息(可以 loader.h中查看结构,其中MH_PIE启用ASLR)
  • reserved 64位预留字段

2.2、Load commands

Load commands 是一张包含很多内容的表。内容包括区域的位置、符号表、动态符号表 等。用于告诉loader如何设置并加载二进制数据,对系统内核加载器和动态链接器(dyld)起指导作用。
load_command的数据结构

struct load_command {
    uint32_t cmd;       /* type of load command */
    uint32_t cmdsize;   /* total size of command in bytes */
};

前 4 个字节表示类型,不同类型的 load command 作用不一样,紧跟其后的 4 个字节表示该 load command 的大小.
可以通过MachOView软件来查看类容(类型)

64位系统load command内容.png

  • LC_SEGMENT_64 将文件中(32位或64位)的段映射到进程地 址空间中
  • LC_DYLD_INFO_ONLY 动态链接相关信息
  • LC_SYMTAB 符号地址
  • LC_DYSYMTAB 动态符号表地址
  • LC_LOAD_DYLINKER dyld加载
  • LC_UUID 文件的UUID
  • LC_VERSION_MIN_MACOSX 支持最低的操作系统版本
  • LC_SOURCE_VERSION 源代码版本
  • LC_MAIN 设置程序主线程的入口地址和栈大小
  • LC_LOAD_DYLIB 依赖库的路径,包含三方库
  • LC_FUNCTION_STARTS 函数起始地址表
  • LC_CODE_SIGNATURE 代码签名
  • LC_ENCRYPTION_INFOLC_ENCRYPTION_INFO_64:加密信息,如果是从App Store上下载的应用,外面被加了一层壳,对应的加密标记(Crypt ID)不为0,如果不是App Store上下载的应用(例如PP助手上),或这个已经被脱过壳的,加密标记(Crypt ID)便为0

LC_SEGMENT(段)

LC_SEGMENT_64LC_SEGMENT(32位系统)是加载的主要命令,翻译成中文叫做“段”,它负责指导内核来设置进程的内存空间,说白了,只要是这个类型的 load command,系统会将其指示的内容全部加载到指定的虚拟内存地址上来。
MachOView看到的内容

LC_SEGMENT_64.png

通过mach-o/loader.h 找到数据结构

struct segment_command { /* for 32-bit architectures */
    uint32_t    cmd;        /* LC_SEGMENT */
    uint32_t    cmdsize;    /* includes sizeof section structs */
    char        segname[16];    /* segment name */
    uint32_t    vmaddr;     /* memory address of this segment */
    uint32_t    vmsize;     /* memory size of this segment */
    uint32_t    fileoff;    /* file offset of this segment */
    uint32_t    filesize;   /* amount to map from the file */
    vm_prot_t   maxprot;    /* maximum VM protection */
    vm_prot_t   initprot;   /* initial VM protection */
    uint32_t    nsects;     /* number of sections in segment */
    uint32_t    flags;      /* flags */
};

struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;        /* LC_SEGMENT_64 */
    uint32_t    cmdsize;    /* includes sizeof section_64 structs */
    char        segname[16];    /* segment name */
    uint64_t    vmaddr;     /* memory address of this segment */
    uint64_t    vmsize;     /* memory size of this segment */
    uint64_t    fileoff;    /* file offset of this segment */
    uint64_t    filesize;   /* amount to map from the file */
    vm_prot_t   maxprot;    /* maximum VM protection */
    vm_prot_t   initprot;   /* initial VM protection */
    uint32_t    nsects;     /* number of sections in segment */
    uint32_t    flags;      /* flags */
};
  • cmd 该加载命令类型,为LC_SEGMENT_64(64位)或LC_SEGMENT(32位)
  • cmdsize 该加载命令大小,包括segement下session结构所占大小
  • segname[16] segment 名字
  • vmaddr 为当前segment分配的虚拟内存地址
  • vmsize 为当前segment分配的虚拟内存大小
  • fileoff 当前segment在 Mach-O 文件中的偏移量
  • filesize 当前segment在 Mach-O 文件中占用的字节
  • maxprot segment所在页所需要的最高内存保护
  • initprot segment所在页原始内存保护
  • nsects segment中section数量
  • flags 标识符

大致可以这么理解:系统 Mach-O 从fileoff处加载filesie大小的内容到虚拟内存vmaddr处,大小为vmsizesegment页权限initport进行初始化,这些权限可以被修改,但是不能超过maxprot的值。通俗来说就是,fileofffilesie指导和说明了内容从哪里来,vmaddrvmsize指导和说明了文件到哪里去。需要留意,对某些segment来说,vmsize可能会大于 filesize,如__DATA、__LINKEDIT。

segname分类(用下划线和大写字母组成)

  • __PAGEZERO:静态链接器创建了__PAGEZERO命名的段作为可执行文件的第一个段,该段在文件中所占大小为0,在 32 位系统上,加载到虚拟内存中是 0x4000,也就是 16kb,在 64 位系统上,加载到虚拟未存中是 0x100000000,也就是 4GB。是一个不可读、不可写、不可执行的空间,能够在空指针访问时抛出异常。
  • __TEXT:代码段,里面包含了可执行代码和其他一些只读数据,该段是可读、可执行,但是不可写。
  • __DATA:数据段,里面主要是存放将会被更改的数据,该段是可读、可写,但不可执行。
  • __LINKEDIT:包含需要被动态链接器使用的符号和其他表,包括符号表、字符串表等,可读,但不可写不可执行。

segname下的section
segname__TEXT__DATA两个segment可以进一步分解为section。之所以按照segment -> section的结构组织方式,是因为在同一个segment下的section,可以控制相同的权限,也可以不完全按照Page的大小进行内存对其,节省内存的空间。而segment对外整体暴露,在程序载入阶段映射成一个完整的虚拟内存,更好的做到内存对齐。

mach-o/loader.h文件中可以找到section的数据结构

struct section { /* for 32-bit architectures */
    char        sectname[16];   /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint32_t    addr;       /* memory address of this section */
    uint32_t    size;       /* size in bytes of this section */
    uint32_t    offset;     /* file offset of this section */
    uint32_t    align;      /* section alignment (power of 2) */
    uint32_t    reloff;     /* file offset of relocation entries */
    uint32_t    nreloc;     /* number of relocation entries */
    uint32_t    flags;      /* flags (section type and attributes)*/
    uint32_t    reserved1;  /* reserved (for offset or index) */
    uint32_t    reserved2;  /* reserved (for count or sizeof) */
};

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint64_t    addr;       /* memory address of this section */
    uint64_t    size;       /* size in bytes of this section */
    uint32_t    offset;     /* file offset of this section */
    uint32_t    align;      /* section alignment (power of 2) */
    uint32_t    reloff;     /* file offset of relocation entries */
    uint32_t    nreloc;     /* number of relocation entries */
    uint32_t    flags;      /* flags (section type and attributes)*/
    uint32_t    reserved1;  /* reserved (for offset or index) */
    uint32_t    reserved2;  /* reserved (for count or sizeof) */
    uint32_t    reserved3;  /* reserved */
};
  • sectname:section名字
  • segname:section所在的segment名称
  • addr:section所在的内存地址
  • size:section的大小
  • offset:section所在的文件偏移
  • align:section的内存对齐边界 (2 的次幂)
  • reloff:重定位信息的文件偏移
  • nreloc:重定位条目的数目
  • flags:标志属性
  • reserved:保留字段

使用 MachOView 进行查看OC工程的macho文件


oc的segname下的section.png

各个section作用如下:

  • __TEXT.__text:主程序代码
  • __TEXT.__stubs__TEXT.__stub_helper:用于帮助动态链接器绑定符号
  • __TEXT.__const:const关键字修饰的常量
  • __TEXT.__objc_methodname:OC方法名
  • __TEXT.__cstring:只读的C语言字符串
  • __TEXT.__objc_classname:OC类名
  • __TEXT.__objc_methtype:OC方法类型(方法签名)
  • __TEXT.__gcc_except_tab__ustring__unwind_info:GCC编译器自动生成,用于确定异常发生是栈所对应的信息(包括栈指针、返回地址及寄存器信息等)
  • __DATA.__got:全局非懒绑定符号指针表
  • __DATA.__la_symbol_ptr:懒绑定符号指针表
  • __DATA.__mod_init_func:C++类的构造函数
  • __DATA.__const:未初始化过的常量
  • __DATA.__cfstring:Core Foundation字符串
  • __DATA.__objc_classlist:OC类列表
  • __DATA.__objc_nlclslist:实现+load方法的 OC 类列表
  • __DATA.__catlist:OC 分类(Category)列表
  • __DATA.__protolist:OC 协议(Protocol)列表
  • __DATA.__imageinfo:镜像信息,可用它区别 OC 1.0与2.0
  • __DATA.__const:OC 初始化过的常量
  • __DATA.__selrefs:OC 选择器(SEL)引用列表
  • __DATA.__protorefs:OC 协议引用列表
  • __DATA.__classrefs:OC 类引用列表
  • __DATA.__superrefs:OC 超类(即父类)引用列表
  • __DATA.__ivar:OC 类的实例变量
  • __DATA.__objc_data:OC 初始化过的变量
  • __DATA.__data:实际初始化数据段
  • __DATA.__common:未初始化过的符号申明
  • __DATA.__bss:未初始化的全局变量

使用 MachOView 进行查看Swift工程的macho文件

swift5的macho.png

在Swift的macho文件中,types是4字节存储信息,存储的是相对地址
注意:swift5的macho会多出几个section(目前知道的就是下面几个,后面了解再补充)

__swift5_types:存储的是Class、Struct、Enum的描述(Descriptor)偏移信息
__swift5_fieldmd:存储的是属性描述(属性信息)的偏移信息
__swift5_reflstr:存储的是属性名称
__swift5_protos:存储的是代理的描述

2.3、Data

Data 区主要就是负责代码和数据记录的。Mach-O 是以 Segment 这种结构来组织数据 的,一个 Segment 可以包含 0 个或多个 Section。根据 Segment 是映射的哪一个 Load Command,Segment 中 section 就可以被解读为是是代码,常量或者一些其他的数据类 型。在装载在内存中时,也是根据 Segment 做内存映射的。

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

推荐阅读更多精彩内容

  • MachO文件 Mach-O是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式。是一种用...
    忻凯同学阅读 1,399评论 0 1
  • 前言 本篇文章主要分析MachO文件(也称作二进制可执行文件),相信大家在平时开发中都会碰到MachO文件这个概念...
    深圳_你要的昵称阅读 642评论 0 2
  • 在上一篇文章中,咱们已经简单的提到了MachO,在用Framework做代码注入的时候,必须先向MachO的Loa...
    蚂蚁也疯狂阅读 1,773评论 1 4
  • MachO文件 Mach-O其实就是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式,类...
    coder_feng阅读 652评论 0 1
  • MachO文件 前言 Mach-O(Mach Object):Mach-O 文件是Mach object文件格式的...
    superFool阅读 734评论 0 0