(五) Mach-O 文件之進程(虛擬)地址空間、ASLR

# ASLR引入
# ASLR
  ## 未使用ASLR
  ## 使用了ASLR
  ## 符號在可執(zhí)行文件谤绳、虛擬地址空間中的地址計算
    ### 符號內(nèi)存占锯、可執(zhí)行文件地址關系
    ### ASLR Offset的獲取
    ### Symbol Address符號化
# Mach-O文件的進程地址空間分布
# 小結(jié)
# 參考鏈接

# ASLR引入

進程在自己私有的虛擬地址空間中啟動袒哥。按照傳統(tǒng)方式,進程每一次啟動時采用的都是固定的可預見的方式消略。然而堡称,這意味著某個給定程序在某個給定架構(gòu)上的進程初始虛擬內(nèi)存鏡像都是基本一致的。而且更嚴重的問題在于艺演,即使是在進程正常運行的生命周期中却紧,大部分內(nèi)存分配的操作都是按照同樣的方式進行的,因此使得內(nèi)存中的地址分布具有非常強的可預測性胎撤。

盡管這有助于調(diào)試晓殊,但是也給黑客提供了更大的施展空間。黑客主要采用的方法是代碼注入:通過重寫內(nèi)存中的函數(shù)指針伤提,黑客就可以將程序的執(zhí)行路徑轉(zhuǎn)到自己的代碼巫俺,將程序的輸入轉(zhuǎn)變?yōu)樽约旱妮斎搿V貙憙?nèi)存最常用的方法是采用緩沖區(qū)溢出(即利用未經(jīng)保護的內(nèi)存復制操作越過上數(shù)組的邊界)肿男,可參考緩沖區(qū)溢出攻擊介汹,將函數(shù)的返回地址重寫為自己的指針。不僅如此舶沛,黑客還有更具創(chuàng)意的技術(shù)嘹承,例如破壞printf()格式化字符串以及基于堆的緩沖區(qū)溢出。此外如庭,任何用戶指針甚至結(jié)構(gòu)化的異常處理程序都可以導致代碼注入叹卷。這里的關鍵問題在于判斷重寫哪些指針,也就是說坪它,可靠地判斷注入的代碼應該在內(nèi)存中的什么位置骤竹。

不論被破解程序的薄弱環(huán)節(jié)在哪里:緩沖區(qū)溢出、格式化字符串攻擊或其他方式哟楷,黑客都可以花大力氣破解一個不安全的程序瘤载,找到這個程序的地址空間布局,然后精心設計一種方法卖擅,這種方法可以可靠地重現(xiàn)程序中的薄弱環(huán)節(jié),并且可以在類似的系統(tǒng)上暴露出一樣的薄弱環(huán)節(jié)墨技。

現(xiàn)在大部分操作系統(tǒng)中都采用了一種稱為地址空間布局隨機化(ASLR) 的技術(shù)惩阶,這是一種避免攻擊的有效保護。進程每一次啟動時扣汪,地址空間都會被簡單地隨機化:只是偏移断楷,而不是攪亂≌副穑基本的布局(程序文本冬筒、數(shù)據(jù)和庫)仍然是一樣的恐锣。然而,這些部分具體的地址都不同了——區(qū)別足夠大舞痰,可以阻擋黑客對地址的猜測土榴。實現(xiàn)方法是通過內(nèi)核將Mach-O的段“平移”某個隨機系數(shù)

# ASLR

地址空間布局隨機化(Address Space Layout Randomization响牛,ASLR)是一種針對緩沖區(qū)溢出的安全保護技術(shù)玷禽,通過對堆、棧呀打、共享庫映射等線性區(qū)布局的隨機化矢赁,通過增加攻擊者預測目的地址的難度,防止攻擊者直接定位攻擊代碼位置贬丛,達到阻止溢出攻擊的目的的一種技術(shù)撩银。iOS4.3開始引入了ASLR技術(shù)。

下面分別來看一下豺憔,未使用ASLR蜒蕾、使用了ASLR下,進程虛擬地址空間內(nèi)的分布焕阿。下圖中左側(cè)是mach-O可執(zhí)行文件咪啡,右側(cè)是鏈接之后的虛擬地址空間,如果對__TEXT暮屡、__DATA等Segment概念不清楚的地方撤摸,可以看一些第二篇關于Mach-O文件結(jié)構(gòu)的介紹。

## 未使用ASLR

  • 函數(shù)代碼存放在__TEXT段中
  • 全局變量存放在__DATA段中
  • 可執(zhí)行文件的內(nèi)存地址是0x0
  • 代碼段(__TEXT)的內(nèi)存地址就是LC_SEGMENT(__TEXT)中的VM Address:arm64設備下褒纲,為0x100000000准夷;非arm64下為0x4000
  • 可以使用size -l -m -x來查看Mach-O的內(nèi)存分布

## 使用了ASLR

  • LC_SEGMENT(__TEXT)的VM Address為0x100000000
  • ASLR隨機產(chǎn)生的Offset(偏移)為0x5000

## 函數(shù)(變量)符號的內(nèi)存地址、可執(zhí)行文件地址計算

### 函數(shù)內(nèi)存地址計算

  • File Offset: 在當前架構(gòu)(MachO)文件中的偏移量莺掠。
  • VM Address: 編譯鏈接后衫嵌,射到虛擬地址中的內(nèi)存起始地址。 VM Address = File Offset + __PAGEZERO Size(__PAGEZERO段在MachO文件中沒有實際大小彻秆,在VM中展開)
  • Load Address: 在運行時加載到虛擬內(nèi)存的起始位置楔绞。Slide是加載到內(nèi)存的偏移,這個偏移值是一個隨機值唇兑,每次運行都不相同酒朵。Load Address = VM Address + Slide(ASLR Offset)

由于dsym符號表是編譯時生成的地址,crash堆棧的地址是運行時地址扎附,這個時候需要經(jīng)過轉(zhuǎn)換才能正確的符號化蔫耽。crash日志里的符號地址被稱為Stack Address,而編譯后的符號地址被稱為Symbol Address留夜,他們之間的關系如下:Stack Address = Symbol Address + Slide

符號化就是通過Stack Address到dsym文件中尋找對應符號信息的過程匙铡。

Hopper图甜、IDA圖形化工具中的地址都是未使用ASLR前的VM Address

### ASLR Offset的獲取

ASLR Offset有的地方也叫做slide,獲取方法:

  • 在運行時由API dyld_get_image_vmaddr_slide()鳖眼,來獲取image虛擬地址的偏移量黑毅。
//函數(shù)原型如下:
extern intptr_t   _dyld_get_image_vmaddr_slide(uint32_t image_index);

//一般使用方法如下:
uint32_t c = _dyld_image_count();
for (uint32_t i = 0; i < c; i++) {
  intptr_t index  = _dyld_get_image_vmaddr_slide(i);
}
  • 通過lldb命令image list -o -f 進行獲取(本地具帮、遠程debugserver調(diào)試都可以)博肋,如下圖:

  • 根據(jù)運行時crash中的 binary image信息 和 ELF 文件的 load command 計算的到。比如下例:

//下面是crash信息蜂厅,其中包括了拋出異常的線程的函數(shù)調(diào)用棧信息匪凡,日志下方有binary image信息,都只摘取了部分:
/*
 第一列掘猿,調(diào)用順序
 第二列病游,對應函數(shù)所屬的 binary image
 第三列,stack address
 第四列稠通,地址的符號+偏移的表示法衬衬,運算結(jié)果等于第三列
*/
Last Exception Backtrace:  
0   CoreFoundation                0x189127100 __exceptionPreprocess + 132  
1   libobjc.A.dylib               0x1959e01fc objc_exception_throw + 60  
2   CoreFoundation                0x189127040 +[NSException raise:format:] + 128  
3   CrashDemo                     0x100a8666c 0x10003c000 + 10790508  
4   libsystem_platform.dylib      0x19614bb0c _sigtramp + 56  
5   CrashDemo                     0x1006ef164 0x10003c000 + 7024996  
6   CrashDemo                     0x1006e8580 0x10003c000 + 6997376  
7   CrashDemo                     0x1006e8014 0x10003c000 + 6995988  
8   CrashDemo                     0x1006e7c94 0x10003c000 + 6995092  
9   CrashDemo                     0x1006f2460 0x10003c000 + 7038048  

/* 
 第一列,虛擬地址空間區(qū)塊改橘;
 第二列滋尉,映射文件名;
 第三列:加載的image的UUID飞主;
 第四列狮惜,映射文件路徑 
*/
Binary Images:  
0x10003c000 - 0x100f7bfff CrashDemo arm64  <b5ae3570a013386688c7007ee2e73978> /var/mobile/Applications/05C398CE-21E9-43C2-967F-26DD0A327932/CrashDemo.app/CrashDemo  
0x12007c000 - 0x1200a3fff dyld arm64  <628da833271c3f9bb8d44c34060f55e0> /usr/lib/dyld


//下面是使用 otool 工具查看到的 MedicalRecordsFolder(我的程序)的 加載命令 。
$otool -l CrashDemo.app/CrashDemo   
CrashDemo.app/CrashDemo:  
Load command 0  
      cmd LC_SEGMENT_64  
  cmdsize 72  
  segname __PAGEZERO  
   vmaddr 0x0000000000000000  
   vmsize 0x0000000100000000  
  fileoff 0  
 filesize 0  
  maxprot 0x00000000  
 initprot 0x00000000  
   nsects 0  
    flags 0x0  
Load command 1  
      cmd LC_SEGMENT_64  
  cmdsize 792  
  segname __TEXT  
   vmaddr 0x0000000100000000  
   vmsize 0x000000000000c000  
  fileoff 0  
 filesize 49152  
  maxprot 0x00000005  
 initprot 0x00000005  
   nsects 9  
    flags 0x0  
……  
Load command 2 
……  

在 binary image 第一行可以看出進程空間的 0x10003c000 - 0x100f7bfff 這個區(qū)域在運行時被映射為 CrashDemo 內(nèi)的內(nèi)容碌识,也就是我們的 ELF 文件(區(qū)域起始地址為0x10003c000)碾篡。

而在 Load Command 中看到的__TEXT的段起始地址卻是 0x0000000100000000

顯而易見:slide = 0x10003c000(Load Address) - 0x100000000(VM Address) = 0x3c000;之后筏餐,就可以通過公式symbol address = stack address - slide; 來計算stack address 在crash log 中已經(jīng)找到了开泽。

### Symbol Address符號化

  • 利用dwarfdump可以從dsym文件中得到symbol Address對應的內(nèi)容:

    • 拿到crash日志后,我們要先確定dsym文件是否匹配魁瞪∧侣桑可以使用下面命令查看dsym文件所有架構(gòu)的UUID:dwarfdump --uuid CrashDemo.app.dSYM,然后跟crash日志中Binary Images中的UUID相比對佩番。
    • 用得到的Symbol Address去 dsym 文件中查詢众旗,命令如下:dwarfdump --arch arm64 --lookup [Symbol Address] CrashDemo.app.dSYM,就可以定位下來具體的代碼趟畏、函數(shù)名、所處的文件滩租、行等信息了
  • 如果只是簡單的獲取符號名赋秀,可以用atos來符號化:
    atos -o [dsym file path] -l [Load Address] -arch [arch type] [Stack Address]

    • 不需要指定Symbol Address利朵,只需要Load Address、Stack Address即可猎莲。

# 進程地址空間

由于ASLR的作用绍弟,進程的地址空間變得流動性非常大。但是盡管具體的地址會隨機“滑動”某個小的偏移量著洼,但整體布局保持不變樟遣。

內(nèi)存空間分為以下幾個段:

  • __PAGEZERO:在32位的系統(tǒng)中,這是內(nèi)存中單獨的一個頁面(4KB)身笤,而且這個頁面所有的訪問權(quán)限都被撤消了豹悬。在 64 位系統(tǒng)上,這個段對應了一個完整的32位地址空間(即前4GB)液荸。這個段有助于捕捉空指針引用(因為空指針實際上就是 0)瞻佛,或捕捉將整數(shù)當做指針引用(因為32位平臺下的 4095 以下的值,以及64位平臺下4GB以下的值都在這個范圍內(nèi))娇钱。由于這個范圍內(nèi)所有訪問權(quán)限(讀伤柄、寫和執(zhí)行)都被撤消了,所以在這個范圍內(nèi)的任何解引用操作都會引發(fā)來自 MMU 的硬件頁錯誤文搂, 進而產(chǎn)生一個內(nèi)核可以捕捉的陷阱适刀。內(nèi)核將這個陷阱轉(zhuǎn)換為C++異常或表示總線錯誤的POSIX信號(SIGBUS) 煤蹭。

PAGEZERO不是設計給進程使用的笔喉,但是多少成為了惡意代碼的溫床。想要通過“額外”代碼感染Mach-O的攻擊者往往發(fā)現(xiàn)可以很方便地通過PAGEZERO實現(xiàn)這個目的疯兼。PAGEZERO通常不屬于目標文件的一部分(其對應的加載指令LC_SEGMENT將filesize指定為0)然遏,但是對此并沒有嚴格的要求.

  • __TEXT:這個段存放的是程序代碼。和其他所有操作系統(tǒng)一樣吧彪,文本段被設置為r-x待侵,即只讀且可執(zhí)行。這不僅可以防止二進制代碼在內(nèi)存中被修改姨裸,還可以通過共享這個只讀段優(yōu)化內(nèi)存的使用秧倾。通過這種方式,同一個程序的多個實例可以僅使用一份TEXT副本傀缩。文本段通常包含多個區(qū)那先,實際的代碼在_text區(qū)中。文本段還可以包含其他只讀數(shù)據(jù)赡艰,例如常量和硬編碼的字符串售淡。
  • __LINKEDIT:由dyld使用,這個區(qū)包含了字符串表、符號表以及其他數(shù)據(jù)揖闸。
  • __IMPORT:用于 i386 的二進制文件的導入表揍堕。
  • __DATA:用于可讀/可寫的數(shù)據(jù)。
  • __MALLOC_TINY:用于小于一個頁面大小的內(nèi)存分配汤纸。
  • __MALLOC_SMALL:用于幾個頁面大小的內(nèi)存分配衩茸。

下面是使用vmmap(1)輸出的一個實例程序a32位硬件設備上運行的進程地址空間,顯示了區(qū)域的名稱贮泞、地址范圍楞慈、權(quán)限(當前權(quán)限和最高權(quán)限)以及映射的名稱(通常對應的是Mach-O目標文件,如果有的話)啃擦。

32位進程的虛擬地址空間布局

# 小結(jié)

應該注意的是囊蓝,盡管ASLR是很顯著的改進,但也不是萬能藥议惰。黑客仍然能找到聰明的方法破解程序慎颗。事實上,目前臭名昭著的“Star 3.0”漏洞就攻破了ASLR言询,這個漏洞越獄了 iPad 2 上的iOS 4.3俯萎。這種破解使用了Retum-Oriented Programming(ROP)攻擊技術(shù),通過緩沖區(qū)溢出破壞棧运杭,以設置完整的棧幀夫啊, 模擬對libSystem的調(diào)用。同樣的技術(shù)也用在iOS 5.0.1的“corona”漏洞中辆憔,這個漏洞成功地攻入了所有的蘋果設備撇眯,包括當時最新的iPhone 4S。

預防攻擊的唯一之道就是編寫更加安全的代碼虱咧,并且采用嚴格的代碼審查熊榛,既要包含自動的技術(shù),也要有人工的介入腕巡。

# 參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末玄坦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绘沉,更是在濱河造成了極大的恐慌煎楣,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件车伞,死亡現(xiàn)場離奇詭異择懂,居然都是意外死亡,警方通過查閱死者的電腦和手機另玖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門困曙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來表伦,“玉大人,你說我怎么就攤上這事赂弓“罅瘢” “怎么了哪轿?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵盈魁,是天一觀的道長。 經(jīng)常有香客問我窃诉,道長杨耙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任飘痛,我火速辦了婚禮珊膜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宣脉。我一直安慰自己车柠,他們只是感情好,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布塑猖。 她就那樣靜靜地躺著竹祷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪羊苟。 梳的紋絲不亂的頭發(fā)上塑陵,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機與錄音蜡励,去河邊找鬼令花。 笑死,一個胖子當著我的面吹牛凉倚,可吹牛的內(nèi)容都是我干的兼都。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼稽寒,長吁一口氣:“原來是場噩夢啊……” “哼扮碧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瓦胎,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤芬萍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搔啊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柬祠,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年负芋,在試婚紗的時候發(fā)現(xiàn)自己被綠了漫蛔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嗜愈。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖莽龟,靈堂內(nèi)的尸體忽然破棺而出蠕嫁,到底是詐尸還是另有隱情,我是刑警寧澤毯盈,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布剃毒,位于F島的核電站,受9級特大地震影響搂赋,放射性物質(zhì)發(fā)生泄漏赘阀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一脑奠、第九天 我趴在偏房一處隱蔽的房頂上張望基公。 院中可真熱鬧,春花似錦宋欺、人聲如沸轰豆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酸休。三九已至,卻和暖如春掌挚,著一層夾襖步出監(jiān)牢的瞬間雨席,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工吠式, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陡厘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓特占,卻偏偏與公主長得像糙置,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子是目,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353