iOS系统分析(二)Mach-O二进制文件分析

更加多技术干货请戳:听云博客

0x01
 Mach-O格式简单介绍

Mach-O文件格式是
OS X 与 iOS 系统上的可执行文件格式,类似于windows的 PE 文件 与
Linux(别的 Unix like)的 ELF
文件,假使不到底搞清楚Mach-O的格式与连锁内容,那么透彻钻研 xnu
内核就无从谈起。

Mach-O文件的格式如下图所示:

图片 1

有如下多少个部分构成:

1.
Header:保存了Mach-O的一部分中坚消息,包蕴了阳台、文件类型、LoadCommands的个数等等。

2.
LoadCommands:这一段紧跟Header,加载Mach-O文件时会使用那里的数据来规定内部存款和储蓄器的遍布。

3.
Data:每一种segment的切实可行数目都保留在此地,那里带有了切实的代码、数据等等。

0x02
FAT二进制数据 ,数据结构定义在 \<mach-o/fat.h\>

图片 2

图片 3

1.
第②段为magic 魔数,那里注意大小端,读出来之后须要看下是0xCAFEBABE照旧0xBEBAFECA(不然即为thin),须求依据那么些来转后续读取的字节的字节序。
 能够看出来 前4byte 为 0xBEBAFECA ,表明为fat。

2.
次之段为arch
count,也正是该App或dSYM中包罗怎样CPU框架结构,比如armv七 、arm64等,这些例子中为2(后4byte
 0x 00 00 00 02),表示包蕴了二种cpu架构。  

  `sizeof(struct fat-header) = 8byte`

3.
连任段中包罗cputype(0x  0C 00  00 01)、cpusubtype (0x 00 00 00
00)、offset (0x 00 10 00  00)、size(0x 00  F0 27
00)等数据,依据fat中的结构定义,依次读取,那里必要评释的是,假使只含有一种CPU架构的话,是未曾那段fat头定义的,能够跳过那有的,直接读取Arch数据。

   `sizeof(struct fat-arch) = 20byte`

4.
基于fat头中读取的offset数据,大家能够跳到文件对应的arch数据的地点,当然假若唯有一种架构的话就不供给总结偏移量了。
下图给出解析的函数

图片 4

0x03
Mach Header二进制数据

透过magic我们可以分别出是32-bit依然64-bit,64-bit多了6个字节的保存字段,那里同样须求留意字节序的难点,也等于判定magic,来规定是还是不是须要更换字节序。
 

`sizeof(struct mach-header-64) = 32byte`  ; `sizeof(struct mach-header) = 28byte`

图片 5

根据mach-header与mach-header_64的定义,很显眼能够见见,Headers的第②功效就是赞助系统连忙的原则性Mach-O文件的运维条件,文件类型。

图片 6

FileType 

因为Mach-O文件不仅用来促成可执行文件,同时还用来实现了其余内容

1.
基础扩张

2.
库文件

3.
CoreDump

4.
 其它

图片 7

上面是一对绝妙用到的文件类型

1.
MH-OBJECT    编写翻译进度中发生的  obj文件 (gcc -c xxx.c
生成xxx.o文件)

2.
MH-EXECUTABLE  可实施二进制文件 (/usr/bin/ls)

3.
MH-CORE      CoreDump (崩溃时的Dump文件)

4.
MH-DYLIB  动态库(/usr/lib/里面的那多少个共享库文件)

5.
MH-DYLINKER  连接器linker(/usr/lib/dyld文件)

6.
MH-KEXT-BUNDLE   内核扩张文件 (自身付出的简单内核模块)

flags

Mach-O
headers还蕴涵了一部分很重点的dyld的加载参数。

图片 8

1.
MH-NOUNDEFS   目的没有未定义的符号,不设有链接正视

2.
MH-DYLDLINK     该对象文件是dyld的输入文件,不可能被再一次的静态链接

3.
MH-PIE      允许专擅的地址空间(开启ASL帕杰罗  -\>Address Space Layout
Randomization)

4.
MH-ALLOW-STACK-EXECUTION   栈内存可执行代码,一般是默许关闭的。

5.
MH-NO-HEAP-EXECUTION   堆内部存款和储蓄器无法实施代码

图片 9

0x04
LoadCommands

Load
Commands 间接就跟在Header后边,全数command占用内部存款和储蓄器的总数在Mach-O
Header里面已经付诸了。在加载过Header之后即令通过解析LoadCommand来加载接下去的多少了。定义如下:

图片 10

cmd字段

根据cmd字段的类别差别,使用了区别的函数来加载。简单的列出一张表看一看在基本代码中分歧的command类型都有何样作用。

1.
LC-SE卡那霉素ENT;LC-SE氯林肯霉素ENT-64   在基本中由load-segment
函数处理(将segment中的数据加载并映射到进程的内存空间去)

2.
LC-LOAD-DYLINKETiguan    在基础中由load-dylinker
函数处理(调用/usr/lib/dyld程序)

3.
LC-UUID 在基础中由load-uuid 函数处理 (加载128-bit的唯一ID)

4.
LC-THREAD  在根本中由load-thread 函数处理
(开启三个MACH线程,不过不分红栈空间)

5.
LC-UNIXTHREAD 在基本中由load-unixthread 函数处理 (开启一个UNIX
posix线程)

6.
LC-CODE-SIGNATURE 在基础中由load-code-signature 函数处理
(进行数字签名)

7.
LC-ENCEvoqueYPTION-INFO 在基础中由 set-code-unprotect 函数处理
(加密二进制文件)

UUID
二进制数据    128byte

UUID是十六个字节(128bit)的一段数据,是文本的绝无仅有标识,前边提到的符号化时,这几个UUID要求求和App二进制文件中的UUID一致,才能被正确的符号化。dwarfdump查看的UUID正是那段数据。读取这一部分数据时通过Command结构读取的,也正是率先段(0x0000001B)表示接下去的数据类型,第2段(0x00000018)数据的深浅(包含Command数据)。 

SymTab
二进制数据

1.
标志表数据块结构,前二段照旧是Command数据。后边4段分别为标志在文书中的偏移量(0x001DF5E0)、符号个数(0x001DF5E0)、字符串在文件中的偏移量(0x0020C3A0)、字符串表大小(0x000729A8)。 

2.
接下去正是读取Segment和Section数据块了,和方面读取数据块结构同样是依据Command结构读取,下图显示的Segment数据和Section数据,它们在二进制文件中它们是三番五次的,也便是每一条Segment数据背后会跟随多条对应的Section数据,Section的多少总数是透过Segment结构中的nsects决定的。 

3.
那边本人写了三个简练地Mach-O解析工具 [https://github.com/liutianshx2012/Tmacho\](https://github.com/liutianshx2012/Tmacho)

图片 11

Segment数据

加载数据时,首要加载的正是LC-SE威他霉素ET活着LC-SE博来霉素ENT_64。别的的Segment的用处在那里不做探索。

LCSE青霉素ENT以及LC-SE地霉素ENT-64
定义如下图。

 

图片 12

图片 13

能够看到,这里当先三分一的多少是用来扶持内核将Segment映射到虚拟内部存款和储蓄器的。

nsects
字段,标示了Segment中有稍许secetion
,section是有血有肉有用的多寡存放的地方。

TEXT的vmaddr也正是先后的加载地址;
—DWA昂CoraF中标明了DWA帕杰罗F数据块的音信,表示dSYM是DWA陆风X8F格式的数据结构。 

` sizeof(struct segment-command) = 56byte   ;   sizeof(struct segment-command-64) = 72byte`

Section数据

图片 14

从Section数据中,我们能够找到—debug-info、—debug-pubnames,
—debug-line等调节和测试新闻,通过这几个调节和测试新闻大家得以找到程序中符号的开始地址、变量类型等音信。即使我们要符号化的话,就能够通过分析那一个数据获得我们想要的新闻。

Symbol
数据

透过SymTab中的数据能够获得Symbol在文书中的位置和个数,Symbol块数据中蕴藏了符号的开局部址、字符串的偏移量等数码,那有个别数据结构能够参见\<nlist.h\>

\<stabl.h\>。在那有个别数目总体读取后,就能够读取全部的记号数据了,约等于接下去的多少。 

Symbol
String 数据

1.
透过SymTab和Symbo中的数据足以拿走每种符号字符串在文书中的偏移量和大小,每一个符号数据是以0末尾的字符串。 

2.
大家通过以上两部分数据的结缘就足以获取每一种symbo在先后中的加载地址了。这么些数据对于尔后做标记工作都不行的有扶持。

3.
到此,关于dSYM文件中底部数据读取就到位了。底部数据都有照应的数据结构定义,读取时相对会相比较便于些,解析数据时要小心字节序的题材,32-bit和64-bit数据结构的距离、字节长度的距离,DWAEscortF版本的距离,各种数据块之间都是紧凑联系的,二个字节的读取偏差就会造成后续数据的读取错误,正所谓差之毫厘,失之千里。

 

原稿链接:http://blog.tingyun.com/web/article/detail/1341