Skip to content

Instantly share code, notes, and snippets.

@hLunaaa
Last active April 17, 2025 19:17
Show Gist options
  • Save hLunaaa/e3730f175d572258baf0049ae37f5fec to your computer and use it in GitHub Desktop.
Save hLunaaa/e3730f175d572258baf0049ae37f5fec to your computer and use it in GitHub Desktop.
vm cpu detection via cpuid and rdtsc
#include <iostream>
#include <thread>
static constexpr u32 dynamic_cycles = 10,
standard_sleep = 500,
vm_cycle_ratio = 100,
cycle_ratio = 75;
// -- rdtsc --
// https://github.com/hfiref0x/VBoxHardenedLoader
// https://docs.microsoft.com/en-us/cpp/intrinsics/rdtsc
// https://www.aldeid.com/wiki/X86-assembly/Instructions/rdtsc
// -- cpuid --
// https://cs.fit.edu/~mmahoney/cse3101/cpuid.asm.txt
// https://en.wikipedia.org/wiki/CPUID#Calling_CPUID
// https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
// -- additional resources --
// http://ref.x86asm.net/coder32.html
// https://en.wikipedia.org/wiki/X86_calling_conventions#stdcall
// msvc intrin.h wraps 0f31 with __rdtsc
// reads time-stamp counters or rdtsc for uptime
u64 rdts_count()
{
__asm
{
rdtsc ; call 0f31
shl eax, 20h ; rax fix
or edx, eax ; bitor
}
}
// we compare to see if static rdtsc delta exceeds
// i would lambda the inline asm but msvc wont allow
u32 rdtsc_static_delta()
{
__asm
{
rdtsc ; call 0f31
xor ecx, ecx ; mov evx, 0
mov ecx, eax ; store low
rdtsc ; repeat
sub eax, ecx ; calculate delta
}
}
// we compare to see if static rdtsc delta exceeds
// this time calling cpuid with eax = 0 to trigger
u32 rdtsc_vm_static_delta()
{
__asm
{
rdtsc ; call 0f31
xor ecx, ecx ; mov evx, 0
mov ecx, eax ; store low
xor eax, eax ; eax = 0
cpuid ; highest function parameter
rdtsc ; repeat
sub eax, ecx ; calculate delta
}
}
// we compute the rdtsc detla dynamically and compare
// with known values to detect the system hypervisor
u32 rdtsc_dynamic_delta() {
u64 mean_delta;
for (auto i = 0; i < dynamic_cycles; i++)
{
mean_delta += static_cast <u64> (rdtsc_static_delta() * 0.1);
this_thread::sleep_for(chrono::milliseconds(standard_sleep));
}
return mean_delta >= dynamic_cycles * cycle_ratio;
}
// we compute the rdtsc detla dynamically and compare
// with known values to detect the system hypervisor
u32 rdtsc_vm_dynamic_delta()
{
u64 mean_delta;
for (auto i = 0; i < dynamic_cycles; i++)
{
mean_delta += static_cast <u64> (rdtsc_vm_static_delta() * 0.1);
this_thread::sleep_for(chrono::milliseconds(standard_sleep));
}
return mean_delta >= dynamic_cycles * vm_cycle_ratio;
}
// msvc cpuid.h wraps 0fa2 via __get_cpuid()
// checks flag on bit=31 for hv presence in system
u32 cpuid_hypervisor()
{
__asm
{
mov eax, 1h ; eax = 1 features
cpuid ; call 0fa2
shr ecx, 1fh ; hv flag in bit = 31
mov eax, ecx ; ret via eax
and eax, 1h ; cmp
}
}
u32 main()
{
auto log_event = [](string_view label, u32 condition) { cout
<< "* " << label << " ~ " << (condition ? "abnormal" : "normal") << endl; };
log_event("rdts_count", rdts_count() == 0);
log_event("cpuid_hypervisor", cpuid_hypervisor());
log_event("rdtsc_dynamic_delta", rdtsc_dynamic_delta());
log_event("rdtsc_vm_dynamic_delta", rdtsc_vm_dynamic_delta());
log_event("rdtsc_static_delta", rdtsc_static_delta() >= 0xfff);
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment