
The first analysis framework for CPU microcode
Custom Processing Unit ist das erste dynamische Analyse-Framework, das in der Lage ist, CPU-Mikrocode auf Software-Ebene zu hooken, zu patchen und zu tracen.
Es funktioniert durch die Nutzung undokumentierter Befehle in Intel-CPUs, die Zugriff auf den CRBUS ermöglichen. Mithilfe unseres Mikrocode-Decompilers haben wir per Reverse Engineering herausgefunden, wie die CPU den CRBUS nutzt, und durch Nachbildung der Interaktionen haben wir volle Kontrolle über die CPU.
Das Static-Analysis-Framework finden Sie als Subtree in diesem Ordner oder unter https://github.com/pietroborrello/ghidra-atom-microcode.
Schauen Sie sich unsere Folien zu dieser Arbeit hier an.
Hinweis: Custom Processing Unit erfordert eine Red-Unlocked-CPU: Derzeit haben nur Goldmont-CPUs (GLM) einen öffentlichen Red Unlock. Wir haben Gigabyte GB-BPCE-3350C mit CPU-Stepping 0x9 und 0xa getestet (CPUID 0x000506C9 und 0x000506CA).
Custom Processing Unit besteht aus einer UEFI-Anwendung und einigen Bibliotheken. Die UEFI-Anwendung interagiert mit der GLM-CPU, während die Bibliotheken verschiedene Helfer bereitstellen, um Mikrocode in die UEFI-Anwendung zu kompilieren und ihre Ausgabe zu analysieren.
GNU_EFI_DIR=<path_to_gnu_efi> make
Dies erstellt die Mikrocode-Quelldateien und die UEFI-Anwendung in cpu.efi. Kopieren Sie cpu.efi in den Ordner \EFI\ des USB-Sticks, stecken Sie ihn in den GLM und booten Sie in die EFI-Shell.
Führen Sie in der EFI-Shell map -r aus, um das USB-Stick-Gerät zu identifizieren, und <deviceid>: zum Einhängen.
Führen Sie ./cpu.efi aus, um die Hilfe anzuzeigen:
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 bietet Helfer, um einfache Anweisungen von der Befehlszeile aus auszuführen:
cpu bietet Schnittstellen zu komplexen CPU-Routinen, deren Ausführung zur Untersuchung des CPU-Verhaltens interessant ist:
u: aktualisiert das CPU-Ucode mit dem bereitgestellten (signierten) Patchf: sammelt Performance-Zähler während der Ausführung von Mikrocodecpu bietet rohe Schnittstellen zu den undokumentierten Anweisungen udbrd und udbgwr.
Die interessantesten Befehle, die sie bereitstellen, sind:
cpu stellt LDAT-Zugriffsroutinen zum Lesen und Schreiben bereit. Geben Sie die Parameter [port] [array] [bank] [idx] [addr] an, um dort zu lesen oder zu schreiben.
Interessante Ports sind:
Bitte beachten Sie, dass der Zugriff auf einige dieser internen Komponenten dazu führen kann, dass die CPU einfriert.
cpu bietet Funktionen zum Installieren von Patches im Mikrocode.
bios/ucode_patches/ucode_patch.u (sehen Sie sich die anderen Patches als Beispiele an).cpu.efi p aus, um den Patch an der in .org angegebenen Adresse zu installieren.Beachten Sie, dass im Mikrocode nur die Adressen zwischen 0x7c00 und 0x7e00 beschreibbar und für Patches sinnvoll sind.
Bei Ausführung von cpu.efi x wird zusätzlich der gepatchte Mikrocode ausgeführt und die Register rax, rbx, rcx, rdx als Ergebnis ausgegeben.
Um Mikrocode automatisch bei bestimmten CPU-Ereignissen oder Mikrocode-Punkten auszuführen, nutzt cpu das Match & Patch.
Es definiert eine Mikrocode-Adresse, die gehookt werden soll, und die Mikrocode-Adresse, zu der beim Auslösen des Hooks gesprungen werden soll.
z: setzt das gesamte Match & Patch zurück.h: installiert einen Hook, gegeben einen Index (0-0x20), eine zu hookende Adresse (0-0x7c00) und eine Zieladresse für die Ausführung (0x7c00-0x7e00).Durch die Installation mehrerer Hooks und die kontinuierliche Ausführung einer Anweisung kann cpu die von einer solchen Anweisung durchgeführten Mikrooperationen aufzeichnen und ausgeben. Zum Aufzeichnen:
// [TRACED INSTRUCTION HERE] in get_trace_clock_at().cpu.efi m auf.
Es wird eine Datei trace.txt erstellt, die alle getroffenen Adressen enthält.uasm-lib/uasm.py -t trace.txt > parsed_trace.txt aus.
Es wird ein vollständiges Trace des während der Anweisung ausgeführten Mikrocodes erzeugt.Beachten Sie, dass uasm.py die Dateien ms_arrayX.txt in seinem Ordner verwendet, um eine Disassemblierung der ausgeführten Mikroinstruktionen zu erzeugen. Diese gelten für GLM mit Stepping 0x9 (CPUID 0x000506C9). Bitte erzeugen Sie die passenden Arrays, falls Sie ein anderes Stepping haben.
Sie können zu diesem Zweck die LDAT-Dump-Funktionen nutzen.
Die CPU besitzt verschiedene aus Sicht der Architektur unzugängliche Puffer, für die wir Routinen zum Dumpen bereitstellen:
smm: SMROM (oder jede andere Adresse, während der SMM-Schutz deaktiviert ist)rom: internes ROMimms: hartkodierte CPU-Immediatesmsrs: interne MSR-KonfigurationenWir stellen einen Assembler bereit, der Header-Dateien erzeugt, die in die UEFI-Anwendung cpu.efi kompiliert werden.
Schauen Sie sich für die Syntax die bereitgestellten Patches in bios/ucode_patches an.
Er unterstützt einfache Operationen und Labels.
Assemblieren Sie einen Mikrocode-Patch mit uasm.py -i ucode_patch.u -o ucode_patch.h.
cpu.efi wird kompiliert und den Mikrocode-Patch, den Sie anwenden möchten, automatisch einbinden.
Datei: code_patch.u
.org 0x7c00
rax:= ZEROEXT_DSZ32(0x00001337)
rbx:= ZEROEXT_DSZ32(0x00001337)
rcx:= ZEROEXT_DSZ32(0x00001337)
rdx:= ZEROEXT_DSZ32(0x00001337)
Kompilieren Sie neu und führen Sie dann auf dem GLM aus:
cpu.efi z # zero out match & patch
cpu.efi p # apply the patch
cpu.efi h 0 0x0428 0x7c00 # rdrand entry point
Nun gibt rdrand jedes Mal 0x1337 in den Registern zurück.
Unsere Arbeit wurde in einem Paper auf dem WOOT 2023 veröffentlicht:
@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},
}
Die im Paper beschriebenen Experimente können mit folgendem Befehl ausgeführt werden:
cpu.efi e [exp_idx]
wobei [exp_idx]: