RDTSC timing detection (rdtsc;cpuid;rdtsc delta)¶
Bir guest, zorlanmış bir VM-exit'in (örn. CPUID) cycle maliyetini onu RDTSC ile bracket'leyerek ölçer; native hardware'a kıyasla şişmiş delta, bir hypervisor altında çalıştığını ele verir.
Mechanism¶
Note
RDTSC, processor'ın 64-bit Time-Stamp Counter'ını (reset'ten beri geçen
cycle'lar) EDX:EAX'e okur. CPUID, Intel VT-x / AMD-V'de koşulsuz bir
VM-exit'tir: hypervisor, CPUID interception'ını devre dışı bırakamaz,
dolayısıyla her guest CPUID, kontrolü host koduna ve geri aktarır (world
switch, VMCS/VMCB save+restore, decode, re-entry). CPUID aynı zamanda
serializing'dir, bu da onu pipeline'ı boşaltan kullanışlı bir fence yapar ki
iki RDTSC okuması iyi tanımlı bir olayı bracket'lesin. Native hardware'da
CPUID, onlarla ~100 cycle mertebesinde maliyetlidir; virtualization altında
VM-exit gidiş-dönüşü tipik olarak yüzlerce ile birkaç bin cycle ekler. Guest
asla bir host secret okumaz — sadece kaçınılmaz exit'i zamanlar,
dolayısıyla latency'nin kendisi VMM'i ele veren side channel'dır.
Walkthrough¶
- Serialize-and-time idiom (x86-64): baştaki CPUID önceki işi boşaltır, sonra ölçülen CPUID'yi RDTSC okumaları ile bracket'le.
xor eax, eax
cpuid ; warm up / serialize before timing
rdtsc ; t0 -> edx:eax
mov esi, eax
mov eax, 0
cpuid ; the instruction we are timing (forces VM-exit)
rdtsc ; t1 -> edx:eax
sub eax, esi ; delta = t1 - t0 (low 32 bits)
- Threshold logic: ölçümü N kez tekrarla, minimum delta'yı al (scheduling noise'una karşı robust) ve bir hardware baseline ile karşılaştır. Native bir CPUID min-delta'sı kabaca onlarla ~100 cycle'dır; yüksek yüzlerce ile >1000 cycle aralığında bir min-delta, zorlanmış bir VM-exit'e, yani bir hypervisor'a işaret eder.
Warning
Raw TSC delta'ları gürültülüdür: SMM interrupt'ları, migration'lar, frequency
scaling ve preemption tek tek sample'ları bozar. Robust detector'lar, tek bir
mutlak threshold yerine birçok iteration üzerinden minimum'u kullanır ve/veya
VM-exit eden bir instruction'ı (CPUID) non-exiting bir baseline'a (örn. sıkı
bir nop/add loop'u) karşı karşılaştırır.
Detection¶
Bir defender / sandbox guest'in probe yaptığını nasıl fark eder:
- CPUID-exit telemetry: RDTSC ile iç içe geçmiş CPUID patlamaları yayan, özellikle aynı leaf'i sıkı bir loop'ta çalıştıran bir guest, VMM'in her CPUID VM-exit'inde loglayabileceği klasik bir timing-probe imzasıdır.
- RDTSC-exit sampling: RDTSC exiting'i geçici olarak etkinleştir ve bir guest'in her CPUID'den hemen önce ve sonra TSC'yi okuduğunu gözlemle.
- Evasive/sandbox-aware malware'i sınıflandırmak için diğer fingerprinting
(
cpuid-hypervisor-present-bit-detection, vendor-leaf probing) ile ilişkilendir.
Mitigation¶
Bir hypervisor detection'ı nasıl yener:
- TSC offsetting / scaling: VMCS TSC-offset'ini (ve TSC scaling ratio'sunu)
öyle programla ki guest'in gördüğü TSC, hiç exit olmamış gibi ilerlesin —
modellenen exit maliyetini guest'in okuduğu counter'dan çıkar. Bkz.
tsc-offset-tsc-scaling-emulation. - RDTSC exiting + fix-up: RDTSC/RDTSCP'yi intercept et ve per-exit latency'yi gizleyen düzeltilmiş bir değer döndür (kvm-rdtsc-hack yaklaşımı sahte bir offset enjekte eder). Trade-off: RDTSC exiting'in kendisi overhead ekler ve saptanabilir.
- Exit maliyetini minimize et: CPUID handling'i yalın tut ki residual delta native'e yakın kalsın; timing ve içerik uyuşsun diye tutarlı CPUID spoofing ile birleştir.
Warning
CPUID VM-exit devre dışı bırakılamadığı için timing evasion best-effort'tur: CPUID'yi non-exiting bir baseline'a karşı karşılaştıran kararlı bir guest yine de virtualization'ı çıkarsayabilir. TSC fix-up'ları çıtayı yükseltir, kapatmaz.