现代 Windows 几乎总是使用 Hyper-V 来提供某些虚拟化功能 (即 WSL2) 或维护增强的安全性 (即 VBS). 在基于 ARM 的设备上, 虚拟机管理程序通常以第二个异常级别 - EL2 运行 (相比之下, 操作系统内核的 EL1 和应用程序的 EL0 则不同). 通常, 操作系统会立即在 EL2 启动, 这是由 ARM Base Boot Requirements 等规范所强制要求的:
常驻 AArch64 UEFI 引导时间环境被指定为使用可用的最高 64 位非安全权限级别. 此级别为 EL1 或 EL2,具体取决于是否使用或支持虚拟化
7.3.1.1 UEFI 在 EL2 上引导
系统必须在 EL2 启动 UEFI, 以允许安装 Hypervisor 或 虚拟化感知操作系统
不幸的是,基于骁龙的计算平台不符合规范的这一部分, 尽管允许虚拟化, 但它在 EL1 中启动 UEFI
这意味着微软引导加载程序必须依赖某种自定义机制来接管 EL2 并引导 Hypervisor. 本文档试图概述接管过程, 重点介绍预期的信任链.
背景信息
高通设备上的 EL2
一般来说, 除了少数例外,骁龙设备永远不会对用户开放 EL2. 在较旧的平台 (如 msm8916) 上, hyp 固件绝对没有任何用处,并且可以安全地替换. (qhypstub 证明了这一点) 在此之后的平台上 (即 msm8937),整个 EL2 完全被禁用 (未加载固件且禁用 hvc
指令)
在现代平台上,例如用于 WoA 的 骁龙 "计算" 芯片, 虚拟机管理程序固件 (QHEE) 稍微复杂一些
除其他外,它还模拟了 SMMU 并提供某些与安全相关的功能, 旨在确保用户无法以任何令人兴奋的方式使用他们的设备
这个 "规范" 中有趣的例外是 ChromeOS 设备, 它们使用 ARM Trusted-Firmware 而不是高通专有引导链, 以及允许操作系统在 EL2 中引导. TF-A 构建仍然依赖于一个巨大的专有库, 它实现了所有与硬件相关的东西, 但这仍然比 WoA 的要好
UEFI和硬件安全启动
在 x86 世界中, "安全启动" 被理解为 UEFI 安全启动, 它允许 UEFI 固件检查已启动的 code 来验证固件密钥环中加载的一组证书. 虽然此密钥环几乎总是预加载了微软签名密钥, 用户通常可以配置自己的密钥或禁用此安全启动功能
与此相反, 硬件安全引导 (常见于嵌入式系统), 依赖于 SoC 一次性可编程内存 (例如 e-fuses). 这意味着, 如果启用了 hw-secure-boot, 则不可能禁用安全启动并启动任何未使用预置密钥签名的代码
在高通设备上, 信任根是 OEM 签名证书的哈希值, 在 SoC 电子保险丝中烧毁. 然后检查固件是否正确签名与此哈希匹配的证书.
值得注意的是, 如果从未预置哈希, 则硬件安全引导被禁用, 并接受任何有效的签名 (参考: qtestsign)
安全启动过程
UEFI 启动
SoC 的初始启动与大多数其他骁龙平台相同. 唯一的区别是启动了更传统的 UEFI 固件 作为 "前端", 而不是 Android 引导加载程序
通过阅读 高通安全启动概述 v2.0, 我们可以猜测固件的启动方式类似于:
PBL - SoC bootrom 作为系统中的第一个代码启动. 它在芯片硅中烧毁, 无法更换. 然后 PBL 加载 XBL 和 XBL_SEC (不安全和安全的引导加载程序). 然后加载 QTEE (TrustZone) 和 QHEE (Hypervisor). 然后,QHEE 会在 EL1 中加载更多的 UEFI 固件. 所有这些组件都通过硬件安全启动进行验证.
然后在 EL1 中启动 UEFI 固件, 并将多个 trustlet 加载到 QTEE 中. 这些 trustlet 提供安全 UEFI 变量 (uefisec.mbn
) 或 微软的 TPM 和安全启动 (mssecapp.mbn
) 等功能. QTEE 遵循硬件安全启动机制, 验证所有 trustlet 是否使用 OEM 密钥进行签名.
之后, UEFI 可以加载操作系统或向用户提供固件配置菜单. 此时,UEFI 将使用自己的安全启动, 该启动基于存储在 efivars 中的可变密钥环 (由 trustlet 烘焙). 在这里, 用户可以选择禁用 UEFI 安全启动并在 EL1 中加载任意代码. 这类似于某些 Android 设备上的 "OEM/BootLoader 解锁", 在这两种情况下, 用户都无法访问更高级别, 例如 EL2.
Windows 启动和 EL2 接管
要引导 Windows, UEFI 会启动 Windows 引导管理器 (bootmgrfw.efi
).然后, 引导管理器 启动 Windows 引导加载程序 (winload.efi
, 请注意: 尽管有扩展, 但这是 Windows 引导应用程序, 与 UEFI 不兼容). 这些 blob 通过 UEFI 安全启动进行验证, 可以通过 禁用安全启动 或 编辑 启动管理器配置 (BCD) 来禁用完整性检查来自由修改或替换
如果 BCD 设置告诉 winload.efi 虚拟化已被禁用, 它会继续在 EL1 中启动操作系统. 否则, 将启动 Secure-Launch
winload.efi 运行 OslpTcbLaunchPhase0
来加载 tcblaunch.exe
并在 EL2 中启动它. SlpAppStart
执行多个 SMC
调用来验证映像并在 EL2 中引导它:
ulonglong SlpAppStart(...,longlong pe_base,uint pe_size,...)
{
// ...
arg_data = SlpAllocateSecureLaunchContext();
if (arg_data >= 0) {
data = (seclaunch_message *)((longlong)&(SecureLaunchState.LaunchContext)->phys_addr
+ (SecureLaunchState.LaunchContext)->page_size);
/* Always returns 0. (Check if SL is available?) */
tmp1 = BlArch64IssueSmc(data,1);
// ...
if ((tmp1 >= 0) && (arg_data = SlpBuildBootApplicationParameters(arg_data,param_1), arg_data >= 0)) {
SlpInitializeTransitionContext();
// ...
data->pe_data = SecureLaunchState.pe_base_copy;
data->pe_size = SecureLaunchState.pe_size;
data->arg_data = (SecureLaunchState.LaunchContext)->big_buffer->this_addr_phys;
data->arg_size = (SecureLaunchState.LaunchContext)->big_buffer->some_size_for_launch_smc;
/* Authenticate the image. */
iVar2 = BlArch64IssueSmc(data,2);
if (iVar2 >= 0) {
// ...
/* Jump to the image in EL2 */
iVar2 = BlArch64IssueSmc(data,4);
//...
}
// ...
}
}
// ...
}
该代码传递 PE 应用程序和包含启动参数的数据结构
SMC 调用被 QHEE 的 hyp_manager_launch
拦截:
第一次调用仅设置一个标志并返回
// ...
if (num == 1) {
ret = 0;
secure_launch_state = secure_launch_state | 8;
}
// ...
第二个调用将镜像文件的数据传递给 mssecapp
进行身份验证:
// ...
switch(num) {
case 2:
// ...
else {
//...
if (cVar2 || !iVar3) {
iVar3 = FUN_80046e98(); // some smc call
if (iVar3 == -0xc) { ret = 0x37; }
else {
/* Make a call to the TZ and check the image */
ret = hyp_manager_lookup_mssecapp_id(smcParams,2,0);
if (ret) {
err_str = "hyp_manager_handle_auth: hyp_manager_tzapp_send failed\n";
goto error;
}
secure_launch_state = secure_launch_state | 2;
}
break;
}
//...
}
print_msg(3,err_str);
ret = 0x11;
break;
// ...
}
// ...
有趣的是, mssecapp 只在下一步执行验证, 因此此调用将始终成功
第三个调用检查第二个调用设置的标志, 如果设置了标志, 则要求 mssecapp
重新定位 PE, 创建内存映射并查找入口点. 然后 hyp 会做各种准备工作, 例如根据接收到的内存映射映射内存, 更新 SMMU 设置, 更改 ACPI 表 并确保只有一个 cpu 在运行, 然后再跳转到接收到的入口点
// ...
switch(num) {
// ...
case 4:
if (((byte)secure_launch_state >> 1 & 1) == 0)
goto not_authenticated;
// ...
else {
// ...
else {
/* Get the entry point address for the PE */
ret = hyp_manager_lookup_mssecapp_id(smcParams,4,&image_addr);
// ...
print_msg(2,"hyp_manager_handle_launch: Jumping to Image:0x%x",image_addr);
print_msg(2,"hyp_manager_handle_launch: Address of params:0x%x",¶ms_data);
// ...
Jump_to_loaded_image(image_addr,¶ms_data);
print_msg(4,"Secure Launch should never return!");
// ...
}
// ...
在此调用之后, tcblaunch.exe
将控制 EL2. BlSlEntryPoint
使用传递的参数进行调用. 此函数设置堆栈, 对输入数据执行一些验证, 然后用新的异常向量覆盖 vbar_el2
并将自身降级为 EL1. 然后它继续引导过程
作为第三次调用的一部分, 镜像由 VerifyApp
函数中的 mssecapp
验证. 与在 Windows 中检查驱动程序的方式类似, MinCrypL_CheckSignedFile
用于验证 PE 是否使用微软的密钥进行签名. 检查的密钥环在 mssecapp.mbn
中是硬编码的, 再加上 trustlet 也使用 OEM 安全启动密钥进行签名, 为 Secure-Launch 建立了一个连续的信任链, 这意味着只有微软批准的代码才能在这些高通设备上以 EL2 运行
实际上, 这意味着固件根据其设计实现了微软操作系统的供应商锁定, 将硬件的某些功能 (如虚拟化) 锁定在付费专区后面 (Windows 中的虚拟化功能仅在专业版中可用). 人们可以推测微软和高通之间发生了阴谋, 但这很可能是高通的固执 和/或 微软对此事的无知的结果.
Secure-Launch 的完整引导流程图:
未来的工作
虽然 Secure-Launch 实现没有为使用 EL2 的替代操作系统提供任何规定, 但可以使用现有的 tcblaunch.exe
将 CPU 切换到 EL2
SLBounce 中提供了此功能的实现