博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mac-O文件加载的全过程(一)
阅读量:5776 次
发布时间:2019-06-18

本文共 3196 字,大约阅读时间需要 10 分钟。

在Mac的开发中, 有没有想过当我们点击可执行文件之后,Mac究竟做了什么事情才让我们的程序运行起来? 对于应用层开发人员或者普通的用户而言, 其实无需知道的这么详细;但是对于内核开发人员而言, 如果能了解这一系列的过程, 那么将增强我们的内核的开发功底。

那么下面我们开始分析我们的鼠标点击之后, Mac都做了什么事情。

1. Mac的历史

这一部分有更好的

2. 准备工作

(1). 你需要下载. 

(2). Xcode,vim或者其他什么浏览源代码的工具。

3. 分析

在分析加载过程的时候, 需要涉及到内核和应用层两个部分。因此这篇文章也将分为两部分分别阐述。


3.1 内核部分

Mac的内核经过多次的发展, 目前这部分被称作XNU。具体为什么叫这个名字, 大家可以去搜索一下。在最初开始设计XNU的时候, Apple是打算设计一个微内核, 也就是将最重要的事情放在内核里面并且内核只负责仲裁而非逻辑处理。 这个内核也就是在Mac OS 9上面使用的内核,但是这个产品是一个效率底下系统。因此当乔布斯回归以后对内核进行了大改, 在内核部分引入了FreeBSD的部分, 这个产品就是我们现在在使用的Mac OSX的内核XNU。 简单的历史说完了, 下面我们来点儿干货。

3.1.1 Mac-O文件格式的分析

既然是要分析Mac-O文件的加载过程, 那么必须涉及到Mach-O的格式问题。 对于这个格式, 我们可以参考下面的图:

从这这张图片中, 我们可以看到Mach-O文件在结构上可以分成三个部分:

(1) 文件头 
(2) 命令区域 
(3) 数据区域(包括数据, 代码等等)

这三个部分共同组成了Mach-O文件格式。下面我们将以此讨论这个三个部分, 在最后我讲给出一些需要注意的部分和部分代码。

3.1.1.1 Mach-O文件头

Mach-O文件头的定义如下:

struct mach_header(_64)

代码来自:${XNU_ROOT}/EXTERNAL_HEADER/mach-o/loader.h:

struct mach_header {  uint32_t    magic;        cpu_type_t  cputype;      cpu_subtype_t   cpusubtype;   uint32_t    filetype;     uint32_t    ncmds;        uint32_t    sizeofcmds;   uint32_t    flags;      };struct mach_header_64 {  uint32_t    magic;        cpu_type_t  cputype;      cpu_subtype_t   cpusubtype;   uint32_t    filetype;     uint32_t    ncmds;        uint32_t    sizeofcmds;   uint32_t    flags;        uint32_t    reserved;   };

上面的头部定义包含了32bit和64bit的头部。字段的含义如下(下面是以64bit的头部说明的):

命令 含义  说明
magic  魔数字  主要用来区分当前Mach-O所支持的CPU架构(当前只有32bit和64bit)。
cputype  CPU类型  主要的CPU类型(32/64bit), 以及其他的属性。
cpusubtype  CPU子类型  cpu具体的类型。
filetype  文件类型  文件类型比较多,比如MH_EXECUTE代表可执行文件。
ncmds  命令个数  也就是下一个segment中得segment的数量。
sizeofcmd  第三个部分的大小  None。
flags  当前Mach-O的属性  比较常见的属性包括MH_PIE(当前文件执行ASLR)等。

更多的值可以参考.

3.1.1.2 Mach-O命令区域

这一部分是Mach-O文件中最重要的部分, 我们从上面的Mach-O结构图中可以看到, 所谓的segment其实是数据区域的一个索引。每个segment都在数据区域对应了一段自己的区域。我们需要做的就是找到这些部分并执行他们。首先我们看一下Segment的结构:

Segment的结构定义如下:

struct load_command

代码来自:${XNU_ROOT}/EXTERNAL_HEADER

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

这个结构对应的成员比较少, cmd代表当前段的类型。cmdsize当前段的大小。 我们主要看看cmd有哪些的类型, 这个至关重要。在这里我需要说明的是, 下面的命令并不代表全部,之所以要在这里列出的它们的原因是这些命令将在内核中被加载。 那么你可能会问, 那么其他的段命令呢?这个你和dyld去说好了。列表如下:

命令 十六进制 作用
LC_SEGMENT
LC_SEGMENT_64 
0x01/0x19  将这些段加载到对应的进程空间上去(区分32位和64位)
LC_LOAD_DYLINKER  0x0E  加载dyld, 值得注意的是,每个Mach-O文件只能有一个段
LC_UUID  0x1B  将UUID这个值保存到执行进程的上下文中,同样每个Mach-O文件只能有一个段
LC_THREAD  0x04  开启一个Mach线程, 但是不分配栈(这个不常见)
LC_UNIXTHREAD  0x05  开启一个UNIX线程,其实最主要的用途是告诉加载器当前主函数的位置.
这条命令在10.8之后被LC_MAIN取代。
LC_MAIN  0x80000028  在10.8之后代替LC_UNIXTHREAD, 告诉加载器当前主函数的位置
LC_CODE_SIGNATURE  0x1D  这个是数字签名段
LC_ENCRYPTION_INFO  0x21  加密二进制文件, 貌似在IOS下使用的比较频繁。

3.1.1.3 Mach-O数据区域

这一部分我们将在dyld的时候着重讲。现在先略过。

3.1.1.4 例子

我们查看一下Mac for QQ的二进制文件格式: 

首先, 我们查看一下QQ文件头, 我们发现这个文件是一个32位的Mach-O文件。文件类型是MH_EXEXUTE,也就是可执行文件。 这个执行文件一共有56个segment, 全部的segment的大小有6580.最后一个数据室flag数据, 可以看到这个可执行文件,在加载的时候必须使用ASLR的保护技术,除此之外还有一个标志位MH_NO_HEAP_EXECUTION,这个标志位是防止当前的可执行文件的数据部分有执行权限, 一旦有执行权限, 黑客可以进行所谓的“堆喷射攻击”。

在看第二张图片, 我们发现当前segment中LC_SEGMENTM可以存在多个;还有就像我们上面说的,对于LC_UUID, LC_MAIN, LC_LOAD_DYLINKER等段来说,有且仅有一个。

值得注意的是, 当前这个可执行文件中存在段LC_MAIN, 因此说明当前QQ的编译环境是Mac OSX 10.8+, 因为我们在表格中说过LC_MAIN是在10.8的时候引入的, 因此只有10.8或者更高版本的系统才能编译出这个二进制文件。

 

http://blog.csdn.net/dongaxis/article/details/41114071

你可能感兴趣的文章
session深入解读
查看>>
利用gcc的__attribute__编译属性section子项构建初始化函数表【转】
查看>>
关于SWT常用组件(按钮,复选框,单选框(Button类))
查看>>
[Android Pro] Android libdvm.so 与 libart.so
查看>>
《响应式web设计》读书笔记(四)HTML5与CSS3
查看>>
[Step By Step]SAP HANA PAL多元指数回归预测分析Multiple Exponential Regression编程实例FORECASTWITHEXPR(预测)...
查看>>
JS魔法堂:mmDeferred源码剖析
查看>>
人不成熟的六大特征
查看>>
神经网络入门指南
查看>>
Spring Boot的启动器Starter详解
查看>>
WITH (NOLOCK)
查看>>
HTAP数据库 PostgreSQL 场景与性能测试之 24 - (OLTP) 物联网 - 时序数据并发写入(含时序索引BRIN)...
查看>>
说说搜索引擎中的人工干预
查看>>
克服大数据集群的挑战
查看>>
Linux下搭建MySQL集群
查看>>
物联网将让数据中心更为复杂,但更加有趣
查看>>
传Facebook研发新功能 发布合作媒体的专门内容
查看>>
美国国土安全部部长约翰逊就Dyn网络攻击事件发表声明
查看>>
《大数据原理:复杂信息的准备、共享和分析》一一2.6 单向哈希函数
查看>>
开放式网络是实现创新的快速通道
查看>>