├── README.md └── main.c /README.md: -------------------------------------------------------------------------------- 1 | # tl;dr 2 | The CPUID instruction takes a lot longer to execute in a VM than it does on bare-metal so we can measure it and confidently say if we are running inside of a VM or not. It is possible because a VM will need to call a VMEXIT to let the VMM execute the instruction bare-metal (and that takes time) or in case of emulators, it needs to manually move the data into the virtual registers (and that also takes time). 3 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | // 10th of September 2020 2 | // by Duchy 3 | #include 4 | 5 | #ifdef _WIN32 6 | #include 7 | #include 8 | #pragma intrinsic(__rdtsc) 9 | #elif __linux__ 10 | #ifndef __clang__ 11 | /* 12 | * proudly ripped and edited from 13 | * https://web.archive.org/web/20180916070251/http://www.mcs.anl.gov/~kazutomo/rdtsc.html 14 | */ 15 | #ifdef __i386__ 16 | static __inline__ unsigned long long int __rdtsc(void) 17 | { 18 | unsigned long long int x; 19 | __asm__ __volatile__ (".byte 0x0f, 0x31" : "=A" (x)); 20 | return x; 21 | } 22 | #elif __x86_64__ 23 | static __inline__ unsigned long long int __rdtsc(void) 24 | { 25 | unsigned hi, lo; 26 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); 27 | return ( (unsigned long long int)lo)|( ((unsigned long long int)hi)<<32 ); 28 | } 29 | #endif 30 | #endif 31 | #endif 32 | 33 | 34 | 35 | int main(){ 36 | unsigned long long int time1, time2, sum = 0; 37 | const unsigned char avg = 100; 38 | 39 | for (int i = 0; i < avg; i++){ 40 | time1 = __rdtsc(); 41 | #ifdef _WIN32 42 | __asm cpuid; 43 | #elif __linux__ 44 | // clobbered eax etc. covers their 64bit counterparts as well 45 | __asm__ volatile("CPUID"::: "eax", "ebx", "ecx", "edx", "memory"); 46 | #endif 47 | time2 = __rdtsc(); 48 | sum += time2 - time1; 49 | } 50 | 51 | sum = sum / avg; 52 | 53 | printf("Ticks on average: %llu\n", sum); 54 | 55 | if(sum > 500){ 56 | puts("Probably a VM"); 57 | }else{ 58 | puts("Probably Bare-Metal"); 59 | } 60 | 61 | return 0; 62 | } 63 | --------------------------------------------------------------------------------