Skip to content

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:

  1. 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 0xCC alignment 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.

  1. 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/rdtscp ile, 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.

  1. 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