
The first analysis framework for CPU microcode
Custom Processing Unit es el primer framework de análisis dinámico capaz de enganchar (hook), parchear y rastrear el microcódigo de la CPU a nivel de software.
Funciona aprovechando instrucciones no documentadas en CPUs Intel que permiten el acceso al CRBUS. Usando nuestro decompilador de microcódigo hicimos ingeniería inversa de cómo la CPU usa el CRBUS y, replicando las interacciones, tenemos control total sobre la CPU.
Encuentre el framework de análisis estático como subárbol en la carpeta this, o en https://github.com/pietroborrello/ghidra-atom-microcode.
Consulte nuestras diapositivas que describen este trabajo aquí.
Nota: Custom Processing Unit requiere una CPU con Red Unlock: actualmente, solo las CPUs Goldmont (GLM) tienen un Red Unlock público. Probamos la Gigabyte GB-BPCE-3350C con CPU stepping 0x9 y 0xa (cpuid 0x000506C9 y 0x000506CA).
Custom Processing Unit está compuesto por una aplicación UEFI y algunas bibliotecas. La aplicación UEFI interactúa con la CPU GLM, mientras que las bibliotecas proporcionan diferentes ayudantes para compilar microcódigo en la aplicación UEFI y analizar su salida.
GNU_EFI_DIR=<path_to_gnu_efi> make
Esto compilará los archivos de microcódigo fuente y la aplicación UEFI en cpu.efi.
Copie cpu.efi en la carpeta \EFI\ de la llave USB, conéctela en la GLM y arranque en el shell EFI.
Ejecute map -r en el shell EFI para identificar el dispositivo de la llave USB y <deviceid>: para montarla.
Ejecute ./cpu.efi para imprimir la ayuda:
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 proporciona ayudantes para ejecutar instrucciones simples desde la línea de comandos:
cpu proporciona interfaces para rutinas complejas de la CPU que son interesantes de ejecutar para estudiar el comportamiento de la CPU:
u: actualiza el ucode de la CPU con el parche (firmado) proporcionado.f: recopila contadores de rendimiento mientras se ejecuta el microcódigo.cpu proporciona interfaces en bruto para las instrucciones no documentadas udbrd y udbgwr.
Los comandos más interesantes que proporcionan son:
cpu expone rutinas de acceso a LDAT para lectura y escritura. Especifique los parámetros [port] [array] [bank] [idx] [addr] para leer o escribir allí.
Los puertos interesantes son:
Tenga en cuenta que acceder a algunos de estos componentes internos puede provocar que la CPU se congele.
cpu proporciona funcionalidades para instalar parches en el microcódigo.
bios/ucode_patches/ucode_patch.u (mire los otros parches como ejemplo).cpu.efi p para instalar el parche en la dirección proporcionada en .org.Tenga en cuenta que, en el microcódigo, solo las direcciones entre 0x7c00 y 0x7e00 son escribibles y significativas para parchear.
Si ejecuta cpu.efi x, también ejecutará el microcódigo parcheado e imprimirá los registros rax, rbx, rcx, rdx como resultado.
Para ejecutar automáticamente microcódigo en ciertos eventos de la CPU o puntos del microcódigo, cpu utiliza el mecanismo Match & Patch.
Define una dirección de microcódigo a enganchar (hook) y la dirección de microcódigo a la que saltar cuando se dispara el hook.
z: restablece todo el match & patch.h: instala un hook, dado un índice (0-0x20), una dirección a enganchar (0-0x7c00) y una dirección de destino a ejecutar (0x7c00-0x7e00).Al instalar múltiples hooks y ejecutar continuamente una instrucción, cpu es capaz de trazar las microoperaciones realizadas por dicha instrucción y volcarlas. Para trazar:
// [TRACED INSTRUCTION HERE] en get_trace_clock_at().cpu.efi m.
Se creará un archivo trace.txt que contiene todas las direcciones que han sido alcanzadas.uasm-lib/uasm.py -t trace.txt > parsed_trace.txt.
Generará un trazado completo del microcódigo ejecutado durante la instrucción.Tenga en cuenta que uasm.py aprovechará los archivos ms_arrayX.txt en su carpeta para generar un desensamblado de las microinstrucciones ejecutadas. Estos son para GLM con stepping 0x9 (cpuid 0x000506C9). Genere las matrices adecuadas en caso de tener un stepping diferente.
Puede usar las funcionalidades de volcado LDAT para este propósito.
La CPU tiene diferentes búferes inaccesibles desde la arquitectura, para los cuales proporcionamos rutinas para volcar:
smm: SMROM (o cualquier otra dirección mientras se desactiva la protección SMM)rom: ROM internaimms: inmediatos fijos de la CPUmsrs: configuraciones internas de MSRsProporcionamos un ensamblador que genera archivos de cabecera para compilarse en la aplicación UEFI cpu.efi.
Mire los parches proporcionados en bios/ucode_patches para conocer la sintaxis.
Soporta operaciones simples y etiquetas.
Ensamble un parche de microcódigo con uasm.py -i ucode_patch.u -o ucode_patch.h.
cpu.efi se compilará e incluirá automáticamente el parche de microcódigo que desee aplicar.
archivo: code_patch.u
.org 0x7c00
rax:= ZEROEXT_DSZ32(0x00001337)
rbx:= ZEROEXT_DSZ32(0x00001337)
rcx:= ZEROEXT_DSZ32(0x00001337)
rdx:= ZEROEXT_DSZ32(0x00001337)
recompile y luego ejecute en la GLM:
cpu.efi z # zero out match & patch
cpu.efi p # apply the patch
cpu.efi h 0 0x0428 0x7c00 # rdrand entry point
ahora cada vez que se ejecute rdrand, devolverá 0x1337 en los registros.
Nuestro trabajo fue publicado en un paper en 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},
}
Los experimentos descritos en el paper se pueden ejecutar con:
cpu.efi e [exp_idx]
con [exp_idx]: