返回

文章详情

获取 AMD SEV 报告(使用 Zig)

Hacker News2026年10月11日 20:10

在普通的虚拟机中,监视器可以读取客户机的内存。AMD SEV(安全加密虚拟化)使用每个虚拟机的密钥加密该内存,监视器无法获得该密钥。SEV-ES 扩展了对保存的 CPU 寄存器状态的保护。SEV-SNP(安全嵌套分页)添加了对内存所有权和映射的检查,以防止恶意监视器替换或重新映射私有页面。这保护了客户机免受宿主的影响;这并不能使客户机内部的软件可信,也不防止宿主停止虚拟机。SNP 允许客户机请求由 AMD 安全处理器签名的报告,提供虚拟机启动状态和配置的证据。它包含启动度量、客户政策、平台安全版本和 64 字节的调用方提供的 REPORT_DATA。在虚拟机启动之前,宿主将其启动软件加载到内存中。这包括客户机的引导固件:初始化虚拟机引导环境并将控制权交给操作系统引导加载程序的启动软件。在 Google Cloud 上,这是基于开放虚拟机固件(OVMF)的 Google 管理的 UEFI 固件。启动软件还可以包括 Coconut SVSM,这是一个提供服务的单独程序,例如虚拟 TPM。UEFI 固件仍然引导操作系统;SVSM 提供需要保护不受操作系统自身影响的服务。AMD 的 SNP 固件运行在安全处理器上。宿主使用 SNP_LAUNCH_START 启动一个启动进程,然后通过 SNP_LAUNCH_UPDATE 以称为内存页面的块提供启动映像。对于每个正常的代码或数据页面,固件哈希安装的字节、其客户机内存地址、页面类型和权限,以及之前的哈希。AMD 的固件在受保护的状态下维护此运行哈希;宿主无法提供替代值。SNP_LAUNCH_FINISH 结束启动。最终的 SHA-384 哈希是启动度量,是该初始映像的指纹,包括 SVSM(如果存在)。随后,固件将此存储值复制到报告中。随着虚拟机的运行,它保持不变。当执行远程验证时,验证器将其与可信任的启动软件和启动配置的预期指纹进行比较。Google 发布包含这些预期度量的签名启动证明以供其固件使用。更改固件字节会更改指纹。将这些页面标记为未测量会跳过哈希其内容,但会更改包含在哈希中的页面类型。无论如何,结果与预期指纹不同,因此验证器可以拒绝验证。随后固件加载的内核和应用程序不会自动包含在此指纹中。检索 Linux 上的报告 Linux 在 /dev/sev-guest 下公开 SNP 客户接口。应用程序发送 SNP_GET_REPORT ioctl,驱动程序处理与 AMD SNP 固件的受保护交换。除了报告数据之外,请求还提供虚拟机特权级别(VMPL):0 到 3,0 为特权最高。这些级别很重要,因为 Linux 不一定是虚拟机内特权最高的软件。例如,Coconut SVSM 可以利用这种分离来托管一个虚拟 TPM,其密钥与客户机内核隔离。在 Linux 中以 root 身份并不意味着你处于 VMPL0。使用你的客户机支持的 VMPL。以下是在 Zig(0.17.0)中使用 Linux 的客户 API 布局的请求。fd 是 x86-64 Linux SNP 客户中的打开的 /dev/sev-guest 描述符:const std = @import ("std"); const linux = std . os . linux ; const ReportRequest = extern struct { user_data : [ 64 ] u8 , vmpl : u32 , reserved : [ 28 ] u8 = @splat (0), }; const GuestRequest = extern struct { msg_version : u8 = 1 , padding : [ 7 ] u8 = @splat (0), req_data : u64 , resp_data : u64 , exitinfo2 : u64 = 0 , }; pub fn fetchReport ( fd : i32 , challenge : [ 64 ] u8 , vmpl : u32 ) ! [ 1184 ] u8 { var request : ReportRequest = .{ . user_data = challenge , . vmpl = vmpl }; var response : [ 4000 ] u8 = @splat (0); var call : GuestRequest = .{ . req_data = @intFromPtr( & request ), . resp_data = @intFromPtr( & response ), }; const SNP_GET_REPORT = 0xc0205300 ; const result = linux . ioctl ( fd , SNP_GET_REPORT , @intFromPtr( & call )); if ( linux . errno ( result ) != . SUCCESS or call . exitinfo2 != 0 ) { return error . ReportRequestFailed ; } const status = std . mem . readInt ( u32 , response [ 0 .. 4 ], . little ); const size = std . mem . readInt ( u32 , response [ 4 .. 8 ], . little ); if ( status != 0 or size != 1184 ) return error . InvalidResponse ; // 报告跟随 32 字节的固件响应头。 return response [ 32 .. ][ 0 .. 1184 ] .* ; } 传递一个新的 64 字节的挑战来自验证者和客户机的 VMPL。挑战会在 REPORT_DATA 中返回,因此验证者可以拒绝旧报告。结果是一个原始报告;其签名和政策仍需检查。我将检索、报告解析和验证打包在 zig-sev-guest 中,这样调用者就不必处理这些。

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

☕请我喝杯咖啡