3131 * - struct for internal cpu operations => line 742
3232 * - struct for internal memoization => line 1213
3333 * - struct for internal utility functions => line 1337
34- * - struct for internal core components => line 10026
35- * - start of VM detection technique list => line 2338
36- * - start of public VM detection functions => line 10690
37- * - start of externally defined variables => line 11638
34+ * - struct for internal core components => line 10065
35+ * - start of VM detection technique list => line 2361
36+ * - start of public VM detection functions => line 10729
37+ * - start of externally defined variables => line 11675
3838 *
3939 *
4040 * ============================== EXAMPLE ===================================
@@ -1408,7 +1408,7 @@ struct VM {
14081408#endif
14091409 }
14101410
1411- #if defined (WINDOWS) && (defined(UNICODE) || defined(_UNICODE))
1411+ #if (WINDOWS) && (defined(UNICODE) || defined(_UNICODE))
14121412 // handle TCHAR conversion
14131413 [[nodiscard]] static bool exists (const TCHAR* path) {
14141414 char c_szText[_MAX_PATH]{};
@@ -1743,7 +1743,7 @@ struct VM {
17431743#endif
17441744 }
17451745
1746- // et available memory space
1746+ // get available memory space
17471747 [[nodiscard]] static u64 get_memory_space () {
17481748#if (WINDOWS)
17491749 MEMORYSTATUSEX statex = { 0 };
@@ -1872,6 +1872,30 @@ struct VM {
18721872#endif
18731873 }
18741874
1875+ // Returns a list of running process names
1876+ [[nodiscard]] static std::unordered_set<std::string> get_running_process_names () {
1877+ std::unordered_set<std::string> processNames;
1878+ DWORD processes[1024 ], bytesReturned;
1879+
1880+ if (!K32EnumProcesses (processes, sizeof (processes), &bytesReturned)) {
1881+ return processNames;
1882+ }
1883+
1884+ DWORD numProcesses = bytesReturned / sizeof (DWORD);
1885+ char processName[MAX_PATH];
1886+
1887+ for (DWORD i = 0 ; i < numProcesses; ++i) {
1888+ HANDLE hProcess = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE , processes[i]);
1889+ if (hProcess != nullptr ) {
1890+ if (K32GetModuleBaseNameA (hProcess, nullptr , processName, sizeof (processName))) {
1891+ processNames.insert (processName);
1892+ }
1893+ CloseHandle (hProcess);
1894+ }
1895+ }
1896+ return processNames;
1897+ }
1898+
18751899 // Retrieves the computer name
18761900 [[nodiscard]] static std::string get_hostname () {
18771901#if (WINDOWS)
@@ -1892,13 +1916,12 @@ struct VM {
18921916 return std::string ();
18931917 }
18941918
1895-
18961919 /* *
18971920 * @brief Checks whether the system is running in a Hyper-V virtual machine or if the host system has Hyper-V enabled
18981921 * @note Hyper-V's presence on a host system can set certain hypervisor-related CPU flags that may appear similar to those in a virtualized environment, which can make it challenging to differentiate between an actual Hyper-V virtual machine (VM) and a host system with Hyper-V enabled.
18991922 * This can lead to false conclusions, where the system might mistakenly be identified as running in a Hyper-V VM, when in reality, it's simply the host system with Hyper-V features active.
19001923 * This check aims to distinguish between these two cases by identifying specific CPU flags and hypervisor-related artifacts that are indicative of a Hyper-V VM rather than a host system with Hyper-V enabled.
1901- * @author idea by Requiem (https://github.com/NotRequiem)
1924+ * @author Requiem (https://github.com/NotRequiem)
19021925 * @returns hyperx_state enum indicating the detected state:
19031926 * - HYPERV_ARTIFACT_VM for host with Hyper-V enabled
19041927 * - HYPERV_REAL_VM for real Hyper-V VM
@@ -2482,7 +2505,7 @@ struct VM {
24822505
24832506 /* *
24842507 * @brief Check if mac address starts with certain VM designated values
2485- * @category All systems (I think)
2508+ * @category Linux and Windows
24862509 * @implements VM::MAC
24872510 */
24882511 [[nodiscard]] static bool mac_address_check () {
@@ -2532,30 +2555,19 @@ struct VM {
25322555 debug (" MAC: " , " not successful" );
25332556 }
25342557#elif (WINDOWS)
2535- PIP_ADAPTER_INFO AdapterInfo;
2536- DWORD dwBufLen = sizeof (IP_ADAPTER_INFO);
2537-
2538- AdapterInfo = (IP_ADAPTER_INFO*)std::malloc (sizeof (IP_ADAPTER_INFO));
2539-
2540- if (AdapterInfo == NULL ) {
2558+ DWORD dwBufLen = 0 ;
2559+ if (GetAdaptersInfo (nullptr , &dwBufLen) != ERROR_BUFFER_OVERFLOW) {
25412560 return false ;
25422561 }
25432562
2544- if (GetAdaptersInfo (AdapterInfo, &dwBufLen) == ERROR_BUFFER_OVERFLOW) {
2545- std::free (AdapterInfo);
2546- AdapterInfo = (IP_ADAPTER_INFO*)std::malloc (dwBufLen);
2547- if (AdapterInfo == NULL ) {
2548- return false ;
2549- }
2563+ PIP_ADAPTER_INFO AdapterInfo = (PIP_ADAPTER_INFO)std::malloc (dwBufLen);
2564+ if (AdapterInfo == nullptr ) {
2565+ return false ;
25502566 }
25512567
25522568 if (GetAdaptersInfo (AdapterInfo, &dwBufLen) == NO_ERROR) {
2553- PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
2554- for (std::size_t i = 0 ; i < 6 ; i++) {
2555- mac[i] = pAdapterInfo->Address [i];
2556- }
2569+ std::memcpy (mac, AdapterInfo->Address , sizeof (mac));
25572570 }
2558-
25592571 std::free (AdapterInfo);
25602572#else
25612573 return false ;
@@ -2574,32 +2586,28 @@ struct VM {
25742586 */
25752587 debug (" MAC: " , ss.str ());
25762588#endif
2577-
25782589 // better expression to fix code duplication
2579- auto compare = [=]( const u8 mac1, const u8 mac2, const u8 mac3) noexcept -> bool {
2590+ auto compare = [mac]( u8 mac1, u8 mac2, u8 mac3) noexcept -> bool {
25802591 return (mac[0 ] == mac1 && mac[1 ] == mac2 && mac[2 ] == mac3);
2581- };
2592+ };
25822593
2583- if (compare (0x08 , 0x00 , 0x27 )) {
2594+ // Check for known virtualization MAC address prefixes
2595+ if (compare (0x08 , 0x00 , 0x27 ))
25842596 return core::add (brands::VBOX);
2585- }
25862597
2587- if (
2588- (compare (0x00 , 0x0C , 0x29 )) ||
2589- (compare (0x00 , 0x1C , 0x14 )) ||
2590- (compare (0x00 , 0x50 , 0x56 )) ||
2591- (compare (0x00 , 0x05 , 0x69 ))
2592- ) {
2598+ if (compare (0x00 , 0x0C , 0x29 ) ||
2599+ compare (0x00 , 0x1C , 0x14 ) ||
2600+ compare (0x00 , 0x50 , 0x56 ) ||
2601+ compare (0x00 , 0x05 , 0x69 ))
2602+ {
25932603 return core::add (brands::VMWARE);
25942604 }
25952605
2596- if (compare (0x00 , 0x16 , 0xE3 )) {
2606+ if (compare (0x00 , 0x16 , 0xE3 ))
25972607 return core::add (brands::XEN);
2598- }
25992608
2600- if (compare (0x00 , 0x1C , 0x42 )) {
2609+ if (compare (0x00 , 0x1C , 0x42 ))
26012610 return core::add (brands::PARALLELS);
2602- }
26032611
26042612 /*
26052613 see https://github.com/kernelwernel/VMAware/issues/105
@@ -3396,17 +3404,14 @@ struct VM {
33963404#if (!WINDOWS)
33973405 return false ;
33983406#else
3399- DWORD pnsize = 0x1000 ;
3400- char provider[0x1000 ];
3407+ char provider[256 ];
3408+ DWORD pnsize = sizeof (provider);
3409+ const DWORD retv = WNetGetProviderNameA (WNNC_NET_RDR2SAMPLE, provider, &pnsize);
34013410
3402- DWORD retv = WNetGetProviderNameA (WNNC_NET_RDR2SAMPLE, provider, &pnsize);
3403- bool result = false ;
3404-
3405- if (retv == NO_ERROR) {
3406- result = (strcmp (provider, " VirtualBox Shared Folders" ) == 0 );
3407- }
3411+ if (retv != NO_ERROR)
3412+ return false ;
34083413
3409- return result ;
3414+ return ( strncmp (provider, " VirtualBox Shared Folders " , 26 ) == 0 ) ;
34103415#endif
34113416 }
34123417
@@ -3651,41 +3656,43 @@ struct VM {
36513656 #if (!WINDOWS)
36523657 return false ;
36533658 #else
3654- if (util::is_proc_running (" joeboxserver.exe" ) || util::is_proc_running (" joeboxcontrol.exe" )) {
3659+ const auto runningProcesses = util::get_running_process_names ();
3660+
3661+ if (runningProcesses.count (" joeboxserver.exe" ) || runningProcesses.count (" joeboxcontrol.exe" )) {
36553662 debug (" VM_PROCESSES: Detected JoeBox process." );
36563663 return core::add (brands::JOEBOX);
36573664 }
36583665
3659- if (util::is_proc_running (" prl_cc.exe" ) || util::is_proc_running (" prl_tools.exe" )) {
3666+ if (runningProcesses. count (" prl_cc.exe" ) || runningProcesses. count (" prl_tools.exe" )) {
36603667 debug (" VM_PROCESSES: Detected Parallels process." );
36613668 return core::add (brands::PARALLELS);
36623669 }
36633670
3664- if (util::is_proc_running (" vboxservice.exe" ) || util::is_proc_running (" vboxtray.exe" )) {
3671+ if (runningProcesses. count (" vboxservice.exe" ) || runningProcesses. count (" vboxtray.exe" )) {
36653672 debug (" VM_PROCESSES: Detected VBox process." );
36663673 return core::add (brands::VBOX);
36673674 }
36683675
3669- if (util::is_proc_running (" vmsrvc.exe" ) || util::is_proc_running (" vmusrvc.exe" )) {
3676+ if (runningProcesses. count (" vmsrvc.exe" ) || runningProcesses. count (" vmusrvc.exe" )) {
36703677 debug (" VM_PROCESSES: Detected VPC process." );
36713678 return core::add (brands::VPC);
36723679 }
36733680
3674- if (util::is_proc_running (" xenservice.exe" ) || util::is_proc_running (" xsvc_depriv.exe" )) {
3681+ if (runningProcesses. count (" xenservice.exe" ) || runningProcesses. count (" xsvc_depriv.exe" )) {
36753682 debug (" VM_PROCESSES: Detected Xen process." );
36763683 return core::add (brands::XEN);
36773684 }
36783685
3679- if (util::is_proc_running (" vm3dservice.exe" ) ||
3680- util::is_proc_running (" VGAuthService.exe" ) ||
3681- util::is_proc_running (" vmtoolsd.exe" )) {
3686+ if (runningProcesses. count (" vm3dservice.exe" ) ||
3687+ runningProcesses. count (" VGAuthService.exe" ) ||
3688+ runningProcesses. count (" vmtoolsd.exe" )) {
36823689 debug (" VM_PROCESSES: Detected VMware process." );
36833690 return core::add (brands::VMWARE);
36843691 }
36853692
3686- if (util::is_proc_running (" vdagent.exe" ) ||
3687- util::is_proc_running (" vdservice.exe" ) ||
3688- util::is_proc_running (" qemuwmi.exe" )) {
3693+ if (runningProcesses. count (" vdagent.exe" ) ||
3694+ runningProcesses. count (" vdservice.exe" ) ||
3695+ runningProcesses. count (" qemuwmi.exe" )) {
36893696 debug (" VM_PROCESSES: Detected QEMU process." );
36903697 return core::add (brands::QEMU);
36913698 }
@@ -7893,6 +7900,33 @@ struct VM {
78937900#ifdef __VMAWARE_DEBUG__
78947901 u64 totalCycles = 0 ;
78957902#endif
7903+ char * flushBuffer = nullptr ; // avoiding volatile on purpose
7904+ constexpr size_t kAlignment = 64 ;
7905+ constexpr size_t kBufferSize = static_cast <size_t >(64 * 1024 ) * 1024 ;
7906+
7907+ #if (WINDOWS)
7908+ #define COMPILER_BARRIER () _ReadWriteBarrier()
7909+ #else
7910+ #define COMPILER_BARRIER () __asm__ __volatile__ (" " ::: " memory" )
7911+ #endif
7912+
7913+ #if (WINDOWS)
7914+ flushBuffer = (char *)_aligned_malloc (kBufferSize , kAlignment );
7915+ if (!flushBuffer) {
7916+ flushBuffer = new (std::nothrow) char [kBufferSize ];
7917+ }
7918+ #elif (LINUX || APPLE)
7919+ int err = posix_memalign ((void **)&flushBuffer, kAlignment , kBufferSize );
7920+ if (err != 0 || !flushBuffer) {
7921+ flushBuffer = new (std::nothrow) char [kBufferSize ];
7922+ }
7923+ #else
7924+ // volatile char* flushBuffer = new volatile char[kBufferSize];
7925+ flushBuffer = new (std::nothrow) char [kBufferSize ];
7926+ #endif
7927+ // Define a rotation scheme over segments. Here, we split the buffer into a number of segments
7928+ constexpr size_t segmentsCount = 8 ; // basically 1/8 of the buffer per iteration
7929+ constexpr size_t segmentSize = kBufferSize / segmentsCount;
78967930 int spikeCount = 0 ;
78977931 for (int i = 0 ; i < classicIterations; i++) {
78987932 u64 start = __rdtsc ();
@@ -7913,21 +7947,27 @@ struct VM {
79137947 if (cycles >= classicThreshold) {
79147948 spikeCount++;
79157949 }
7916- // to induce cache flushing
7917- constexpr size_t bufferSize = static_cast <size_t >(64 * 1024 ) * 1024 ;
7918- volatile char * flushBuffer = new volatile char [bufferSize];
7950+ // Instead of flushing the entire buffer every iteration (which would decrease performance a lot),
7951+ // flush only one segment per iteration
7952+ size_t segmentIndex = i % segmentsCount;
7953+ size_t offsetStart = segmentIndex * segmentSize;
7954+ size_t offsetEnd = offsetStart + segmentSize;
79197955
7920- // better than thread sleeps
7921- for (size_t j = 0 ; j < bufferSize ; j += 64 ) {
7956+ // this detection works better when inducing cache flushing without thread sleeps
7957+ for (size_t j = offsetStart ; j < offsetEnd ; j += 64 ) {
79227958 flushBuffer[j] = static_cast <char >(j);
7923- #if (x86 && (GCC || CLANG || MSVC))
7924- _mm_clflush (const_cast <const void *>(
7925- reinterpret_cast <const volatile void *>(&flushBuffer[j])));
7959+ #if defined(x86) && (defined(GCC) || defined(CLANG) || defined(MSVC))
7960+ COMPILER_BARRIER ();
7961+ // _mm_clflushopt not available on some systems
7962+ _mm_clflush (reinterpret_cast <const void *>(&flushBuffer[j]));
79267963#endif
79277964 }
7928-
7929- delete[] flushBuffer;
79307965 }
7966+ #if (WINDOWS)
7967+ _aligned_free ((void *)flushBuffer);
7968+ #else
7969+ free ((void *)flushBuffer);
7970+ #endif
79317971
79327972#ifdef __VMAWARE_DEBUG__
79337973 const double averageCycles = static_cast <double >(totalCycles) / classicIterations;
0 commit comments