
The first analysis framework for CPU microcode
Custom Processing Unit は、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 シェルで起動します。
EFI シェルで 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 ucode を更新します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 がコンパイルされ、適用したいマイクロコードパッチが自動的に含まれます。
file: 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 が返されます。
私たちの研究は 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},
}
論文で説明されている実験は次のコマンドで実行できます:
cpu.efi e [exp_idx]
[exp_idx]: