跳轉到內容

逆向工程/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作業系統,在摩托羅拉6800068040PowerPC架構上。史蒂夫·喬布斯後來離開蘋果,建立了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個部分。

核心部分

[編輯 | 編輯原始碼]
  1. 硬體平臺專家
  2. Mach 3.0 子系統 (OSFMK)
  3. BSD 4.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(G3G4和G5)、英特爾32位、英特爾64位、英特爾至強64位Mac ProXServe)和ARMiPhone)架構的硬體差異。

常用工具

[編輯 | 編輯原始碼]

蘋果公司將用於編譯/建立軟體和反彙編/除錯軟體的通用工具命名為“開發者工具”。 開發者工具可以在 Mac OS X 10.4 及更高版本的安裝 DVD 以及 Apple Developer Connection (ADC) 網站上找到。 加入 ADC 是免費的,強烈推薦。 ADC 網站提供最新的文件、工具,甚至示例原始碼。 ADC 網站應該是您進行研究的第一個地方。 您可以在 Apple 的官方 XCode 和工具 網站上找到開發者工具的摘要。 除開發者文件外,Mac OS X 平臺上常用的用於逆向工程的工具列在下面。

使用的開發者工具

[edit | edit source]
  1. gdb (GNU 偵錯程式)
  2. nm (目標檔案符號表檢視器)
  3. otool (目標檔案顯示工具)
  4. fs_usage (檔案系統監控工具)
  5. lsof (檔案描述符表檢視器)
  6. vmmap (虛擬記憶體區域檢視器)
  7. lipo (通用二進位制檔案處理程式)
  8. file (二進位制檔案格式分析器)

所有上述工具都在開發者工具安裝過程中安裝。 截至目前(2008 年 8 月 3 日),當前開發者工具版本為 3.1(版本 2199)。

第三方工具

  1. [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]
  1. 外部
  2. 內部

還存在第三類符號,稱為“隱藏”或“剝離”符號。 這些符號不會顯示在 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 月底完成。

華夏公益教科書