簡介歡迎來到Firebloom iBoot系列文章的第二篇。在上一篇文章中,我們為讀者詳細介紹了Firebloom在iBoot中的實現方式,內存分配的表現形式,以及編譯器是如何使用它們的。現在,讓我們首先回顧一下用於描述內存分配的結構體:
00000000safe_allocationstruc;(sizeof=0x20,mappedto_1)
00000000raw_ptrDCQ?
00000008lower_bound_ptrDCQ?
00000010upper_bound_ptrDCQ?
00000018typeDCQ?
00000020safe_allocationends在上一篇文章中,我們重點介紹了Firebloom是如何使用lower_bound_ptr和upper_bound_ptr指針的,它們主要用於為內存空間提供安全保護,即防止任何形式的後向/前向越界訪問。在這篇文章中,我們將重點介紹type指針。
我們強烈建議讀者在閱讀這篇博文之前,首先閱讀我們的第一篇文章,以了解相關的背景知識。和上一篇文章一樣,我們的研究工作是在iBoot.d53g.RELEASE.im4p上進行的,對應於安裝了ios 14.4(18D52)系統的iPhone 12機器。
繼續我們的逆向之旅在之前的文章中,我們為讀者介紹過do_safe_allocation函數,其所用是封裝內存分配API並對safe_allocation結構體進行初始化,其中偏移量+0x18處的指針指向了一些描述類型信息的結構體。我們還通過一個例子表明,在使用分配的內存之前,系統會通過該指針進行相應的類型檢查。現在,是時候看看這種功能是如何工作的,以及這個type指針起到了那些作用。
我發現許多與type指針有關的邏輯(類型轉換、在安全分配的內存之間進行複制,等等),這些邏輯真的值得我們逆向研究一番。我想最好是從構件開始,然後逐級而上。
複製指針與內存為了幫助大家理解,我們決定從一些例子入手。為此,讓我們從panic_memcpy_bad_type開始進行逆向——它是do_firebloom_panic的11個交叉引用之一。
iBoot:00000001FC1AA818panic_memcpy_bad_type;CODEXREF:call_panic_memcpy_bad_type+3C↑p
iBoot:00000001FC1AA818
iBoot:00000001FC1AA818var_20=-0x20
iBoot:00000001FC1AA818var_10=-0x10
iBoot:00000001FC1AA818var_s0=0
iBoot:00000001FC1AA818
iBoot:00000001FC1AA818PACIBSP
iBoot:00000001FC1AA81CSTPX22,X21,[SP,#-0x10+var_20]!
iBoot:00000001FC1AA820STPX20,X19,[SP,#0x20+var_10]
iBoot:00000001FC1AA824STPX29,X30,[SP,#0x20+var_s0]
iBoot:00000001FC1AA828ADDX29,SP,#0x20
iBoot:00000001FC1AA82CMOVX19,X2
iBoot:00000001FC1AA830MOVX20,X1
iBoot:00000001FC1AA834MOVX21,X0
iBoot:00000001FC1AA838ADRPX8,#0x1FC2F2248@PAGE
iBoot:00000001FC1AA83CLDRX8,[X8,#0x1FC2F2248@PAGEOFF]
iBoot:00000001FC1AA840CBZX8,loc_1FC1AA848
iBoot:00000001FC1AA844BLRAAZX8
iBoot:00000001FC1AA848
iBoot:00000001FC1AA848loc_1FC1AA848;CODEXREF:panic_memcpy_bad_type+28↑j
iBoot:00000001FC1AA848ADRX0,aMemcpyBadType;'memcpy_bad_type'
iBoot:00000001FC1AA84CNOP
iBoot:00000001FC1AA850MOVX1,X21
iBoot:00000001FC1AA854MOVX2,X20
iBoot:00000001FC1AA858MOVX3,X19
iBoot:00000001FC1AA85CBLdo_firebloom_panic
iBoot:00000001FC1AA85C;Endoffunctionpanic_memcpy_bad_type我們將從最簡單的東西開始,即復制指針的操作。
在我之前的文章中,我特別指出:現在復制一個指針(即進行指針賦值)需要移動一個由4個64位值組成的元組。通常來說,我們可以將其視為是2個LDP與2個STP指令。現在,我們通過下面的函數來舉例說明:
iBoot:00000001FC15AD74move_safe_allocation_x20_to_x19;CODEXREF:sub_1FC15A7E0+78↑p
iBoot:00000001FC15AD74;wrap_memset_type_safe+68↑p.
iBoot:00000001FC15AD74LDPX8,X9,[X20]
iBoot:00000001FC15AD78LDPX10,X11,[X20,#0x10]
iBoot:00000001FC15AD7CSTPX10,X11,[X19,#0x10]
iBoot:00000001FC15AD80STPX8,X9,[X19]
iBoot:00000001FC15AD84RET
iBoot:00000001FC15AD84;Endoffunctionmove_safe_allocation_x20_to_x19如今,這種由2個LDP指令和2個STP指令組成的模式,在iBoot中是非常常見的(這是很正常的,因為指針賦值經常發生),所以,我們會在許多地方看到這樣的內聯代碼。雖然這對於指針賦值很有用,但在許多情況下,我們想要做的卻是複制內容——例如,調用memcpy的時候。因此,有趣的事情就出現了:是否應該允許在兩個“safe_allocations”內存之間調用memcpy?
理論上,我們可以執行下面的代碼:
memcpy(dst-raw_ptr,src-raw_ptr,length);但是,請記住,每段safe_allocation內存都具有相應的type指針,該指針指向某個結構體,以便提供與當前處理的類型有關的更多信息。這些信息可以用於進一步的檢查和驗證。例如,我們希望看到一些邏輯來檢查dst和src的類型是否為基本類型(即這些類型不包含對其他結構體、嵌套結構體等結構體的引用,如short/int/float/double/等類型,就是屬於基本類型)。
這很重要,因為如果src或dst是非基本類型,我們就需要確保只有在它們的類型在某種程度上相等時才將src複製到dst。或者,type實際上保存了更多與結構體有關的元數據,因此需要確保更多的安全屬性。
因此,我想了解一下Firebloom是如何描述基本類型的。在對類型轉換功能,以及其他功能代碼進行逆向分析之後,我終於搞清楚了這一點。有趣的是,分析過程再簡單不過了——我們在函數cast_impl中找到了很多有用的字符串。例如:
aCannotCastPrimDCB'Cannotcastprimitivetypetonon-primitivetype',0借助於交叉引用,我們在下面的代碼中發現,X21寄存器就是來自safe_allocation內存的type指針:
iBoot:00000001FC1A0CF8;X21isthetypepointer
iBoot:00000001FC1A0CF8LDRX11,[X21]
iBoot:00000001FC1A0CFCANDX11,X11,#0xFFFFFFFFFFFFFFF8
iBoot:00000001FC1A0D00LDRBW12,[X11]
iBoot:00000001FC1A0D04TSTW12,#7
iBoot:00000001FC1A0D08;oneofthe3LSBbitsisnot0,non-primitivetype
iBoot:00000001FC1A0D08B.NEcannot_cast_primitive_to_non_primitive_type
iBoot:00000001FC1A0D0CLDRX11,[X11,#0x20]
iBoot:00000001FC1A0D10LSRX11,X11,#0x23;'#'
iBoot:00000001FC1A0D14CBNZX11,cannot_cast_primitive_to_non_primitive_type
.
iBoot:00000001FC1A0E70cannot_cast_primitive_to_non_primitive_type
iBoot:00000001FC1A0E70;CODEXREF:cast_impl+478↑j
iBoot:00000001FC1A0E70;cast_impl+484↑j
iBoot:00000001FC1A0E70ADRX11,aCannotCastPrim;'Cannotcastprimitivetypetonon-primi'.好了,現在我們知道Firebloom是如何標記和測試基本類型的了。這段代碼只是將一種類型轉換為另一種類型的功能實現中的一小部分,特別是這裡的X21寄存器是我們要轉換為的safe_allocation結構體中的type指針。在進行類型轉換時,我們驗證要轉換的類型是基本類型;同時,還要驗證轉換後的目標類型也是基本類型(否則就會出現panic)。
為了完成這項檢查,代碼會對type指針進行解引用,進而得到另一個指針(我們稱之為type_descriptor)。然後,將其最低的3個二進制位屏蔽掉(它們可能對應於一個編碼,這就是所有用到該指針的地方都會在解除其引用之前都屏蔽它的原因),然後對該指針解除引用。
現在,如果以下兩個屬性都滿足要求,那麼,該類型被認為是“基本類型”:
第一個qword的低3位都為0。
在偏移量0x20處存儲的值的高29位都為0。
太好了,我們剛剛了解了基本類型是如何表示的。在本文的後面部分,我們將詳細介紹這些值的具體含義。
有了這些知識,我們就可以著手了解Firebloom到底是如何在iBoot中封裝memset和memcpy函數的了。現在,讓我們從memset函數開始:
iBoot:00000001FC15A99Cwrap_memset_safe_allocation;CODEXREF:sub_1FC04E5D0+124↑p
iBoot:00000001FC15A99C;sub_1FC04ED68+8↑j.
iBoot:00000001FC15A99C
iBoot:00000001FC15A99Cvar_30=-0x30
iBoot:00000001FC15A99Cvar_20=-0x20
iBoot:00000001FC15A99Cvar_10=-0x10
iBoot:00000001FC15A99Cvar_s0=0
iBoot:00000001FC15A99C
iBoot:00000001FC15A99CPACIBSP
iBoot:00000001FC15A9A0SUBSP,SP,#0x60
iBoot:00000001FC15A9A4STPX24,X23,[SP,#0x50+var_30]
iBoot:00000001FC15A9A8STPX22,X21,[SP,#0x50+var_20]
iBoot:00000001FC15A9ACSTPX20,X19,[SP,#0x50+var_10]
iBoot:00000001FC15A9B0STPX29,X30,[SP,#0x50+var_s0]
iBoot:00000001FC15A9B4ADDX29,SP,#0x50
iBoot:00000001FC15A9B8;void*memset(void*s,intc,size_tn);
iBoot:00000001FC15A9B8;X0-dst(s)
iBoot:00000001FC15A9B8;X1-char(c)
iBoot:00000001FC15A9B8;X2-length(n)
iBoot:00000001FC15A9B8MOVX21,X2
iBoot:00000001FC15A9BCMOVX22,X1
iBoot:00000001FC15A9C0MOVX20,X0
iBoot:00000001FC15A9C4MOVX19,X8
iBoot:00000001FC15A9C8;verifyupper_bound-raw_ptr=x2(length)
iBoot:00000001FC15A9C8BLcheck_ptr_bounds
iBoot:00000001FC15A9CCLDRX23,[X20,#safe_allocation.type]
iBoot:00000001FC15A9D0MOVX0,X23
iBoot:00000001FC15A9D4;checkifdstisaprimitivetype
iBoot:00000001FC15A9D4BLis_primitive_type
iBoot:00000001FC15A9D8TBNZW0,#0,call_memset
iBoot:00000001FC15A9DCCBNZW22,detected_memset_bad_type
iBoot:00000001FC15A9E0MOVX0,X23
iBoot:00000001FC15A9E4BLget_type_length
iBoot:00000001FC15A9E8;divideandmultiplythelengthargument
iBoot:00000001FC15A9E8;bythetype'ssize,todetect
iBoot:00000001FC15A9E8;partial/unalignmentwrites
iBoot:00000001FC15A9E8UDIVX8,X21,X0
iBoot:00000001FC15A9ECMSUBX8,X8,X0,X21
iBoot:00000001FC15A9F0CBNZX8,detected_memset_bad_n
iBoot:00000001FC15A9F4
iBoot:00000001FC15A9F4call_memset;CODEXREF:wrap_memset_safe_allocation+3C↑j
iBoot:00000001FC15A9F4LDRX0,[X20,#safe_allocation]
iBoot:00000001FC15A9F8MOVX1,X22
iBoot:00000001FC15A9FCMOVX2,X21
iBoot:00000001FC15AA00BL_memset
iBoot:00000001FC15AA04BLmove_safe_allocation_x20_to_x19
iBoot:00000001FC15AA08LDPX29,X30,[SP,#0x50+var_s0]
iBoot:00000001FC15AA0CLDPX20,X19,[SP,#0x50+var_10]
iBoot:00000001FC15AA10LDPX22,X21,[SP,#0x50+var_20]
iBoot:00000001FC15AA14LDPX24,X23,[SP,#0x50+var_30]
iBoot:00000001FC15AA18ADDSP,SP,#0x60;'`'
iBoot:00000001FC15AA1CRETAB
iBoot:00000001FC15AA20;---------------------------------------------------------------------------
iBoot:00000001FC15AA20
iBoot:00000001FC15AA20detected_memset_bad_type;CODEXREF:wrap_memset_safe_allocation+40↑j
iBoot:00000001FC15AA20BLcall_panic_memset_bad_type
iBoot:00000001FC15AA24;---------------------------------------------------------------------------
iBoot:00000001FC15AA24
iBoot:00000001FC15AA24detected_memset_bad_n;CODEXREF:wrap_memset_safe_allocation+54↑j
iBoot:00000001FC15AA24BL
Recommended Comments