Spaghettifying DRAM:一条 XOR 指令解锁 CPU 上的一切

开源安全CPU

Spaghettifying DRAM: 一条 XOR 指令解锁 CPU 上的一切

Christopher Domas 在 Black Hat 安全会议上以挖掘 x86 架构盲区闻名。2015 年他展示了 Memory Sinkhole,通过利用 x86 处理器的设计缺陷实现通用提权;2017 年他用页故障分析穷举 x86 指令集,挖掘隐藏指令;2025 年他转向硬件故障注入,用机器检查异常突破 CPU 特权边界。2026 年,他的新研究 Spaghettifying DRAM 将目光投向了更底层的领域:DRAM 控制器的地址重映射机制。

核心发现浓缩在一行汇编指令中:

text
xor dword [0xf80c2094], 0x00400000

这一条 XOR 指令翻转 DRAM 控制器中一个配置寄存器的 bank-swizzle 模式位。此后,物理地址与实际 DRAM 存储单元之间的映射关系被瞬间重写。代码中 &x 不再指向原来的数据——它现在指向内存中另一个位置的值。CPU 上层的所有安全屏障——页表、EPT、SMM、SGX、SEV——都建立在物理地址的稳定性之上。它们监控的是物理地址,不是 DRAM 坐标。当你重新排列了 DRAM 坐标,上层的屏障全都不再有效。

内存地址翻译的全栈管线

理解这条攻击的破坏力,需要理解一个指针解引用 *p 从虚拟地址到 DRAM 物理介质的完整旅程。Domas 将其称为「*p 的奥德赛」。

当 CPU 执行 mov eax, [0x6f800000] 时,地址 0x6f800000 是一个虚拟地址。在它触碰 DRAM 的任何一个 bit 之前,需要穿越以下层叠管线:

CPU 核心与 MMU 层面,虚拟地址先经过规范形式检查(64 位地址的高位符号扩展),再叠加段基址(FS.base / GS.base),然后探测 TLB。TLB 未命中则触发硬件页表遍历,从 CR3 寄存器出发,依次走 PML5、PML4、PDPT、PD、PT 五级页表,最终得到物理地址。每一级都要检查权限位(读写、用户/内核、执行、SMEP/SMAP、保护键)。

如果运行在虚拟机中,物理地址还要再经历 EPT/NPT 二次页表遍历。IOMMU 对设备发起的 DMA 有独立的页表。之后进入 CPU uncore 层面,MTRR 和 PAT 决定内存类型(WB/WC/UC),L1/L2/LLC 缓存逐级探测,跨核跨 socket 的 MESI/MOESI 一致性协议广播。

穿过数据织带(Data Fabric)后,物理地址到达内存控制器(MCT/IMC)。但这里还有最后一道翻译:DRAM hole remap(高内存重映射)、内存区域排除重映射、通道交织哈希(XOR 选定位得到通道号)、rank 交织哈希、bank 交织哈希、bank swizzle / XOR scramble(厂商和 BIOS 可配置的混淆)、chip-select 归一化(DCT),以及 DDR5 的子通道选择。最终输出的是 DRAM 坐标:bank group、bank、row(RAS)、column(CAS)。

Spaghettifying DRAM 的工作位置就是这条管线的最底层——MCT/DCT 层。在这里,来自数据织带的物理地址被最后一次重写,变成发送到 DIMM 的原始 DRAM 坐标。所有上层安全机制——SEV、SGX、TDX、TrustZone、CCA、pKVM、CoVE、SEP、PSP、ME、T-SEG、SMRAM、C6 stash——都建立在这最后一层翻译之上。它们信任物理地址,却看不见物理地址之下发生了什么。

一位翻转,全线崩溃

Bank swizzle 是 DRAM 控制器中用于将物理地址位与 DRAM bank/rank 位进行 XOR 混淆的机制,目的是均衡内存访问分布、减少 bank 冲突。在 AMD Family 16h 处理器上,DCT 配置寄存器中有一个 bank-swizzle 模式位,而且这个寄存器没有被锁定。

翻转这一位的效果是:物理地址到 DRAM 坐标的映射关系瞬间改变。原来存储在物理地址 A 的数据,现在被系统重新映射到了 DRAM 中另一个位置——而物理地址 A 对应的 DRAM 位置上,现在放的是另一块数据。

这并不意味着系统会立即崩溃。Domas 的技巧在于速度和精准:在翻转前,禁用 AP 核心使其只有一个核心运行,预热 TLB 使其缓存目标地址的翻译结果,暖化缓存使目标数据已在缓存中,禁用中断,CLFLUSH 刷新目标地址强制 DRAM 读取,MFENCE/LFENCE 序列化内存访问确保时序可控。翻转 DCT 位后立即读取目标地址——此时读到的是「spaghettified」视图下的数据。读完后再翻转回来恢复正常的 DRAM 视图。整个过程在极短的窗口内完成,操作系统和上层安全机制完全无感知。

实际的攻击序列如下:

asm
mov  eax, [0xf80c2094]   ; prime mmio TLB
mov  eax, [0x6f800000]   ; prime target TLB
pushf                     ; preserve flags
cli                       ; interrupts off
clflush [0x6f800000]     ; evict the target, force dram read
mfence                    ; barrier
lfence                    ; reordered into spaghettified view
xor  dword [0xf80c2094], 1<<22  ; flip dct swizzle → spaghettify dram
mov  ebx, [0x6f800000]   ; fetch target in spaghettified view
xor  dword [0xf80c2094], 1<<22  ; restore dct swizzle → unscramble
mfence                    ; barrier
lfence                    ; reordered into coherent world view
popf                      ; interrupts back on

关键在于:从翻转 DCT 位到读取目标地址之间的时间窗口内,不能有任何其他 DRAM 访问。一旦操作系统的其他组件在这个窗口内访问了 DRAM,读到错误数据会导致系统立即崩溃。

解锁四层保护

基于这一技术,Domas 展示了如何读取和写入四类被硬件隔离保护的内存区域。

Platform Security Processor (PSP):AMD 的 PSP 是一个独立的 ARM 安全处理器,拥有自己的固件和加密引擎,存储在 DRAM 的保留区域中,对操作系统完全不可见。通过 spaghettified DRAM 视图,攻击者可以读取 PSP 保存的 CPU 状态——包括 GS/per-CPU 基址、CR3 页表根、APIC 基址、MTRR、保存的 RIP 等寄存器值。这些在 ring-0 层面本应不可达的内部 CPU 状态,通过 DRAM 别名路径变得可读。

System Management Mode (SMM):SMM 是 x86 上最高特权级的执行模式,比 ring-0 还高,运行在独立的 SMRAM 内存区域中。操作系统、hypervisor 甚至虚拟化扩展都无法窥探 SMRAM。spaghettified DRAM 绕过了这一隔离。

C6 DRAM:当 CPU 核心进入 C6 深度节能状态时,核心的全部状态——包括微码补丁——被保存到 DRAM 中的 fenced 保留区域。这个保存区域(C6 stash)对操作系统和 ring-0 驱动完全不可见。但通过 DRAM 别名路径,攻击者可以读取甚至修改其中保存的微码补丁。仓库中提供了完整的验证流程:dump C6 stash 中的微码体,与 /lib/firmware/amd-ucode/ 下的已知补丁比对,确认匹配。

CPU 微码:这是最危险的应用。C6 stash 中保存的微码补丁是 CPU 核心从 idle 唤醒时重新加载的版本。用同样的 DRAM 写入工具 dram_poke,攻击者可以修改这份微码副本。修改后的微码会在核心下次从 C6 状态唤醒时被执行。CPU 微码控制着指令的解码和执行——修改微码意味着可以在指令集层面植入后门,且完全存在于微码层,操作系统和安全软件无法检测。

工具链

skitter-creek-bath-salts 仓库提供了完整的工具链:

dram_read 是最基础的读取工具,接受物理地址和两个标志位 --do-swizzle / --do-bankswap,执行翻转-读取-恢复的完整序列。

dram_poke 用于写入受保护内存区域。它依赖预先求解的 spaghettification map——每个 map 是由 unspaghettify.py --save-map 生成的,其输入则是 gather_aliases.py 收集的地址别名对。map 的核心机制是 GF(2) 伪逆矩阵:对受保护范围内每个 dword 的别名地址,通过一次性计算的 GF(2) 伪逆恢复。多个在不同 (at_swizzle, at_bankswap) 组合下收集的 map 可以叠加使用,因为每种 spaghettification 会留下不同的 rank-deficient 空洞,多个 map 互相填补覆盖。

dram_dump 用于批量读取受保护内存范围,使用与 dram_poke 相同的 map 机制。

dram_carveoutsdram_state 用于定位系统中的 DRAM 保留区域和控制器状态。

整个分析流程:先用 gather_aliases.py 在不同 DCT 配置下收集地址别名对,再用 unspaghettify.py 求解物理地址到 spaghettified 地址的映射关系并保存为 map 文件,最后用 dram_dump / dram_poke 基于 map 读写受保护内存。

Skitter Creek Bath Salts — DRAM Scrambling Since 1887

目标平台与通用性

研究的目标平台是 AMD Family 16h 处理器。选择这一代的原因很具体:它是最后一代在公开数据手册中完整记录了 DRAM 控制器翻译寄存器的 AMD 架构,而且数据手册显示这些寄存器无法被锁定。从 Family 17h(Zen 架构)开始,AMD 直接从数据手册中删除了这些信息。

但这不意味着新一代处理器是安全的。Domas 明确指出:通道交织、rank 交织、bank 交织、swizzle、chip-select 归一化——所有现代内存控制器都实现了这些机制的不同版本。AMD、Intel、ARM、RISC-V,移动端、服务器端、嵌入式,底层架构形状是一样的。不同的只是寄存器的地址和具体配置。skitter-creek-bath-salts 展示的是管线的一个起点,而非终点。

同样,这也不意味着攻击仅限于 AMD。Intel 处理器的内存控制器同样存在地址重映射层,只是 Intel 在数据手册中对这些细节更加保密。ARM 和 RISC-V 平台的内存控制器设计也遵循类似的架构原则。问题的根源不在于某个特定厂商的实现缺陷,而在于一个架构假设:所有上层安全机制都信任物理地址到 DRAM 坐标的映射是不可变的。

安全影响

这项研究的核心启示在于暴露了一个架构层面的安全盲区。

现代处理器的安全架构层层堆叠:虚拟化(EPT/NPT)保护 hypervisor,TEE(SGX/TDX/SEV/TrustZone)保护可信执行环境,SMM 保护固件级代码,PSP/ME 保护管理引擎。这些机制各司其职,都在各自的层面建立了严格的隔离边界。但它们全部建立在同一个地基之上:内存控制器对物理地址的忠实翻译。

当你能在物理地址到 DRAM 坐标的最后一层翻译中动手脚,上面所有层次的隔离都失去了根基。页表说这个物理地址属于内核空间,内存控制器说没问题,但实际读写的数据已经不在那个物理地址对应的 DRAM 位置上了。SGX enclave 的数据加密了,TDX 的可信内存隔离了,SEV 的虚拟机加密了——但加密和解密发生在 DRAM 访问链路之上。在 DRAM 坐标被重映射后,加密的数据可以被定向读取,甚至可以被替换为攻击者构造的数据。

这种攻击需要 ring-0 权限(内核驱动级访问 DCT 寄存器),因此本身不是一个从用户态发起的远程攻击。它的威胁模型更接近于:恶意固件/驱动、供应链攻击后的持久化、以及硬件安全审计工具。但作为安全研究,它的价值在于揭示了一个此前未被认真对待的攻击面。整个行业将安全边界建立在物理地址的稳定性上,却从未系统性地审视物理地址之下那一层是否值得信任。

Christopher Domas 在此前的 Black Hat 演讲中反复强调一个观点:处理器不是一个可信的黑盒。我们基于厂商提供的数据手册构建安全模型,就像基于软件的 README 来判断它不是恶意程序。Spaghettifying DRAM 将这个观察推向了新的深度——即使你审查了从虚拟地址到物理地址的每一层翻译,在最底层的 DRAM 控制器中,仍然存在着未被审视的攻击面。

仓库以 MIT 许可开源,Black Hat 2026 的完整演讲视频将在会议后发布。

来源:GitHub - skitter-creek-bath-salts | Hacker News 讨论 | Christopher Domas @xoreaxeaxeax