
The first analysis framework for CPU microcode
Custom Processing Unit 是首个能够在软件层面挂钩(hook)、修补(patch)和追踪 CPU 微码的动态分析框架。
它利用 Intel CPU 中允许访问 CRBUS 的未公开指令来工作。借助我们的微码反编译器,我们对 CPU 如何使用 CRBUS 进行了逆向工程,并通过复现这些交互来实现对 CPU 的完全控制。
静态分析框架以子树形式位于此文件夹中,或参见 https://github.com/pietroborrello/ghidra-atom-microcode。
查看这里的幻灯片以了解该工作的介绍。
注意:Custom Processing Unit 需要一颗已红锁解锁(Red-Unlocked)的 CPU:目前,只有 Goldmont CPU (GLM) 具有公开的 Red Unlock。我们已在 CPU stepping 为 0x9 和 0xa(cpuid 0x000506C9 与 0x000506CA)的 Gigabyte GB-BPCE-3350C 上进行了测试。
Custom Processing Unit 由一个 UEFI 应用程序和若干库组成。UEFI 应用程序负责与 GLM CPU 交互,而库则提供各种辅助功能,用于将微码编译进 UEFI 应用程序并分析其输出。
GNU_EFI_DIR=<path_to_gnu_efi> make
这将把源码微码文件和 UEFI 应用程序构建为 cpu.efi。
将 cpu.efi 复制到 USB 闪存盘的 \EFI\ 文件夹中,将其插入 GLM 并启动进入 EFI shell。
在 EFI shell 中运行 map -r 以识别 USB 闪存盘设备,并使用 <deviceid>: 挂载它。
运行 ./cpu.efi 以打印帮助信息:
Usage:
patch: <tool> p
patch & exec: <tool> x
perf: <tool> f
zero out m&p: <tool> z
hook: <tool> h [m&p idx] [uop addr] [patch addr]
template: <tool> m
dump imms: <tool> di
dump rom: <tool> dr
dump msrs: <tool> dm
dump SMM: <tool> ds [address] [size]
cpuid: <tool> c [rax] [rcx]
rdmsr: <tool> rm [msr]
wrmsr: <tool> wm [msr]
read: <tool> r [cmd] [addr]
write: <tool> w [cmd] [addr] [value]
invoke: <tool> i [addr]
update ucode: <tool> u [size]
ldat read: <tool> lr [port] [array] [bank] [idx] [addr] [optional size]
ldat write: <tool> lw [port] [array] [bank] [idx] [addr] [value]
cpu 提供了从命令行运行简单指令的辅助功能:
cpu 为一些有趣的复杂 CPU 例程提供了接口,可用于研究 CPU 行为:
u:使用提供的(已签名)补丁更新 CPU 微码f:运行微码时收集性能计数器cpu 为未公开指令 udbrd 和 udbgwr 提供了原始接口。
其中最有趣的命令包括:
cpu 暴露了用于读写的 LDAT 访问例程。在那里读写时需要指定参数 [port] [array] [bank] [idx] [addr]。
值得关注的端口包括:
请注意,访问其中一些内部组件可能会导致 CPU 死机。
cpu 提供了在微码中安装补丁的功能。
bios/ucode_patches/ucode_patch.u 中编写微码补丁(参考其他补丁的示例)。cpu.efi p,将补丁安装到 .org 中提供的地址。请注意,在微码中,只有 0x7c00 和 0x7e00 之间的地址是可写的,并且对修补有意义。
运行 cpu.efi x 时,它还会执行已修补的微码,并打印 rax、rbx、rcx、rdx 寄存器作为结果。
为了在特定 CPU 事件或微码点自动执行微码,cpu 利用了匹配与修补(Match and Patch)机制。
它定义了要挂钩的微码地址,以及挂钩被触发时要跳转到的微码地址。
z:重置所有匹配与修补。h:安装一个挂钩,需要给定索引(0-0x20)、要挂钩的地址(0-0x7c00)和要执行的目标地址(0x7c00-0x7e00)。通过安装多个挂钩并持续执行某条指令,cpu 能够追踪该指令所执行的微操作并转储它们。追踪步骤如下:
get_trace_clock_at() 中的 // [TRACED INSTRUCTION HERE] 之后写入要追踪的指令。cpu.efi m 进行追踪。
它会创建一个包含所有被命中地址的 trace.txt 文件。uasm-lib/uasm.py -t trace.txt > parsed_trace.txt。
它将生成指令执行期间所运行微码的完整跟踪记录。请注意,uasm.py 将利用其文件夹中的 ms_arrayX.txt 文件为所执行的微指令生成反汇编代码。这些文件适用于 stepping 为 0x9(cpuid 0x000506C9)的 GLM。如果你的 stepping 不同,请生成相应的数组。
你可以使用 LDAT 转储功能来实现这一目的。
CPU 具有架构上不可访问的各种缓冲区,我们为此提供了转储例程:
smm:SMROM(或在禁用 SMM 保护时的任何其他地址)rom:内部 ROMimms:CPU 硬编码立即数msrs:内部 MSR 配置我们提供了一个汇编器,用于生成可编译进 cpu.efi UEFI 应用程序的头文件。
请查看 bios/ucode_patches 中提供的补丁以了解语法。
它支持简单操作和标签。
使用 uasm.py -i ucode_patch.u -o ucode_patch.h 汇编微码补丁。
cpu.efi 在编译时将自动包含你想要应用的微码补丁。
文件:code_patch.u
.org 0x7c00
rax:= ZEROEXT_DSZ32(0x00001337)
rbx:= ZEROEXT_DSZ32(0x00001337)
rcx:= ZEROEXT_DSZ32(0x00001337)
rdx:= ZEROEXT_DSZ32(0x00001337)
重新编译,然后在 GLM 中运行:
cpu.efi z # zero out match & patch
cpu.efi p # apply the patch
cpu.efi h 0 0x0428 0x7c00 # rdrand entry point
现在每次执行 rdrand 时,它都会在寄存器中返回 0x1337。
@inproceedings{Borrello2023CustomProcessingUnit,
title = {{CustomProcessingUnit}: Reverse Engineering and Customization of Intel Microcode},
author = {Borrello, Pietro and Easdon, Catherine and Schwarzl, Martin and Czerny, Roland and Schwarz, Michael},
booktitle = {IEEE Workshop on Offensive Technologies (WOOT 23)},
year = {2023},
}
论文中描述的实验可以通过以下方式运行:
cpu.efi e [exp_idx]
其中 [exp_idx] 为: