簡介2021年2月,蘋果公司發布了關於iBoot內存安全的新舉措,並將其納入蘋果安全平台的一部分。他們的描述中提到,“蘋果公司修改了用於構建iBoot引導程序的C編譯器工具鏈,以提高其安全性”,並對其工作進行了一些概述。以下是引自該文件中的相關內容:
內存安全的iBoot實現在iOS 14和iPadOS 14中,蘋果公司修改了用於構建iBoot引導程序的C編譯器工具鏈,以提高其安全性。修改後的工具鏈實現了旨在防禦C程序中常見的內存和類型安全問題的代碼。例如,它有助於防止以下類型的安全漏洞:
緩衝區溢出,通過確保所有指針攜帶邊界信息,在訪問內存時進行驗證。
堆的利用,通過將堆數據與元數據分開,並準確地檢測錯誤條件,如重複釋放錯誤。
類型混淆,通過確保所有指針攜帶運行時的類型信息,並在指針類型轉換操作中進行相應的檢查。
通過將所有的動態內存分配按靜態類型進行隔離,避免由釋放後使用錯誤引起的類型混淆。
這項技術適用於配備Apple A13 Bionic或後續芯片的iPhone,以及配備A14 Bionic芯片的iPad。
我覺得,把一些關於實現、格式和蘋果在這方面所做的令人興奮的工作的信息放在一起可能會更好。順便說一句,在iBoot二進製文件中,有一些非常有用的信息字符串,它們很快就被發佈到了Twitter上。
我對這項工作非常著迷,因為上述描述給人的印像是用軟件實現的“輕量級的CHERI版本”。根據蘋果公司的描述,在新版本的iBoot中,指針攜帶的不僅僅是一個地址——同時,它們還攜帶了邊界和類型信息,這樣的話,編譯器就可以為代碼引入新的內存安全驗證。
我喜歡刨根問底,所以,不妨讓我們一起潛心研究一番,看看我們能發現什麼新玩意。
需要說明的是,這項研究是在iBoot.d53g.RELEASE.im4p、iPhone 12以及ios 14.4(18D52)環境中進行的。
著手進行逆向工程首先,讓我們看看系統檢測到內存安全違規後是如何進行處理的。當內存安全違規發生時,觸發panic是非常有意義的,事實上,我們在二進製文件中提供了一個“__firebloom_panic”字符串。利用這一點,我們可以為周圍的函數進行命名,並重點關注下面這個簡單的函數:
iBoot:00000001FC1AA5A0 firebloom_panic
iBoot:00000001FC1AA5A0
iBoot:00000001FC1AA5A0 var_B8=-0xB8
iBoot:00000001FC1AA5A0 var_B0=-0xB0
iBoot:00000001FC1AA5A0 var_18=-0x18
iBoot:00000001FC1AA5A0 var_10=-0x10
iBoot:00000001FC1AA5A0 var_s0=0
iBoot:00000001FC1AA5A0
iBoot:00000001FC1AA5A0 PACIBSP
iBoot:00000001FC1AA5A4 SUB SP, SP, #0xD0
iBoot:00000001FC1AA5A8 STP X20, X19, [SP,#0xC0+var_10]
iBoot:00000001FC1AA5AC STP X29, X30, [SP,#0xC0+var_s0]
iBoot:00000001FC1AA5B0 ADD X29, SP, #0xC0
iBoot:00000001FC1AA5B4 MOV X19, X0
iBoot:00000001FC1AA5B8 ADD X0, SP, #0xC0+var_B8
iBoot:00000001FC1AA5BC BL sub_1FC1A9A08
iBoot:00000001FC1AA5C0 ADD X8, X29, #0x10
iBoot:00000001FC1AA5C4 STUR X8, [X29,#var_18]
iBoot:00000001FC1AA5C8 ADR X1, aPasPanic ; 'pas panic: '
iBoot:00000001FC1AA5CC NOP
iBoot:00000001FC1AA5D0 ADD X0, SP, #0xC0+var_B8
iBoot:00000001FC1AA5D4 BL do_trace
iBoot:00000001FC1AA5D8 LDUR X2, [X29,#var_18]
iBoot:00000001FC1AA5DC ADD X0, SP, #0xC0+var_B8
iBoot:00000001FC1AA5E0 MOV X1, X19
iBoot:00000001FC1AA5E4 BL sub_1FC1A9A48
iBoot:00000001FC1AA5E8 LDR X0, [SP,#0xC0+var_B0]
iBoot:00000001FC1AA5EC BL __firebloom_panic
我們可以都看到,這個函數有11個交叉引用。我把其中一個命名為“do_firebloom_panic”,它也有11個交叉引用,並且每個交叉引用都能捕捉到不同類型的違規行為。
好的,現在我們就有了一個(部分)清單,列出了firebloom會明確檢測並引起恐慌的錯誤。因為其中一些新的檢查是針對已知的定義良好的函數(memset, memcpy),所以,接下來可以期待看到新的memset和memcpy的封裝函數,並在其中加入新的檢查。通過跟踪交叉引用鏈並不斷逆向該流程,我們很容易就能找到這些封裝函數。
然而,我很好奇其餘的驗證會是什麼情況:例如,我們在哪裡/如何看到ptr_under/ptr_over?好吧,函數panic_ptr_over有179處交叉引用,其中很多只是帶有一些哈希值的封裝函數。這些封裝函數也有一些交叉引用,不過這些是來自實際的代碼,並且當內存安全違規發生時會觸發恐慌。通過跟進執行流程,我們可以發現很多示例,可以幫我們搞清楚它們的使用情況。
我只相信實際的例子,因為沒有什麼比代碼更能說明一切了,所以,我們就通過一個執行流程,來舉例說明:
iBoot:00000001FC05C5AC loop ; CODE XREF: sub_1FC05C548+94↓j
iBoot:00000001FC05C5AC CMP X10, X9
iBoot:00000001FC05C5B0 B.EQ return
iBoot:00000001FC05C5B4 ; fetch ptr and lower bounds
iBoot:00000001FC05C5B4 LDP X11, X13, [X0]
iBoot:00000001FC05C5B8 ; advance the ptr to ptr+offset, it's a loop
iBoot:00000001FC05C5B8 ADD X12, X11, X9
iBoot:00000001FC05C5BC CMP X12, X13
iBoot:00000001FC05C5C0 B.CC detected_ptr_under
iBoot:00000001FC05C5C4 ; fetch upper bounds
iBoot:00000001FC05C5C4 LDR X13, [X0,#0x10]
iBoot:00000001FC05C5C8 CMP X12, X13
iBoot:00000001FC05C5CC B.CS detected_ptr_over
iBoot:00000001FC05C5D0 ; actually dereference the pointer
iBoot:00000001FC05C5D0 LDR W11, [X11,X9]
iBoot:00000001FC05C5D4 STR W11, [X8,#0x1DC]
iBoot:00000001FC05C5D8 ADD X9, X9, #4
iBoot:00000001FC05C5DC B loop
iBoot:00000001FC05C5E0 ; ---------------------------------------------------------------------------
iBoot:00000001FC05C5E0
iBoot:00000001FC05C5E0 return ; CODE XREF: sub_1FC05C548+68↑j
iBoot:00000001FC05C5E0 LDUR X8, [X29,#var_8]
iBoot:00000001FC05C5E4 ADRP X9, #a160d@PAGE ; '160D'
iBoot:00000001FC05C5E8 NOP
iBoot:00000001FC05C5EC LDR X9, [X9,#a160d@PAGEOFF] ; '160D'
iBoot:00000001FC05C5F0 CMP X9, X8
iBoot:00000001FC05C5F4 B.NE do_panic
iBoot:00000001FC05C5F8 LDP X29, X30, [SP,#0x70+var_s0]
iBoot:00000001FC05C5FC ADD SP, SP, #0x80
iBoot:00000001FC05C600 RETAB
iBoot:00000001FC05C604 ; ---------------------------------------------------------------------------
iBoot:00000001FC05C604
iBoot:00000001FC05C604 do_panic ; CODE XREF: sub_1FC05C548+AC↑j
iBoot:00000001FC05C604 BL call_panic
iBoot:00000001FC05C608 ; ---------------------------------------------------------------------------
iBoot:00000001FC05C608
iBoot:00000001FC05C608 detected_ptr_under ; CODE XREF: sub_1FC05C548+78↑j
iBoot:00000001FC05C608 BL call_panic_ptr_under_5383366e236c433
iBoot:00000001FC05C60C ; ---------------------------------------------------------------------------
iBoot:00000001FC05C60C
iBoot:00000001FC05C60C detected_ptr_over ; CODE XREF: sub_1FC05C548+84↑j
iBoot:00000001FC05C60C BL call_panic_ptr_over_5383366e236c433
iBoot:00000001FC05C610 ; ---------------------------------------------------------------------------
因此,在訪問偏移量為X9處的指針(在0x01fc05c5d0)之前,代碼將根據某些界限來檢查PTR+偏移量是否越界。其中,原始指針和邊界指針(下界和上界)是從某個結構體中檢索的(稍後我將對其進行定義)。在此之前,為了讓更好地了解相關的函數,讓我們先看看相關的panic封裝函數:
iBoot:00000001FC05D384 call_panic_ptr_over_5383366e236c433 ; CODE XREF: sub_1FC05C548:detected_ptr_over↑p
iBoot:00000001FC05D384 ; DATA XREF: call_panic_ptr_over_5383366e236c433+24↓o
iBoot:00000001FC05D384
iBoot:00000001FC05D384 var_8=-8
iBoot:00000001FC05D384 var_s0=0
iBoot:00000001FC05D384
iBoot:00000001FC05D384 PACIBSP
iBoot:00000001FC05D388 SUB SP, SP, #0x20
iBoot:00000001FC05D38C STP X29, X30, [SP,#0x10+var_s0]
iBoot:00000001FC05D390 ADD X29, SP, #0x10
iBoot:00000001FC05D394 ADRL X8, a5383366e236c43 ; '5383366e236c433'
iBoot:00000001FC05D39C STR X8, [SP,#0x10+var_8]
iBoot:00000001FC05D3A0 MOV X8, X30
iBoot:00000001FC05D3A4 XPACI X8
iBoot:00000001FC05D3A8 ADR X16, call_panic_ptr_over_5383366e236c433
iBoot:00000001FC05D3AC NOP
iBoot:00000001FC05D3B0 PACIZA X16
iBoot:00000001FC05D3B4 SUB X2, X8, X16
iBoot:00000001FC05D3B8 ADD X0, SP, #0x10+var_8
iBoot:00000001FC05D3BC MOV W1, #1
iBoot:00000001FC05D3C0 BL panic_ptr_over
iBoot:00000001FC05D3C0 ; End of function call_panic_ptr_over_5383366e236c433
以及:
iBoot:00000001FC1AA980 panic_ptr_over ; CODE XREF: sub_1FC04CBD0+3C↑p
iBoot:00000001FC1AA980 ; sub_1FC04EC2C+3C↑p .
iBoot:00000001FC1AA980
iBoot:00000001FC1AA980 var_20=-0x20
iBoot:00000001FC1AA980 var_10=-0x10
iBoot:00000001FC1AA980 var_s0=0
iBoot:00000001FC1AA980
iBoot:00000001FC1AA980 PACIBSP
iBoot:00000001FC1AA984 STP X22, X21, [SP,#-0x10+var_20]!
iBoot:00000001FC1AA988 STP X20, X19, [SP,#0x20+var_10]
iBoot:00000001FC1AA98C STP X29, X30, [SP,#0x20+var_s0]
iBoot:00000001FC1AA990 ADD X29, SP, #0x20
iBoot:00000001FC1AA994 MOV X19, X2
iBoot:00000001FC1AA998 MOV X20, X1
iBoot:00000001FC1AA99C MOV X21, X0
iBoot:00000001FC1AA9A0 ADRP X8, #0x1FC2F2270@PAGE
iBoot:00000001FC1AA9A4 LDR X8, [X8,#0x1FC2F2270@PAGEOFF]
iBoot:00000001FC1AA9A8 CBZ X8, do_panic
iBoot:00000001FC1AA9AC BLRAAZ X8
iBoot:00000001FC1AA9B0
iBoot:00000001FC1AA9B0 do_panic ; CODE XREF: panic_ptr_over+28↑j
iBoot:00000001FC1AA9B0 ADR X0, aPtrOver ; 'ptr_over'
iBoot:00000001FC1AA9B4 NOP
iBoot:00000001FC1AA9B8 MOV X1, X21
iBoot:00000001FC1AA9BC MOV X2, X20
iBoot:00000001FC1AA9C0 MOV X3, X19
iBoot:00000001FC1AA9C4 BL do_firebloom_panic
iBoot:00000001FC1AA9C4 ; End of function panic_ptr_over
很好,看起來非常簡單。
讓我們看看同樣的模式是否在其他地方重複出現;例如,下面這個:
在這個例子中,你可以看到一個循環語句在遍歷一個元素數組(每個元素大小為0x20),並對每個元素調用一些函數。而且,不出所料,這里以相同的方式使用了相同的“指針結構體”。
格式函數與輔助函數因此,我們有理由相信,內存分配用到的結構體如下所示:
00000000 safe_allocation struc ; (sizeof=0x20, mappedto_1)
00000000 raw_ptr DCQ ? offset
00000008 lower_bound_ptr DCQ ? offset
00000010 upper_bound_ptr DCQ ? offset
00000018 field_18 DCQ ?
000000