逆向工程/Mac OS X
此页面可能需要更新以反映当前知识。 你可以帮助更新它,讨论进度,或请求帮助。 |
苹果电脑的Mac OS X是苹果麦金塔电脑上使用的标准操作系统。其他操作系统,主要是Linux,已被移植到Mac硬件上,并且也有一些努力将OS X移植到非Mac的基于英特尔的硬件上,但这些努力都没有达到“标准捆绑”所获得的普及程度。
Mac OS X在计算机界被许多人誉为既美观又易于使用。OS X建立在BSD和Mach核心之上,但包含一定数量的特定于Mac的软件。
尽量将此内容保持在Mac OS X的通用逆向工程主题上,而不是“破解”或仅出于安全目的进行逆向工程。我已经为这些主题创建了专门的部分,所有集中于这些主题的材料都应保存在那里。谢谢!--Macpunk 2007年7月9日 (UTC) 04:17
历史上,在OS X之前,Mac运行的是Mac OS操作系统,在摩托罗拉68000到68040和PowerPC架构上。史蒂夫·乔布斯后来离开苹果,创建了NeXT。在苹果完成其硬件迁移到PowerPC平台之后,它开始寻找一个新的内核,以利用这种新的硬件架构。许多项目开始并失败,这和其他因素导致苹果的衰落。为了利用新的架构,它转向了Be Inc.,购买其新的BeOS,但这后来失败了,因为Be Inc.要价过高。然后苹果转向NeXT,不仅收购了NeXT OS,还收购了史蒂夫·乔布斯。史蒂夫·乔布斯迅速控制了苹果,并将NeXT架构定位为苹果老化的Mac OS的替代品。替代产品最初被称为Rhapsody,它拥有更老的Mac OS风格。史蒂夫·乔布斯认为界面没有达到预期,因此他的前NeXT工程师团队开发了Aqua,Mac OS X诞生了。
Mac OS X 10.0 "Cheetah"到10.4.3 "Tiger"只能运行在第四代和第五代PowerPC架构上。对于苹果来说,很明显,IBM在第五代PowerPC(称为PowerPC G5)的开发和制造方面遇到了麻烦。此外,IBM承诺苹果一年后发布G5笔记本电脑版本,但至今尚未发布。然后苹果决定从PowerPC架构迁移到基于英特尔的架构。苹果选择了英特尔32位酷睿双核架构。苹果的第二代英特尔产品在不到一年的时间内发布,运行的是英特尔64位酷睿2双核架构。
苹果最初包含一个可信平台模块 (TPM)来帮助抑制Mac OS X的盗版。后来,苹果转向了一个简单的AES加密系统,其中加密密钥存储在内核设备驱动程序中。这导致了解密甚至加密Mac OS X可执行二进制文件的能力。新的TPM系统不再存在于任何现代Mac中。新的加密系统仅适用于基于英特尔的Mac,如果尝试在PowerPC平台上使用,则会产生各种错误。
苹果承诺在未来几年内继续支持PowerPC和英特尔平台。如今,每个Mac OS X系统都附带其二进制文件,采用通用二进制格式,可以在PowerPC和基于英特尔的Mac上运行。通用二进制只是源文件编译多次(每个架构一次),然后粘合在一起。当操作系统读取这个通用二进制文件时,它会选择该编译代码的正确版本并执行它。由于并非所有二进制文件都是通用的,因此苹果针对英特尔平台发布了一个名为Rosetta的软件组件,它可以动态地将PowerPC系统调用转换为英特尔系统调用,允许PowerPC二进制文件在英特尔Mac上执行。
所有Mac OS X (OS X)版本都建立在XNU内核和Mach-O文件格式之上。XNU内核是一个混合内核。内核分为4个部分。
- 硬件平台专家
- Mach 3.0 子系统 (OSFMK)
- BSD 4.4 子系统
- IOKit 子系统和框架
虽然传统的Mach内核是一个微内核,但苹果却用单内核设计实现了其Mach 3.0变体。Mach子系统只是由卡内基梅隆大学设计的Mach 3.0内核的部分实现。这个部分实现包括Mach消息系统、Mach虚拟内存系统和Mach进程管理器。
BSD 4.4子系统是FreeBSD 4.x内核的微型实现。随着时间的推移,苹果一直在缩减和减少这个内核子系统的功能集,希望消除除运行BSD源代码软件在XNU内核上运行的必要部分以外的所有内容。最初,该子系统支持BSD设备驱动程序,这些驱动程序可以与硬件直接通信。不幸的是,BSD子系统中的设备驱动程序架构只能支持直接主内存访问,并与运行进程的用户模式部分进行交互。
IOKit框架是C++编程语言的子集,称为嵌入式C++。IOKit子系统驱动使用IOKit框架编写的组件。IOKit的目的是统一和简化驱动程序架构,同时在主要和次要OS版本之间保持一定程度的兼容性。IOKit总体上取得了巨大成功,因为其他一些BSD操作系统已经移植或实现了类似于IOKit的系统(例如DragonFly BSD)。
硬件平台专家处理PowerPC(G3、G4和G5)、英特尔32位、英特尔64位、英特尔至强64位(Mac Pro和XServe)和ARM(iPhone)架构的硬件差异。
苹果公司将用于编译/创建软件和反汇编/调试软件的通用工具命名为“开发者工具”。 开发者工具可以在 Mac OS X 10.4 及更高版本的安装 DVD 以及 Apple Developer Connection (ADC) 网站上找到。 加入 ADC 是免费的,强烈推荐。 ADC 网站提供最新的文档、工具,甚至示例源代码。 ADC 网站应该是您进行研究的第一个地方。 您可以在 Apple 的官方 XCode 和工具 网站上找到开发者工具的摘要。 除开发者文档外,Mac OS X 平台上常用的用于逆向工程的工具列在下面。
使用的开发者工具
[edit | edit source]- gdb (GNU 调试器)
- nm (目标文件符号表查看器)
- otool (目标文件显示工具)
- fs_usage (文件系统监控工具)
- lsof (文件描述符表查看器)
- vmmap (虚拟内存区域查看器)
- lipo (通用二进制文件处理程序)
- file (二进制文件格式分析器)
所有上述工具都在开发者工具安装过程中安装。 截至目前(2008 年 8 月 3 日),当前开发者工具版本为 3.1(版本 2199)。
第三方工具
- [1] class-dump 用于解析 Objective-C 运行时信息。
逆向工程基础
[edit | edit source]架构
[edit | edit source]由于您希望在 Mac OS X 平台上进行逆向工程的大多数目标二进制文件都采用 Mach-O 通用二进制文件格式,因此您应该决定要对哪个目标二进制文件平台进行逆向工程。 要获取特定二进制文件具有的格式列表,您可以调用“file”程序。 例如
使用文件“/bin/ls”的常见示例
$ file /bin/ls /bin/ls: Mach-O universal binary with 2 architectures /bin/ls (for architecture i386): Mach-O executable i386 /bin/ls (for architecture ppc7400): Mach-O executable ppc
另一个示例,这次更罕见,使用文件“/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices”
$ file /System/Library/Frameworks/ApplicationServices.framework/ApplicationServices /System/Library/Frameworks/ApplicationServices.framework/ApplicationServices: Mach-O universal binary with 4 architectures /System/Library/Frameworks/ApplicationServices.framework/ApplicationServices (for architecture ppc7400): Mach-O dynamically linked shared library ppc /System/Library/Frameworks/ApplicationServices.framework/ApplicationServices (for architecture ppc64): Mach-O 64-bit dynamically linked shared library ppc64 /System/Library/Frameworks/ApplicationServices.framework/ApplicationServices (for architecture i386): Mach-O dynamically linked shared library i386 /System/Library/Frameworks/ApplicationServices.framework/ApplicationServices (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64
符号
[edit | edit source]确定要用于逆向工程的基准架构后,您就可以转储符号表。 这对于将来可能有用。 例如
来自 i386 架构的常见符号表转储
$ nm -arch i386 /bin/echo U ___error 00001000 A __mh_execute_header U _exit U _malloc U _strerror$UNIX2003 U _strlen U _write$UNIX2003 U _writev$UNIX2003
上述符号可以分为两个主要类别
符号类型
[edit | edit source]- 外部
- 内部
还存在第三类符号,称为“隐藏”或“剥离”符号。 这些符号不会显示在 nm 上,很难找到它们的用途以及它们是否存在。
每个符号类型都有一个范围。 范围可以是私有的,也可以是公用的。 过去,您可以将动态链接器设置为“扁平命名空间”,这会将私有符号转换为仅供您的程序使用的公共符号,但是据报道,此功能已在大多数库上被禁用。
私有符号是指可以由整个程序或程序的一部分寻址但不能由其他任何人寻址的符号。 公共符号是指在其他平台上通常称为“导出”的符号。 公共符号可以被链接到该二进制文件的任何内容(在编译时或运行时)访问。
内部符号
[edit | edit source]内部符号是在程序中定义的符号,因此在运行时不会导入(动态链接)。 但是,内部符号可以是链接在 编译时 的外部符号,并且该符号的源是目标文件或静态库。 您可以快速识别内部符号,因为包含符号的行在符号类型字母之前有一个 十六进制 数字。 外部符号在数字应该出现的位置留有空格。 符号表中指定的数字表示符号的代码或数据在文件中从什么偏移量开始。 此值是相对的,在 运行时 会有所不同。 在运行时查找内存中符号的一种方法是查找磁盘上两个符号的相对位置,第一个是众所周知的符号,第二个是未知符号,然后提取差异。 获得差异后,只需将差异应用于第一个符号的地址,就可以在内存中找到第二个符号。 例如
示例
[edit | edit source]查找第一个和第二个符号
$ nm /System/Library/Frameworks/QTKit.framework/QTKit /System/Library/Frameworks/QTKit.framework/QTKit(single module): {...} 0005a638 T _copyBitmapRepToGWorld 0008b017 t _createDisplayList {...}
在上面的示例中,我们的第一个符号是“_copyBitmapRepToGWorld”,在程序中称为“copyBitmapRepToGWorld”。 我们的第二个符号是“_createDisplayList”,在程序中是未知的,因为它是一个私有符号(参见私有符号)。 确定符号“_createDisplayList”的函数定义后,为程序使用定义该符号就变得很重要。 为此,假设“_createDisplayList”C 函数原型将是
void * createDisplayList(void);
上面的原型将在我们的目标 QTKit 的源代码中定义。 不幸的是,这对我们没有帮助,因为函数原型和符号名称对于我们的程序来说都是未知的。 要解决此问题,我们只需计算上面符号的差值(差值为 0x309DF),并将我们的函数原型定义为:
void * (* createDisplayList)(void);
然后,您将通过让另一个函数(例如 main)在您第一次使用该函数之前执行以下命令来为该函数分配地址:
createDisplayList = copyBitmapRepToGWorld + 0x309DF;
一些程序可以摆脱在函数之外的 1 个命令中执行上述操作,我不会推荐这样做,因为 Mac OS X 动态链接器 dyld 有时会在您进入主函数之前但变量的初始值已定义之后更改符号地址的值。
外部符号
[edit | edit source]外部符号是在其他地方定义的符号,例如库(参见下面的库)。 要读取外部符号,您只需剥离前导“_”。 如果符号的名称中包含“$”,则第一个“$”之后的任何内容都是动态链接器的一个提示,表明此符号是一个显式外部符号,应该与外部库中该符号的精确版本匹配。 显式符号对于程序创建者非常有用,因为它允许他们难以覆盖符号或出现运行时链接不匹配错误。 符号名称左侧的字母(在上面的示例中为“U”)表示符号的类型,例如函数或数据结构。
PowerPC
[edit | edit source]基本指令包括 li(加载立即数)和 mr(移动寄存器)。
堆栈
[edit | edit source]PowerPC 堆栈的工作方式与其他任何堆栈完全相同。 它是一个 LIFO 结构,并且向下增长(朝向较低的内存地址)。 逆向工程 PowerPC 二进制文件时要记住的最重要细节是 PowerPC 芯片没有内置的堆栈实现。 没有指定用于跟踪堆栈底部的寄存器,也没有用于将数据压入和弹出堆栈的指令。 所有操作都是通过通用寄存器和各种算术指令完成的。
(本节将包含 PowerPC 特定信息,例如 PowerPC 函数调用如何执行,参数如何传递给函数,堆栈格式等。)--Macpunk 06:19, 2007 年 7 月 8 日 (UTC)
(本节将包含英特尔特有的信息,例如英特尔函数调用如何执行、如何将参数传递给函数、堆栈格式等。)--Macpunk 2007年7月8日06:19 (UTC)
此页面或 逆向工程 书籍的这一节是 存根。如果您有关于此主题的信息,请在这里写下。
此页面或 逆向工程 书籍的这一节是 存根。如果您有关于此主题的信息,请在这里写下。
此页面或 逆向工程 书籍的这一节是 存根。如果您有关于此主题的信息,请在这里写下。
- 维基教科书:PowerPC汇编
- 关于Mac OS X 逆向工程的简要教程 [2]
- Cocoa 逆向工程 [3]
- KellogS' OS X 逆向工程简介 [4]
- Mac OS X 非实用且非真实世界的破解入门 [5]
- 什么是 Mac OS X? [6]
本文件的大部分内容由 JosephC7 撰写,虽然这些信息已免费授权给维基教科书出版,但应注意,作者仅要求您在转载此信息时提供作者的用户名并链接到其在 wikibooks.org 上的用户页面。此信息不需要或不请求任何费用,预计如果转载此信息,也应免费提供,无需补偿。本文件正在建设中,预计将于 2008 年 8 月底完成。