Hypervisor-assisted hook detection (split-view inconsistency)¶
EPT/altp2m stealth hook'larını guest içinden tespit etmek: bir page'in okunduğu hâli ile execute edildiği hâli arasındaki divergence'ı ortaya çıkararak ya da bir split view'ın yol açtığı VM-exit'leri timing'leyerek.
Mechanism¶
Note
EPT-based stealth hook'lar, bir EPT (Intel) ya da NPT (AMD) leaf entry'sinin bir
guest-physical frame üzerinde bağımsız Read, Write ve Execute permission
bit'leri taşımasına dayanır (bkz. EPT split). Host tek bir GFN için
iki machine frame tutar: bir clean frame (orijinal byte'lar, read'lere servis edilir)
ve bir hooked frame (örn. 0xCC/int3 ya da bir JMP detour, instruction fetch'lere
servis edilir). Bu bit'lerin sahibi second-level translation olduğu için, hooked
adresi guest içinden okuyan biri clean byte'ları görürken CPU hooked byte'ları fetch
edip execute eder. Bu asimetri — code-read ile code-exec arasındaki inconsistency —
sıradan memory integrity check'lerine görünmezdir, ama bir detector'ın tam da yüzeye
çıkarmaya çalıştığı özelliktir.
Bir guest EPT state'ini doğrudan okuyamaz. Bu yüzden detection dolaylı çalışır: split view'ının ya da onu sürdürmenin maliyetinin gözlemlenebilir hâle geldiği bir durum kurarak — ya hypervisor'ın consistent tutmayı başaramadığı bir read/exec divergence'ı provoke ederek ya da split'in eklediği fazladan VM-exit'leri ölçerek.
Walkthrough¶
momo5502'nin analizinden üç public detection stratejisi, artan robustness sırasıyla:
- Write-to-padding (split-consistency) check. Birçok hook engine clean page'i
read/write, hooked page'i execute-only işaretler ve yalnızca hook'u hooked
frame'e propagate eder — guest write'larını oraya propagate etmez. Bir detector
kullanılmayan padding'e (fonksiyonlar arasındaki
0xCCalignment byte'larına) bir sentinel yazar, sonra o byte'ın execute edilmesini ya da başka şekilde yansıtılmasını ayarlar. Eğer write clean frame'e düşer ama execution stale bir hooked frame'den çekilirse, bu divergence bir split'i ele verir.
!!! warning Bu "kötü ya da lazy bir implementation'a dayanır" — doğru bir engine write'ları her iki frame'e de mirror eder (ya da write'ı clean page'e karşı single-step'ler) ve bu check'i etkisiz kılar.
- RDTSC timing check. Hooked, execute-trapped bir page'in her fetch'i (ya da
permission'ları flip eden bir split page'in her read'i) bir VM-exit'e mal olur.
Detector,
rdtsc/rdtscpile, instruction byte'larını okuyan bir loop ile aynı page üzerinde read ve execute'u alternate eden bir loop'u timing'ler:
uint64_t t0 = __rdtscp(&aux);
for (i = 0; i < N; ++i) { read_page(target); } // baseline
uint64_t t1 = __rdtscp(&aux);
for (i = 0; i < N; ++i) { exec_then_read(target); }// provokes exits
uint64_t t2 = __rdtscp(&aux);
Unhooked memory'de iki faz arasında yalnızca küçük bir fark (momo5502'nin testlerinde ~2x mertebesinde) gözlenirken, EPT-hooked bir page'de divergence, VM-exit ve page-swap overhead'i yüzünden önemli ölçüde daha yüksek maliyetler raporlanır — momo5502'nin kendi ölçümlerinde "10, hatta 100 kat fazla" mertebesine varır (bkz. References, momo5502, "Detecting Hypervisor-assisted Hooking"). Bu rakamlar tek bir test setup'ından gelen indicative değerlerdir, normatif değil: kesin oranlar donanım, hypervisor ve workload'a göre değişir.
!!! warning
rdtsc'nin kendisi virtualize edilebilir: bir hypervisor VM-exit handler'ında
harcadığı cycle'ları çıkarabilir, böylece guest'in delta'sı normal görünür. Bu
yüzden saf timestamp check'leri authoritative değildir.
- Thread-race (cross-core) timing. Yalan söyleyen bir
rdtsc'den kaçınmak için senkronize iki thread farklı physical core'lara pin'lenir: biri software clock olarak paylaşılan bir counter'ı artırırken diğeri ölçülen operasyonu execute eder. Tamamlanma anındaki counter değeri time ölçüsüdür. Clock ayrı bir core'da yaşadığı için hypervisor onu kolayca geri saramaz; momo5502 bunun "mitigate etmesi kesinlikle trivial olmadığını" not eder.
Beklenen observable: plain read'ler altında özdeş test eden ama read/exec alternasyonu ya da cross-core timing altında keskin biçimde ayrışan bir baseline page ile bir hooked page.
Detection¶
Bu zaten bir detection technique'i. Tersine, bir hypervisor da bu detector'ları
fingerprint'leyebilir: küçük bir code window üzerinde sıkı rdtsc/counter loop'ları ya
da function padding'ine tekrarlı write'lar, split view arayan bir guest'in karakteristik
davranışıdır.
Mitigation¶
- Defensive bir monitor için, guest write'larını her iki frame'e de propagate et (ya da write'ları trap'leyip clean page'e karşı servis et) ki write-check asla diverge etmesin.
- Mümkün olduğunda timing'i normalize et (virtualize edilmiş
rdtsc'den exit maliyetini çıkar), ama cross-core counter race'lerinin ve APERF/IET tarzı instruction-time ölçümlerinin mükemmel şekilde gizlenemeyeceğini kabul et — kararlı bir guest'e karşı mükemmel stealth ulaşılabilir değildir.
References¶
- Maurice Heumann (momo5502), "Detecting Hypervisor-assisted Hooking" — https://momo5502.com/posts/2022-05-02-detecting-hypervisor-assisted-hooking/
- secret club, "Hypervisors for Memory Introspection and Reverse Engineering" — https://secret.club/2025/06/02/hypervisors-for-memory-introspection-and-reverse-engineering.html