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

Mach-O基本結構回顧

在深刻學習Mach-O文件以前,先來回顧一下以前學習的Mach-O的基本結構,能夠到官網查看Mach-O文件的介紹html

Mach-O的組成

Mach-O文件有三部分組成python

  • Header中包含文件類型、目標架構類型等等基本信息
  • Load Commands是描述文件在虛擬內存中的邏輯結構和佈局,至關於簡介和目錄索引
  • Raw Segment Data中存放了全部在Load Commands中定義的Segment所對應的原始數據

Mach-O深刻探究

Header

在Mach-O文件中,Header部分存放了文件的基本描述信息,以下:sass

  • 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)中的信息爲例bash

  • 最頂部的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段,會引發程序崩潰markdown

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技術app

未使用ASLR技術時,Mach-O文件加載進內存後如何佈局?

在未使用ASLR技術時,Mach-O被加載進內存後,是從地址0x000000000開始存放,前文說到,Mach-O文件自己是不存在__PAGEZERO的,在Mach-O文件被加載到虛擬內存以後,系統會給Mach-O文件分配一個__PAGEZERO,它的開始位置是0x000000000,結束位置是0x100000000。而且它的大小是固定的。dom

Mach-O自己的內容在虛擬內存中存放的開始位置從0x100000000開始,也就是緊接着__PAGEZERO的結束地址存放。並且在下圖中,__TEXT段的File Offset爲0,File Size爲63062016,這表明着在Mach-O文件中,從0x000000000位置開始到0x003C24000爲止存放的都是__TEXT段的內容。ssh

而__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,就能夠獲得加載進內存以後的真實地址。
相關文章
相關標籤/搜索