
The first analysis framework for CPU microcode
Custom Processing Unit은 CPU 마이크로코드를 소프트웨어 수준에서 훅(hook)하고, 패치하고, 추적할 수 있는 최초의 동적 분석 프레임워크입니다.
이는 Intel CPU에서 CRBUS에 접근할 수 있게 해주는 문서화되지 않은 명령어를 활용하여 동작합니다. Microrocode 디컴파일러를 사용하여 CPU가 CRBUS를 사용하는 방식을 리버스 엔지니어링했으며, 해당 상호작용을 재현함으로써 CPU를 완전히 제어할 수 있습니다.
정적 분석 프레임워크는 이 폴더의 서브트리에서 찾거나 https://github.com/pietroborrello/ghidra-atom-microcode 에서 확인할 수 있습니다.
이 작업을 설명하는 슬라이드는 여기에서 확인하세요.
참고: Custom Processing Unit은 Red-Unlocked CPU가 필요합니다. 현재 Goldmont CPU(GLM)만 공개된 Red Unlock이 있습니다. 우리는 CPU 스테핑 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: 모든 match & patch를 재설정합니다.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 파일을 활용하여 실행된 마이크로명령어의 디스어셈블리를 생성합니다. 이는 스테핑 0x9(cpuid 0x000506C9)인 GLM용입니다. 스테핑이 다른 경우 적절한 배열을 생성하세요.
이 목적을 위해 LDAT 덤프 기능을 사용할 수 있습니다.
CPU에는 아키텍처에서 접근할 수 없는 다양한 버퍼가 있으며, 이를 덤프하는 루틴을 제공합니다:
smm: SMROM(SMM 보호를 비활성화한 상태에서 다른 주소도 가능)rom: 내부 ROMimms: CPU 하드코딩된 즉시값msrs: 내부 MSRs 구성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 # match & patch를 0으로 초기화
cpu.efi p # 패치 적용
cpu.efi h 0 0x0428 0x7c00 # rdrand 진입점
이제 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]: