Guest page-table walk (GVA->GPA software translation)¶
MMU'yu guest dışından software'de emule etmek: guest CR3'ten başlayıp virtual address'i PML4/PDPT/PD/PT index'lerine decode ederek ve her level'ın entry'sini takip ederek bir guest virtual address'i guest-physical address'e çözmek.
Mechanism¶
Note
Hardware paging, bir virtual address'i CR3'te köklenen bir table ağacında walk ederek translate eder. Guest physical memory'ye sahip olan ama guest'in CPU state'ine sahip olmayan bir introspection engine, herhangi bir guest virtual address'i anlamlandırmak için bu walk'u software'de yeniden üretmek zorundadır. Dayandığı invariant, x86-64 paging format'ının sabit ve architectural olmasıdır: 48-bit canonical bir VA, dört adet 9-bit index artı bir 12-bit offset'e bölünür ve her table 512 adet sekiz-byte entry'dir. Root verildiğinde (CR3 — bkz. guest CR3 / kpgd resolution) ve guest-physical frame'leri okuma yeteneğiyle, engine GVA -> GPA'yı deterministik biçimde çözebilir.
Bu, semantic gap'i köprülemenin ikinci yarısıdır: CR3 root'u verir, walk mapping'i verir. Bu, MMU'nun saf software emulation'ıdır — guest işbirliği yok, guest içinde agent yok. (Virtualization altında gerçek path iki boyutludur — guest table'ları bir GPA üretir, EPT bunu host physical address'e translate eder — ama introspection engine tipik olarak guest-physical frame'leri doğrudan okur, dolayısıyla bir GPA'ya inmek için yalnızca guest table'larını walk eder.)
Walkthrough¶
Address decomposition (4 KB page'ler, x86-64). Bir va virtual address'i için:
PML4 index = (va >> 39) & 0x1FF # bits 47:39
PDPT index = (va >> 30) & 0x1FF # bits 38:30
PD index = (va >> 21) & 0x1FF # bits 29:21
PT index = (va >> 12) & 0x1FF # bits 20:12
offset = va & 0xFFF # bits 11:0
Walk. Her level: table'ın physical base'ini al, index * 8 ekle (entry'ler 8
byte — eşdeğer olarak index'i 3 sola kaydır), 8-byte entry'yi oku, Present'i kontrol
et ve frame bit'lerini bir sonraki table'ın base'i olarak al.
pml4_base = CR3 & 0x000FFFFFFFFFF000
pml4e = read_gpa(pml4_base + pml4_index*8)
if not (pml4e & 1): fault # Present bit
pdpt_base = pml4e & 0x000FFFFFFFFFF000
pdpte = read_gpa(pdpt_base + pdpt_index*8)
if not (pdpte & 1): fault
if (pdpte & PS): gpa = (pdpte & ~((1<<30)-1)) | (va & ((1<<30)-1)) # 1 GiB page
pd_base = pdpte & 0x000FFFFFFFFFF000
pde = read_gpa(pd_base + pd_index*8)
if not (pde & 1): fault
if (pde & PS): gpa = (pde & ~((1<<21)-1)) | (va & ((1<<21)-1)) # 2 MiB page
pt_base = pde & 0x000FFFFFFFFFF000
pte = read_gpa(pt_base + pt_index*8)
if not (pte & 1): fault
page_frame = pte & 0x000FFFFFFFFFF000
gpa = page_frame | offset # 4 KiB page
PS (page-size) bit'i walk'u PDPT (1 GiB) veya PD (2 MiB) level'ında kısa devre
yaptırır; aksi hâlde tam dört-level'lı walk bir 4 KiB frame verir. Her level bir
guest-physical read'e mal olur, dolayısıyla cold bir 4 KiB translation dört read'tir
— hardware page walk'un software karşılığı.
Warning
Software walk'lar, gerçeklikten desync olmamak için MMU ile aynı edge case'lere
uymalıdır: large-page (PS) entry'leri, canonical-address gerekliliği (bit
63:48, bit 47'yi sign-extend eder), KPTI'ın split user/kernel root'ları ve
NX/permission bit'leri. Ayrıca canlı guest'le race ederler — guest, engine iki
level'ı okurken arada bir page table'ı değiştirebilir (bir TOCTOU window'u), bu
yüzden güvenilir sonuçlar için tutarlı bir snapshot ya da paused bir vCPU gerekir.
Detection¶
- Walk tamamen VMX root'tan guest-physical memory'nin pasif read'lerinden ibarettir; guest tarafından görünür bir architectural state üretmez ve guest tarafından doğrudan tespit edilemez.
- Bir guest introspection'ı yalnızca dolaylı olarak çıkarabilir (örn. walk'lar CR3-load exiting ile sürülüyorsa context-switch latency'si), walk'un kendisini değil.
Mitigation¶
(Savunma açısından bu walk introspection capability'sidir; "mitigation" görünümü anti-introspection'dır.)
- Bir guest rootkit, naif bir walker'ı yanıltmak için page-table entry'lerini forge edebilir veya hızla mutate edebilir; sağlam engine'ler table'ların snapshot'ını alır, vCPU'yu pause eder ya da yeniden validate eder.
- Her paging feature'ına uyun (large page, NX, KPTI, canonical check'ler); böylece bir guest, hardware'in onurlandıracağı ama hatalı bir walker'ın atlayacağı bir mapping'i gizleyemez.
- Walk'u guest-state reconstruction ile eşleştirin; böylece çözülen frame'leri ham byte'lara güvenmek yerine bağlam içinde yorumlayın.