作者 m6aa8k 2016-07-11 15:35:00
被查看了768次 , 本文转载自:乌云知识库

揭开苹果EFI固件密码重置与SCBO文件的神秘面纱

翻译:m6aa8k

原文地址:https://reverse.put.as/2016/06/25/apple-efi-firmware-passwords-and-the-scbo-myth/

原文详细介绍了苹果EFI固件密码重置与SCBO文件的逆向过程。翻译不当之处,欢迎高手批评指正!

0x00 前言

最初鼓捣苹果的EFI实现的时候,当时的目标是设法重置MacBook的固件密码。经过初步研究后,我发现了一个“神奇”的SCBO文件,可以加载到一个USB闪存驱动器中,启动后可以清除密码。虽然常规的处理流程应该是先去联系苹果的支持人员。不过,由于我没有这台Mac的原始发票,所以我觉得这条路是走不通的,因为如果此法可行的话,那么任何窃取了这台机器的人都能够重新设置密码了。当我发现一个据称在销售SCBO文件的网站后,引起了我的高度兴趣:只要提供必要的哈希值(后面详述),支付100美元,就可以得到一个可用的SCBO文件。有视频(虽然是葡萄牙语,但你可以观摩整个过程)称这里提供的文件确实有效,甚至有人声称通用的SCBO文件可以解锁多种Mac。

由于当时几乎没有关于SCBO内容的信息,这虽然引起了我的好奇心,但我却没有进一步跟进。当我从新加坡举办的SyScan360归来后,需要一个新的研究方向,这才重新将其拾起来。

我关注的核心的问题是,是否真的有人已经弄出了SCBO文件密钥生成器。如果这是真的,那么将意味着苹果的EFI包含有重大安全漏洞。理解SCBO文件的工作机制是多么令人着迷的一件事情啊,那就让我们开启一段逆向EFI冒险之旅吧……

当时我在网络上只找到了一份SCBO文件,虽然不太理想(无法比较不同文件之间的差别),那至少要比一个也没有强多了。这个样本文件名为SCBO_original.zip

p1 图1

上图展示了样本文件的全部内容。我们可以看到,前四个字节为字符串'SCBO',这是一个神奇的数字(0x4F424353)。在几个字节之后,我们可以看到另一个字符串,它看起来像是某种序列号。此信息是可以验证的,如果该字符串的一部分在每个Mac主板上都可以发现在的话(我的Macbook上发现了样本中的部分信息,我猜Imac和其他机器上也包含相同的信息)。 该字符串的其他部分和随后的二进制数据暂时都是未知数。这个文件的总长度为324字节。

0x01 SCBO文件是如何生成

正如前面所提到的,苹果的支持部门是能够生成这些文件的,但你必须提供一些关键信息。要获得这些信息,你必须在出现固件密码提示屏幕时,同时按下SHIFT + CONTROL + OPTION + COMMAND + S,从而得到一个字符串。这就是苹果支持部门所要求提供的字符串,同时,它跟我们在SCBO文件里面看到的字符串一模一样。如前所述,第一个数字是机器序列号,而后面的十六位数字是每次设置、删除或修改固件密码时现场生成的一个数值。我知道这些是因为我之前逆向过苹果的固件密码程序,并观察过该程序与设置 EFI NVRAM 变量的内核扩展之间的通信。

p2 图2

现在,我们对其有了更多的了解,但是,能不能通过修改和重复利用这个样本SCBO文件来重置任意其他 Mac 的固件密码呢?

答案是否定的。如果我们在测试Mac上建立一个固件密码,必须生成所需要的字符串,然后对SCBO进行相应的修改。 计算机将处理该文件并重新设置系统,但密码不会重置。

这为我们提供了另一个信息:这个SCBO的内容会进行某种完整性检验。如果没有这种检验的话,任何人都可以修改SCBO内容,这太让人意外了。

但是,如果这是真的,那些人叫卖的SCBO文件为何貌似完全有效呢? 因此,我们有必要更深入地挖掘和逆向负责处理该文件的EFI代码。

现在开始,我们将真正进入激动人心的逆向之旅!

首先,我们需要提取出所有EFI二进制代码,这有两种途径,一是途径从存有EFI内容的Flash芯片转储中提取,另一种是从EFI更新文件中发现的SCAP文件中提取(不知何故,fd格式也可用于某些Mac上面)。

我维护了一个最新的苹果固件更新库,如果你需要早期的EFI更新,或想验证自己的EFI闪存是否遭到修改的话,可以轻松从这里下载所需的更新。 利用UEFITool,你可以轻松地从转储和SCAP中提取内容(如果需要大量提取所有文件的话,最好使用UEFIExtract工具代替)。如果需要从NVRAM分区提取内容(该功能超级有用,感谢Nikolaj!)的话,则必须使用UEFITool的new_engine分支。

就本文来说,目标Mac是一个的MacBook Pro8.2系统,并且所有文件都是从MBP81_0047_2CB_LOCKED.scap这个固件更新文件中提取的。您可以使用其他的固件文件:GUID仍然不变,但地址和某些内容可能会有所不同。

有效载荷提取来之后,我们终于可以着手寻找合适的逆向突破口了。开始的时候,最好的线索自然是SCBO文件的魔法值了,因为它必然会在代码中的某个地方进行相应的检查。对付这种类型的任务,我最喜欢的工具是bgrep。有了它,我们可以在文件中查找特定的字节序列,这个功能对于定位二进制数据来说极其有用。我们想要查找的字节是5343424F,即“SCBO”的魔法值。如果你想查找字符串,请不要忘了,在EFI二进制文件中的字符串,大部分都是Unicode(宽度为两个字节)。

只有一个符合要求,即一个DXE阶段的二进制文件,其GUID为9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C。请注意,在(U)EFI的世界里是没有文件名这一说的,所有的东西都是通过一个128位的GUID来引用的。

p3 图3

下面我们把这个二进制代码加载到反汇编器中,尝试了解其中到底发生了什么。我们可以发现,检查该魔术字节的代码如下所示:

p4 图4

这意味着,我们在这个问题上找到了一个好的切入点,下面要进一步了解这个函数的要去向何方,以及它是如何被调用的。

0x02 谁将使用SCBO文件

为了提高逆向效率,我们需要尽量收集尽可能多的相关信息。下面介绍如何利用该SCBO文件来重设固件密码:

1.格式化闪存驱动器GUID分区方案和Mac OS扩展格式。将它命名为Firmware(注:并非真正需要固件!)。

2.将名为“SCBO”的二进制文件拖拽到桌面。

3.打开终端。

4.在终端执行下列命令:

#!bash
cp ~/Desktop/SCBO /Volumes/Firmware/.SCBO

5.在终端执行以下命令:

#!bash
cp ~/Desktop/SCBO /Volumes/Firmware/._SCBO

6.弹出闪存驱动器。

7.关闭电脑。

8.把闪存驱动器插入到计算机。

9.启动电脑,同时按住Option键。

10.你应该看到锁形符号,一会儿后,电脑应重新启动,显示启动管理器。

这给了我们一条重要的线索,我们应该去寻找具有文件系统访问权限的代码,然后去读取这两个文件之一。如果我们看看当前反汇编的二进制字符串,就会明白,我们已经在正确的轨道上了。

p5 图5

复制到闪存驱动器的.SCBO文件名就是在这些字符串中引用的,但IDA却无法找到任何一个引用它的字符串(IDA的bug?很有可能!)。

逆向(U)EFI的二进制文件是很烦人的事情,因为每个外部函数是函数指针,所以反汇编输出不是很简明易懂,因此需要一些辅助工具来改善这一点。为此,我们可以借助于Snare创建的ida-efiutils,它实际上是一组脚本,可以通过重命名函数指针、偏移量和结构来改善反汇编输出。由于它提供的功能仍然无法满足我的需求,同时我也不是Python爱好者,所以我横下心来鼓捣出了自己的IDA C 插件,并将其命名为EFISwissKnife。

该插件可以完成更多的任务,如给已知的函数提供原型和文档注释,生成统计数据,以及从已安装和使用的协议中提取信息并保存到数据库中。有了它,我们可以方便地找出哪些二进制文件正在安装和使用哪些协议,无需在二进制文件中搜索字符串,再也不用浪费时间查明某协议是由哪个模块实现的了。

下图为我们展示了无任何插件帮助下IDA对start()函数的反汇编输出结果。

p6 图6

同时,我们还可以看看运行EFI SwissKnife后的结果。

p7 图7

从上面可以看到,它识别了两种被调用到的(U)EFI 引导服务:SetWatchdogTimer和LocateProtocol,同时,也对LocateProtocol使用的GUID给出了相应的注释。

它的统计功能,还给出了我们就能够在此二进制文件找到的Guid的相关信息,以及用到(U)EFI运行时和启动服务的GUID的有关信息。它们对于迅速掌握该二进制代码当前活动非常有用。

p8 图8

在改进版反汇编输出的帮助下,我们继续尝试去理解与SCBO文件有关的事情。

由于逆向工程的艺术成分较多,科学成分较少,因此我会首先告诉你发生了什么,然后进一步介绍它是如何发生的。

首先,此EFI二进制文件安装时,会有一个事件通知。USB闪存驱动器插入时,会触发该通知,并调用一个回调函数。

这个回调函数的任务之一,是尝试从闪存中读取SCBO文件,并验证它的格式是否正确(例如检查幻数等)。如果SCBO的内容看起来没有问题,然后将通过GUID 5D62B28D-6ED2-40B4-A560-6CD79B93D366设置一个新的EFI NVRAM变量,".SCBO_0000"。这个GUID还可以在固件密码实用程序中找到。这个GUID并非只用于这个变量,还用于其他变量,例如FWAppCmd等。当利用固件密码实用程序设置、更改或删除一个新的密码时,就会在NVRAM中观察到这个变量。 在“.SCBO_0000”变量设置成功后,系统将通过ResetSystem服务进行重启。

事件通知代码可以在start()函数中找到。 负责创建该事件的代码如下所示︰

p9 图9

在这段代码中,最有趣的当属CreateEvent服务的第三个参数,即notifyfunction。当事件触发时,就会执行这个回调函数。下面的代码仅仅用于注册该事件,这里来说,是一个文件系统相关的事件。

p10 图10

现在让我们开始考察回调函数的代码。第一个感兴趣的细节是,它试图通过GUID为75FAB4B4-6AC1-429A-A000-6B0B95E71CA1的代码来定位一个新协议。这个议定是由GUID为818544B5-1B9D-4E7B-8F7D-835AAEAF3B5C的EFI二进制代码安装的。继续往下看,我们终于找到了一段让人感兴趣的代码,它用来处理读取SCBO文件的内容相关事宜(我已经将原始的函数重新命名为 read_scbo_file_contents)。

p11 图11

这个函数内部,我们找到了一些文件方面的操作。

p12 图12

上面的代码负责打开USB闪存驱动器卷,以便能够读取其中的内容。就本例而言,在我们需要检查EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID协议的布局,从而了解协议的每一个函数指针都在干什么。通常情况下,我是通过定位字符串EDK2的源码查找协议的,虽然EDK2规范要比苹果的EFI fork更复杂(AMIBios源码的泄露,为我们的搜索提供了另一个好去处,特别是EDK2以外的代码,如电源管理代码等)。下面的代码给出了EFI_SIMPLE_FILE_SYSTEM_PROTOCOL的结构:

p13 图13

这个协议只有单个函数,即OpenVolume。我们感兴趣的是阅读它的说明内容部分,从而了解它成功执行后会发生什么。

p14 图14

OpenVolume函数将打开卷(这里是USB闪存驱动器)的根目录,并且返回句柄EFI_FILE_PROTOCOL。为了了解其功能,我们还需要考察另一个协议。当我们逆向(U)EFI的时候,为了查找各种协议和函数有关的信息需要不断在代码中查找特定字符串。因为根本就没有帮助文档!

p15 图15

我们可以观察到,EFI_FILE_PROTOCOL已经提供了读写文件系统上的文件所需的基本功能。虽然逆向大量的协议非常烦人,但过了一段时间之后,我们就会因(U)EFI设计之高雅而流连忘返。

先前讨论的反汇编代码,会先试图打开根卷,如果成功的话,就会使用返回的句柄尝试从USB闪存驱动器卷打开“.SCBO”文件。如果打开了该文件,它就会使用EFI_FILE_PROTOCOL的GetInfo函数查看该文件的大小,然后分配相应内存,最终将其内容读入分配的缓冲区中。

p16 图16

该文件分两步读取,先读入前面的12个字节,这实际上是SCBO头部的大小。如果头部内容无误的话,则继续读取其余的内容。

p17 图17

从上面的代码片断我们可以看出,它读取了前12个字节内容,并对头部的结构进行了检查。该SCBO文件支持多个数据单元,这意味着可能允许单个USB闪存驱动器重置多个MAC的密码。这对于一个必须重置许多台Mac的系统管理员来说非常有用。这样的话,所谓“通用”的重置SCBO文件好像也就能讲得通了,它只是一个带有多个数据单元的SCBO文件而已——这只是纯粹的推测,因为这种文件尚未公开发现。这个函数的其他部分代码,将会把文件的位置重设为起始位置,从头开始将整个SCBO内容读入一个预先分配的内存缓冲区中。

如果SCBO内容读取成功,接下来就会调用来自另一个协议的函数,该协议将验证该Mac的序列号和当前nonce是否与该SCBO文件中的内容相匹配。记住,每次修改固件密码时,nonce都会改变。

p18 图18

如果序列号和nonce值通过了检查,将在EFI NVRAM中新建一个名为“.SCBO_0000”的变量,如果在SCBO文件中没有更多数据单元要处理的话,系统就会重置。新变量保存了SCBO文件中除12字节的头部之外的所有数据,也即总长度为312字节。

p19 图19

现在我们对SCBO功能的运行机制已经有了更深一步的了解。

如果该SCBO文件的内容与当前的Mac匹配的话,一个新的变量将被建立,并先于任何其他操作之前重新启动计算机。这意味着将又一次读取EFI二进制文并处理新的变量。当前二进制代码只负责读取SCBO文件并完成简单的完整性验证,但它不能清除固件密码。此功能是留给二进制代码818544b5-1b9d-4e7b-8f7d-835aaeaf3b5c的。

变量“.SCBO_0000”可以在下面的固件转储中找到。如果你看一下这个变量的大小,就会发现正好是312字节,这与我们的预期值完全一致。

p20 图20

与二进制代码9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C相关的逆向过程到此结束。现在,我们已经知道它做了什么,接下来,我们将研究真正有趣的二进制代码。

0x03 固件密码功能是如何实现的

在逆向新的二进制之前,让我们先来了解一下固件密码功能是如何实现的。

两年前,我逆向了固件密码实用工具,并在该工作的基础上建立了一个EFI密码暴力破解工具。它帮我确定出该EFI变量包含的固件密码信息是“CBF2CC32”。该密码是利用SHA256以MAC(Message Autentication Code,MAC )来存放的,其中包含可变的回合数。暴力破解固件密码方法不适应于长度超过四位数的密码,因为回合数越高,就越不可能在可接受的时间内破解出来。下面的结构就是描述该变量的内容的:

p21 图21

给定该结构后,暴力破解工具只需要通过IOKit检索这些信息就可以开始破解过程了,直到密码与当前哈希匹配为止。对于四位数密码来说,这个过程只需要一两分钟的时间。

下面,让我们快速浏览一下待逆向的新EFI二进制文件中的main()函数。

p22 图22

Main()函数非常简单。首先,将表指针BootServices和RunTimeServices存放在本地变量中,然后调用一个函数,最后安装协议,该协议是被我们最初逆向的EFI二进制代码所调用的。

p23 图23

安装的这个协议包含有七个函数指针。该函数是由第一个二进制文件调用的,偏移量是 0x18,sub_10000828 。这里,我们要验证哪些EFI二进制文件会调用这个协议。在EFISwissKnife数据库的帮助下,这是个非常简单的事情:

p24 图24

第一列存放使用此协议的EFI二进制代码的GUID。涉及EFI固件密码验证的二进制代码是2D61B52A-69EF-497D-8317-5574AEC89BE4。这个二进制文件还安装另一个协议,这个协议是被其他一些二进制代码所调用的,这些二进制代码也许与用户输入和屏幕绘图有关,但是尚未查明。

p25 图25

如果设法修改这个函数以使其总是返回零,将其重新封装到固件转储中,刷新固件,这样,任何固件密码都能被接受。这意味着,我们的方向是正确的。

0x04 开启新思路

现在,我要重构剧本,因为我在逆向这些东西之前,最初想要做的是利用Trammell的无限循环技巧处理75FAB4B4-6AC1-429A-A000-6B0B95E71CA1协议的各个函数,并找到我们感兴趣的,给它们打补丁使其返回零,并找出哪些函数用于验证密码。

协议函数sub_10000704会检索变量“CBF2CC32”,根据用户输入的密码生成哈希值,并与与该变量中的信息进行比较。常量SHA1,SHA256和SHA512可以在818544B5-1B9D-4E7B-8F7D-835AAEAF3B5C二进制代码中看到。

如果你有SPI刷写工具,要想删除Apple 的EFI固件密码的话,只需转储闪存内容,删除“cbf2cc32”变量(实际上只需要翻转其名称中的一个比特即可),然后刷新修改后的固件即可。或者找到变量,直接擦除或修改它,这样就无需刷写所有内容。

实际上,还有另一种方法。变量“3E6D568B”具有特殊用途,因为如果删除它,NVRAM就会被重置为缺省状态,而该状态下是没有设置固件密码的。

这样,什么论坛啊,价格让人蛋疼的EFI密码重置硬件啊,统统都免了:只需一个SP刷写工具和SOIC夹,你就可以搞定一切。

这个75FAB4B4-6AC1-429A-A000-6B0B95E71CA1协议代码中的其余函数中,只有一个与SCBO有关,它被9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C二进制代码所调用,这个二进制代码的作用是验证SCBO的序列号和nonce是否与当前的Mac 相匹配(见图15,offset 0x18,sub_10000828)。

这意味着变量“.SCBO_0000”还会被其他函数处理。其实就是在main()中调用的sub_10000314函数(图18)。这个函数将对该变量相应的处理,并且如果“3E6D568B”变量出现前面提及的情况的时候,重置NVRAM。

p26 图26

位于函数sub_10000314前面的变量"3E6D568B"将被NVRAM所检索,如果它不存在的话,代码流将被重定向到地址0x100003CD处。

p27 图27

在上面的截图中,始于地址 0x100003CD 的代码删除了几个变量,其中包括"CBF2CC32",并再次创建"3E6D568B",以使得Mac不会陷入死循环。我将这个函数标注为"zero_EFI_variable",它的作用是通过将参数 DataSize 设置为零来删除变量。根据(U)EFI的文档来看,"大小为零的变量将被删除"。

0x05 跟踪变量SCBO的处理代码

下面的问题是,如何跟踪处理变量SCBO的代码?

对于(U)EFI的二进制文件来说,进行静态分析可不是件轻松的事情:如果我们要修补代码并查看运行结果的话,每次刷写都需要5到8分钟。此外,相应的调试器的成本也很贵(如用于(U)EFI的JTAG调试器),许多都在6K美元以上。

对于这个大型的函数和它调用的其他函数,许多部分我已经做过逆向分析了,虽然没找到处理SCBO变量内容的代码,但是,我并未打算逆向所有东西,同时也受够了这种修改代码然后刷写的方式——实在是太慢了。

当时,我又萌生了一个新点子! 利用Unicorn Engine框架创建一个EFI模拟器和调试程序如何? 我感觉,这还不是非常困难和耗时,因为EFI环境是自成一体的——例如没有连接器和系统调用。我也知道,此二进制文件是多少有点独立,仅使用了很少的Boot和RunTime服务,并且涉及的外部协议也很少。由于Boot和RunTime服务的总数量非常少,这就意味着,有没有大量的代码需要我们进行仿真。

鼓捣了没几天,EFI DXE仿真器就诞生了。我真是服了我自己:终于能够在用户态运行调试EFI二进制代码了,逆向速度里面有了前所未有的提升,这样就能快速搞清楚前面那个难缠的代码到底在搞什么鬼了。

p28 图28

它具有gdbinit风格的用户体验,并且能够模拟许多基本命令,例如设置断点、函数的跟踪进入及跳出、内存转储、修改内存及寄存器等,虽然这些命令都很简单,但是对于EFI调试来说却极其有用。尽管该调试器较之于Unicorn/QEMU JIT的设计来说还有一些局限性(例如无法直接修改RIP或EFLAGS寄存器),但是对于基本任务来说绝对够用了。我已经模拟了许多核心启动和运行时服务,如获取和设置变量及NVRAM区、 分配/复制/设置内存、加载更多的镜像,以及安装/定位新的协议。虽然功能算不上完整,这是一个非常有用的工具,但是对于当前的以及将来的(U)EFI工程的关键性开发来说,非常有用。

经过一番折腾,在逆向这个函数之后,我终于搞懂了SCBO的功能。 首先,SCBO的文件结构可以利用下列数据结构来描述:

p29 图29

我们最初搞不明白的那些二进制数据,不过是一个2048位的RSA签名。除非有人找到了苹果的私有密钥,否则,根本无法创建SCBO密钥生成器。那么,网上的那些视频,以及有人宣称他们能够从网站购买SCBO文件是怎么回事呢? 我敢打赌,这是由于某种未知的原因,某些家伙能够向苹果的支持系统提交非法请求,然后将收到的SCBO文件出售,以获取不菲的收入。这些人可能是在苹果支持中心工作,甚至有可能是苹果公司的内鬼。只有苹果公司才能真正调查和跟踪这些文件的来源。另一种选择是,通过漏洞,不过我还没有找到。这些代码和设计看起来毫无破绽,至今还没有找到明显的安全漏洞。

为了验证这一假设,我弄来了一些以前用来验证SCAP的固件更新的签名的代码,最后终于证明我得到的SCBO文件的确是一个具有有效苹果签名的SCBO文件,但是,却无法通过修改它来用于其他机器,除非给某些固件代码打补丁(这实际上没用,因为如果你可以给固件代码打补丁的话,还不如直接重置这些变量)。

p30 图30

处理SCBO内容的核心函数是sub_100021f0。它的首要任务之一是分配一个0x110(272)字节的缓冲区来保存苹果公司的公共密钥。

p31 图31

这个缓冲区具有下列所示的数据结构:

p32 图32

地址位于0x1000128c的函数,将负责从EFI的“文件系统”检索苹果的公开钥匙。 该固件包含五个不同的2048位的公钥,可以从EFI文件B2CB10B1-714A-4E0C-9ED3-35688B2C99F0中找到。

p33 图33

每个原始文件的长度是276字节,这就是说前20个字节都只是头部信息,对于我们来说毫无意义。我们只需要删除这些头部字节,就能获得256个字节的公钥。在Trammell Hudson 的Thunderstike演示文稿中,有一个重要的细节是密钥字节是倒置的。如果想要在自己的工具中使用此公钥的话,我们必须把这些字节颠倒过来。再次提醒,密钥不是直接由函数提取的,而是像通常那样,由一个协议来实现这种功能。该协议的GUID为ac5e4829-a8fd-440b-af33-9ffe013b12d8,并通过二进制代码8b24e4d4-c84c-4ffc-81e5-d3eacc3f08dd进行安装。逆向整个协议是毫无意义的;我了解到它会检索苹果的公钥,这就够了。

为避免模拟与文件系统相关的操作,我直接使用了Unicorn代码钩子,并注入了正确的公钥。 我们使用了图33中的第三个公钥来验证SCBO的签名,其SHA256为94218318fe5aaada2889bbd5f9789cf4afa15bd6eb7654ad66f1f199cf70f8ad(整个原始文件是通过UEFITool提取出来的)。

此外,还将分配另一个32字节的缓冲区来保存SHA256的哈希值。稍后,我们将看到此缓冲区的作用是,保存这个SCBO变量的前56个字节的校验和。

下一步是在物理内存中,从地址0x0FFFFFF08处提取序列号。如果你引导一个Linux安装并使用 Chipsec 来读取物理内存的话,你就能够读取Mac的独立编号(在较旧的macOS版本上,您也可以使用 AppleHWAccess.kext 或 DirectHW.kext 来读取内存,但在新版的系统中已经不可用了)。

p34 图34

下一步是从变量"BC9772C5"中提取当前nonce。目的是建立跟SCBO中看到的一模一样的序列号+nonce字符串。

p35 图35

我们可以利用调试器观察在调用包含printf的函数前后的相应变化。

p36 图36

p37 图37

生成的这个字符串将用来替换SCBO缓冲器区中的序列号+nonce串。签名验证代码将使用从Mac获得的当前值,而非从SCBO文件中获得的值。

p38 图38

这样,我们可以计算SCBO前56字节内容,field1,field2,以及前述的SCBO_CONTENTS结构中的序列号+nonce的哈希值了。

p39 图39

我们可以利用调试器来观察这些结果。如果我们从SCBO文件中提取的相应内容并计算哈希值,如果与我们之前的哈希值匹配的话,则意味着我们的思路是正确的。

p40 图40

p41 图41

最后一步是验证RSA签名,以保证序列号+nonce没有被篡改。

p42 图42

由于苹果的公钥不止一个,所以我们将会看到一个循环结构,这意味着该签名将针对固件“文件系统”中发现的所有苹果密钥进行验证。如果返回一个有效的结果,那么该密码将被删除,实际上就是从NVRAM中清除“cbf2cc32”。

p43 图43

如果参数DataSize(R9寄存器,地址 0x100024F8)为零,则该变量就会从NVRAM中被删除。此代码再一次表明了, EFI密码功能确实是通过NVRAM变量"CBF2CC32"实现的。

0x06 小结

好了,SCBO及其格式的神秘面纱已经被我们彻底揭开了。在这个过程中,我的EFI DXE仿真器和调试器功不可没,极大提高了我的逆向过程的速度。我们看到,该SCBO的功能设计是健壮的,但是网上在售的貌似有效地SCBO文件对我来说仍然是一个迷。我敢打赌,这可能是由内鬼通过苹果技术支持中心获得了苹果系统的访问权限,但再次声明,这只有苹果公司才能查明原因。

如果你忘掉自己的固件密码,现在可以自己重置它了,只要SPI Flash芯片不是较新的BGA类型(新的Mac电脑当前使用的就是这种类型的芯片,不过通过一种隐秘的调试端口,照样可以达到同样的目的!)。你只需要一个装置来转储闪存芯片,删除变量,然后刷写上修改后的版本,或直接删除变量(我总是喜欢完全转储然后重新刷写闪存的方式)。

当然,这些信息可能被小偷偷用来销售被盗的Mac电脑,但实际上网上已经到处都能获得相关东西了,不过,本文并没有透露任何以前未曾揭露的秘密。

我不仅希望你喜欢这篇文章,更希望能通过它激起对(U)EFI逆向技术的兴趣。当然,由于缺乏相应的调试器,该技术并没有用户态或内核逆向技术那样轻松,不过,只要稍微多做一些努力,这些困难仍然是可以解决的。目前,出于某些原因,我还不打算公开自己的EFI仿真程序的代码。

祝阅读愉快!

本文转载自:乌云知识库