深入学习Firebloom(iBoot) - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

深入学习Firebloom(iBoot)

fanyeee 技术 2022-06-30 11:00:00
85555
收藏

导语:在本文中,我们将为读者详细介绍了Firebloom在iBoot中的实现方式,内存分配的表现形式,以及编译器是如何使用它们的。

简介

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个交叉引用,并且每个交叉引用都能捕捉到不同类型的违规行为。

1.png

好的,现在我们就有了一个(部分)清单,列出了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

很好,看起来非常简单。 

让我们看看同样的模式是否在其他地方重复出现;例如,下面这个:

1.png

在这个例子中,你可以看到一个循环语句在遍历一个元素数组(每个元素大小为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 ?

00000020 safe_allocation ends

很好。我们可以把它看成是一种“胖/有界指针”。这里并没有直接使用指向内存的原始64位指针,而是使用了一个结构体来代表含有额外元数据的指针。

显然,如果使用32个字节(即4个64位的值)来表示一个指针的话,会对许多操作都产生影响。考虑一下复制指针的简单操作——我们现在需要读/写含有4个值得元组(通常包含2个LDP和2个STP),这样就无法通过一行代码,如p2 = p;来完成读写任务了。

我希望能够找到这样的内存分配函数:它能够分配一个内存块并在结构体中初始化这些边界。我是通过反向查找更多的调用堆栈来寻找这种函数的,但是回头看,貌似有一个更加简单的方法可用!

如果检查do_firebloom_panic的交叉引用,就会发现一个非常有趣的封装函数:call_panic_allocation_size_error。这个函数的交叉引用数量不多(不到5个),并且调用方是一组非常类似的函数,其中最简单的一个如下所示:

iBoot:00000001FC1A1CF0 do_safe_allocation                      ; CODE XREF: sub_1FC0523D8+8↑j

iBoot:00000001FC1A1CF0                                         ; sub_1FC05259C+70↑p ...

iBoot:00000001FC1A1CF0

iBoot:00000001FC1A1CF0 var_20          = -0x20

iBoot:00000001FC1A1CF0 var_18          = -0x18

iBoot:00000001FC1A1CF0 var_10          = -0x10

iBoot:00000001FC1A1CF0 var_s0          =  0

iBoot:00000001FC1A1CF0

iBoot:00000001FC1A1CF0                 PACIBSP

iBoot:00000001FC1A1CF4                 SUB             SP, SP, #0x30

iBoot:00000001FC1A1CF8                 STP             X20, X19, [SP,#0x20+var_10]

iBoot:00000001FC1A1CFC                 STP             X29, X30, [SP,#0x20+var_s0]

iBoot:00000001FC1A1D00                 ADD             X29, SP, #0x20

iBoot:00000001FC1A1D04 ; X8 - the structure to initialize

iBoot:00000001FC1A1D04                 MOV             X19, X8

iBoot:00000001FC1A1D08 ; X0 and X1 are probably `count` and `bytes`

iBoot:00000001FC1A1D08                 UMULH           X8, X1, X0

iBoot:00000001FC1A1D0C                 CBNZ            X8, allocation_size_error_detected

iBoot:00000001FC1A1D10 ; X20 - size of the allocation

iBoot:00000001FC1A1D10                 MUL             X20, X1, X0

iBoot:00000001FC1A1D14                 ADRP            X8, #0x1FC2F50B8@PAGE

iBoot:00000001FC1A1D18                 ADD             X8, X8, #0x1FC2F50B8@PAGEOFF

iBoot:00000001FC1A1D1C                 STR             X8, [SP,#0x20+var_18]

iBoot:00000001FC1A1D20                 STR             WZR, [SP,#0x20+var_20]

iBoot:00000001FC1A1D24                 ADRP            X2, #0x1FC2F50B0@PAGE

iBoot:00000001FC1A1D28                 ADD             X2, X2, #0x1FC2F50B0@PAGEOFF

iBoot:00000001FC1A1D2C                 ADRL            X3, off_1FC2D6EC0

iBoot:00000001FC1A1D34                 ADRL            X1, qword_1FC2D6E80

iBoot:00000001FC1A1D3C ; PAC-sign the allocation API

iBoot:00000001FC1A1D3C                 ADR             X16, do_allocation

iBoot:00000001FC1A1D40                 NOP

iBoot:00000001FC1A1D44                 PACIZA          X16

iBoot:00000001FC1A1D48                 MOV             X6, X16

iBoot:00000001FC1A1D4C                 MOV             X0, #0

iBoot:00000001FC1A1D50                 MOV             X4, X20

iBoot:00000001FC1A1D54                 MOV             W5, #1

iBoot:00000001FC1A1D58                 MOV             X7, X1

iBoot:00000001FC1A1D5C ; call the allocation API, allocates a chunk

iBoot:00000001FC1A1D5C ; the return value (X0) is X19, this function

iBoot:00000001FC1A1D5C ; has "MOV X0, X19" in its return

iBoot:00000001FC1A1D5C                 BL              wrap_do_allocation

iBoot:00000001FC1A1D60                 ADRL            X8, off_1FC2D6EF8

iBoot:00000001FC1A1D68                 STR             X8, [X19,#0x18]

iBoot:00000001FC1A1D6C                 STR             X0, [X19]

iBoot:00000001FC1A1D70 ; check if allocation succeeded

iBoot:00000001FC1A1D70                 CBZ             X0, allocation_failed

iBoot:00000001FC1A1D74 ; X0 - based of the allocation

iBoot:00000001FC1A1D74 ; X8 - X0 + allocation_size, upper bound

iBoot:00000001FC1A1D74                 ADD             X8, X0, X20

iBoot:00000001FC1A1D78 ; store the based (i.e. lower bound) and the upper bound

iBoot:00000001FC1A1D78 ; to the structure, at offsets +0x8, +0x10

iBoot:00000001FC1A1D78                 STP             X0, X8, [X19,#8]

iBoot:00000001FC1A1D7C                 LDP             X29, X30, [SP,#0x20+var_s0]

iBoot:00000001FC1A1D80                 LDP             X20, X19, [SP,#0x20+var_10]

iBoot:00000001FC1A1D84                 ADD             SP, SP, #0x30 ; '0'

iBoot:00000001FC1A1D88                 RETAB

iBoot:00000001FC1A1D8C ; ---------------------------------------------------------------------------

iBoot:00000001FC1A1D8C

iBoot:00000001FC1A1D8C allocation_failed                       ; CODE XREF: do_safe_allocation+80↑j

iBoot:00000001FC1A1D8C                 ADD             X8, X19, #8

iBoot:00000001FC1A1D90 ; allocation failed, set NULL to both

iBoot:00000001FC1A1D90 ; lower and upper bounds

iBoot:00000001FC1A1D90                 STP             XZR, XZR, [X8]

iBoot:00000001FC1A1D94                 LDP             X29, X30, [SP,#0x20+var_s0]

iBoot:00000001FC1A1D98                 LDP             X20, X19, [SP,#0x20+var_10]

iBoot:00000001FC1A1D9C                 ADD             SP, SP, #0x30 ; '0'

iBoot:00000001FC1A1DA0                 RETAB

iBoot:00000001FC1A1DA4 ; ---------------------------------------------------------------------------

iBoot:00000001FC1A1DA4

iBoot:00000001FC1A1DA4 allocation_size_error_detected          ; CODE XREF: do_safe_allocation+1C↑j

iBoot:00000001FC1A1DA4                 BL              call_panic_allocation_size_error

iBoot:00000001FC1A1DA4 ; End of function do_safe_allocation

太棒了! 这正是我们要找的函数。这个函数分配了一个内存块,并设置了一个结构体来描述它,其布局是我们通过逆向二进制代码了解到的。

你可能想知道其余的分配函数是什么样子的。好吧,就像你可能假设的那样,这是有一个非常类似的函数,不同之处在于最后调用了memset0:

iBoot:00000001FC1AA58C memset_0                                ; CODE XREF: sub_1FC1A0890+3CC↑p

iBoot:00000001FC1AA58C                                         ; do_safe_allocation_and_zeroing:zero_the_allocation↑j ...

iBoot:00000001FC1AA58C                 CBZ             X1, return

iBoot:00000001FC1AA590

iBoot:00000001FC1AA590 loop                                    ; CODE XREF: memset_0+C↓j

iBoot:00000001FC1AA590                 STRB            WZR, [X0],#1

iBoot:00000001FC1AA594                 SUBS            X1, X1, #1

iBoot:00000001FC1AA598                 B.NE            loop

iBoot:00000001FC1AA59C

iBoot:00000001FC1AA59C return                                  ; CODE XREF: memset_0↑j

iBoot:00000001FC1AA59C                 RET

iBoot:00000001FC1AA59C ; End of function memset_0 

加上这3个新的内存分配API,貌似有100多个交叉引用,哈哈,成功就在眼前了!

Malloc函数

虽然在iBoot中有100多处代码直接调用了do_safe_allocation函数,但仍然存在对malloc的调用。实际上,有很多方法可以发现iBoot中的malloc,其中一个简单的方法是先寻找“%s malloc failed”字符串,然后检查在它前面调用的函数。通过这种方法,我们可以看到do_allocation实际上就是malloc调用的!

下面给出malloc的代码:

iBoot:00000001FC15ABF8 malloc                                  ; CODE XREF: sub_1FC19F50C+58↓p

iBoot:00000001FC15ABF8                                         ; sub_1FC19F77C+464↓p ...

iBoot:00000001FC15ABF8                 B               call_do_allocation

iBoot:00000001FC15ABF8 ; End of function malloc

下面是call_do_allocation的代码:

iBoot:00000001FC1A1B30 call_do_allocation

iBoot:00000001FC1A1B30

iBoot:00000001FC1A1B30 var_10= -0x10

iBoot:00000001FC1A1B30 var_8= -8

iBoot:00000001FC1A1B30 var_s0=  0

iBoot:00000001FC1A1B30

iBoot:00000001FC1A1B30 PACIBSP

iBoot:00000001FC1A1B34 SUB             SP, SP, #0x20

iBoot:00000001FC1A1B38 STP             X29, X30, [SP,#0x10+var_s0]

iBoot:00000001FC1A1B3C ADD             X29, SP, #0x10

iBoot:00000001FC1A1B40 MOV             X4, X0

iBoot:00000001FC1A1B44 ADRP            X8, #0x1FC2F50B8@PAGE

iBoot:00000001FC1A1B48 ADD             X8, X8, #0x1FC2F50B8@PAGEOFF

iBoot:00000001FC1A1B4C STR             X8, [SP,#0x10+var_8]

iBoot:00000001FC1A1B50 STR             WZR, [SP,#0x10+var_10]

iBoot:00000001FC1A1B54 ADRP            X2, #0x1FC2F50B0@PAGE

iBoot:00000001FC1A1B58 ADD             X2, X2, #0x1FC2F50B0@PAGEOFF

iBoot:00000001FC1A1B5C ADRL            X3, off_1FC2D6EC0

iBoot:00000001FC1A1B64 ADRL            X1, qword_1FC2D6E80

iBoot:00000001FC1A1B6C ADR             X16, do_allocation

iBoot:00000001FC1A1B70 NOP

iBoot:00000001FC1A1B74 PACIZA          X16

iBoot:00000001FC1A1B78 MOV             X6, X16

iBoot:00000001FC1A1B7C MOV             X0, #0

iBoot:00000001FC1A1B80 MOV             W5, #1

iBoot:00000001FC1A1B84 MOV             X7, X1

iBoot:00000001FC1A1B88 BL              wrap_do_allocation

iBoot:00000001FC1A1B8C LDP             X29, X30, [SP,#0x10+var_s0]

iBoot:00000001FC1A1B90 ADD             SP, SP, #0x20 ; ' '

iBoot:00000001FC1A1B94 RETAB

iBoot:00000001FC1A1B94 ; End of function call_do_allocation

对我来说,发现这一点很重要,原因有二:

它能佐证我们对内存分配API的理解是正确的。

那些调用malloc而不是do_safe_allocation的地方无法得到Firebloom的安全保护。

这是一个有趣的问题:为什么这些代码没有更新到新的机制呢?答案可能是:

也许苹果公司通过静态分析表明,这些地方即使提供Firebloom安全机制也不会出现安全问题?

也许有些库还没有支持Firebloom?也许这种缓解机制正处于过渡阶段?

类型安全

到目前为止,我们已经知道这个结构体含有原始指针和下限/上限指针。通过分析二进制文件,我们对这一点进行了确认,并搞清楚了其工作机制。

然而,我确实期望在这里看到一些类型信息,因为:

首先,我们有panic_memset_bad_type这样的函数;

苹果公司在其声明中明确提到了这一点。

好吧,大家可能还记得,前面讲过的内存分配函数确实会将一个值存储到我们的结构体的偏移量0x18处(所以,我们将结构体的长度定义为0x20)。如果我们将跟进panic_memset_bad_type函数的调用情况,就会发现:

iBoot:00000001FC15A9CC                 LDR             X23, [X20,#0x18]

iBoot:00000001FC15A9D0                 MOV             X0, X23

iBoot:00000001FC15A9D4                 BL              check_type

iBoot:00000001FC15A9D8                 TBNZ            W0, #0, call_memset

iBoot:00000001FC15A9DC                 CBNZ            W22, detected_memset_bad_type

是的!貌似偏移量0x18就是用于存储类型的。我将在后续文章中详细说明类型安全的具体实现。就这里来说,我们只需知道type字段由各个调用方设置为do_safe_allocation就足够了,通过代码很容易看出这一点。

有关类型指针的更多信息,请访问第二篇文章:Firebloom (iBoot) - the type descriptor。

小结

很高兴看到更多关于内存安全方面的工作,并且有更多新玩意可供研究总是很棒的。

这项改动非常有趣:它肯定有助于缓解一些内存安全漏洞;然而,相应的代价也是很大的,主要体现在:

内存开销:这些新指针占用0x20字节的内存,而不是0x8。以这种方式保护的内存指针消耗的内存空间是原来的4倍。

代码长度:显然代码的长度增加了不少——因为管理新元数据需要更多的指令、更多的分支、更多的检查等。

性能影响:现在,由于数量众多的解除引用操作都封装了额外的指令(从内存中加载数据),肯定会导致性能降低。

当然,我还没有测量这些开销在新旧版本iBoot之间的差异,所以,这都是理论上的分析。但我可以肯定,这种成本是的确存在的,并且苹果公司找到了将其控制在可以接受范围内的方法。

我知道,上述情况听起来很糟糕,但说实话,iBoot正是适合进行这种修改的地方。如果苹果(或任何其他厂商)公司能够在内核中进行如此昂贵的修改,我将感到非常惊讶,但iBoot是一个非常轻量级的、受控的环境。它可以访问整个DRAM,而且它有一个非常有限和具体的目的。而且,它对于保护第二阶段的引导程序是非常有用的,而后者是安全引导过程的关键部分。

这是一个很好的例子,表明苹果公司进行的另一项努力,以通过缓解大量的一阶原语来提高安全性。

本文翻译自:https://saaramar.github.io/iBoot_firebloom/转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务