1. 程式人生 > IOS開發 >iOS逆向學習之九(深入研究Mach-O結構)

iOS逆向學習之九(深入研究Mach-O結構)

Mach-O基本結構回顧

在深入學習Mach-O檔案之前,先來回顧一下之前學習的Mach-O的基本結構,可以到官網檢視Mach-O檔案的介紹

Mach-O的組成

Mach-O檔案有三部分組成

  • Header中包含檔案型別、目標架構型別等等基本資訊
  • Load Commands是描述檔案在虛擬記憶體中的邏輯結構和佈局,相當於簡介和目錄索引
  • Raw Segment Data中存放了所有在Load Commands中定義的Segment所對應的原始資料

Mach-O深入探究

Header

在Mach-O檔案中,Header部分存放了檔案的基本描述資訊,如下:

  • Magic Number代表當前Mach-O檔案的架構是MH_MAGIC_64,所支援的架構是arm64架構
  • CPU Type、CPU SubType代表CPU的型別和子型別,在原始碼<mach/machine.h>中夠可以看到具體的定義

  • File Type代表檔案的型別,圖中的檔案型別表示可執行檔案型別
  • Number of Load Commands 和 Size of Load Commands 表示Load Commands的數量和大小
  • Flags 代表動態連結器(dyld)的標誌
  • Reserved 保留欄位

Load Commands

Load Commands指定了檔案在虛擬記憶體中的邏輯結構和佈局,如下

在Load Commands中儲存了各種段的基本資訊,下面以LC_SEMENT_64(__PAGEZERO)

中的資訊為例

  • 最頂部的Command代表Load Command的型別是LC_SEMENT_64,具體含義是將檔案中的段對映到程序地址空間
  • Command Size 表示當前Load Command本身的大小
  • Segment Name 是Load Command的名稱,當前的Load Command名稱為__PAGEZERO
  • VM Address 表示__PAGEZERO段載入到虛擬記憶體中的地址,從0x000000000開始
  • VM Size 表示__PAGEZERO段在虛擬記憶體中所佔據的空間大小
  • File Offset 表示當前__PAGEZERO段在Mach-O檔案中的位置。
  • File Size 表示__PAGEZERO
    段在Mach-O檔案中的大小,此處File Size為0表示在Mach-O檔案中並沒有__PAGEZERO段,在Mach-O檔案被載入進虛擬記憶體中,才會附加上__PAGEZERO段。
  • Maxinum VM Protection 表示當前段在虛擬記憶體中所需要的最高記憶體保護
  • Initial VM Protection 表示當前段的初始記憶體保護
  • Number of Sections 表示當前段中所包含的Section的數量
  • Flag 標誌位

__PAGEZERO是Mach-O載入進記憶體之後附加的一塊區域,它不可讀,不可寫,主要用來捕捉NULL指標的引用。如果訪問__PAGEZERO段,會引起程式崩潰

Raw Segment Data

在Raw Segment Data中就存放了所有段的原始資料

  • __TEXT段中存放了所有函式程式碼
  • __DATA段中存放了所有全域性變數資訊

使用size 指令檢視Mach-O記憶體分佈

size -l -m -x Mach-O檔案路徑
複製程式碼

ASLR

什麼是ASLR?

ASLR其實就是Address Space Layout Randomization,地址空間佈局隨機化。它是一種針對緩衝區溢位的安全保護技術,通過對堆、棧、共享庫對映等線性區佈局的隨機化,通過增加攻擊者預測目的地址的難度,防止攻擊者直接定位攻擊程式碼位置,達到阻止溢位攻擊的目的的一種技術。在iOS 4.3開始引入ASLR技術

未使用ASLR技術時,Mach-O檔案載入進記憶體後如何佈局?

在未使用ASLR技術時,Mach-O被載入進記憶體後,是從地址0x000000000開始存放,前文說到,Mach-O檔案本身是不存在__PAGEZERO的,在Mach-O檔案被載入到虛擬記憶體之後,系統會給Mach-O檔案分配一個__PAGEZERO,它的開始位置是0x000000000,結束位置是0x100000000。並且它的大小是固定的。

Mach-O本身的內容在虛擬記憶體中存放的開始位置從0x100000000開始,也就是緊接著__PAGEZERO的結束地址存放。而且在下圖中,__TEXT段的File Offset為0,File Size為63062016,這代表著在Mach-O檔案中,從0x000000000位置開始到0x003C24000為止存放的都是__TEXT段的內容。

__TEXT段在虛擬記憶體中存放的開始位置是0x100000000,終止位置是0x103C24000,這說明__TEXT段是原封不動的從Mach-O檔案載入進虛擬記憶體中,緊接著__PAGEZERO存放的。

通過分析剩下的__DATA段、__LinkEDIT段等等可以得出以下結論

PS:在arm64架構中,__PAGEZERO段的終止位置是從0x100000000(8個0)而在非arm64架構中,__PAGEZERO段的終止位置是從0x4000(3個0)開始

使用了ASLR技術後,Mach-O檔案載入進記憶體後如何佈局?

在使用了ASLR技術之後,在Mach-O檔案載入進記憶體之後,__PAGEZERO的開始位置就不是從0x000000000開始存放了,ASLR會隨機產生一個地址偏移Offset,而__PAGEZERO的開始位置需要在0x000000000的基礎上加上偏移量Offset的值,才是真正的存放地址。 假設隨機偏移量Offset是0x000005000,那麼__PAGEZERO的開始位置就是0x000005000,結束位置就是0x100005000。剩下的__TEXT段、__DATA段和__LINKEDIT段則依次偏移Offset即可,如下:

獲取函式在虛擬記憶體中的真實記憶體地址

於Mach-O檔案被載入進虛擬記憶體中時,由於使用了ASLR技術,導致記憶體地址產生Offset,所以要想獲取函式的準確的記憶體地址,就需要知道當前具體的偏移量。然後使用以下公式就可得出函式在虛擬記憶體中的記憶體地址

函式的記憶體地址(VM Address) = File Offset + ASLR Offset + __PAGEZERO Size
複製程式碼
  • File Offset 表示當前函式在Mach-O檔案中的存放位置
  • ASLR Offset 表示隨機地址偏移量
  • __PAGEZERO Size 表示__PAGEZERO段的size

通常我們使用Hopper、IDA等工具檢視Mach-O檔案所看到的地址都是未使用ASLR的VM Address,要想獲取函式的真實虛擬記憶體地址,就需要找到Mach-O載入進虛擬記憶體後的隨機偏移量Offset

上圖中函式test的起始地址是0x6558,也就是說它的File Offset為0x6558。這個是它在Mach-O檔案中的地址偏移。

動態除錯,獲取程式ASLR的偏移量

運用上一章動態除錯的知識,我們來一步一步獲取ASLR的偏移量

  • 首先在Mac上使用tcprelay.py開啟Mac埠號對映
python tcprelay.py -t 22:10088 9999:10089
複製程式碼
  • 然後通過SSH連線iPhone
ssh root@localhost -p 10088
複製程式碼
  • 在iPhone上使用啟動Debugserver,將要動態除錯的App附加到Debugserver上,此處以聽雲App為例
debugserver *:9999 -a ting
複製程式碼
  • 在Mac上啟動LLDB,然後通過Mac的10089埠連線Debugserver服務
➜  ~ lldb
(lldb) process connect connect://localhost:10089
複製程式碼
  • 使用image list命令得到App可執行檔案的路徑
(lldb) image list -o -f grep | ting
[  0] 0x0000000000080000 /var/mobile/Containers/Bundle/Application/14C4F899-BD7B-41A4-BC1A-61892E7B943B/ting.app/ting(0x0000000100080000)
複製程式碼
  • 可以看出,0x0000000000080000就是聽雲可執行檔案的起始地址,也就是ASLR的偏移量,然後,使用Hopper Disassmbler可以獲取到未使用ASLR的地址,加上0x0000000000080000,就可以得到載入進記憶體之後的真實地址。