2727 *
2828 *
2929 * ============================== SECTIONS ==================================
30- * - enums for publicly accessible techniques => line 550
31- * - struct for internal cpu operations => line 741
32- * - struct for internal memoization => line 1212
33- * - struct for internal utility functions => line 1336
34- * - struct for internal core components => line 10031
35- * - start of VM detection technique list => line 2337
36- * - start of public VM detection functions => line 10695
37- * - start of externally defined variables => line 11641
30+ * - enums for publicly accessible techniques => line 551
31+ * - struct for internal cpu operations => line 742
32+ * - struct for internal memoization => line 1213
33+ * - struct for internal utility functions => line 1337
34+ * - struct for internal core components => line 10067
35+ * - start of VM detection technique list => line 2363
36+ * - start of public VM detection functions => line 10731
37+ * - start of externally defined variables => line 11677
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,32 @@ 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+ #if (WINDOWS)
1879+ DWORD processes[1024 ], bytesReturned;
1880+
1881+ if (!K32EnumProcesses (processes, sizeof (processes), &bytesReturned)) {
1882+ return processNames;
1883+ }
1884+
1885+ DWORD numProcesses = bytesReturned / sizeof (DWORD);
1886+ char processName[MAX_PATH];
1887+
1888+ for (DWORD i = 0 ; i < numProcesses; ++i) {
1889+ HANDLE hProcess = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE , processes[i]);
1890+ if (hProcess != nullptr ) {
1891+ if (K32GetModuleBaseNameA (hProcess, nullptr , processName, sizeof (processName))) {
1892+ processNames.insert (processName);
1893+ }
1894+ CloseHandle (hProcess);
1895+ }
1896+ }
1897+ #endif
1898+ return processNames;
1899+ }
1900+
18751901 // Retrieves the computer name
18761902 [[nodiscard]] static std::string get_hostname () {
18771903#if (WINDOWS)
@@ -1892,13 +1918,12 @@ struct VM {
18921918 return std::string ();
18931919 }
18941920
1895-
18961921 /* *
18971922 * @brief Checks whether the system is running in a Hyper-V virtual machine or if the host system has Hyper-V enabled
18981923 * @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.
18991924 * 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.
19001925 * 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)
1926+ * @author Requiem (https://github.com/NotRequiem)
19021927 * @returns hyperx_state enum indicating the detected state:
19031928 * - HYPERV_ARTIFACT_VM for host with Hyper-V enabled
19041929 * - HYPERV_REAL_VM for real Hyper-V VM
@@ -2482,7 +2507,7 @@ struct VM {
24822507
24832508 /* *
24842509 * @brief Check if mac address starts with certain VM designated values
2485- * @category All systems (I think)
2510+ * @category Linux and Windows
24862511 * @implements VM::MAC
24872512 */
24882513 [[nodiscard]] static bool mac_address_check () {
@@ -2532,30 +2557,19 @@ struct VM {
25322557 debug (" MAC: " , " not successful" );
25332558 }
25342559#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 ) {
2560+ DWORD dwBufLen = 0 ;
2561+ if (GetAdaptersInfo (nullptr , &dwBufLen) != ERROR_BUFFER_OVERFLOW) {
25412562 return false ;
25422563 }
25432564
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- }
2565+ PIP_ADAPTER_INFO AdapterInfo = (PIP_ADAPTER_INFO)std::malloc (dwBufLen);
2566+ if (AdapterInfo == nullptr ) {
2567+ return false ;
25502568 }
25512569
25522570 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- }
2571+ std::memcpy (mac, AdapterInfo->Address , sizeof (mac));
25572572 }
2558-
25592573 std::free (AdapterInfo);
25602574#else
25612575 return false ;
@@ -2574,32 +2588,28 @@ struct VM {
25742588 */
25752589 debug (" MAC: " , ss.str ());
25762590#endif
2577-
25782591 // better expression to fix code duplication
2579- auto compare = [=]( const u8 mac1, const u8 mac2, const u8 mac3) noexcept -> bool {
2592+ auto compare = [mac]( u8 mac1, u8 mac2, u8 mac3) noexcept -> bool {
25802593 return (mac[0 ] == mac1 && mac[1 ] == mac2 && mac[2 ] == mac3);
2581- };
2594+ };
25822595
2583- if (compare (0x08 , 0x00 , 0x27 )) {
2596+ // Check for known virtualization MAC address prefixes
2597+ if (compare (0x08 , 0x00 , 0x27 ))
25842598 return core::add (brands::VBOX);
2585- }
25862599
2587- if (
2588- (compare (0x00 , 0x0C , 0x29 )) ||
2589- (compare (0x00 , 0x1C , 0x14 )) ||
2590- (compare (0x00 , 0x50 , 0x56 )) ||
2591- (compare (0x00 , 0x05 , 0x69 ))
2592- ) {
2600+ if (compare (0x00 , 0x0C , 0x29 ) ||
2601+ compare (0x00 , 0x1C , 0x14 ) ||
2602+ compare (0x00 , 0x50 , 0x56 ) ||
2603+ compare (0x00 , 0x05 , 0x69 ))
2604+ {
25932605 return core::add (brands::VMWARE);
25942606 }
25952607
2596- if (compare (0x00 , 0x16 , 0xE3 )) {
2608+ if (compare (0x00 , 0x16 , 0xE3 ))
25972609 return core::add (brands::XEN);
2598- }
25992610
2600- if (compare (0x00 , 0x1C , 0x42 )) {
2611+ if (compare (0x00 , 0x1C , 0x42 ))
26012612 return core::add (brands::PARALLELS);
2602- }
26032613
26042614 /*
26052615 see https://github.com/kernelwernel/VMAware/issues/105
@@ -3396,17 +3406,14 @@ struct VM {
33963406#if (!WINDOWS)
33973407 return false ;
33983408#else
3399- DWORD pnsize = 0x1000 ;
3400- char provider[0x1000 ];
3401-
3402- DWORD retv = WNetGetProviderNameA (WNNC_NET_RDR2SAMPLE, provider, &pnsize);
3403- bool result = false ;
3409+ char provider[256 ];
3410+ DWORD pnsize = sizeof (provider);
3411+ const DWORD retv = WNetGetProviderNameA (WNNC_NET_RDR2SAMPLE, provider, &pnsize);
34043412
3405- if (retv == NO_ERROR) {
3406- result = (strcmp (provider, " VirtualBox Shared Folders" ) == 0 );
3407- }
3413+ if (retv != NO_ERROR)
3414+ return false ;
34083415
3409- return result ;
3416+ return ( strncmp (provider, " VirtualBox Shared Folders " , 26 ) == 0 ) ;
34103417#endif
34113418 }
34123419
@@ -3651,41 +3658,43 @@ struct VM {
36513658 #if (!WINDOWS)
36523659 return false ;
36533660 #else
3654- if (util::is_proc_running (" joeboxserver.exe" ) || util::is_proc_running (" joeboxcontrol.exe" )) {
3661+ const auto runningProcesses = util::get_running_process_names ();
3662+
3663+ if (runningProcesses.count (" joeboxserver.exe" ) || runningProcesses.count (" joeboxcontrol.exe" )) {
36553664 debug (" VM_PROCESSES: Detected JoeBox process." );
36563665 return core::add (brands::JOEBOX);
36573666 }
36583667
3659- if (util::is_proc_running (" prl_cc.exe" ) || util::is_proc_running (" prl_tools.exe" )) {
3668+ if (runningProcesses. count (" prl_cc.exe" ) || runningProcesses. count (" prl_tools.exe" )) {
36603669 debug (" VM_PROCESSES: Detected Parallels process." );
36613670 return core::add (brands::PARALLELS);
36623671 }
36633672
3664- if (util::is_proc_running (" vboxservice.exe" ) || util::is_proc_running (" vboxtray.exe" )) {
3673+ if (runningProcesses. count (" vboxservice.exe" ) || runningProcesses. count (" vboxtray.exe" )) {
36653674 debug (" VM_PROCESSES: Detected VBox process." );
36663675 return core::add (brands::VBOX);
36673676 }
36683677
3669- if (util::is_proc_running (" vmsrvc.exe" ) || util::is_proc_running (" vmusrvc.exe" )) {
3678+ if (runningProcesses. count (" vmsrvc.exe" ) || runningProcesses. count (" vmusrvc.exe" )) {
36703679 debug (" VM_PROCESSES: Detected VPC process." );
36713680 return core::add (brands::VPC);
36723681 }
36733682
3674- if (util::is_proc_running (" xenservice.exe" ) || util::is_proc_running (" xsvc_depriv.exe" )) {
3683+ if (runningProcesses. count (" xenservice.exe" ) || runningProcesses. count (" xsvc_depriv.exe" )) {
36753684 debug (" VM_PROCESSES: Detected Xen process." );
36763685 return core::add (brands::XEN);
36773686 }
36783687
3679- if (util::is_proc_running (" vm3dservice.exe" ) ||
3680- util::is_proc_running (" VGAuthService.exe" ) ||
3681- util::is_proc_running (" vmtoolsd.exe" )) {
3688+ if (runningProcesses. count (" vm3dservice.exe" ) ||
3689+ runningProcesses. count (" VGAuthService.exe" ) ||
3690+ runningProcesses. count (" vmtoolsd.exe" )) {
36823691 debug (" VM_PROCESSES: Detected VMware process." );
36833692 return core::add (brands::VMWARE);
36843693 }
36853694
3686- if (util::is_proc_running (" vdagent.exe" ) ||
3687- util::is_proc_running (" vdservice.exe" ) ||
3688- util::is_proc_running (" qemuwmi.exe" )) {
3695+ if (runningProcesses. count (" vdagent.exe" ) ||
3696+ runningProcesses. count (" vdservice.exe" ) ||
3697+ runningProcesses. count (" qemuwmi.exe" )) {
36893698 debug (" VM_PROCESSES: Detected QEMU process." );
36903699 return core::add (brands::QEMU);
36913700 }
@@ -7900,6 +7909,33 @@ struct VM {
79007909#ifdef __VMAWARE_DEBUG__
79017910 u64 totalCycles = 0 ;
79027911#endif
7912+ char * flushBuffer = nullptr ; // avoiding volatile on purpose
7913+ constexpr size_t kAlignment = 64 ;
7914+ constexpr size_t kBufferSize = static_cast <size_t >(64 * 1024 ) * 1024 ;
7915+
7916+ #if (WINDOWS)
7917+ #define COMPILER_BARRIER () _ReadWriteBarrier()
7918+ #else
7919+ #define COMPILER_BARRIER () __asm__ __volatile__ (" " ::: " memory" )
7920+ #endif
7921+
7922+ #if (WINDOWS)
7923+ flushBuffer = (char *)_aligned_malloc (kBufferSize , kAlignment );
7924+ if (!flushBuffer) {
7925+ flushBuffer = new (std::nothrow) char [kBufferSize ];
7926+ }
7927+ #elif (LINUX || APPLE)
7928+ int err = posix_memalign ((void **)&flushBuffer, kAlignment , kBufferSize );
7929+ if (err != 0 || !flushBuffer) {
7930+ flushBuffer = new (std::nothrow) char [kBufferSize ];
7931+ }
7932+ #else
7933+ // volatile char* flushBuffer = new volatile char[kBufferSize];
7934+ flushBuffer = new (std::nothrow) char [kBufferSize ];
7935+ #endif
7936+ // Define a rotation scheme over segments. Here, we split the buffer into a number of segments
7937+ constexpr size_t segmentsCount = 8 ; // basically 1/8 of the buffer per iteration
7938+ constexpr size_t segmentSize = kBufferSize / segmentsCount;
79037939 int spikeCount = 0 ;
79047940 for (int i = 0 ; i < classicIterations; i++) {
79057941 u64 start = __rdtsc ();
@@ -7920,21 +7956,27 @@ struct VM {
79207956 if (cycles >= classicThreshold) {
79217957 spikeCount++;
79227958 }
7923- // to induce cache flushing
7924- constexpr size_t bufferSize = static_cast <size_t >(64 * 1024 ) * 1024 ;
7925- volatile char * flushBuffer = new volatile char [bufferSize];
7959+ // Instead of flushing the entire buffer every iteration (which would decrease performance a lot),
7960+ // flush only one segment per iteration
7961+ size_t segmentIndex = i % segmentsCount;
7962+ size_t offsetStart = segmentIndex * segmentSize;
7963+ size_t offsetEnd = offsetStart + segmentSize;
79267964
7927- // better than thread sleeps
7928- for (size_t j = 0 ; j < bufferSize ; j += 64 ) {
7965+ // this detection works better when inducing cache flushing without thread sleeps
7966+ for (size_t j = offsetStart ; j < offsetEnd ; j += 64 ) {
79297967 flushBuffer[j] = static_cast <char >(j);
7930- #if (x86 && (GCC || CLANG || MSVC))
7931- _mm_clflush (const_cast <const void *>(
7932- reinterpret_cast <const volatile void *>(&flushBuffer[j])));
7968+ #if defined(x86) && (defined(GCC) || defined(CLANG) || defined(MSVC))
7969+ COMPILER_BARRIER ();
7970+ // _mm_clflushopt not available on some systems
7971+ _mm_clflush (reinterpret_cast <const void *>(&flushBuffer[j]));
79337972#endif
79347973 }
7935-
7936- delete[] flushBuffer;
79377974 }
7975+ #if (WINDOWS)
7976+ _aligned_free ((void *)flushBuffer);
7977+ #else
7978+ free ((void *)flushBuffer);
7979+ #endif
79387980
79397981#ifdef __VMAWARE_DEBUG__
79407982 const double averageCycles = static_cast <double >(totalCycles) / classicIterations;
0 commit comments