Skip to content

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

  1. 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)
  1. 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.
if (min_delta > THRESHOLD /* e.g. ~500 cycles, tune per CPU */)
    running_under_hypervisor = 1;

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.

References