【翻译】iBoot 安全机制 firebloom 简介

admin 2022年5月5日08:43:02评论199 views字数 13998阅读46分39秒阅读模式

本文来自 MSRC 的 Saar Amar(Twitter:@AmarSaar),同时也是 pasten CTF 的成员。他发布过一些关于 ARM 最新安全特性的研究,如 MTE 和 CHERI ISA,也对一些 iOS 内核漏洞有做分析。这篇文章是他对 iBoot 安全机制 firebloom 的逆向分析。

英文原文 saaramar.github.io/iBoot_firebloom/

本来想直接插入原文链接,但微信说这是一个非法域名,有点可怕。


(众所周知的 2019 年的 checkm8 漏洞之后)在 2021 年 2 月,Apple 在官网发布了关于 iBoot 内存安全的新内容,作为 Apple Security Platform 的一部分。文档中提到,Apple 修改了用于生成 iBoot 的 bootloader 代码的 C 编译器工具链,以提升安全性;对具体的实现还有一些笼统的描述。以下翻译自官方文档:

内存安全的 iBoot 实现

在 iOS 14 和 iPadOS 14 上,Apple 修改了用于生成 iBoot 的 bootloader 代码的编译器工具链来提升安全性。这些改动用于防止 C 语言程序常见的内存安全和类型安全问题。例如,它可以帮助预防大多数如下列出的漏洞类型:

  • 缓冲区溢出:确保所有的指针都引入边界信息,并在访问时验证
  • 堆利用:将堆数据和元数据进行隔离,并加强对程序错误(例如双重释放)的检测
  • 类型混淆:确保所有指针都包含运行时的类型信息,并在类型转换时校验
  • 释放后重用导致的类型混淆:基于静态类型将内存分配在不同的堆

这项技术在搭载了 A13 或更新芯片的 iPhone ,以及搭载 A14 或更新芯片的 iPad 上可用

我认为写一些文字来分析具体实现、格式以及 Apple 所做的令人振奋的工作再好不过了。除此之外,在 iBoot 的二进制文件上可以直接看到一些非常有用的字符串信息——有两位研究人员很早就在推上发过了。

【翻译】iBoot 安全机制 firebloom 简介

【翻译】iBoot 安全机制 firebloom 简介

我对这项工作很着迷,因为前文的描述让我觉得这就是一个“轻量级的纯软件实现的 CHERI”(译者注:CHERI 全称 Capability Hardware Enhanced RISC Instructions,是一种通过对指令集架构进行扩展,在非内存安全的程序语言上实现更安全的内存访问的技术)。根据 Apple 的描述,在新版的 iBoot 上,指针除了地址还包含了更多信息——包括边界和类型,从而让编译器可以在代码中引入新的内存安全检查。

我喜欢打破沙锅问到底,所以让我们深入分析,看看能学到什么。

这份分析在 iBoot.d53g.RELEASE.im4p 上完成,对应版本是 iPhone 12 的 iOS 14.4 (18D52)。

开始逆向

首先,让我们看看检测到内存安全问题时会如何处理。当内存访问违例发生时是有必要触发 panic 的,理所应当二进制里就会有一个 __firebloom_panic 字符串。根据这个特征,我们可以找到对应的函数。

  1. iBoot:00000001FC1AA5A0 firebloom_panic
  2. iBoot:00000001FC1AA5A0
  3. iBoot:00000001FC1AA5A0 var_B8= -0xB8
  4. iBoot:00000001FC1AA5A0 var_B0= -0xB0
  5. iBoot:00000001FC1AA5A0 var_18= -0x18
  6. iBoot:00000001FC1AA5A0 var_10= -0x10
  7. iBoot:00000001FC1AA5A0 var_s0= 0
  8. iBoot:00000001FC1AA5A0
  9. iBoot:00000001FC1AA5A0 PACIBSP
  10. iBoot:00000001FC1AA5A4 SUB SP, SP, #0xD0
  11. iBoot:00000001FC1AA5A8 STP X20, X19, [SP,#0xC0+var_10]
  12. iBoot:00000001FC1AA5AC STP X29, X30, [SP,#0xC0+var_s0]
  13. iBoot:00000001FC1AA5B0 ADD X29, SP, #0xC0
  14. iBoot:00000001FC1AA5B4 MOV X19, X0
  15. iBoot:00000001FC1AA5B8 ADD X0, SP, #0xC0+var_B8
  16. iBoot:00000001FC1AA5BC BL sub_1FC1A9A08
  17. iBoot:00000001FC1AA5C0 ADD X8, X29, #0x10
  18. iBoot:00000001FC1AA5C4 STUR X8, [X29,#var_18]
  19. iBoot:00000001FC1AA5C8 ADR X1, aPasPanic ; "pas panic: "
  20. iBoot:00000001FC1AA5CC NOP
  21. iBoot:00000001FC1AA5D0 ADD X0, SP, #0xC0+var_B8
  22. iBoot:00000001FC1AA5D4 BL do_trace
  23. iBoot:00000001FC1AA5D8 LDUR X2, [X29,#var_18]
  24. iBoot:00000001FC1AA5DC ADD X0, SP, #0xC0+var_B8
  25. iBoot:00000001FC1AA5E0 MOV X1, X19
  26. iBoot:00000001FC1AA5E4 BL sub_1FC1A9A48
  27. iBoot:00000001FC1AA5E8 LDR X0, [SP,#0xC0+var_B0]
  28. iBoot:00000001FC1AA5EC BL __firebloom_panic

这个函数有 11 处交叉引用。我将其中一个命名为 do_firebloom_panic,而这个调用者有另外 11 处交叉引用,每一处都负责处理一种不同的访问违例类型:

【翻译】iBoot 安全机制 firebloom 简介

不错,现在我们有了 firebloom 所要检测的程序错误的(一部分)列表,这些错误都会引发 panic。因为一些新的检查专门针对注入 memset 和 memcpy 这类特定的函数,我们可以期待看到这些函数将被改写为新的封装函数(wrapper),而异常检测就在这些调用里。根据交叉引用不断回溯程序流程,很容易就能找到这些封装函数。

然而我很好奇其他的访问违例是怎么样的——例如,我们将如何/在哪里找到 ptr_under 和 ptr_over(分别对应两种越界访问)?函数 panic_ptr_over 有 179 处调用,其中大部分都是简单的加入 hash 参数的包装。这些封装函数的一部分交叉引用指向真正的错误检测(panic)逻辑。继续跟下去,我们能看到一些不错的示例来展示 iBoot 是如何使用这些函数的。

多说无益,没有什么比代码更清晰,所以我们直接看这段程序:

  1. iBoot:00000001FC05C5AC loop ; CODE XREF: sub_1FC05C548+94j
  2. iBoot:00000001FC05C5AC CMP X10, X9
  3. iBoot:00000001FC05C5B0 B.EQ return
  4. iBoot:00000001FC05C5B4 ; 获取指针和下界
  5. iBoot:00000001FC05C5B4 LDP X11, X13, [X0]
  6. iBoot:00000001FC05C5B8 ; ptr 循环递增到 ptr+offset
  7. iBoot:00000001FC05C5B8 ADD X12, X11, X9
  8. iBoot:00000001FC05C5BC CMP X12, X13
  9. iBoot:00000001FC05C5C0 B.CC detected_ptr_under
  10. iBoot:00000001FC05C5C4 ; 获取数组上界
  11. iBoot:00000001FC05C5C4 LDR X13, [X0,#0x10]
  12. iBoot:00000001FC05C5C8 CMP X12, X13
  13. iBoot:00000001FC05C5CC B.CS detected_ptr_over
  14. iBoot:00000001FC05C5D0 ; 真正解引用指针的地方
  15. iBoot:00000001FC05C5D0 LDR W11, [X11,X9]
  16. iBoot:00000001FC05C5D4 STR W11, [X8,#0x1DC]
  17. iBoot:00000001FC05C5D8 ADD X9, X9, #4
  18. iBoot:00000001FC05C5DC B loop
  19. iBoot:00000001FC05C5E0 ; ---------------------------------------------------------------------------
  20. iBoot:00000001FC05C5E0
  21. iBoot:00000001FC05C5E0 return ; CODE XREF: sub_1FC05C548+68j
  22. iBoot:00000001FC05C5E0 LDUR X8, [X29,#var_8]
  23. iBoot:00000001FC05C5E4 ADRP X9, #a160d@PAGE ; "160D"
  24. iBoot:00000001FC05C5E8 NOP
  25. iBoot:00000001FC05C5EC LDR X9, [X9,#a160d@PAGEOFF] ; "160D"
  26. iBoot:00000001FC05C5F0 CMP X9, X8
  27. iBoot:00000001FC05C5F4 B.NE do_panic
  28. iBoot:00000001FC05C5F8 LDP X29, X30, [SP,#0x70+var_s0]
  29. iBoot:00000001FC05C5FC ADD SP, SP, #0x80
  30. iBoot:00000001FC05C600 RETAB
  31. iBoot:00000001FC05C604 ; ---------------------------------------------------------------------------
  32. iBoot:00000001FC05C604
  33. iBoot:00000001FC05C604 do_panic ; CODE XREF: sub_1FC05C548+ACj
  34. iBoot:00000001FC05C604 BL call_panic
  35. iBoot:00000001FC05C608 ; ---------------------------------------------------------------------------
  36. iBoot:00000001FC05C608
  37. iBoot:00000001FC05C608 detected_ptr_under ; CODE XREF: sub_1FC05C548+78j
  38. iBoot:00000001FC05C608 BL call_panic_ptr_under_5383366e236c433
  39. iBoot:00000001FC05C60C ; ---------------------------------------------------------------------------
  40. iBoot:00000001FC05C60C
  41. iBoot:00000001FC05C60C detected_ptr_over ; CODE XREF: sub_1FC05C548+84j
  42. iBoot:00000001FC05C60C BL call_panic_ptr_over_5383366e236c433
  43. iBoot:00000001FC05C610 ; ---------------------------------------------------------------------------

代码很有意思。在使用 x9 寄存器提供的偏移量访问指针(对应 0x01FC05C5D0 的指令)之前,代码先检查 ptr+offset 是否越界。原始指针和数组的边界(上界、下界)信息从特定的结构体(稍后解释)中取出。在解释这一步之前,为了给读者感受一下这些函数是如何调用的,我们来看这个 panic 的封装:

  1. iBoot:00000001FC05D384 call_panic_ptr_over_5383366e236c433 ; CODE XREF: sub_1FC05C548:detected_ptr_overp
  2. iBoot:00000001FC05D384 ; DATA XREF: call_panic_ptr_over_5383366e236c433+24o
  3. iBoot:00000001FC05D384
  4. iBoot:00000001FC05D384 var_8 = -8
  5. iBoot:00000001FC05D384 var_s0 = 0
  6. iBoot:00000001FC05D384
  7. iBoot:00000001FC05D384 PACIBSP
  8. iBoot:00000001FC05D388 SUB SP, SP, #0x20
  9. iBoot:00000001FC05D38C STP X29, X30, [SP,#0x10+var_s0]
  10. iBoot:00000001FC05D390 ADD X29, SP, #0x10
  11. iBoot:00000001FC05D394 ADRL X8, a5383366e236c43 ; "5383366e236c433"
  12. iBoot:00000001FC05D39C STR X8, [SP,#0x10+var_8]
  13. iBoot:00000001FC05D3A0 MOV X8, X30
  14. iBoot:00000001FC05D3A4 XPACI X8
  15. iBoot:00000001FC05D3A8 ADR X16, call_panic_ptr_over_5383366e236c433
  16. iBoot:00000001FC05D3AC NOP
  17. iBoot:00000001FC05D3B0 PACIZA X16
  18. iBoot:00000001FC05D3B4 SUB X2, X8, X16
  19. iBoot:00000001FC05D3B8 ADD X0, SP, #0x10+var_8
  20. iBoot:00000001FC05D3BC MOV W1, #1
  21. iBoot:00000001FC05D3C0 BL panic_ptr_over
  22. iBoot:00000001FC05D3C0 ; End of function call_panic_ptr_over_5383366e236c433

还有这里:

  1. iBoot:00000001FC1AA980 panic_ptr_over ; CODE XREF: sub_1FC04CBD0+3Cp
  2. iBoot:00000001FC1AA980 ; sub_1FC04EC2C+3Cp ...
  3. iBoot:00000001FC1AA980
  4. iBoot:00000001FC1AA980 var_20 = -0x20
  5. iBoot:00000001FC1AA980 var_10 = -0x10
  6. iBoot:00000001FC1AA980 var_s0 = 0
  7. iBoot:00000001FC1AA980
  8. iBoot:00000001FC1AA980 PACIBSP
  9. iBoot:00000001FC1AA984 STP X22, X21, [SP,#-0x10+var_20]!
  10. iBoot:00000001FC1AA988 STP X20, X19, [SP,#0x20+var_10]
  11. iBoot:00000001FC1AA98C STP X29, X30, [SP,#0x20+var_s0]
  12. iBoot:00000001FC1AA990 ADD X29, SP, #0x20
  13. iBoot:00000001FC1AA994 MOV X19, X2
  14. iBoot:00000001FC1AA998 MOV X20, X1
  15. iBoot:00000001FC1AA99C MOV X21, X0
  16. iBoot:00000001FC1AA9A0 ADRP X8, #0x1FC2F2270@PAGE
  17. iBoot:00000001FC1AA9A4 LDR X8, [X8,#0x1FC2F2270@PAGEOFF]
  18. iBoot:00000001FC1AA9A8 CBZ X8, do_panic
  19. iBoot:00000001FC1AA9AC BLRAAZ X8
  20. iBoot:00000001FC1AA9B0
  21. iBoot:00000001FC1AA9B0 do_panic ; CODE XREF: panic_ptr_over+28j
  22. iBoot:00000001FC1AA9B0 ADR X0, aPtrOver ; "ptr_over"
  23. iBoot:00000001FC1AA9B4 NOP
  24. iBoot:00000001FC1AA9B8 MOV X1, X21
  25. iBoot:00000001FC1AA9BC MOV X2, X20
  26. iBoot:00000001FC1AA9C0 MOV X3, X19
  27. iBoot:00000001FC1AA9C4 BL do_firebloom_panic
  28. iBoot:00000001FC1AA9C4 ; End of function panic_ptr_over

好,很简单。

让我们看看同样的模式有没有出现在别的地方。例如这一处:

【翻译】iBoot 安全机制 firebloom 简介

在这段示例中,你可以看到一个循环遍历一个数组的所有元素(每一个的大小是 0x20),将每个元素作为参数调用一些函数。而且,不出我所料,所谓的“指针结构体”在这里被同样的方式使用了。

格式化和帮助函数

结合前文的分析,我们推测内存分配所使用的结构如下所示:

  1. 00000000 safe_allocation struc ; (sizeof=0x20, mappedto_1)
  2. 00000000 raw_ptr DCQ ? ; offset
  3. 00000008 lower_bound_ptr DCQ ? ; offset
  4. 00000010 upper_bound_ptr DCQ ? ; offset
  5. 00000018 field_18 DCQ ?
  6. 00000020 safe_allocation ends

不错。我们可以将其视作“胖指针”(原文 Fat/Bounded Pointer,指带有访问边界信息的指针)。iBoot 并没有直接使用 64 位原始指针作为内存地址,而是使用一个结构体代表指针,并保存额外的元数据。

显而易见地,使用 32 字节(4 个 64bit 值)来表示一个指针,会对许多操作产生影响。考虑一个简单的指针赋值操作,不能再直接用 p2 = p; ,而是需要读写一个四元组(通常编译为两个 LDP 和两个 LDP 指令)。

我很想找到新的内存分配函数,也就是那些分配一个块并初始化元数据结构体的函数。我很轻松地通过继续向上查找调用栈发现了目标。

如果你查看 do_firebloom_panic 函数的交叉引用,可以看到一个很有意思的封装函数 call_panic_allocation_size_error。这个函数被调用得不多(不到五处),调用者是一组非常相似的函数。最简单的一处调用如下:

  1. iBoot:00000001FC1A1CF0 do_safe_allocation ; CODE XREF: sub_1FC0523D8+8j
  2. iBoot:00000001FC1A1CF0 ; sub_1FC05259C+70p ...
  3. iBoot:00000001FC1A1CF0
  4. iBoot:00000001FC1A1CF0 var_20 = -0x20
  5. iBoot:00000001FC1A1CF0 var_18 = -0x18
  6. iBoot:00000001FC1A1CF0 var_10 = -0x10
  7. iBoot:00000001FC1A1CF0 var_s0 = 0
  8. iBoot:00000001FC1A1CF0
  9. iBoot:00000001FC1A1CF0 PACIBSP
  10. iBoot:00000001FC1A1CF4 SUB SP, SP, #0x30
  11. iBoot:00000001FC1A1CF8 STP X20, X19, [SP,#0x20+var_10]
  12. iBoot:00000001FC1A1CFC STP X29, X30, [SP,#0x20+var_s0]
  13. iBoot:00000001FC1A1D00 ADD X29, SP, #0x20
  14. iBoot:00000001FC1A1D04 ; X8:需要初始化的结构体指针
  15. iBoot:00000001FC1A1D04 MOV X19, X8
  16. iBoot:00000001FC1A1D08 ; X0 X1 可能对应大小和数据指针
  17. iBoot:00000001FC1A1D08 UMULH X8, X1, X0
  18. iBoot:00000001FC1A1D0C CBNZ X8, allocation_size_error_detected
  19. iBoot:00000001FC1A1D10 ; X20:分配的大小
  20. iBoot:00000001FC1A1D10 MUL X20, X1, X0
  21. iBoot:00000001FC1A1D14 ADRP X8, #0x1FC2F50B8@PAGE
  22. iBoot:00000001FC1A1D18 ADD X8, X8, #0x1FC2F50B8@PAGEOFF
  23. iBoot:00000001FC1A1D1C STR X8, [SP,#0x20+var_18]
  24. iBoot:00000001FC1A1D20 STR WZR, [SP,#0x20+var_20]
  25. iBoot:00000001FC1A1D24 ADRP X2, #0x1FC2F50B0@PAGE
  26. iBoot:00000001FC1A1D28 ADD X2, X2, #0x1FC2F50B0@PAGEOFF
  27. iBoot:00000001FC1A1D2C ADRL X3, off_1FC2D6EC0
  28. iBoot:00000001FC1A1D34 ADRL X1, qword_1FC2D6E80
  29. iBoot:00000001FC1A1D3C ; 对分配 API 函数指针加 PAC 签名
  30. iBoot:00000001FC1A1D3C ADR X16, do_allocation
  31. iBoot:00000001FC1A1D40 NOP
  32. iBoot:00000001FC1A1D44 PACIZA X16
  33. iBoot:00000001FC1A1D48 MOV X6, X16
  34. iBoot:00000001FC1A1D4C MOV X0, #0
  35. iBoot:00000001FC1A1D50 MOV X4, X20
  36. iBoot:00000001FC1A1D54 MOV W5, #1
  37. iBoot:00000001FC1A1D58 MOV X7, X1
  38. iBoot:00000001FC1A1D5C ; 调用 API 分配一块内存
  39. iBoot:00000001FC1A1D5C ; X19 就是返回值(X0)(X0)
  40. iBoot:00000001FC1A1D5C ; 这个函数内部在返回时会 "MOV X0, X19"
  41. iBoot:00000001FC1A1D5C BL wrap_do_allocation
  42. iBoot:00000001FC1A1D60 ADRL X8, off_1FC2D6EF8
  43. iBoot:00000001FC1A1D68 STR X8, [X19,#0x18]
  44. iBoot:00000001FC1A1D6C STR X0, [X19]
  45. iBoot:00000001FC1A1D70 ; 检查是否分配成功
  46. iBoot:00000001FC1A1D70 CBZ X0, allocation_failed
  47. iBoot:00000001FC1A1D74 ; X0:分配内存块的基址
  48. iBoot:00000001FC1A1D74 ; X8X0 + allocation_size,也就是上界
  49. iBoot:00000001FC1A1D74 ADD X8, X0, X20
  50. iBoot:00000001FC1A1D78 ; 保存上界和下界到结构体,分别对应
  51. iBoot:00000001FC1A1D78 ; +0x8, +0x10 的成员
  52. iBoot:00000001FC1A1D78 STP X0, X8, [X19,#8]
  53. iBoot:00000001FC1A1D7C LDP X29, X30, [SP,#0x20+var_s0]
  54. iBoot:00000001FC1A1D80 LDP X20, X19, [SP,#0x20+var_10]
  55. iBoot:00000001FC1A1D84 ADD SP, SP, #0x30 ; '0'
  56. iBoot:00000001FC1A1D88 RETAB
  57. iBoot:00000001FC1A1D8C ; ---------------------------------------------------------------------------
  58. iBoot:00000001FC1A1D8C
  59. iBoot:00000001FC1A1D8C allocation_failed ; CODE XREF: do_safe_allocation+80j
  60. iBoot:00000001FC1A1D8C ADD X8, X19, #8
  61. iBoot:00000001FC1A1D90 ; 分配失败,上界和下界置零
  62. iBoot:00000001FC1A1D90 STP XZR, XZR, [X8]
  63. iBoot:00000001FC1A1D94 LDP X29, X30, [SP,#0x20+var_s0]
  64. iBoot:00000001FC1A1D98 LDP X20, X19, [SP,#0x20+var_10]
  65. iBoot:00000001FC1A1D9C ADD SP, SP, #0x30 ; '0'
  66. iBoot:00000001FC1A1DA0 RETAB
  67. iBoot:00000001FC1A1DA4 ; ---------------------------------------------------------------------------
  68. iBoot:00000001FC1A1DA4
  69. iBoot:00000001FC1A1DA4 allocation_size_error_detected ; CODE XREF: do_safe_allocation+1Cj
  70. iBoot:00000001FC1A1DA4 BL call_panic_allocation_size_error
  71. iBoot:00000001FC1A1DA4 ; End of function do_safe_allocation

美妙!这就是我要的滑板鞋找的函数。这个函数分配内存块并设置对应的元数据,具体的结构在前文已经分析过。

你可能想知道其余的分配函数长什么样。其实,可能正如你假设的那样,有一个很相似的函数,唯一的区别是在结尾使用 memset0(也就是 calloc 调用的)初始化数据。

  1. iBoot:00000001FC1AA58C memset_0 ; CODE XREF: sub_1FC1A0890+3CCp
  2. iBoot:00000001FC1AA58C ; do_safe_allocation_and_zeroing:zero_the_allocationj ...
  3. iBoot:00000001FC1AA58C CBZ X1, return
  4. iBoot:00000001FC1AA590
  5. iBoot:00000001FC1AA590 loop ; CODE XREF: memset_0+Cj
  6. iBoot:00000001FC1AA590 STRB WZR, [X0],#1
  7. iBoot:00000001FC1AA594 SUBS X1, X1, #1
  8. iBoot:00000001FC1AA598 B.NE loop
  9. iBoot:00000001FC1AA59C
  10. iBoot:00000001FC1AA59C return ; CODE XREF: memset_0j
  11. iBoot:00000001FC1AA59C RET
  12. iBoot:00000001FC1AA59C ; End of function memset_0

以上三个新的内存分配 API 加起来有超过 100 处调用。看上去八*不离十了。

malloc

除了有超过100 处代码直接调用 do_safe_allocation 函数之外,我还看到一些旧版本见过的 malloc 调用被保留。有很多种办法可以识别 iBoot 中的 malloc,一个简单的方式就是查找出现字符串 %s malloc failed 的函数。接下来可以看到,实际上新的 malloc 内部直接调用了 do_allocation

如下是 malloc 的代码:

  1. iBoot:00000001FC15ABF8 malloc ; CODE XREF: sub_1FC19F50C+58p
  2. iBoot:00000001FC15ABF8 ; sub_1FC19F77C+464p ...
  3. iBoot:00000001FC15ABF8 B call_do_allocation
  4. iBoot:00000001FC15ABF8 ; End of function malloc

以及 call_do_allocation

  1. iBoot:00000001FC1A1B30 call_do_allocation
  2. iBoot:00000001FC1A1B30
  3. iBoot:00000001FC1A1B30 var_10= -0x10
  4. iBoot:00000001FC1A1B30 var_8= -8
  5. iBoot:00000001FC1A1B30 var_s0= 0
  6. iBoot:00000001FC1A1B30
  7. iBoot:00000001FC1A1B30 PACIBSP
  8. iBoot:00000001FC1A1B34 SUB SP, SP, #0x20
  9. iBoot:00000001FC1A1B38 STP X29, X30, [SP,#0x10+var_s0]
  10. iBoot:00000001FC1A1B3C ADD X29, SP, #0x10
  11. iBoot:00000001FC1A1B40 MOV X4, X0
  12. iBoot:00000001FC1A1B44 ADRP X8, #0x1FC2F50B8@PAGE
  13. iBoot:00000001FC1A1B48 ADD X8, X8, #0x1FC2F50B8@PAGEOFF
  14. iBoot:00000001FC1A1B4C STR X8, [SP,#0x10+var_8]
  15. iBoot:00000001FC1A1B50 STR WZR, [SP,#0x10+var_10]
  16. iBoot:00000001FC1A1B54 ADRP X2, #0x1FC2F50B0@PAGE
  17. iBoot:00000001FC1A1B58 ADD X2, X2, #0x1FC2F50B0@PAGEOFF
  18. iBoot:00000001FC1A1B5C ADRL X3, off_1FC2D6EC0
  19. iBoot:00000001FC1A1B64 ADRL X1, qword_1FC2D6E80
  20. iBoot:00000001FC1A1B6C ADR X16, do_allocation
  21. iBoot:00000001FC1A1B70 NOP
  22. iBoot:00000001FC1A1B74 PACIZA X16
  23. iBoot:00000001FC1A1B78 MOV X6, X16
  24. iBoot:00000001FC1A1B7C MOV X0, #0
  25. iBoot:00000001FC1A1B80 MOV W5, #1
  26. iBoot:00000001FC1A1B84 MOV X7, X1
  27. iBoot:00000001FC1A1B88 BL wrap_do_allocation
  28. iBoot:00000001FC1A1B8C LDP X29, X30, [SP,#0x10+var_s0]
  29. iBoot:00000001FC1A1B90 ADD SP, SP, #0x20 ; ' '
  30. iBoot:00000001FC1A1B94 RETAB
  31. iBoot:00000001FC1A1B94 ; End of function call_do_allocation

这段对我很重要,有两个原因:

  1. 这从另一方面证明我们之前对内存分配 API 的理解是正确的
  2. 那些直接调用 malloc 而不是 do_safe_allocation 的代码,分配到的内存并不会保存 firebloom 的元数据

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

  • 也许 Apple 使用静态分析证明了这些这些地方可以不用 firebloom 的保护
  • 也许一些依赖库还没有适配 firebloom?可能这个缓解措施还在逐步推进中?

类型安全

好了,我们现在知道有这个带有原始指针和数组边界的新结构体。我们在二进制里识别到了相关代码,并分析了其工作原理。

然而,我本来期望能在这个结构体里看到类型信息,因为:

  1. 首先,我们有一些诸如 panic_memset_bad_type 的函数
  2. Apple 明确地在文档里提到了这点

好吧,如果你还有印象,前文的内存分配函数确实将一个值保存到结构体的 0x18 偏移上(所以我把结构体的尺寸定为 0x20)。如果我们跟进函数 panic_memset_bad_type 的调用,我们可以找到例如这样的代码:

  1. iBoot:00000001FC15A9CC LDR X23, [X20,#0x18]
  2. iBoot:00000001FC15A9D0 MOV X0, X23
  3. iBoot:00000001FC15A9D4 BL check_type
  4. iBoot:00000001FC15A9D8 TBNZ W0, #0, call_memset
  5. iBoot:00000001FC15A9DC CBNZ W22, detected_memset_bad_type

对的!看上去 0x18 偏移就是用来存储类型信息的。有机会我将详细解释更多类型安全的实现细节。在这里只需要知道一点就够了,firebloom 的类型信息由 do_safe_allocation 的调用方传入并保存,这在代码里不难发现。

小结和思考

很高兴能看到更多内存安全技术上的工作,也很高兴能有新的东西可以折腾。

这项改动非常有趣。它显然能帮助缓解一些内存安全漏洞,但代价也相当昂贵,体现在一些不同的方面:

  1. 额外的内存开支:新的指针格式使用 0x20 个字节的内存,而不是 8 个字节。受保护的指针所占用内存是原来的 4 倍。
  2. 生成代码的长度:显而易见,采用了 firebloom 的二进制需要更多的代码。更多的指令来管理新的元数据,更多的条件分支,更多的检查等。
  3. 性能损耗:大量的解引用操作被替换成更多的指令(用以从内存中加载数据),对性能产生影响。

显然我没有实际测算过新老版本 iBoot 之间的开销差异,所以上面的分析都只是基于理论。但我有把握相信代价存在,而 Apple 找到了一个折中的方式让其顺利运行。

我知道这么说听上去很糟糕,但老实说,iBoot 正好是一个适合引入此类机制的地方。如果 Apple(或其他厂商)在内核中启用如此开销昂贵的保护,我将会感到非常诧异。iBoot 是一个非常轻量、可控的环境。它具有整个 DRAM 的访问权限,其设计具有有限的和非常明确的目的。这项机制对保护第二阶段的 bootloader 很有帮助,而后者是整个安全引导过程中至关重要的一环。

对于 Apple 而言这是一个非常不错的例子,表明其对大量的一阶原语开发针对性的缓解措施,从而提升软件的安全性。

原文始发于微信公众号(非尝咸鱼贩):【翻译】iBoot 安全机制 firebloom 简介

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月5日08:43:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【翻译】iBoot 安全机制 firebloom 简介https://cn-sec.com/archives/973949.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息