UEFI如何运作?

pro*_*481 9 assembly operating-system bios bootloader uefi

我正在研究UEFI这个术语时的引导加载程序.我可以理解一些关于UEFI的事情.但是,在什么模式下(Real,Protected,Long),UEFI的系统启动了吗?如果正常的引导加载程序无法使用UEFI,那么在处理UEFI时,引导加载程序的替代方案是什么?我是否需要任何其他编程来创建一个,而不是组装?

Ale*_*lex 9

UEFI固件在64位平台上以64位长模式运行,在32位平台上以扁平模式运行; 与BIOS不同,UEFI具有自己的架构,独立于CPU和自己的设备驱动程序.UEFI可以挂载分区并读取某些文件系统.

当配备有UEFI的x86计算机时,该接口在系统存储中搜索标记有特定全局唯一标识符(GUID)的分区,该标识将其标记为EFI系统分区(ESP).BTW Windows不会挂载此分区,您无法在操作系统中看到它.但是有一个技巧,你只需将VBR中的分区类型(使用HexWorkshop)更改为常规FAT32代码,它就会被挂载到操作系统中.

此分区包含为EFI体系结构编译的应用程序.通常,您不必处理汇编程序来编写UEFI应用程序/加载程序,它只是一个常规的C代码.默认情况下,它位于"EFI/BOOT/BOOTX64.EFI".当手动或自动选择引导加载程序时,UEFI会将其读入内存并控制引导过程.

  • @barlop:上电后执行的第一条指令是从硬连线到 CPU 的物理地址(复位向量)中获取的。此时,CPU 处于“虚幻”模式,CS 段基址位于低 1MiB 之外,但其他方面与 16 位实模式相同。系统设计人员确保地址映射到闪存 ROM。该代码最终会在从磁盘读取任何内容之前切换到 64 位模式。但在此之前它必须配置 DRAM 控制器和类似的东西!它通常会在某些时候使用缓存即 RAM(无填充)模式...... (3认同)
  • 据我了解,UEFI以16位https://github.com/tianocore/edk2/blob/master/UefiCpuPkg/ResetVector/Vtf0/Ia16/ResetVectorVtf0.asm#L52开始,这里是它从16位实模式转换到32位保护模式 https://github.com/tianocore/edk2/blob/master/IntelFsp2Pkg/FspSecCore/SecMain.c#L52 (2认同)
  • 当我问“UEFI 之前运行什么?”时,你说“引导加载程序”。但引导加载程序并不在 UEFI 之前!Grub 是一个引导加载程序,它在 UEFI 之后运行。(也许引导加载程序在某种程度上是一个含糊不清的术语) (2认同)

Bud*_*ddy 6

这是这个问题的一个很好的答案

其他现代 64 位机器具有新的 EFI 固件。这些根本不会从光盘的扇区 #0 加载引导程序。它们通过 EFI 引导管理器加载和运行 EFI 引导加载程序应用程序进行引导。此类程序在保护模式下运行。这是 EFI 引导程序。

EFI 固件通常会在退出处理器重置的几条指令内切换到保护模式。切换到保护模式是在 EFI 固件初始化的所谓“SEC 阶段”的早期完成的。从技术上讲,32 位及更高版本的 x86 处理器甚至不会以正确的实模式启动,而是以俗称的虚模式启动。(CS 寄存器的初始段描述符没有描述传统的实模式映射,这使得它“不真实”。)

因此,可以说这些 EFI 系统在本地引导到 EFI 引导加载程序时(即,当它们不使用兼容性支持模块时)从不正确进入实模式,因为它们从非实模式直接切换到保护模式并从那时起保持保护模式。

  • @barlop:我想说,以 16 位(非)实模式启动是 x86 早期启动的一部分。在固件准备好从磁盘加载某些内容并为其加载的代码提供标准化配置和启动 API 之前,它不是 EFI / UEFI(或旧版 BIOS)。在此之前,固件必须配置该特定系统的内存控制器,以及类似的东西,并且该部分是完全私有的,不受任何标准管辖。16 位不是 UEFI 的一部分,因此,UEFI 固件必须先退出该模式,然后才能跳转到从磁盘加载的任何代码,并提供该 ABI。 (3认同)

Lew*_*sey 5

在下面,一些句子是我见过的最佳来源的副本、合并和措辞改进,然后我通过使用我自己的硬件案例研究来改进和纠正他们的未知数/错误。

引导保护

Intel Boot Guard 是 Intel 在第四代 Intel Core (Haswell) 中引入的一项技术,用于验证启动过程。这是通过在制造过程中将 BIOS 签名的公钥闪存到现场可编程熔丝 (FPF) 中来实现的,FPF 是英特尔 ME(在 PCH 中)内部的一次性可编程存储器;通过这种方式,它拥有 BIOS 的公钥,并且可以在每次后续启动时验证正确的签名。一旦由制造商启用,英特尔 Boot Guard 就不能再被禁用。

据英特尔称,Boot Guard 有两种不同的模式。典型的 PC OEM 将其配置为 “验证启动”模式。PC 制造商将他们的公钥融合到硬件本身中。如果 UEFI 固件未由 OEM 签名(即由 OEM 创建),计算机将停止并拒绝启动。这就是您无法修改 UEFI 固件的原因。还有第二个选项:“测量启动”模式,在该模式下,硬件使用英特尔 TXT 来安全存储有关启动过程的信息(在受信任的平台模块 (TPM) 中)或借助 SMX 的英特尔平台信任技术 (PTT)。然后操作系统可以检查这些信息,如果有问题,就会向用户显示错误。

安全启动

启用并完全配置后,安全启动可帮助计算机抵御恶意软件的攻击和感染。安全启动通过验证其数字签名来检测对启动加载程序、关键操作系统文件和未经授权的选项 ROM 的篡改。检测在攻击或感染系统之前被阻止运行。UEFI 安全启动假定 OEM 平台固件是可信计算库 (TCB)(即它已使用 BootGuard 技术初始化并隐式信任它)。


验证启动过程

UEFI前阶段

当 OEM 收到 PCH 时,ME 仍处于“制造模式”并运行固件的特殊部分,该部分将从闪存 ROM 的部分复制“OEM 公钥哈希”和“引导保护配置文件配置”策略值到现场可编程熔断器 (FPF) 中,使其永久且不可更改。然后它设置一个保险丝以指示它已退出制造模式,以便固件的这部分不会再次运行。可以使用英特尔闪存映像工具 (FITC) 在闪存映像中调整这些值,但除非您有办法强制 ME 进入制造模式,否则闪存映像中的值将被忽略。

Bootguard 配置文件如下:

在此处输入图片说明

启用保护 BIOS 环境:如果设置,这可能意味着 ACM 将 IBB 段复制到 CPU 缓存中,以便它以缓存作为 RAM (CAR) 模式运行并禁用所有 DMA 以防止设备能够修改它。

一旦电源可用,ME CPU 从其片上启动 ROM 启动,检查一些带和保险丝以确定其配置,然后通常从 SPI 闪存的 ME 区域复制闪存分区表 (FPT)(它通常位于 BIOS 区域下方)到其片上 SRAM。它通过使用 SPI 闪存最低地址处的闪存描述符来定位 ME 区域。

在此处输入图片说明

引导 ROM 使用 FPT 定位 FTPR 分区,并将其从 SPI 闪存复制到片上 SRAM。然后它检查存储在分区清单中的密钥的 SHA-1 哈希值是否与其片上 ROM 中的密钥相匹配,并验证分区清单其余部分的 RSA 签名。分区表包含分区中每个模块的哈希值,允许在将模块复制到片上 SRAM 以执行后验证模块。

在此处输入图片说明

当 ME 启动 x86 CPU 时,可能在bup.met. BIST,然后是 BSP MP 初始化算法。CS:FFF0 处的传统复位向量(其中 CS 为 0xF000,段描述符缓存包含基址 0xFFFF0000)不再是 x86 CPU 在复位时执行的第一条指令。相反,片上微代码在 0xFFFFFFC0 处获取 FIT 指针(使用 0xF000:FFC0 和虚幻模式段描述符 hack——因为是寄存器的初始状态),它指向 SPI 闪存 BIOS 区域中某处的 FIT 表。

此图显示了 16 字节 FIT 条目,其模式距末尾 3 个字节。

#pragma pack (1)
typedef struct {
  UINT64     Address;
  UINT8      Size[3];
  UINT8      Rsvd;
  UINT16     Version;
  UINT8      Type:7;
  UINT8      C_V:1;
  UINT8      Checksum;
} FIRMWARE_INTERFACE_TABLE_ENTRY;
Run Code Online (Sandbox Code Playgroud)

在我的系统上,在 FFFFFFC0 到 FFD90100 处有一个 FIT 指针

FIT 表指向微代码更新、ACM、BootGuard 启动策略清单(包含 IBBS)和 BootGuard 密钥清单等。

我的 FIT 不包含任何0x7 条目。记录类型 7 仅由传统英特尔® TXT FIT 引导使用,如果不使用后者则不需要。

BootGuard 引导策略 (IBBM) 包含:

Intel BootGuard Boot Policy Manifest found at base FD3C00h
Tag: ACBP Version: 10h HeaderVersion: 01h
PMBPMVersion: 10h PBSVN: 00h ACMSVN: 02h NEMDataStack: 0010h

Initial Boot Block Element found at base FD3C28h
Tag: IBBS       Version: 10h         Unknown: 0Fh
Flags: 00000000h    IbbMchBar: FED10000h VtdBar: 00000000h
PmrlBase: FED90000h PmrlLimit: 00000000h  EntryPoint: 00100000h

Post IBB Hash:
0000000000000000000000000000000000000000000000000000000000000000

IBB Digest:
B9CCC06B77AEACC51768981D07CBE9E43D34DB6795752C4B998312241B26F874

IBB Segments:
Flags: 0000h Address: FFE10000h Size: 001C3C00h
Flags: 0000h Address: FFFD4C00h Size: 00000080h
Flags: 0000h Address: FFFD5C80h Size: 0000A380h
Flags: 0000h Address: FFFE8000h Size: 00018000h

Boot Policy Signature Element found at base FD3CDDh
Tag: PMSG Version: 10h

Boot Policy RSA Public Key (Exponent: 10001h):
....
Boot Policy RSA Public Key Hash:
....
Boot Policy RSA Signature:
....  
Run Code Online (Sandbox Code Playgroud)

密钥清单包含:

Intel BootGuard Key Manifest (KEYM) found at base FD4C80h
Tag: KEYM Version: 10h KmVersion: 10h KmSvn: 00h KmId: 0Fh

Key Manifest RSA Public Key Hash:
...
Boot Policy RSA Public Key Hash:
...
Key Manifest RSA Public Key (Exponent: 10001h):
...
Key Manifest RSA Signature:
... //it does actually contain a signature, I just removed these for space
Run Code Online (Sandbox Code Playgroud)

然后由微代码设置缓存为 RAM (CAR)(又名 AC-RAM,无填充模式和无驱逐模式)。然后在 FIT 中搜索与 CPU ID 匹配的微码更新。当前的微码将它们从闪存线性复制到 L3 缓存中,并使用片上对称 AES 密钥解密,然后使用(片上?)RSA 密钥进行验证。这些微码更新很可能还包含 ACM 的密钥散列。然后通过写入 UCODE MSR 应用微代码更新,CPU 将其读出,我假设是通过正常的内存访问。FIT 绝对必须包含一个微码补丁,它修补微码 SRAM 以补充现有的微码 ROM。[1]

接下来,微代码返回 FIT 以找到启动 ACM(又名 BIOS 或 Bootguard ACM),并将它的奇怪副本复制到 L3(看起来多个超线程正在复制 4KB 块? - 根据该来源)。ACM 包含一个 RSA 公钥;微码将其与芯片上的密钥或存储在微码更新中的密钥进行比较,如果不匹配则停止 CPU。然后微码检查 ACM 上的签名,如果不匹配则再次停止。

Startup ACM 完全运行在 L3 之外。ACM通过 MSR 从 ME接收 OEM 公钥哈希(验证密钥清单的密钥哈希)和 Bootguard配置文件

ACM 将 BootGuard 密钥清单从 SPI 闪存(由 FIT 指向并由 标识__KEYM__)读取到 L3 中,并散列存储在其中的 RSA 公钥。如果它与 OEM 公钥哈希不匹配,或者密钥清单上的 OEM 公钥签名或者如果存储的 KmSvn 不正确,ACM 将根据 Bootguard 配置文件位采取行动。如果匹配,它会在 FIT 中定位 Bootguard 策略(并由 标识__ACBP__)并将其复制到 L3 中。然后,ACM 计算策略中 RSA 公钥的哈希值,并将其与存储在密钥清单中的 SHA256 哈希值进行比较。如果匹配失败,或者策略上的 RSA 签名不匹配,则 ACM 将再次根据配置文件设置采取行动。

ACM 使用现在经过验证的 Bootguard 策略结构将初始引导块 (IBB) 段读入 L3,并在复制它们时对其进行散列。如果此计算出的哈希与策略中的“IBB 摘要”不匹配,则 ACM 会根据配置文件设置采取行动。第 4 个 IBB 段包含重置向量,因此它可以防止被修改。目前还不清楚它是让 CPU 处于 CAR 模式,还是只是将它们缓存在 L3 中但没有禁用驱逐,即没有启用 CAR 模式。但是,SEC 需要(禁用)并设置新的 CAR 模式。

在此处输入图片说明

使用 RwEverything 转储物理内存,或为您的 ME 版本下载 CSME 系统工具然后执行fptw64 -d -me -bios dump.bin将转储闪存(通常您只能转储 BIOS 区域而不是 ME 区域)。现在可以在 UEFITool NE Alpha 58 中分析转储,并且 FIT 在正文中显示 ACM 的入口点。因此,如果您右键单击,提取它并在 IDA 中以 32 位打开它,入口点将位于3BB1h + 18h = 3BC9h

3BC9   mov     ax, ds
3BCC   mov     ss, ax
3BCF   mov     es, ax
3BD2   mov     fs, ax
3BD5   mov     gs, ax
3BD8   mov     esp, ebp
3BDA   add     esp, 1000h
3BE0   mov     eax, ebp
3BE2   add     eax, 4C8h
3BE7   lidt    fword ptr [eax]
3BEA   push    ebp
3BEB   call    sub_392A
3BF0   mov     ebx, eax
3BF2   mov     edx, 0
3BF7   mov     eax, 3
3BFC   getsec
Run Code Online (Sandbox Code Playgroud)

启动 ACM 始终是最终的 ACM,它GETSEC[EXITAC]指向0xFEE90000bootguard 策略清单中的 IBBS 基址 + 入口点地址(位于第一个 IBB 中),该清单似乎位于 ME 区域中,并且可能包含切换 CPU 的代码回到虚幻模式并跳转到第 4 个 IBB 段中的传统重置向量。GETSEC只能在保护模式下执行,所以很明显CPU在启动ACM中处于保护模式,所以必须在进入前通过微码使能。传统复位向量位于 0xFFFFFFF0,在我的系统上是相对跳转到 FFFFFFF5 - 3BD = FFFFFC38,这是 SEC 核心入口点。

证券交易委员会

在此处输入图片说明

SEC 核心是来自 FFFFCA14 - FFFFCA17 的原始部分、来自 FFFFCA18 - FFFFFFBB 的 PE32 图像、来自 FFFFFFBC - FFFFFFBF 的原始部分以及来自 FFFFFFC0-FFFFFFFF(包含复位向量)的原始部分。

复位向量跳转到的SEC Core中PE32 Image的入口点包含:

0x00:  DB E3                      fninit 
0x02:  0F 6E C0                   movd   mm0, eax   //move BIST value to mm0
0x05:  0F 31                      rdtsc  
0x07:  0F 6E EA                   movd   mm5, edx
0x0a:  0F 6E F0                   movd   mm6, eax  //save tsc
0x0d:  66 33 C0                   xor    eax, eax //clear eax

0x10:  8E C0                      mov    es, ax
0x12:  8C C8                      mov    ax, cs
0x14:  8E D8                      mov    ds, ax
0x16:  B8 00 F0                   mov    ax, 0xf000
0x19:  8E C0                      mov    es, ax
0x1b:  67 26 A0 F0 FF 00 00       mov    al, byte ptr es:[0xfff0]
0x22:  3C EA                      cmp    al, 0xea
0x24:  74 0E                      je     0x34   //if ea is at ffff0h then jump to the 0xf000e05b check 

0x26:  BA F9 0C                   mov    dx, 0xcf9
0x29:  EC                         in     al, dx    //read port 0xcf9
0x2a:  3C 04                      cmp    al, 4    
0x2c:  75 25                      jne    0x53      
0x2e:  BA F9 0C                   mov    dx, 0xcf9 //perform warm reset since if CPU only reset is issued not all MSRs are restored to their defaults
0x31:  B0 06                      mov    al, 6
0x33:  EE                         out    dx, al  

0x34:  67 66 26 A1 F1 FF 00 00    mov    eax, dword ptr es:[0xfff1]
0x3c:  66 3D 5B E0 00 F0          cmp    eax, 0xf000e05b
0x42:  75 0F                      jne    0x53      //if it isn't, move to notwarmstart

0x44:  B9 1B 00                   mov    cx, 0x1b //if it is equal, read bsp bit from apic_base msr
0x47:  0F 32                      rdmsr  
0x49:  F6 C4 01                   test   ah, 1
0x4c:  74 41                      je     0x8f   //if the and operation with 00000001b produces a zero result i.e. it's an AP then jump to cli, hlt

0x4e:  EA F0 FF 00 F0             ljmp   0xf000:0xfff0 //if it's the BSP, exit unreal mode by far jumping to 0xffff0 which reloads the segment descriptor cache with a 0 base

notwarmstart:
0x53:  B0 01                      mov    al, 1
0x55:  E6 80                      out    0x80, al  //send 1 as a debug POST code
0x57:  66 BE 68 FF FF FF          mov    esi, 0xffffff68
0x5d:  66 2E 0F 01 14             lgdt   cs:[si] //loads 32&16 GDT pointer (not 16&6, due to 66 prefix) at 16bit address fff68 in si into GDTR (base:ffffff28 limit:003f); will be accessing alias and not shadow ROM

//enter 16 bit protected mode//
0x62:  0F 20 C0                   mov    eax, cr0
0x65:  66 83 C8 03                or     eax, 3   //Set PE bit (bit #0) & MP bit (bit #1)
0x69:  0F 22 C0                   mov    cr0, eax  //Activate protected mode
0x6c:  0F 20 E0                   mov    eax, cr4 
0x6f:  66 0D 00 06 00 00          or     eax, 0x600 //Set OSFXSR bit (bit #9) & OSXMMEXCPT bit (bit #10)
0x75:  0F 22 E0                   mov    cr4, eax

//set up selectors for 32 bit protected mode entry
0x78:  B8 18 00                   mov    ax, 0x18 //segment descriptor at 0x18 in GDT is (raw): 00cf93000000ffff
0x7b:  8E D8                      mov    ds, ax
0x7d:  8E C0                      mov    es, ax
0x7f:  8E E0                      mov    fs, ax
0x81:  8E E8                      mov    gs, ax
0x83:  8E D0                      mov    ss, ax
0x85:  66 BE 6E FF FF FF          mov    esi, 0xffffff6e
0x8b:  66 2E FF 2C                ljmp   cs:[si]   //transition to flat 32 bit protected mode and jump to address at 0x0:0xffffff6e aka. 0xffffff6e which is fffffcd8. CS contains 0 remember (it's the base that is 0xffff) so it will load the first entry.  This address is also in the SEC Core PE32 Image
                                                  
0x8f:  FA                         cli    
0x90:  F4                         hlt    
.
.
.
Run Code Online (Sandbox Code Playgroud)