Mach-o格式、进程以及线程内幕(二)

Mach-O二进制格式

    UNIX基本上标准化了一个通用的可移植的二进制格式,这个格式成为Executable and Library Format(简称ELF)。这个格式具有良好的文档,而且还有一整套binutils工具用于维护和调试这个格式的文件,甚至同样的CPU架构上的不同UNIX之间还允许二进制级别的可移植性(例如Linux和Solaris--这是真的,x86版本的Solaris可以原生地执行某些Linux二进制文件)。然而OS X却维护了一个自己独有的二进制格式:Mach-Object(简写为Mach-O),这是另一个源于NextSTEP的遗产。
    Mach-O和苹果的一些文档对Mach-O的格式进行了解释。Mach-O格式具有一个固定的文件头。这个文件头的详细信息在<mach-o/loader.h>头文件中,如图4-3所示。
    文件头一开始是魔数值,加载器可以通过这个魔数值快速判断这个二机制文件用于32位(MH_MAGIC, #define为0xFEEDFACE)还是64位(MH_MAGIC_64, #define为0xFEEDFACF)。在魔数值之后跟着的是cpu类型以及子类型字段,这两个字段和通用二进制文件中的相同字段作用是一样的---------用于确保二进制文件适合并且可以在当前架构下运行。除此之外,32位架构和64位架构的文件头结构就没有实质差别了:除了64位的头文件还包含一个额外的预留字段之外,这个字段目前没有使用。


image.png

    由于这样的同一种二进制格式用于多种目标文件类型(可执行文件,库文件,核心转储文件以及内核扩展文件等),那么下一个int类型的字段filetype就用于表示目标文件的类型,这个字段的可取值以宏的形式定义在<mach-o/loader.h>头文件中。在我们使用的系统中常见的值如表4-3所示。


image.png

    文件头中还包含了重要的标志,这些标志也定义在<mach-o/loader.h>文件中,如图:
image.png

从上图中可以看出,有两个标志和“执行” 相关:MH_ALLOW_STACK_EXECUTION和MH_NO_HEAP_EXECTION.这两个标志都用于防止某些数据的执行,通常称为NX(Non-eXecutable,可参阅内存页面中的一个同名标志位)。通过将数据所在的内存页面标记为不可执行,(一般情况下)可以防止黑客进行代码注入,因为黑客不能方便地执行数据段中的代码。如果试图执行数据段中的代码,则会引发一个硬件异常,进程会终止---------让进程崩溃,从而避免执行注入的代码。

    由于代码注入的常见方法是使用栈变量(即自动变量),因此默认情况下栈都标记为不可执行,而这个标志可以用于覆盖这种行为(非常危险)。堆则默认可执行。尽管完全可能,但是通过堆注入代码相对困难一些。

    这两个设置可以在系统级别进行:通过sysctl(8)修改vm.allow_stack_exec和vm.allow_heap_exec变量。在发生冲突时,以更宽松的设置为准(即false优先于true)。在iOS中,没有暴露sysctl接口,堆和栈都默认不可执行。
    Mach-O文件头的主要功能在于加载命令(load command)。加载命令紧跟在文件头之后,文件头中的两个字段--------ncmds和sizeofincmds---------用于解析加载命令。之后会详细讨论。


image.png

otool工具善于分析加载命令和文本段,但是不合适分析数据段和其他区域。本书的支持网站上有一个名为jtool的工具,这个工具的目的是增强otool的功能。这个工具可以处理iOS 5.1和Mountain Lion之前(含)的所有类型的二进制文件。这个工具将nm和strings、segedit、size和otool的功能整合在一个二进制文件中,不仅特别适合脚本化操作,还支持一些新的特性。

    Mach-O文件头中包含了非常详细的指令,这些指令在被调用时清晰地指导了如何设置并加载二进制数据。这些指令,或称为“加载命令”,紧跟在基本的mach_header之后,每一条指令都采用“类型-长度-值”的格式:32位的cmd值(表示类型),32位的cmdsize值(32位二进制位4的倍数,64位二进制为8的倍数),以及命令本身(由cmdsize指定的任意长度)。有一些命令是由内核加载器(定义在bsd/kern/mach_loader.c文件中)直接使用的,其他命令是由动态链接器处理的。
    加载命令总共有30多条。如图列出了内核使用的那些命令(之后在讨论链接编辑器时会讨论剩下的命令):


image.png

加载过程在内核的部分负责新进程的基本设置--------分配虚拟内存,创建主线程,以及处理任何可能的代码签名/加密的工作。然而对于动态链接的可执行文件(大部分可执行文件都是动态链接的)来说,真正的库加载和符号解析的工作都是通过LC_LOAD_DYLINKER命令指定的动态链接器在用户态完成的。控制权会转交给链接器,链接器进而接着处理头文件中的其他加载命令(本章稍后会讨论库的加载)。
    下面详细讨论这些加载命令。
    LC_SEGMENT 以及进程虚拟内存设置
    LC_SEGMENT(或LC_SEGMENT_64)命令是最主要的加载命令,这条命令指导内核如何设置新运行的进程的内存空间。这些“段”直接从Mach-O二进制文件加载到内存中。
    每一条LC_SEGMETN[_64]命令都提供了段布局的所有必要细节信息,如图


image.png

image.png

    有了LC_SEGMENT命令,设置进程虚拟内存的过程就变成遵循LC_SEGMENT命令的简单操作。对于每一个段,将文件中相应的内容加载到内存中:从偏移量为fileoff处加载filesize字节到虚拟内存地址vmaddr处的vmsize字节。每一个段的页面根据iniprot进行初始化,initprot指定了如何通过读/写/执行位初始化页面的保护级别。段的保护设置可以动态改变,但是不能超过maxprot中指定的值(在iOS中,+x和+w是互斥的)。
    _PAGEZERO段(空指针陷阱)、_TEXT段(程序代码)、_DATA段(程序数据)和_LINKEDIT(链接器使用的符号和其他表)段提供了LC_SEGMENT命令。段有时候也可以进一步分解为区(section)。如图列出了一些常见的区:
image.png

    段也可以设置一些<mach/loader.h>头文件中定义的标志。苹果使用的一个标志是SG_PROTECTED_VERSION_1(0x08),表示这个段的页面是“受保护的”,即加密的。苹果通过这个种技术加密一些二进制文件,例如Finder。如图所示:


image.png

    为了支持这种代码加密,XNU内核包含了一个特殊的自定义(外部)虚拟内存管理器,其名称为“Apple protect”
    在创建Mach-O对象时,可以通过-segcreate开关让Xcode的ld创建段。Xcode还包含一个名为segedit的特殊工具,可以用于提取或替换Mach-O文件中的段。这个工具可以用于提取内嵌的文本信息,例如内核的PRELINK_INFO区,此外,本书的伴随工具jtool也提供了这个功能。jtool还提供了另一个Xcode工具size的功能,能够打印出每一个段的大小和地址。
    LC_UNIXTHREAD
    当所有的库都完成加载之后,dyld的工作也完成了,之后由LC_UNIXTHREAD命令负责启动二进制程序的主线程(因此主线程总是在可执行文件中,而不会在其他二进制文件中,例如库文件)。根据架构的不同,这条命令列出所有初始化寄存器的状态,不同架构的寄存器状态不同,这些不同的架构包括i386_THREAD_STATE、x86 _THREAD_STATE64以及iOS中的ARM_THREAD_STATE。在任何一种架构中,大部分寄存器应该都会初始化为0,其中指令指针(Intel的IP)或程序计数器(ARM的r15)是例外,这些寄存器保存了程序入口点的地址。

    在苹果完成抛弃PPC平台之前,在Lion中,还有一个PPC_THREAD_STATE。在某些包含了PPC代码的胖二进制文件中还能看到这个类型(例如在Snow Leopard中尝试运行otool -arch ppc -l/mach_kernel)。在这种情况下,寄存器srr0保存代码的入口点。

    LC_THREAD
    和LC_UNIXTHREAD的功能类似,LC_THREAD用于核心转储文件。Mach-O核心转储文件实际上是一个组LC_SEGMENT(或LC_SEGMENT_64)命令的集合,这些命令负责建立起进程的内存镜像(只不过现在进程已经无效了),然后就是最后一条LC_THREAD命令。LC_THREAD命令也包含几种不同的类型,每一种类型对应不同的机器状态(即线程、浮点和异常)。只要创建一个核心转储(这太简单了!)并通过otool -l查看这个核心转储文件就可以轻松找到这条命令了。

    LC_MAIN
    从Mountain Lion开始,一条新的加载命令LC_MAIN替代了LC_UNIXTHREAD命令。这条命令的作用是设置程序主线程的入口点地址和大小。这条命令比LC_UNIXTHREAD命令更实用一个些,

    LC_CODE_SIGNATURE
    Mach-O二进制文件有一个重要特性就是可以进行数字签名。尽管在OS X中仍然没怎么实用数字签名,不过由于代码签名和新改进的沙盒机制绑定在一起,所以签名的使用率也越来越高。在iOS中,代码签名是强制要求的,这也是苹果尽可能对系统封锁的另一种尝试:在iOS中只有苹果自己的签名才会被认可。在OS X中,codesign工具可以用于操纵和显示代码签名。man手册页,以及Apples code signing guide和Mac OS X Code Signing In Depth文档都从系统管理的角度详细解释了代码签名机制。

    LC_CODE_SIGNATURE包含了Mach-O二进制文件的代码签名,如果这个签名和代码本身不匹配(或者如果在iOS上这条命令不存在),那么内核会立即给进程发送一个SIGKILL信号将进程杀掉,没有商量的余地,毫不留情。在ios4之前,还可以通过两条sysctl命令覆盖负责强制执行(利用内核的MAC,即Mandatory Access Control)的内核变量,从而实现禁用代码签名检查:

sysctl -w security.mac.pro_enforce=0 // 禁用进程的MAC
sysctl -w security.mac.vnode_enforce=0 //禁用VNode的MAC

    而在之后版本的iOS中,苹果意识到只要能够获得root权限,越狱者就可以覆盖内核变量。因此这些变量成了读变量。untelthered越狱(即完美越狱)因为利用了一个内核漏洞所以可以修改这个些变量。由于这些变量的默认值都是启用签名检查,所以不完美越狱会导致非苹果签名的应用程序崩溃-------除非i设备以完美越狱的方式引导。
    此外,通过Saurik的ldid这类工具可以在Mach-O中嵌入伪代码签名。这个工具可以替代OS X的codesign,允许生成自我签署的伪签名。这在iOS中尤为重要,因为签名和沙盒模型的应用程序“entitlement”绑定一起,而后者在iOS中是强制要求的。entitlement是声明式的许可(以plist的形式保存),必须内嵌在Mach-O中并且通过签名盖章,从而允许执行安全敏感的操作时具有运行时权限。
    OS X和iOS都有一个特殊的系统调用csops用于代码签名的操作。

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

推荐阅读更多精彩内容