
The first analysis framework for CPU microcode
Custom Processing Unit est le premier framework d'analyse dynamique capable d'intercepter, de patcher et de tracer le microcode du CPU au niveau logiciel.
Il fonctionne en exploitant des instructions non documentées des CPU Intel qui permettent d'accéder au CRBUS. Grâce à notre décompilateur de microcode, nous avons rétro-conçu la façon dont le CPU utilise le CRBUS et, en reproduisant ces interactions, nous obtenons un contrôle total sur le CPU.
Le framework d'analyse statique est disponible sous forme de sous-arbre dans ce dossier, ou sur https://github.com/pietroborrello/ghidra-atom-microcode.
Consultez nos diapositives décrivant ces travaux ici.
Remarque : Custom Processing Unit nécessite un CPU Red-Unlocked : actuellement, seuls les CPU Goldmont (GLM) disposent d'un Red Unlock public. Nous avons testé le Gigabyte GB-BPCE-3350C avec les steppings 0x9 et 0xa (cpuid 0x000506C9 et 0x000506CA).
Custom Processing Unit est composé d'une application UEFI et de quelques bibliothèques. L'application UEFI interagit avec le CPU GLM, tandis que les bibliothèques fournissent différentes aides pour compiler le microcode dans l'application UEFI et analyser sa sortie.
GNU_EFI_DIR=<path_to_gnu_efi> make
Cela compilera les fichiers source du microcode et l'application UEFI en cpu.efi.
Copiez cpu.efi dans le dossier \EFI\ de la clé USB, branchez-la sur le GLM et démarrez dans le shell EFI.
Exécutez map -r dans le shell EFI pour identifier le périphérique de la clé USB et <deviceid>: pour la monter.
Exécutez ./cpu.efi pour afficher l'aide :
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 fournit des aides pour exécuter des instructions simples depuis la ligne de commande :
cpu fournit des interfaces vers des routines CPU complexes qu'il est intéressant d'exécuter pour étudier le comportement du CPU :
u : met à jour le microcode du CPU avec le patch (signé) fournif : collecte les compteurs de performance pendant l'exécution du microcodecpu fournit des interfaces brutes vers les instructions non documentées udbrd et udbgwr.
Les commandes les plus intéressantes qu'elles fournissent sont :
cpu expose des routines d'accès LDAT pour lire et écrire. Spécifiez les paramètres [port] [array] [bank] [idx] [addr] pour lire ou écrire à cet endroit.
Les ports intéressants sont :
Veuillez noter que l'accès à certains de ces composants internes peut geler le CPU.
cpu fournit des fonctionnalités pour installer des patchs dans le microcode.
bios/ucode_patches/ucode_patch.u (regardez les autres patchs pour des exemples)cpu.efi p pour installer le patch à l'adresse fournie dans .org.Notez que dans le microcode, seules les adresses entre 0x7c00 et 0x7e00 sont inscriptibles et pertinentes pour le patch.
En exécutant cpu.efi x, le microcode patché sera également exécuté et les registres rax, rbx, rcx, rdx seront affichés en résultat.
Pour exécuter automatiquement du microcode lors de certains événements CPU ou points du microcode, cpu exploite le mécanisme Match and Patch.
Il définit une adresse de microcode à intercepter et l'adresse de microcode vers laquelle sauter lorsque le hook est déclenché.
z : réinitialise tous les match & patch.h : installe un hook, avec un index (0-0x20), une adresse à intercepter (0-0x7c00) et une adresse cible à exécuter (0x7c00-0x7e00).En installant plusieurs hooks et en exécutant une instruction en continu, cpu est capable de tracer les micro-opérations effectuées par cette instruction et de les extraire. Pour tracer :
// [TRACED INSTRUCTION HERE] dans get_trace_clock_at().cpu.efi m.
Cela créera un fichier trace.txt contenant toutes les adresses qui ont été touchées.uasm-lib/uasm.py -t trace.txt > parsed_trace.txt.
Cela générera une trace complète du microcode exécuté pendant l'instruction.Notez que uasm.py utilisera les fichiers ms_arrayX.txt de son dossier pour générer un désassemblage des micro-instructions exécutées. Ceux-ci concernent le GLM avec le stepping 0x9 (cpuid 0x000506C9). Veuillez générer les tableaux appropriés si vous avez un stepping différent.
Vous pouvez utiliser les fonctionnalités de dump LDAT à cette fin.
Le CPU possède différents tampons inaccessibles depuis l'architecture, pour lesquels nous fournissons des routines de dump :
smm : SMROM (ou toute autre adresse tout en désactivant la protection SMM)rom : ROM interneimms : immédiats codés en dur du CPUmsrs : configurations internes des MSRNous fournissons un assembleur qui génère des fichiers d'en-tête à compiler dans l'application UEFI cpu.efi.
Regardez les patches fournis dans bios/ucode_patches pour la syntaxe.
Il prend en charge les opérations simples et les étiquettes.
Assemblez un patch de microcode avec uasm.py -i ucode_patch.u -o ucode_patch.h.
cpu.efi sera compilé et inclura automatiquement le patch de microcode que vous souhaitez appliquer.
fichier : code_patch.u
.org 0x7c00
rax:= ZEROEXT_DSZ32(0x00001337)
rbx:= ZEROEXT_DSZ32(0x00001337)
rcx:= ZEROEXT_DSZ32(0x00001337)
rdx:= ZEROEXT_DSZ32(0x00001337)
recompilez, puis exécutez dans le GLM :
cpu.efi z # zero out match & patch
cpu.efi p # apply the patch
cpu.efi h 0 0x0428 0x7c00 # rdrand entry point
désormais, chaque fois que rdrand sera exécuté, il retournera 0x1337 dans les registres.
Nos travaux ont été publiés dans un article à WOOT 2023 :
@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},
}
Les expériences décrites dans l'article peuvent être exécutées avec :
cpu.efi e [exp_idx]
avec [exp_idx] :