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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

原文出自【听云技术博客】:http://blog.tingyun.com/web/article/detail/1341


0x01  Mach-O格式简单介绍

Mach-O文件格式是 OS X 与 iOS 系统上的可执行文件格式,类似于windows的 PE 文件 与 Linux(其他 Unix like)的 ELF 文件,如果不彻底搞清楚Mach-O的格式与相关内容,那么深入研究 xnu 内核就无从谈起。

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

1.png

有如下几个部分组成:

1. Header:保存了Mach-O的一些基本信息,包括了平台、文件类型、LoadCommands的个数等等。

2. LoadCommands:这一段紧跟Header,加载Mach-O文件时会使用这里的数据来确定内存的分布。

3. Data:每一个segment的具体数据都保存在这里,这里包含了具体的代码、数据等等。

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

06.png

08.png

1. 第一段为magic 魔数,这里注意大小端,读出来之后需要看下是0xCAFEBABE还是 0xBEBAFECA(否则即为thin),需要根据这个来转后续读取的字节的字节序。  可以看出来 前4byte 为 0xBEBAFECA ,说明为fat。

2. 第二段为arch count,也就是该App或dSYM中包含哪些CPU架构,比如armv7、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数据的位置,当然如果只有一种架构的话就不需要计算偏移量了。 下图给出解析的函数

05.png

0x03 Mach Header二进制数据

通过magic我们可以区分出是32-bit还是64-bit,64-bit多了4个字节的保留字段,这里同样需要注意字节序的问题,也就是判断magic,来确定是否需要转换字节序。  

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

07.png

根据mach-header与mach-header_64的定义,很明显可以看出,Headers的主要作用就是帮助系统迅速的定位Mach-O文件的运行环境,文件类型。

2.png

FileType 

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

1. 内核扩展

2. 库文件

3. CoreDump

4.  其它

3.png

下面是一些精彩用到的文件类型

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的加载参数。

4.png

1. MH-NOUNDEFS   目标没有未定义的符号,不存在链接依赖

2. MH-DYLDLINK     该目标文件是dyld的输入文件,无法被再次的静态链接

3. MH-PIE      允许随机的地址空间(开启ASLR  -\>Address Space Layout Randomization)

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

5. MH-NO-HEAP-EXECUTION   堆内存无法执行代码

5.png

0x04 LoadCommands

Load Commands 直接就跟在Header后面,所有command占用内存的总和在Mach-O Header里面已经给出了。在加载过Header之后就是通过解析LoadCommand来加载接下来的数据了。定义如下:

6.png

cmd字段

根据cmd字段的类型不同,使用了不同的函数来加载。简单的列出一张表看一看在内核代码中不同的command类型都有哪些作用。

1. LC-SEGMENT;LC-SEGMENT-64   在内核中由load-segment 函数处理(将segment中的数据加载并映射到进程的内存空间去)

2. LC-LOAD-DYLINKER    在内核中由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-ENCRYPTION-INFO 在内核中由 set-code-unprotect 函数处理 (加密二进制文件)

UUID 二进制数据    128byte

UUID是16个字节(128bit)的一段数据,是文件的唯一标识,前面提到的符号化时,这个UUID必须要和App二进制文件中的UUID一致,才能被正确的符号化。dwarfdump查看的UUID就是这段数据。读取这部分数据时通过Command结构读取的,也就是第一段(0x0000001B)表示接下来的数据类型,第二段(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)

09.png

Segment数据

加载数据时,主要加载的就是LC-SEGMET活着LC-SEGMENT_64。其他的Segment的用途在这里不做深究。

LCSEGMENT以及LC-SEGMENT-64 定义如下图。


7.png

10.png

可以看出,这里大部分的数据是用来帮助内核将Segment映射到虚拟内存的。

nsects 字段,标示了Segment中有多少secetion ,section是具体有用的数据存放的地方。

TEXT的vmaddr也就是程序的加载地址; —DWARF中表明了DWARF数据块的信息,表示dSYM是DWARF格式的数据结构。 

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

Section数据

8.png

从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数据结构的差异、字节长度的差异,DWARF版本的差异,每个数据块之间都是紧密联系的,一个字节的读取偏差就会造成后续数据的读取错误,正所谓差之毫厘,失之千里。



目录
相关文章
|
10天前
|
机器学习/深度学习 数据采集 存储
时间序列预测新突破:深入解析循环神经网络(RNN)在金融数据分析中的应用
【10月更文挑战第7天】时间序列预测是数据科学领域的一个重要课题,特别是在金融行业中。准确的时间序列预测能够帮助投资者做出更明智的决策,比如股票价格预测、汇率变动预测等。近年来,随着深度学习技术的发展,尤其是循环神经网络(Recurrent Neural Networks, RNNs)及其变体如长短期记忆网络(LSTM)和门控循环单元(GRU),在处理时间序列数据方面展现出了巨大的潜力。本文将探讨RNN的基本概念,并通过具体的代码示例展示如何使用这些模型来进行金融数据分析。
68 2
|
12天前
|
Java
Java“解析时到达文件末尾”解决
在Java编程中,“解析时到达文件末尾”通常指在读取或处理文件时提前遇到了文件结尾,导致程序无法继续读取所需数据。解决方法包括:确保文件路径正确,检查文件是否完整,使用正确的文件读取模式(如文本或二进制),以及确保读取位置正确。合理设置缓冲区大小和循环条件也能避免此类问题。
|
15天前
|
SQL 关系型数据库 MySQL
数据库导入SQL文件:全面解析与操作指南
在数据库管理中,将SQL文件导入数据库是一个常见且重要的操作。无论是迁移数据、恢复备份,还是测试和开发环境搭建,掌握如何正确导入SQL文件都至关重要。本文将详细介绍数据库导入SQL文件的全过程,包括准备工作、操作步骤以及常见问题解决方案,旨在为数据库管理员和开发者提供全面的操作指南。一、准备工作在导
50 0
|
10天前
|
自然语言处理 数据处理 Python
python操作和解析ppt文件 | python小知识
本文将带你从零开始,了解PPT解析的工具、工作原理以及常用的基本操作,并提供具体的代码示例和必要的说明【10月更文挑战第4天】
120 60
|
3天前
|
数据安全/隐私保护 流计算 开发者
python知识点100篇系列(18)-解析m3u8文件的下载视频
【10月更文挑战第6天】m3u8是苹果公司推出的一种视频播放标准,采用UTF-8编码,主要用于记录视频的网络地址。HLS(Http Live Streaming)是苹果公司提出的一种基于HTTP的流媒体传输协议,通过m3u8索引文件按序访问ts文件,实现音视频播放。本文介绍了如何通过浏览器找到m3u8文件,解析m3u8文件获取ts文件地址,下载ts文件并解密(如有必要),最后使用ffmpeg合并ts文件为mp4文件。
|
8天前
|
消息中间件 中间件 数据库
NServiceBus:打造企业级服务总线的利器——深度解析这一面向消息中间件如何革新分布式应用开发与提升系统可靠性
【10月更文挑战第9天】NServiceBus 是一个面向消息的中间件,专为构建分布式应用程序设计,特别适用于企业级服务总线(ESB)。它通过消息队列实现服务间的解耦,提高系统的可扩展性和容错性。在 .NET 生态中,NServiceBus 提供了强大的功能,支持多种传输方式如 RabbitMQ 和 Azure Service Bus。通过异步消息传递模式,各组件可以独立运作,即使某部分出现故障也不会影响整体系统。 示例代码展示了如何使用 NServiceBus 发送和接收消息,简化了系统的设计和维护。
22 3
|
8天前
|
存储 SQL 分布式计算
湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
【10月更文挑战第7天】湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
12 1
|
13天前
|
开发工具 Android开发 iOS开发
深入解析安卓与iOS开发环境的优劣
【10月更文挑战第4天】 本文将深入探讨安卓和iOS两大主流移动操作系统的开发环境,从技术架构、开发工具、用户体验等方面进行详细比较。通过分析各自的优势和不足,帮助开发者更好地理解这两个平台的异同,从而为项目选择最合适的开发平台提供参考。
16 3
|
14天前
|
存储 搜索推荐 数据库
运用LangChain赋能企业规章制度制定:深入解析Retrieval-Augmented Generation(RAG)技术如何革新内部管理文件起草流程,实现高效合规与个性化定制的完美结合——实战指南与代码示例全面呈现
【10月更文挑战第3天】构建公司规章制度时,需融合业务实际与管理理论,制定合规且促发展的规则体系。尤其在数字化转型背景下,利用LangChain框架中的RAG技术,可提升规章制定效率与质量。通过Chroma向量数据库存储规章制度文本,并使用OpenAI Embeddings处理文本向量化,将现有文档转换后插入数据库。基于此,构建RAG生成器,根据输入问题检索信息并生成规章制度草案,加快更新速度并确保内容准确,灵活应对法律与业务变化,提高管理效率。此方法结合了先进的人工智能技术,展现了未来规章制度制定的新方向。
17 3
|
15天前
|
IDE Android开发 iOS开发
探索安卓与iOS系统的技术差异:开发者的视角
本文深入分析了安卓(Android)与苹果iOS两大移动操作系统在技术架构、开发环境、用户体验和市场策略方面的主要差异。通过对比这两种系统的不同特点,旨在为移动应用开发者提供有价值的见解,帮助他们在不同平台上做出更明智的开发决策。

推荐镜像

更多