Mach-O文件内部揭秘

在windows上可执行文件的格式是exe,在Linux上ELF是可执行文件,而在苹果系统上,Mac OS X和ios系统上,可执行文件的格式是Mach-O格式。官方解释地址:https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/MachORuntime/index.html

1、Mach与Mach-O

这里先提醒大家一下,Mach不是Mac,Mac是苹果电脑Macintosh的简称,而Mach则是一种操作系统内核。Mach内核被NeXT公司的NeXTSTEP操作系统使用。在Mach上,一种可执行的文件格是就是Mach-O(Mach Object file format)。Mac OS X是Unix的“后代”,但所主要支持的可执行文件格式是Mach-O。

iOS是从OS X演变而来,所以同样是支持Mach-O格式的可执行文件。

2、ios可执行文件初探

作为ios开发者,我们比较熟悉ipa包,这种文件格式,然而,实际上这只是一个变相的zip的压缩包。我们将其解压之后发现


这其实是一个Payload的包 打开这个包 不能发现 这是一个test1.app的文件 也就是xcode生成的product文件

而这其实也就是一个文件夹,打开这个文件夹(显示包含内容)发现其中有一个同名的test1的可执行文件,这就是我们最终要寻找的在ios上的可执行文件

我们用file命令来查看这个文件的文件类型


这是一个64位的Mach-O 格式的可执行文件


3、Mach-O文件细究

根据苹果官方文档提供Mach-O文件的数据主体可分为三个部分


头部(Header)、加载命令(Load Commands)、数据(Data)

而数据部分则又被分割成了一段段的Segments。

下面我们使用otool工具来一探究竟

a) 先来查看下这个可执行文件的头部是怎样的


一堆看不明白的东西,分别予以解释

magic:0xfeedfacf 。这个东西是Mach-O的魔数。简单介绍下什么叫做魔数:很多类型的文件,其起始的几个字节的内容是固定的(或是有意填充,或是本就如此)。根据这几个字节的内容就可以确定文件类型,因此这几个字节的内容被称为魔数 (magic number)。

在OS X上 可执行文件的标识有这样几个魔数:cafebabe、feedface、feadfacf、还有就是以#!开头的。

cputype、cpusubtype:指的是CPU类型和CPU子类型

在/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/mach里找到machine.h的定义 如下图


这里有支持的cpu的详细编号命名 0代表ARM的格式都将支持

caps: 额 官网上都没介绍 这个待查

filetype 2:代表可执行文件

ncmds:指的是加载命令(load commands)的数量,例子中一共22个,编号0-21

sizeofcmds:表示要load的22个commands的总的大小

flags:可用来检验是否开启了PIE,如开启了则需要移除方能正常使用MachOView才能把文件结构检测出来

它有这么几种定义值:我们的文件无未定义引用 (MH_NOUNDEFS),是为动态链接器(MH_DYLDLINK),使用two-level名Cheng绑定(MH_TWOLEVEL)且应被加载到随机地址 (MH_PIE).

b)再来看加载命令(Load Commands) 截取其中一个Commands来分析


Load command 0 :command编号

cmdLC_SEGMENT_64:即是将文件中的段映射进内存中的地址空间

segname:16字节段名称

vmaddr:段虚拟内存的起始地址

vmsize:段虚拟内存的大小

fileoff:段在内存中的偏移量

filesize:段在文件中的大小

maxprot:段页面所需要的最高内存保护

initprot:段页面初始内存保护

nsects:段中包含section的数量

flags:其他杂项标志位

c)接下来看data,注意到command和data都是以segment为大单元字节,但是在data里还有section字节 所以重点介绍section的组织格式 截取一个section供分析


sectname:section的名字

segname:section归属为哪一个segment

addr:secction起始内存地址

size:section的大小

offset:该section的文件偏移量

align:字节大小对齐

reloff:重定位入口的文件偏移

nreloc:需要重定位的入口数量

flags:包含了section的类型和独自的属性

最后两项保留用

了解了这些 才能根据Mach-O的文件结构 去分析类的名称和类的方法 即是class-dump的实现原理,同时这也是MachOView等的分析原理

参考自:http://turingh.github.io/2016/03/07/mach-o%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E5%88%86%E6%9E%90/

附上otool命令大全

-f print the fat headers

-a print the archive header

-h print the mach header

-l print the load commands

-L print shared libraries used

-D print shared library id name

-t print the text section (disassemble with -v)

-p   start dissassemble from routine name

-s print contents of section

-d print the data section

-o print the Objective-C segment

-r print the relocation entries

-S print the table of contents of a library

-T print the table of contents of a dynamic shared library

-M print the module table of a dynamic shared library

-R print the reference table of a dynamic shared library

-I print the indirect symbol table

-H print the two-level hints table

-G print the data in code table

-v print verbosely (symbolically) when possible

-V print disassembled operands symbolically

-c print argument strings of a core file

-X print no leading addresses or headers

-m don't use archive(member) syntax

-B force Thumb disassembly (ARM objects only)

-q use llvm's disassembler (the default)

-Q use otool(1)'s disassembler

-mcpu=arg use `arg' as the cpu for disassembly

-j print opcode bytes

-P print the info plist section as strings

-C print linker optimization hints

推荐阅读更多精彩内容