From 86deeb3884b81f467b420a4162b7c7247ecf9cff Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:56:20 +0100 Subject: [PATCH 01/16] hyperv_fucker prototype --- TODO.md | 1 + assets/hyperv_fucker.png | Bin 0 -> 147468 bytes src/vmaware.hpp | 559 ++++++++++++++++++++++----------------- 3 files changed, 317 insertions(+), 243 deletions(-) create mode 100644 assets/hyperv_fucker.png diff --git a/TODO.md b/TODO.md index 41982f0..245ae0d 100644 --- a/TODO.md +++ b/TODO.md @@ -41,6 +41,7 @@ - [ ] maybe add internal is_cached functionalities in the cache fetchers - [ ] make the whole cache table into a mutex so i can claim it's thread-safe - [ ] make a medium post about it +- [ ] test the VM::modify_score() function # Distant plans - add the library to conan.io when released diff --git a/assets/hyperv_fucker.png b/assets/hyperv_fucker.png new file mode 100644 index 0000000000000000000000000000000000000000..d7f617817763076977f14e94b7c01e4c17639cf8 GIT binary patch literal 147468 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 literal 0 HcmV?d00001 diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 09be76c..c1be31c 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -703,26 +703,6 @@ struct VM { #endif } - // credits to VMProtect for the idea - // https://github.com/Obfuscator-Collections/VMProtect/blob/d70e027f9204c318e0190d336d9e0baa6048b197/runtime/loader.cc#L2478 - [[nodiscard]] static bool is_hyperv_root_partition() { - if (cpu_manufacturer(cpu::leaf::hypervisor) != "Microsoft Hv") { - core_debug("HYPERV_ROOT: no partition found due to wrong manufacturer"); - return false; - } - - if (global_flags.test(ENABLE_HYPERV_HOST)) { - core_debug("HYPERV_ROOT: no partition found due to no flag"); - return false; - } - - u32 unused, ebx = 0; - cpuid(unused, ebx, unused, unused, 0x40000003); - if (ebx & 1) { - core_debug("HYPERV_ROOT: found partition"); - } - return (ebx & 1); - } [[nodiscard]] static std::string cpu_manufacturer(const u32 p_leaf) { auto cpuid_thingy = [](const u32 p_leaf, u32* regs, std::size_t start = 0, std::size_t end = 4) -> bool { @@ -929,7 +909,7 @@ struct VM { // both Hyper-V and VirtualPC have the same string value if (brand_str == hyperv) { - if (is_hyperv_root_partition()) { + if (util::hyperv_fucker()) { return false; } return core::add(HYPERV, VPC); @@ -1076,6 +1056,24 @@ struct VM { return (!brand_cache.empty()); } }; + + struct hyperv { + static bool is_hyperv_host; + static bool is_stored; + + static bool fetch() { + return is_hyperv_host; + } + + static void store(const bool p_is_hyperv_host) { + is_hyperv_host = p_is_hyperv_host; + is_stored = true; + } + + static bool is_cached() { + return is_stored; + } + }; }; // miscellaneous functionalities @@ -1575,27 +1573,69 @@ struct VM { #else return false; #endif - } + } - [[nodiscard]] static std::string get_hostname() { + [[nodiscard]] static std::string get_hostname() { #if (MSVC) - char ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; - DWORD cbComputerName = sizeof(ComputerName); + char ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD cbComputerName = sizeof(ComputerName); - if (GetComputerName(ComputerName, &cbComputerName)) { - return std::string(ComputerName); - } + if (GetComputerName(ComputerName, &cbComputerName)) { + return std::string(ComputerName); + } #elif (LINUX) - char hostname[HOST_NAME_MAX]; + char hostname[HOST_NAME_MAX]; - if (gethostname(hostname, sizeof(hostname)) == 0) { - return std::string(hostname); - } + if (gethostname(hostname, sizeof(hostname)) == 0) { + return std::string(hostname); + } #endif - return std::string(); + return std::string(); + } + + + /** + * @brief Checks whether Hyper-V host artifacts are present instead of an actual Hyper-V VM + * @note idea and credits to Requiem (https://github.com/NotRequiem) + */ + [[nodiscard]] static bool hyperv_fucker() { +#if (!MSVC) + return false; +#else + if (memo::hyperv::is_cached()) { + return memo::hyperv::fetch(); } + auto add = [](const bool result) -> bool { + memo::hyperv::store(result); + return result; + }; + + u32 eax, unused = 0; + cpu::cpuid(eax, unused, unused, unused, cpu::leaf::hypervisor); + + if (eax == 12) { + const char* p = SMBIOS_string(); + + if (std::strcmp(p, "VIRTUAL MACHINE") == 0) { + return add(false); + } + + if (motherboard_string("Microsoft Corporation")) { + return add(false); + } + + // at this point, it's fair to assume it's a Hyper-V on host + return add(true); + } else if (eax == 11) { + // actual Hyper-V VM + return add(false); + } else { + return add(false); + } +#endif + } #if (MSVC) /** @@ -1895,8 +1935,184 @@ struct VM { return major_version; } + + + [[nodiscard]] char* SMBIOS_string() { + HKEY hk = 0; + int ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\mssmbios\\data", 0, KEY_ALL_ACCESS, &hk); + if (ret != ERROR_SUCCESS) { + return nullptr; + } + + bool is_vm = false; + unsigned long type = 0; + unsigned long length = 0; + ret = RegQueryValueExW(hk, L"SMBiosData", 0, &type, 0, &length); + + if (ret != ERROR_SUCCESS) { + RegCloseKey(hk); + return nullptr; + } + + if (length == 0) { + RegCloseKey(hk); + return nullptr; + } + + char* p = static_cast(LocalAlloc(LMEM_ZEROINIT, length)); + + if (p == nullptr) { + RegCloseKey(hk); + return nullptr; + } + + ret = RegQueryValueExW(hk, L"SMBiosData", 0, &type, (unsigned char*)p, &length); + + if (ret != ERROR_SUCCESS) { + LocalFree(p); + RegCloseKey(hk); + return nullptr; + } + + return p; + } + + bool motherboard_string(const wstring_t vm_string) { + HRESULT hres; + + hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) { + debug("VPC_BOARD: Failed to initialize COM library. Error code: ", hres); + return false; + } + + hres = CoInitializeSecurity( + NULL, + -1, // use the default authentication service + NULL, // use the default authorization service + NULL, // reserved + RPC_C_AUTHN_LEVEL_DEFAULT, // authentication + RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation + NULL, // authentication info + EOAC_NONE, // additional capabilities + NULL // reserved + ); + + if (FAILED(hres)) { + debug("VPC_BOARD: Failed to initialize security. Error code: ", hres); + CoUninitialize(); + return false; + } + + IWbemLocator* pLoc = NULL; + IWbemServices* pSvc = NULL; + + hres = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (LPVOID*)&pLoc + ); + + if (FAILED(hres)) { + debug("VPC_BOARD: Failed to create IWbemLocator object. Error code: ", hres); + CoUninitialize(); + return false; + } + + hres = pLoc->ConnectServer( + _bstr_t(L"ROOT\\CIMV2"), // Namespace + NULL, // User name + NULL, // User password + 0, // Locale + NULL, // Security flags + 0, // Authority + 0, // Context object pointer + &pSvc + ); + + if (FAILED(hres)) { + debug("VPC_BOARD: Failed to connect to WMI. Error code: ", hres); + pLoc->Release(); + CoUninitialize(); + return false; + } + + hres = CoSetProxyBlanket( + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + NULL, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + NULL, // client identity + EOAC_NONE // proxy capabilities + ); + + if (FAILED(hres)) { + debug("VPC_BOARD: Failed to set proxy blanket. Error code: ", hres); + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return false; + } + + IEnumWbemClassObject* enumerator = NULL; + hres = pSvc->ExecQuery( + bstr_t("WQL"), + bstr_t("SELECT * FROM Win32_BaseBoard"), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &enumerator + ); + + if (FAILED(hres)) { + debug("VPC_BOARD: Query for Win32_BaseBoard failed. Error code: ", hres); + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return false; + } + + IWbemClassObject* pclsObj = NULL; + ULONG uReturn = 0; + bool is_vm = false; + + while (enumerator) { + HRESULT hr = enumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if (uReturn == 0) { + break; + } + + VARIANT vtProp; + VariantInit(&vtProp) + hr = pclsObj->Get(L"Manufacturer", 0, &vtProp, 0, 0); + + if (SUCCEEDED(hr)) { + if (vtProp.vt == VT_BSTR && _wcsicmp(vtProp.bstrVal, vm_string) == 0) { + is_vm = true; + VariantClear(&vtProp); + break; + } + + VariantClear(&vtProp); + } + + pclsObj->Release(); + } + + enumerator->Release(); + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + + return is_vm; + } + #endif - }; + }; private: // START OF PRIVATE VM DETECTION TECHNIQUE DEFINITIONS @@ -1987,16 +2203,16 @@ struct VM { return false; } - if (cpu::is_hyperv_root_partition()) { + if (util::hyperv_fucker()) { return false; } - constexpr u8 hypervisor_bit = 31; + constexpr u32 hypervisor_bit = (1 << 31); u32 unused, ecx = 0; cpu::cpuid(unused, unused, ecx, unused, 1); - return (ecx & (1 << hypervisor_bit)); + return (ecx & hypervisor_bit); #endif } catch (...) { @@ -2013,7 +2229,7 @@ struct VM { #if (!x86) return false; #else - if (cpu::is_hyperv_root_partition()) { + if (util::hyperv_fucker()) { return false; } @@ -3972,135 +4188,7 @@ struct VM { #if (!MSVC) return false; #else - HRESULT hres; - - hres = CoInitializeEx(0, COINIT_MULTITHREADED); - if (FAILED(hres)) { - debug("VPC_BOARD: Failed to initialize COM library. Error code: ", hres); - return false; - } - - hres = CoInitializeSecurity( - NULL, - -1, // use the default authentication service - NULL, // use the default authorization service - NULL, // reserved - RPC_C_AUTHN_LEVEL_DEFAULT, // authentication - RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation - NULL, // authentication info - EOAC_NONE, // additional capabilities - NULL // reserved - ); - - if (FAILED(hres)) { - debug("VPC_BOARD: Failed to initialize security. Error code: ", hres); - CoUninitialize(); - return false; - } - - IWbemLocator* pLoc = NULL; - IWbemServices* pSvc = NULL; - - hres = CoCreateInstance( - CLSID_WbemLocator, - 0, - CLSCTX_INPROC_SERVER, - IID_IWbemLocator, - (LPVOID*)&pLoc - ); - - if (FAILED(hres)) { - debug("VPC_BOARD: Failed to create IWbemLocator object. Error code: ", hres); - CoUninitialize(); - return false; - } - - hres = pLoc->ConnectServer( - _bstr_t(L"ROOT\\CIMV2"), // Namespace - NULL, // User name - NULL, // User password - 0, // Locale - NULL, // Security flags - 0, // Authority - 0, // Context object pointer - &pSvc - ); - - if (FAILED(hres)) { - debug("VPC_BOARD: Failed to connect to WMI. Error code: ", hres); - pLoc->Release(); - CoUninitialize(); - return false; - } - - hres = CoSetProxyBlanket( - pSvc, // Indicates the proxy to set - RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx - RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx - NULL, // Server principal name - RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx - RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx - NULL, // client identity - EOAC_NONE // proxy capabilities - ); - - if (FAILED(hres)) { - debug("VPC_BOARD: Failed to set proxy blanket. Error code: ", hres); - pSvc->Release(); - pLoc->Release(); - CoUninitialize(); - return false; - } - - IEnumWbemClassObject* enumerator = NULL; - hres = pSvc->ExecQuery( - bstr_t("WQL"), - bstr_t("SELECT * FROM Win32_BaseBoard"), - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, - NULL, - &enumerator - ); - - if (FAILED(hres)) { - debug("VPC_BOARD: Query for Win32_BaseBoard failed. Error code: ", hres); - pSvc->Release(); - pLoc->Release(); - CoUninitialize(); - return false; - } - - IWbemClassObject* pclsObj = NULL; - ULONG uReturn = 0; - bool is_vm = false; - - while (enumerator) { - HRESULT hr = enumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); - - if (uReturn == 0) { - break; - } - - VARIANT vtProp; - VariantInit(&vtProp); - hr = pclsObj->Get(L"Manufacturer", 0, &vtProp, 0, 0); - - if (SUCCEEDED(hr)) { - if (vtProp.vt == VT_BSTR && _wcsicmp(vtProp.bstrVal, L"Microsoft Corporation") == 0) { - is_vm = true; - VariantClear(&vtProp); - break; - } - - VariantClear(&vtProp); - } - - pclsObj->Release(); - } - - enumerator->Release(); - pSvc->Release(); - pLoc->Release(); - CoUninitialize(); + const bool is_vm = util::motherboard_string("Microsoft Corporation"); if (is_vm) { return core::add(VPC); @@ -4413,41 +4501,7 @@ struct VM { #if (!MSVC) return false; #else - HKEY hk = 0; - int ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\mssmbios\\data", 0, KEY_ALL_ACCESS, &hk); - if (ret != ERROR_SUCCESS) { - return false; - } - - bool is_vm = false; - unsigned long type = 0; - unsigned long length = 0; - ret = RegQueryValueExW(hk, L"SMBiosData", 0, &type, 0, &length); - - if (ret != ERROR_SUCCESS) { - RegCloseKey(hk); - return false; - } - - if (length == 0) { - RegCloseKey(hk); - return false; - } - - char* p = static_cast(LocalAlloc(LMEM_ZEROINIT, length)); - - if (p == nullptr) { - RegCloseKey(hk); - return false; - } - - ret = RegQueryValueExW(hk, L"SMBiosData", 0, &type, (unsigned char*)p, &length); - - if (ret != ERROR_SUCCESS) { - LocalFree(p); - RegCloseKey(hk); - return false; - } + const char* p = util::SMBIOS_string(); auto ScanDataForString = [](unsigned char* data, unsigned long data_length, unsigned char* string2) -> unsigned char* { std::size_t string_length = strlen(reinterpret_cast(string2)); @@ -7452,7 +7506,7 @@ struct VM { #if (!x86) return false; #else - if (cpu::is_hyperv_root_partition()) { + if (util::hyperv_fucker()) { return false; } @@ -7787,7 +7841,7 @@ struct VM { #if (!x86) return false; #else - if (cpu::is_hyperv_root_partition()) { + if (util::hyperv_fucker()) { return false; } @@ -7825,7 +7879,7 @@ struct VM { #if (!x86) return false; #else - if (cpu::is_hyperv_root_partition()) { + if (util::hyperv_fucker()) { return false; } @@ -8460,44 +8514,14 @@ information about the hypervisor Linux is running on } if (flag == SPOOFABLE) { - flag_collector.set(VM::MAC); - flag_collector.set(VM::DOCKERENV); - flag_collector.set(VM::HWMON); - flag_collector.set(VM::CURSOR); - flag_collector.set(VM::VMWARE_REG); - flag_collector.set(VM::VBOX_REG); - flag_collector.set(VM::USER); - flag_collector.set(VM::DLL); - flag_collector.set(VM::REGISTRY); - flag_collector.set(VM::CWSANDBOX_VM); - flag_collector.set(VM::VM_FILES); - flag_collector.set(VM::HWMODEL); - flag_collector.set(VM::COMPUTER_NAME); - flag_collector.set(VM::HOSTNAME); - flag_collector.set(VM::KVM_REG); - flag_collector.set(VM::KVM_DRIVERS); - flag_collector.set(VM::KVM_DIRS); - flag_collector.set(VM::LOADED_DLLS); - flag_collector.set(VM::QEMU_DIR); - flag_collector.set(VM::MOUSE_DEVICE); - flag_collector.set(VM::VM_PROCESSES); - flag_collector.set(VM::LINUX_USER_HOST); - flag_collector.set(VM::HYPERV_REG); - flag_collector.set(VM::MAC_MEMSIZE); - flag_collector.set(VM::MAC_IOKIT); - flag_collector.set(VM::IOREG_GREP); - flag_collector.set(VM::MAC_SIP); - flag_collector.set(VM::HKLM_REGISTRIES); - flag_collector.set(VM::QEMU_GA); - flag_collector.set(VM::QEMU_PROC); - flag_collector.set(VM::VPC_PROC); - flag_collector.set(VM::VM_FILES_EXTRA); - flag_collector.set(VM::UPTIME); - flag_collector.set(VM::CUCKOO_DIR); - flag_collector.set(VM::CUCKOO_PIPE); - flag_collector.set(VM::HYPERV_HOSTNAME); - flag_collector.set(VM::GENERAL_HOSTNAME); - flag_collector.set(VM::BLUESTACKS_FOLDERS); + for (const auto& tmp : technique_table) { + const u8 technique_macro = tmp.first; + const technique &tuple = tmp.second; + + if (tuple.spoofable) { + flag_collector.set(technique_macro); + } + } } flag_collector.set(flag); @@ -9015,6 +9039,52 @@ information about the hypervisor Linux is running on } + /** + * @brief Change the certainty score of a technique + * @param technique flag, then the new percentage score to overwite + * @return void + * @warning ⚠️ FOR DEVELOPMENT USAGE ONLY, NOT MEANT FOR PUBLIC USE ⚠️ + */ + static void modify_score( + const enum_flags flag, + const std::uint8_t percent + // clang doesn't support std::source_location for some reason +#if (CPP >= 20 && !CLANG) + , const std::source_location& loc = std::source_location::current() +#endif + ) { + auto throw_error = [&](const char* text) -> void { + std::stringstream ss; +#if (CPP >= 20 && !CLANG) + ss << ", error in " << loc.function_name() << " at " << loc.file_name() << ":" << loc.line() << ")"; +#endif + ss << ". Consult the documentation's parameters for VM::add_custom()"; + throw std::invalid_argument(std::string(text) + ss.str()); + }; + + if (percent > 100) { + throw_error("Percentage parameter must be between 0 and 100"); + } + +#if (CPP >= 23) + [[assume(percent <= 100)]]; +#endif + + if (static_cast(flag) < technique_end) { + throw_error("The flag is not a technique flag"); + } + + using table_t = std::map; + + auto modify = [](table_t &table, const enum_flags flag, const u8 percent) -> void { + core::technique &tmp = table.at(flag); + table[flag] = { percent, tmp.run, tmp.spoofable }; + }; + + modify(const_cast(core::technique_table), flag, percent); + } + + /** * @brief disable the provided technique flags so they are not counted to the overall result * @param technique flag(s) only @@ -9116,6 +9186,9 @@ VM::flagset VM::memo::cache_keys = 0; std::string VM::memo::brand::brand_cache = ""; std::string VM::memo::multi_brand::brand_cache = ""; std::string VM::memo::cpu_brand::brand_cache = ""; +bool VM::memo::hyperv::is_hyperv_host = false; +bool VM::memo::hyperv::is_stored = false; + #ifdef __VMAWARE_DEBUG__ VM::u16 VM::total_points = 0; #endif From 2e9e16e3d8dd3261450b99962246cf1179b713bc Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 16:15:36 +0100 Subject: [PATCH 02/16] hyperv_fucker prototype 2 --- CMakeLists.txt | 2 +- src/vmaware.hpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff2963d..81f9543 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,7 @@ if (MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Od") elseif(CMAKE_BUILD_TYPE MATCHES "Release") MESSAGE(STATUS "Build set to release mode") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O3") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2") endif() elseif(LINUX) if(CMAKE_BUILD_TYPE MATCHES "Debug") diff --git a/src/vmaware.hpp b/src/vmaware.hpp index c1be31c..a92a089 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1622,7 +1622,7 @@ struct VM { return add(false); } - if (motherboard_string("Microsoft Corporation")) { + if (motherboard_string(L"Microsoft Corporation")) { return add(false); } @@ -1937,7 +1937,7 @@ struct VM { } - [[nodiscard]] char* SMBIOS_string() { + [[nodiscard]] static char* SMBIOS_string() { HKEY hk = 0; int ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\mssmbios\\data", 0, KEY_ALL_ACCESS, &hk); if (ret != ERROR_SUCCESS) { @@ -1977,7 +1977,7 @@ struct VM { return p; } - bool motherboard_string(const wstring_t vm_string) { + [[nodiscard]] static bool motherboard_string(const wchar_t* vm_string) { HRESULT hres; hres = CoInitializeEx(0, COINIT_MULTITHREADED); @@ -2087,7 +2087,7 @@ struct VM { } VARIANT vtProp; - VariantInit(&vtProp) + VariantInit(&vtProp); hr = pclsObj->Get(L"Manufacturer", 0, &vtProp, 0, 0); if (SUCCEEDED(hr)) { @@ -4188,7 +4188,7 @@ struct VM { #if (!MSVC) return false; #else - const bool is_vm = util::motherboard_string("Microsoft Corporation"); + const bool is_vm = util::motherboard_string(L"Microsoft Corporation"); if (is_vm) { return core::add(VPC); From ba0eea2d4a850a516da40d8aa3a156e576835c56 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:18:54 +0100 Subject: [PATCH 03/16] hyperv_fucker prototype 3 --- docs/documentation.md | 2 + src/cli.cpp | 49 +++++++-------- src/vmaware.hpp | 142 +++++++++++++++++++----------------------- 3 files changed, 86 insertions(+), 107 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index 5cda705..bdc7154 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -199,6 +199,8 @@ This will essentially return the VM brand as a `std::string`. The exact possible - `Intel KGT (Trusty)` - `Microsoft Azure Hyper-V` - `Xbox NanoVisor (Hyper-V)` +- `SimpleVisor` +- `Hyper-V artifact (not an actual VM)` If none were detected, it will return `Unknown`. It's often NOT going to produce a satisfying result due to technical difficulties with accomplishing this, on top of being highly dependent on what mechanisms detected a VM. This is especially true for VMware sub-versions (ESX, GSX, Fusion, etc...) Don't rely on this function for critical operations as if it's your golden bullet. It's arguably unreliable and it'll most likely return `Unknown` (assuming it is actually running under a VM). diff --git a/src/cli.cpp b/src/cli.cpp index bfaaed9..fdb5e36 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -137,7 +137,6 @@ R"(Usage: -t | --type returns the VM type (if a VM was found) Extra: - --disable-hyperv-host disable the possibility of Hyper-V default virtualisation result on host OS --disable-notes no notes will be provided --spoofable allow spoofable techniques to be ran (not included by default) @@ -247,6 +246,7 @@ Intel KGT (Trusty) Microsoft Azure Hyper-V Xbox NanoVisor (Hyper-V) SimpleVisor +Hyper-V artifact (not an actual VM) )"; std::exit(0); @@ -258,6 +258,7 @@ std::string type(const std::string &brand_str) { } const std::map type_table { + // type 1 { "Xen HVM", "Hypervisor (type 1)" }, { "VMware ESX", "Hypervisor (type 1)" }, { "ACRN", "Hypervisor (type 1)" }, @@ -274,6 +275,7 @@ std::string type(const std::string &brand_str) { { "Intel KGT (Trusty)", "Hypervisor (type 1)" }, { "SimpleVisor", "Hypervisor (type 1)" }, + // type 2 { "VirtualBox", "Hypervisor (type 2)" }, { "VMware", "Hypervisor (type 2)" }, { "VMware Express", "Hypervisor (type 2)" }, @@ -286,6 +288,7 @@ std::string type(const std::string &brand_str) { { "NetBSD NVMM", "Hypervisor (type 2)" }, { "OpenBSD VMM", "Hypervisor (type 2)" }, + // sandbox { "Cuckoo", "Sandbox" }, { "Sandboxie", "Sandbox" }, { "Hybrid Analysis", "Sandbox" }, @@ -295,6 +298,7 @@ std::string type(const std::string &brand_str) { { "Comodo", "Sandbox" }, { "ThreatExpert", "Sandbox" }, + // misc { "Bochs", "Emulator" }, { "BlueStacks", "Emulator" }, { "Microsoft x86-to-ARM", "Emulator" }, @@ -305,7 +309,8 @@ std::string type(const std::string &brand_str) { { "Microsoft Virtual PC/Hyper-V", "Hypervisor (either type 1 or 2)" }, { "Lockheed Martin LMHS", "Hypervisor (unknown type)" }, { "Wine", "Compatibility layer" }, - { "Apple VZ", "Unknown" } + { "Apple VZ", "Unknown" }, + { "Hyper-V artifact (not an actual VM)", "No VM" } }; auto it = type_table.find(brand_str); @@ -560,19 +565,24 @@ void general() { std::string brand = VM::brand(VM::MULTIPLE, spoofable_setting); - std::cout << "VM brand: " << (brand == "Unknown" ? red : green) << brand << ansi_exit << "\n"; + std::cout << "VM brand: " << ((brand == "Unknown") || (brand == "Hyper-V artifact (not an actual VM)") ? red : green) << brand << ansi_exit << "\n"; + // meaning "if there's no brand conflicts" if (brand.find(" or ") == std::string::npos) { const std::string brand = VM::brand(VM::MULTIPLE, spoofable_setting); const std::string type_value = type(brand); std::cout << "VM type: "; + + std::string color = ""; - if (type_value == "Unknown") { - std::cout << red << "Unknown" << ansi_exit << "\n"; + if (type_value == "Unknown" || type_value == "No VM") { + color = red; } else { - std::cout << green << type_value << ansi_exit << "\n"; + color = green; } + + std::cout << color << type_value << ansi_exit << "\n"; } const char* percent_color = ""; @@ -634,28 +644,8 @@ void general() { << "\n\n"; - auto is_hyperv_present = []() -> bool { - std::map brand_map = VM::brand_map(); - bool is_hyperv_vpc_present = false; - - for (const auto p_brand : brand_map) { - if (p_brand.second == 0) { - continue; - } - - if ( - (std::strcmp(p_brand.first, "Microsoft Hyper-V") == 0) || - (std::strcmp(p_brand.first, "Virtual PC") == 0) - ) { - is_hyperv_vpc_present = true; - } - } - - return is_hyperv_vpc_present; - }; - - if ((hyperv_setting == VM::ENABLE_HYPERV_HOST) && is_hyperv_present() && notes_enabled) { - std::cout << note << " If you know you are running on host, Hyper-V leaves VM artifacts in CPUIDs which makes the system look like it's running in a Hyper-V VM when it's not. If you want to disable this mechanism, run with \"--disable-hyperv-host\", or disable Hyper-V in your system.\n\n"; + if ((hyperv_setting == VM::ENABLE_HYPERV_HOST) && (brand == "Hyper-V artifact (not an actual VM)") && notes_enabled) { + std::cout << note << " If you know you are running on host, Hyper-V leaves VM artifacts in CPUIDs which makes the system look like it's running in a Hyper-V VM when it's not. If you want to disable this mechanism, uninstall Hyper-V in your system.\n\n"; } else if (notes_enabled) { if (!arg_bitset.test(SPOOFABLE)) { std::cout << tip << "To enable spoofable techniques, run with the \"--spoofable\" argument\n\n"; @@ -768,6 +758,9 @@ int main(int argc, char* argv[]) { if (arg_bitset.test(HYPERV) == false) { setting_bits.set(VM::ENABLE_HYPERV_HOST); + } else { + std::cerr << "--disable-hyperv-host has been deprecated, the determination of whether it's a host Hyper-V or VM Hyper-V is now done automatically"; + return 1; } if (arg_bitset.test(SPOOFABLE)) { diff --git a/src/vmaware.hpp b/src/vmaware.hpp index a92a089..33b2302 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -519,6 +519,7 @@ struct VM { static constexpr const char* AZURE_HYPERV = "Microsoft Azure Hyper-V"; static constexpr const char* NANOVISOR = "Xbox NanoVisor (Hyper-V)"; static constexpr const char* SIMPLEVISOR = "SimpleVisor"; + static constexpr const char* HYPERV_ARTIFACT = "Hyper-V artifact (not an actual VM)"; static flagset global_flags; // for certain techniques where the flags MUST be accessible @@ -1609,6 +1610,11 @@ struct VM { auto add = [](const bool result) -> bool { memo::hyperv::store(result); + + if (result == true) { + core::add(HYPERV_ARTIFACT); + } + return result; }; @@ -1944,9 +1950,9 @@ struct VM { return nullptr; } - bool is_vm = false; unsigned long type = 0; unsigned long length = 0; + ret = RegQueryValueExW(hk, L"SMBiosData", 0, &type, 0, &length); if (ret != ERROR_SUCCESS) { @@ -1974,6 +1980,7 @@ struct VM { return nullptr; } + RegCloseKey(hk); return p; } @@ -4503,54 +4510,30 @@ struct VM { #else const char* p = util::SMBIOS_string(); - auto ScanDataForString = [](unsigned char* data, unsigned long data_length, unsigned char* string2) -> unsigned char* { - std::size_t string_length = strlen(reinterpret_cast(string2)); - - for (std::size_t i = 0; i <= (data_length - string_length); i++) { - if (strncmp(reinterpret_cast(&data[i]), reinterpret_cast(string2), string_length) == 0) { - return &data[i]; - } - } - - return 0; - }; - - auto AllToUpper = [](char* str, std::size_t len) { - for (std::size_t i = 0; i < len; ++i) { - str[i] = static_cast(std::toupper(static_cast(str[i]))); - } - }; - - AllToUpper(p, length); - - // cleaner and better shortcut than typing reinterpret_cast a million times - auto cast = [](char* p) -> unsigned char* { - return reinterpret_cast(p); - }; + bool is_vm = false; - unsigned char* x1 = ScanDataForString(cast(p), length, (unsigned char*)("INNOTEK GMBH")); - unsigned char* x2 = ScanDataForString(cast(p), length, (unsigned char*)("VIRTUALBOX")); - unsigned char* x3 = ScanDataForString(cast(p), length, (unsigned char*)("SUN MICROSYSTEMS")); - unsigned char* x4 = ScanDataForString(cast(p), length, (unsigned char*)("VBOXVER")); - unsigned char* x5 = ScanDataForString(cast(p), length, (unsigned char*)("VIRTUAL MACHINE")); + const bool x1 = (std::strcmp(p, "INNOTEK GMBH") == 0); + const bool x2 = (std::strcmp(p, "VIRTUALBOX") == 0); + const bool x3 = (std::strcmp(p, "SUN MICROSYSTEMS") == 0); + const bool x4 = (std::strcmp(p, "VBOXVER") == 0); + const bool x5 = (std::strcmp(p, "VIRTUAL MACHINE") == 0); if (x1 || x2 || x3 || x4 || x5) { is_vm = true; #ifdef __VMAWARE_DEBUG__ - if (x1) { debug("VBOX_MSSMBIOS: x1 = ", x1); } - if (x2) { debug("VBOX_MSSMBIOS: x2 = ", x2); } - if (x3) { debug("VBOX_MSSMBIOS: x3 = ", x3); } - if (x4) { debug("VBOX_MSSMBIOS: x4 = ", x4); } - if (x5) { debug("VBOX_MSSMBIOS: x5 = ", x5); } + if (x1) { debug("VBOX_MSSMBIOS: x1 = ", p); } + if (x2) { debug("VBOX_MSSMBIOS: x2 = ", p); } + if (x3) { debug("VBOX_MSSMBIOS: x3 = ", p); } + if (x4) { debug("VBOX_MSSMBIOS: x4 = ", p); } + if (x5) { debug("VBOX_MSSMBIOS: x5 = ", p); } #endif } LocalFree(p); - RegCloseKey(hk); if (is_vm) { if (x5) { - return true; + return true; // Hyper-V and VirtualBox both have the same BIOS string with "VIRTUAL MACHINE" } return core::add(VBOX); @@ -9039,6 +9022,46 @@ information about the hypervisor Linux is running on } + /** + * @brief disable the provided technique flags so they are not counted to the overall result + * @param technique flag(s) only + * @link https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#vmdetect + * @return flagset + */ + template + static flagset DISABLE(Args ...args) { + flagset flags = core::disabled_arg_handler(args...); + + flags.flip(); + flags.set(NO_MEMO, 0); + flags.set(HIGH_THRESHOLD, 0); + flags.set(SPOOFABLE, 0); + flags.set(ENABLE_HYPERV_HOST, 0); + flags.set(MULTIPLE, 0); + + return flags; + } + + + /** + * @brief return a vector of detected brand strings (DEVELOPMENT FUNCTION, NOT MEANT FOR PUBLIC USE) + * @param any flag combination in VM structure or nothing + * @warning ⚠️ FOR DEVELOPMENT USAGE ONLY, NOT MEANT FOR PUBLIC USE ⚠️ + */ + template + static std::map brand_map(Args ...args) { + flagset flags = core::arg_handler(args...); + + // are all the techiques already run? if not, run all of them to get the necessary info to fetch the brand + if (!memo::all_present() || core::is_enabled(flags, NO_MEMO)) { + u16 tmp = core::run_all(flags); + UNUSED(tmp); + } + + return core::brand_scoreboard; + } + + /** * @brief Change the certainty score of a technique * @param technique flag, then the new percentage score to overwite @@ -9070,7 +9093,7 @@ information about the hypervisor Linux is running on [[assume(percent <= 100)]]; #endif - if (static_cast(flag) < technique_end) { + if (static_cast(flag) >= technique_end) { throw_error("The flag is not a technique flag"); } @@ -9083,46 +9106,6 @@ information about the hypervisor Linux is running on modify(const_cast(core::technique_table), flag, percent); } - - - /** - * @brief disable the provided technique flags so they are not counted to the overall result - * @param technique flag(s) only - * @link https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#vmdetect - * @return flagset - */ - template - static flagset DISABLE(Args ...args) { - flagset flags = core::disabled_arg_handler(args...); - - flags.flip(); - flags.set(NO_MEMO, 0); - flags.set(HIGH_THRESHOLD, 0); - flags.set(SPOOFABLE, 0); - flags.set(ENABLE_HYPERV_HOST, 0); - flags.set(MULTIPLE, 0); - - return flags; - } - - - /** - * @brief return a vector of detected brand strings (DEVELOPMENT FUNCTION, NOT MEANT FOR PUBLIC USE) - * @param any flag combination in VM structure or nothing - * @warning ⚠️ FOR DEVELOPMENT USAGE ONLY, NOT MEANT FOR PUBLIC USE ⚠️ - */ - template - static std::map brand_map(Args ...args) { - flagset flags = core::arg_handler(args...); - - // are all the techiques already run? if not, run all of them to get the necessary info to fetch the brand - if (!memo::all_present() || core::is_enabled(flags, NO_MEMO)) { - u16 tmp = core::run_all(flags); - UNUSED(tmp); - } - - return core::brand_scoreboard; - } }; MSVC_ENABLE_WARNING(ASSIGNMENT_OPERATOR NO_INLINE_FUNC SPECTRE) @@ -9176,7 +9159,8 @@ std::map VM::core::brand_scoreboard { { VM::INTEL_KGT, 0 }, { VM::AZURE_HYPERV, 0 }, { VM::NANOVISOR, 0 }, - { VM::SIMPLEVISOR, 0 } + { VM::SIMPLEVISOR, 0 }, + { VM::HYPERV_ARTIFACT, 0 } }; From d9ca5d21cf877b32ab045c0bd454e63abad2775f Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:40:22 +0100 Subject: [PATCH 04/16] hyperv_fucker prototype 4 --- src/cli.cpp | 4 ++-- src/vmaware.hpp | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/cli.cpp b/src/cli.cpp index fdb5e36..898e236 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -569,8 +569,8 @@ void general() { // meaning "if there's no brand conflicts" if (brand.find(" or ") == std::string::npos) { - const std::string brand = VM::brand(VM::MULTIPLE, spoofable_setting); - const std::string type_value = type(brand); + const std::string tmp_brand = VM::brand(VM::MULTIPLE, spoofable_setting); + const std::string type_value = type(tmp_brand); std::cout << "VM type: "; diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 33b2302..65e4402 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -2214,12 +2214,10 @@ struct VM { return false; } - constexpr u32 hypervisor_bit = (1 << 31); - u32 unused, ecx = 0; cpu::cpuid(unused, unused, ecx, unused, 1); - return (ecx & hypervisor_bit); + return (ecx & (1 << 31)); #endif } catch (...) { @@ -4510,6 +4508,11 @@ struct VM { #else const char* p = util::SMBIOS_string(); + if (p == nullptr) { + debug("MSSMBIOS: nullptr detected, returned false"); + return false; + } + bool is_vm = false; const bool x1 = (std::strcmp(p, "INNOTEK GMBH") == 0); @@ -4529,8 +4532,6 @@ struct VM { #endif } - LocalFree(p); - if (is_vm) { if (x5) { return true; // Hyper-V and VirtualBox both have the same BIOS string with "VIRTUAL MACHINE" From ea2646686e81770273246b0419159b91dabd9c80 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:01:01 +0100 Subject: [PATCH 05/16] hyperv_fucker prototype 5 --- src/vmaware.hpp | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 65e4402..9a9df1c 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1605,6 +1605,7 @@ struct VM { return false; #else if (memo::hyperv::is_cached()) { + core_debug("HYPERV_FUCKER: returned from cache = ", memo::hyperv::fetch()); return memo::hyperv::fetch(); } @@ -1620,15 +1621,22 @@ struct VM { u32 eax, unused = 0; cpu::cpuid(eax, unused, unused, unused, cpu::leaf::hypervisor); + core_debug("HYPERV_FUCKER: eax = ", eax); if (eax == 12) { const char* p = SMBIOS_string(); + core_debug("HYPERV_FUCKER: SMBIOS string = ", p); + if (std::strcmp(p, "VIRTUAL MACHINE") == 0) { return add(false); } - if (motherboard_string(L"Microsoft Corporation")) { + const bool motherboard = motherboard_string(L"Microsoft Corporation"); + + core_debug("HYPERV_FUCKER: motherboard string = ", motherboard); + + if (motherboard == true) { return add(false); } @@ -8771,6 +8779,7 @@ information about the hypervisor Linux is running on constexpr const char* TMP_HYPERV_VPC = "Microsoft Virtual PC/Hyper-V"; constexpr const char* TMP_AZURE = "Microsoft Azure Hyper-V"; constexpr const char* TMP_NANOVISOR = "Xbox NanoVisor (Hyper-V)"; + constexpr const char* TMP_HYPERV_ARTIFACT = "Hyper-V artifact (not an actual VM)"; #else constexpr const char* TMP_QEMU = QEMU; constexpr const char* TMP_KVM = KVM; @@ -8790,6 +8799,7 @@ information about the hypervisor Linux is running on constexpr const char* TMP_HYPERV_VPC = "Microsoft Virtual PC/Hyper-V"; constexpr const char* TMP_AZURE = AZURE_HYPERV; constexpr const char* TMP_NANOVISOR = NANOVISOR; + constexpr const char* TMP_HYPERV_ARTIFACT = HYPERV_ARTIFACT; #endif std::map brands; @@ -8844,27 +8854,32 @@ information about the hypervisor Linux is running on brands.emplace(std::make_pair(TMP_HYPERV_VPC, 2)); } - merger(TMP_AZURE, TMP_HYPERV, TMP_AZURE); - merger(TMP_AZURE, TMP_VPC, TMP_AZURE); + merger(TMP_HYPERV, TMP_HYPERV_ARTIFACT, TMP_HYPERV_ARTIFACT); + merger(TMP_VPC, TMP_HYPERV_ARTIFACT, TMP_HYPERV_ARTIFACT); + merger(TMP_HYPERV_VPC, TMP_HYPERV_ARTIFACT, TMP_HYPERV_ARTIFACT); + + merger(TMP_AZURE, TMP_HYPERV, TMP_AZURE); + merger(TMP_AZURE, TMP_VPC, TMP_AZURE); merger(TMP_AZURE, TMP_HYPERV_VPC, TMP_AZURE); - merger(TMP_NANOVISOR, TMP_HYPERV, TMP_NANOVISOR); - merger(TMP_NANOVISOR, TMP_VPC, TMP_NANOVISOR); + merger(TMP_NANOVISOR, TMP_HYPERV, TMP_NANOVISOR); + merger(TMP_NANOVISOR, TMP_VPC, TMP_NANOVISOR); merger(TMP_NANOVISOR, TMP_HYPERV_VPC, TMP_NANOVISOR); - merger(TMP_QEMU, TMP_KVM, TMP_QEMU_KVM); - merger(TMP_KVM, TMP_HYPERV, TMP_KVM_HYPERV); - merger(TMP_QEMU, TMP_HYPERV, TMP_QEMU_KVM_HYPERV); - merger(TMP_QEMU_KVM, TMP_HYPERV, TMP_QEMU_KVM_HYPERV); - merger(TMP_KVM, TMP_KVM_HYPERV, TMP_KVM_HYPERV); - merger(TMP_QEMU, TMP_KVM_HYPERV, TMP_QEMU_KVM_HYPERV); - merger(TMP_QEMU_KVM, TMP_KVM_HYPERV, TMP_QEMU_KVM_HYPERV); + merger(TMP_QEMU, TMP_KVM, TMP_QEMU_KVM); + merger(TMP_KVM, TMP_HYPERV, TMP_KVM_HYPERV); + merger(TMP_QEMU, TMP_HYPERV, TMP_QEMU_KVM_HYPERV); + merger(TMP_QEMU_KVM, TMP_HYPERV, TMP_QEMU_KVM_HYPERV); + merger(TMP_KVM, TMP_KVM_HYPERV, TMP_KVM_HYPERV); + merger(TMP_QEMU, TMP_KVM_HYPERV, TMP_QEMU_KVM_HYPERV); + merger(TMP_QEMU_KVM, TMP_KVM_HYPERV, TMP_QEMU_KVM_HYPERV); + triple_merger(TMP_QEMU, TMP_KVM, TMP_KVM_HYPERV, TMP_QEMU_KVM_HYPERV); - merger(TMP_VMWARE, TMP_FUSION, TMP_FUSION); - merger(TMP_VMWARE, TMP_EXPRESS, TMP_EXPRESS); - merger(TMP_VMWARE, TMP_ESX, TMP_ESX); - merger(TMP_VMWARE, TMP_GSX, TMP_GSX); + merger(TMP_VMWARE, TMP_FUSION, TMP_FUSION); + merger(TMP_VMWARE, TMP_EXPRESS, TMP_EXPRESS); + merger(TMP_VMWARE, TMP_ESX, TMP_ESX); + merger(TMP_VMWARE, TMP_GSX, TMP_GSX); merger(TMP_VMWARE, TMP_WORKSTATION, TMP_WORKSTATION); using brand_element_t = std::pair; From 880a6ddfa8ed7399c27c840155e146e7dc1e8623 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:09:10 +0100 Subject: [PATCH 06/16] hyperv_fucker prototype 6 --- src/vmaware.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 9a9df1c..7e27e1d 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1619,8 +1619,11 @@ struct VM { return result; }; - u32 eax, unused = 0; - cpu::cpuid(eax, unused, unused, unused, cpu::leaf::hypervisor); + char out[sizeof(int32_t) * 4 + 1] = { 0 }; // e*x size + number of e*x registers + null terminator + cpu::cpuid((int*)out, cpu::leaf::hypervisor); + + const u32 eax = static_cast(out[0]); + core_debug("HYPERV_FUCKER: eax = ", eax); if (eax == 12) { @@ -2249,7 +2252,7 @@ struct VM { char out[sizeof(int32_t) * 4 + 1] = { 0 }; // e*x size + number of e*x registers + null terminator cpu::cpuid((int*)out, cpu::leaf::hypervisor); - debug("HYPERV_STR: eax: ", static_cast(out[0]), + debug("HYPERVISOR_STR: eax: ", static_cast(out[0]), "\nebx: ", static_cast(out[1]), "\necx: ", static_cast(out[2]), "\nedx: ", static_cast(out[3]) From d6a175b93a1499509b9f7be8415b630bc69f3a7b Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:49:55 +0100 Subject: [PATCH 07/16] hyperv_fucker prototype 7 --- src/vmaware.hpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 7e27e1d..2bc946a 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1627,11 +1627,11 @@ struct VM { core_debug("HYPERV_FUCKER: eax = ", eax); if (eax == 12) { - const char* p = SMBIOS_string(); + std::string p = SMBIOS_string(); core_debug("HYPERV_FUCKER: SMBIOS string = ", p); - if (std::strcmp(p, "VIRTUAL MACHINE") == 0) { + if (p == "VIRTUAL MACHINE") { return add(false); } @@ -1954,10 +1954,11 @@ struct VM { } - [[nodiscard]] static char* SMBIOS_string() { + [[nodiscard]] static std::string SMBIOS_string() { HKEY hk = 0; int ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\mssmbios\\data", 0, KEY_ALL_ACCESS, &hk); if (ret != ERROR_SUCCESS) { + debug("SMBIOS_string(): ret = error"); return nullptr; } @@ -1968,11 +1969,13 @@ struct VM { if (ret != ERROR_SUCCESS) { RegCloseKey(hk); + debug("SMBIOS_string(): ret = error 2"); return nullptr; } if (length == 0) { RegCloseKey(hk); + debug("SMBIOS_string(): length = 0"); return nullptr; } @@ -1980,6 +1983,7 @@ struct VM { if (p == nullptr) { RegCloseKey(hk); + debug("SMBIOS_string(): p = nullptr"); return nullptr; } @@ -1988,11 +1992,13 @@ struct VM { if (ret != ERROR_SUCCESS) { LocalFree(p); RegCloseKey(hk); + debug("SMBIOS_string(): ret = error 3"); return nullptr; } RegCloseKey(hk); - return p; + std::string tmp(p); + return tmp; } [[nodiscard]] static bool motherboard_string(const wchar_t* vm_string) { @@ -4519,11 +4525,16 @@ struct VM { #else const char* p = util::SMBIOS_string(); + if (p == nullptr) { debug("MSSMBIOS: nullptr detected, returned false"); return false; } +#ifdef __VMAWARE_DEBUG__ + debug("VBOX_MSSMBIOS: string = ", p); +#endif + bool is_vm = false; const bool x1 = (std::strcmp(p, "INNOTEK GMBH") == 0); @@ -4534,13 +4545,6 @@ struct VM { if (x1 || x2 || x3 || x4 || x5) { is_vm = true; -#ifdef __VMAWARE_DEBUG__ - if (x1) { debug("VBOX_MSSMBIOS: x1 = ", p); } - if (x2) { debug("VBOX_MSSMBIOS: x2 = ", p); } - if (x3) { debug("VBOX_MSSMBIOS: x3 = ", p); } - if (x4) { debug("VBOX_MSSMBIOS: x4 = ", p); } - if (x5) { debug("VBOX_MSSMBIOS: x5 = ", p); } -#endif } if (is_vm) { From 929ac5882ceaa0df8511d313c308a2af0dc98d93 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 20:29:24 +0100 Subject: [PATCH 08/16] hyperv_fucker prototype 8 --- src/vmaware.hpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 2bc946a..6e3be12 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -4523,10 +4523,9 @@ struct VM { #if (!MSVC) return false; #else - const char* p = util::SMBIOS_string(); + std::string p = util::SMBIOS_string(); - - if (p == nullptr) { + if (p.empty()) { debug("MSSMBIOS: nullptr detected, returned false"); return false; } @@ -4537,11 +4536,11 @@ struct VM { bool is_vm = false; - const bool x1 = (std::strcmp(p, "INNOTEK GMBH") == 0); - const bool x2 = (std::strcmp(p, "VIRTUALBOX") == 0); - const bool x3 = (std::strcmp(p, "SUN MICROSYSTEMS") == 0); - const bool x4 = (std::strcmp(p, "VBOXVER") == 0); - const bool x5 = (std::strcmp(p, "VIRTUAL MACHINE") == 0); + const bool x1 = (p == "INNOTEK GMBH"); + const bool x2 = (p == "VIRTUALBOX"); + const bool x3 = (p == "SUN MICROSYSTEMS"); + const bool x4 = (p == "VBOXVER"); + const bool x5 = (p == "VIRTUAL MACHINE"); if (x1 || x2 || x3 || x4 || x5) { is_vm = true; From 831c42c0f06eac4391f5799f6e1dba7b9d1a6ead Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 21:38:23 +0100 Subject: [PATCH 09/16] hyperv_fucker prototype 9 --- src/vmaware.hpp | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 6e3be12..a7b20bd 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1627,7 +1627,7 @@ struct VM { core_debug("HYPERV_FUCKER: eax = ", eax); if (eax == 12) { - std::string p = SMBIOS_string(); + const std::string p = SMBIOS_string(); core_debug("HYPERV_FUCKER: SMBIOS string = ", p); @@ -1996,9 +1996,41 @@ struct VM { return nullptr; } + // TODO: rewrite this, it sucks + auto ScanDataForString = [](unsigned char* data, unsigned long data_length, unsigned char* string2) -> unsigned char* { + std::size_t string_length = strlen(reinterpret_cast(string2)); + + for (std::size_t i = 0; i <= (data_length - string_length); i++) { + if (strncmp(reinterpret_cast(&data[i]), reinterpret_cast(string2), string_length) == 0) { + return &data[i]; + } + } + + return 0; + }; + + auto AllToUpper = [](char* str, std::size_t len) { + for (std::size_t i = 0; i < len; ++i) { + str[i] = static_cast(std::toupper(static_cast(str[i]))); + } + }; + + AllToUpper(p, length); + + // cleaner and better shortcut than typing reinterpret_cast a million times + auto cast = [](char* p) -> unsigned char* { + return reinterpret_cast(p); + }; + RegCloseKey(hk); - std::string tmp(p); - return tmp; + + if (ScanDataForString(cast(p), length, (unsigned char*)("INNOTEK GMBH"))) { std::string tmp(p); return tmp; } + if (ScanDataForString(cast(p), length, (unsigned char*)("VIRTUALBOX"))) { std::string tmp(p); return tmp; } + if (ScanDataForString(cast(p), length, (unsigned char*)("SUN MICROSYSTEMS"))) { std::string tmp(p); return tmp; } + if (ScanDataForString(cast(p), length, (unsigned char*)("VBOXVER"))) { std::string tmp(p); return tmp; } + if (ScanDataForString(cast(p), length, (unsigned char*)("VIRTUAL MACHINE"))) { std::string tmp(p); return tmp; } + + return nullptr; } [[nodiscard]] static bool motherboard_string(const wchar_t* vm_string) { @@ -4523,7 +4555,7 @@ struct VM { #if (!MSVC) return false; #else - std::string p = util::SMBIOS_string(); + const std::string p = util::SMBIOS_string(); if (p.empty()) { debug("MSSMBIOS: nullptr detected, returned false"); From 87eb8d40fb57e1f812c58e2dc7f2f965ab3ce844 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:00:59 +0100 Subject: [PATCH 10/16] hyperv_fucker prototype 10 --- .github/workflows/build_run_win_32_debug.bat | 3 +- .github/workflows/build_run_win_64_debug.bat | 3 +- src/vmaware.hpp | 38 ++++++++++++++------ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build_run_win_32_debug.bat b/.github/workflows/build_run_win_32_debug.bat index 57d07df..ffa0176 100644 --- a/.github/workflows/build_run_win_32_debug.bat +++ b/.github/workflows/build_run_win_32_debug.bat @@ -8,5 +8,4 @@ copy "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Redist\M copy "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\debug_nonredist\x86\Microsoft.VC143.DebugCRT\vcruntime140d.dll" Debug\ copy "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\debug_nonredist\x86\Microsoft.VC143.DebugCRT\msvcp140d.dll" Debug\ cd Debug -vmaware.exe -vmaware.exe --disable-hyperv-host \ No newline at end of file +vmaware.exe \ No newline at end of file diff --git a/.github/workflows/build_run_win_64_debug.bat b/.github/workflows/build_run_win_64_debug.bat index 1c6154a..f3b0c05 100644 --- a/.github/workflows/build_run_win_64_debug.bat +++ b/.github/workflows/build_run_win_64_debug.bat @@ -8,5 +8,4 @@ copy "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Redist\M copy "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\debug_nonredist\x86\Microsoft.VC143.DebugCRT\vcruntime140d.dll" Debug\ copy "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.30.30704\debug_nonredist\x86\Microsoft.VC143.DebugCRT\msvcp140d.dll" Debug\ cd Debug -vmaware.exe -vmaware.exe --disable-hyperv-host \ No newline at end of file +vmaware.exe \ No newline at end of file diff --git a/src/vmaware.hpp b/src/vmaware.hpp index a7b20bd..d2560a5 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1996,7 +1996,6 @@ struct VM { return nullptr; } - // TODO: rewrite this, it sucks auto ScanDataForString = [](unsigned char* data, unsigned long data_length, unsigned char* string2) -> unsigned char* { std::size_t string_length = strlen(reinterpret_cast(string2)); @@ -2006,7 +2005,7 @@ struct VM { } } - return 0; + return 0; }; auto AllToUpper = [](char* str, std::size_t len) { @@ -2017,20 +2016,37 @@ struct VM { AllToUpper(p, length); - // cleaner and better shortcut than typing reinterpret_cast a million times auto cast = [](char* p) -> unsigned char* { return reinterpret_cast(p); }; + unsigned char* x1 = ScanDataForString(cast(p), length, (unsigned char*)("INNOTEK GMBH")); + unsigned char* x2 = ScanDataForString(cast(p), length, (unsigned char*)("VIRTUALBOX")); + unsigned char* x3 = ScanDataForString(cast(p), length, (unsigned char*)("SUN MICROSYSTEMS")); + unsigned char* x4 = ScanDataForString(cast(p), length, (unsigned char*)("VBOXVER")); + unsigned char* x5 = ScanDataForString(cast(p), length, (unsigned char*)("VIRTUAL MACHINE")); + + std::string result = ""; + bool is_vm = false; + + if (x1 || x2 || x3 || x4 || x5) { + is_vm = true; + #ifdef __VMAWARE_DEBUG__ + if (x1) { debug("SMBIOS: x1 = ", x1); result = x1; } + if (x2) { debug("SMBIOS: x2 = ", x2); result = x2; } + if (x3) { debug("SMBIOS: x3 = ", x3); result = x3; } + if (x4) { debug("SMBIOS: x4 = ", x4); result = x4; } + if (x5) { debug("SMBIOS: x5 = ", x5); result = x5; } + #endif + } + LocalFree(p); RegCloseKey(hk); - if (ScanDataForString(cast(p), length, (unsigned char*)("INNOTEK GMBH"))) { std::string tmp(p); return tmp; } - if (ScanDataForString(cast(p), length, (unsigned char*)("VIRTUALBOX"))) { std::string tmp(p); return tmp; } - if (ScanDataForString(cast(p), length, (unsigned char*)("SUN MICROSYSTEMS"))) { std::string tmp(p); return tmp; } - if (ScanDataForString(cast(p), length, (unsigned char*)("VBOXVER"))) { std::string tmp(p); return tmp; } - if (ScanDataForString(cast(p), length, (unsigned char*)("VIRTUAL MACHINE"))) { std::string tmp(p); return tmp; } + if (is_vm) { + return result; + } - return nullptr; + return ""; } [[nodiscard]] static bool motherboard_string(const wchar_t* vm_string) { @@ -4558,12 +4574,12 @@ struct VM { const std::string p = util::SMBIOS_string(); if (p.empty()) { - debug("MSSMBIOS: nullptr detected, returned false"); + debug("MSSMBIOS: empty, returned false"); return false; } #ifdef __VMAWARE_DEBUG__ - debug("VBOX_MSSMBIOS: string = ", p); + debug("MSSMBIOS: string = ", p); #endif bool is_vm = false; From 4cefe360c61c7f23718932b94a7d3c2e24aaeef2 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:17:05 +0100 Subject: [PATCH 11/16] hyperv_fucker prototype 11 --- src/vmaware.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index d2560a5..9c4afb1 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -2032,11 +2032,11 @@ struct VM { if (x1 || x2 || x3 || x4 || x5) { is_vm = true; #ifdef __VMAWARE_DEBUG__ - if (x1) { debug("SMBIOS: x1 = ", x1); result = x1; } - if (x2) { debug("SMBIOS: x2 = ", x2); result = x2; } - if (x3) { debug("SMBIOS: x3 = ", x3); result = x3; } - if (x4) { debug("SMBIOS: x4 = ", x4); result = x4; } - if (x5) { debug("SMBIOS: x5 = ", x5); result = x5; } + if (x1) { debug("SMBIOS: x1 = ", x1); std::string tmp(x1); result = tmp; } + if (x2) { debug("SMBIOS: x2 = ", x2); std::string tmp(x2); result = tmp; } + if (x3) { debug("SMBIOS: x3 = ", x3); std::string tmp(x3); result = tmp; } + if (x4) { debug("SMBIOS: x4 = ", x4); std::string tmp(x4); result = tmp; } + if (x5) { debug("SMBIOS: x5 = ", x5); std::string tmp(x5); result = tmp; } #endif } LocalFree(p); From 066496b1196d8d16691dd499b1bedb938a7432f1 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:26:46 +0100 Subject: [PATCH 12/16] hyperv_fucker prototype 12 --- src/vmaware.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 9c4afb1..d8e53e3 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -2032,11 +2032,11 @@ struct VM { if (x1 || x2 || x3 || x4 || x5) { is_vm = true; #ifdef __VMAWARE_DEBUG__ - if (x1) { debug("SMBIOS: x1 = ", x1); std::string tmp(x1); result = tmp; } - if (x2) { debug("SMBIOS: x2 = ", x2); std::string tmp(x2); result = tmp; } - if (x3) { debug("SMBIOS: x3 = ", x3); std::string tmp(x3); result = tmp; } - if (x4) { debug("SMBIOS: x4 = ", x4); std::string tmp(x4); result = tmp; } - if (x5) { debug("SMBIOS: x5 = ", x5); std::string tmp(x5); result = tmp; } + if (x1) { debug("SMBIOS: x1 = ", x1); std::string tmp(reinterpret_cast(x1)); result = tmp; } + if (x2) { debug("SMBIOS: x2 = ", x2); std::string tmp(reinterpret_cast(x2)); result = tmp; } + if (x3) { debug("SMBIOS: x3 = ", x3); std::string tmp(reinterpret_cast(x3)); result = tmp; } + if (x4) { debug("SMBIOS: x4 = ", x4); std::string tmp(reinterpret_cast(x4)); result = tmp; } + if (x5) { debug("SMBIOS: x5 = ", x5); std::string tmp(reinterpret_cast(x5)); result = tmp; } #endif } LocalFree(p); From 8a8aa919e16834268b1fb6f213d231043a7c9c4f Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Sat, 10 Aug 2024 04:10:52 +0100 Subject: [PATCH 13/16] added event logs technique --- docs/documentation.md | 205 ++++++++++++------------ src/cli.cpp | 1 + src/vmaware.hpp | 356 ++++++++++++++++++++++++++++++------------ 3 files changed, 357 insertions(+), 205 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index bdc7154..c16d236 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -324,108 +324,108 @@ VM::add_custom(50, new_technique); VMAware provides a convenient way to not only check for VMs, but also have the flexibility and freedom for the end-user to choose what techniques are used with complete control over what gets executed or not. This is handled with a flag system. -| Flag alias | Description | Cross-platform? (empty = yes) | Certainty | Admin? | GPL-3.0? | 32-bit? | -| ---------- | ----------- | --------------- | --------- | ------ | -------- | ------- | -| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 | | 100% | | | | -| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | | 50% | | | | -| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | | 100% | | | | -| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | | 45% | | | | -| `VM::RDTSC` | Benchmark RDTSC and evaluate its speed, usually it's very slow in VMs | Linux and Windows | 10% | | | | -| `VM::THREADCOUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings (nowadays physical CPUs should have at least 4 threads for modern CPUs) | | 35% | | | | -| `VM::MAC` | Check if mac address starts with certain VM designated values | Linux and Windows | 60% | | | | -| `VM::TEMPERATURE` | Check if thermal directory in linux is present, might not be present in VMs | Linux | 15% | | | -| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | Linux | 70% | | | | -| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | Linux | 65% | | | | -| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | Linux | 10% | | | | -| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | Linux | 80% | | | | -| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | Linux | 55% | Admin | | | -| `VM::DMESG` | Check if dmesg output matches a VM brand | Linux | 55% | Admin | | | -| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | Linux | 75% | | | | -| `VM::SIDT5` | Check if the 5th byte after sidt is null | Linux | 45% | | | | -| `VM::CURSOR` | Check if cursor isn't active for 5 seconds (sign of automated VM environment) | Windows | 5% | | | | -| `VM::VMWARE_REG` | Check for VBox RdrDN | Windows | 65% | | | | -| `VM::VBOX_REG` | Look for any VirtualBox-specific registry data | Windows | 65% | | | | -| `VM::USER` | checks for default usernames, often a sign of a VM | Windows | 35% | | | | -| `VM::DLL` | Check for VM-specific DLLs | Windows | 50% | | | | -| `VM::REGISTRY` | Check for VM-specific registry values | Windows | 75% | | | | -| `VM::CWSANDBOX_VM` | Check if CWSandbox-specific file exists | Windows | 10% | | | | -| `VM::VM_FILES` | Find for VMware and VBox specific files | Windows | 10% | | | | -| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | MacOS | 75% | | | | -| `VM::DISK_SIZE` | Check if disk size is under or equal to 50GB | Linux | 60% | | | | -| `VM::VBOX_DEFAULT` | Check for default RAM and DISK sizes set by VirtualBox | Linux and Windows | 55% | Admin | | | -| `VM::VBOX_NETWORK` | Check for VirtualBox network provider string | Windows | 70% | | | | -| `VM::COMPUTER_NAME` | Check if the computer name (not username to be clear) is VM-specific | Windows | 40% | | GPL | | -| `VM::WINE_CHECK` | Check wine_get_unix_file_name file for Wine | Windows | 85% | | GPL | | -| `VM::HOSTNAME` | Check if hostname is specific | Windows | 25% | | GPL | | -| `VM::MEMORY` | Check if memory space is far too low for a physical machine | Windows | 35% | | GPL | | -| `VM::VBOX_WINDOW_CLASS` | Check for the window class for VirtualBox | Windows | 10% | | GPL | | -| `VM::LOADED_DLLS` | Check for loaded DLLs in the process | Windows | 75% | | GPL | | -| `VM::KVM_REG` | Check for KVM-specific registry strings | Windows | 75% | | GPL | | -| `VM::KVM_DRIVERS` | Check for KVM-specific .sys files in system driver directory | Windows | 55% | | GPL | | -| `VM::KVM_DIRS` | Check for KVM directory "Virtio-Win" | Windows | 55% | | GPL | | -| `VM::AUDIO` | Check if audio device is present | Windows | 35% | | GPL | | -| `VM::QEMU_DIR` | Check for QEMU-specific blacklisted directories | Windows | 45% | | GPL | | -| `VM::MOUSE_DEVICE` | Check for the presence of a mouse device | Windows | 20% | | GPL | | -| `VM::VM_PROCESSES` | Check for any VM processes that are active | Windows | 30% | | | | -| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | Linux | 25% | | | | -| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | Windows | 40% | | | | -| `VM::VMID_0X4` | Check if the CPU manufacturer ID matches that of a VM brand with leaf 0x40000000 | | 100% | | | | -| `VM::PARALLELS_VM` | Check for any indication of Parallels VM through BIOS data | Windows | 50% | | | | -| `VM::RDTSC_VMEXIT` | check through alternative RDTSC technique with VMEXIT | | 25% | | | | -| `VM::QEMU_BRAND` | Match for QEMU CPU brands with "QEMU Virtual CPU" string | | 100% | | | | -| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | | 95% | | | | -| `VM::VPC_BOARD` | Check through the motherboard and match for VirtualPC-specific string | Windows | 20% | | | | -| `VM::HYPERV_WMI` | Check WMI query for "Hyper-V RAW" string | Windows | 80% | | | | -| `VM::HYPERV_REG` | Check presence for Hyper-V specific string in registry | Windows | 80% | | | | -| `VM::BIOS_SERIAL` | Check if the BIOS serial is valid (null = VM) | Windows | 60% | | | | -| `VM::VBOX_FOLDERS` | Check for VirtualBox-specific string for shared folder ID | Windows | 45% | | | | -| `VM::MSSMBIOS` | Check MSSMBIOS registry for VM-specific strings | Windows | 75% | | | | -| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | MacOS | 30% | | | | -| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | MacOS | 80% | | | | -| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | MacOS | 75% | | | | -| `VM::MAC_SIP` | Check if System Integrity Protection is disabled (likely a VM if it is) | MacOS | 85% | | | | -| `VM::HKLM_REGISTRIES` | Check HKLM registries for specific VM strings | Windows | 70% | | | | -| `VM::QEMU_GA` | Check for "qemu-ga" process | Linux | 20% | | | | -| `VM::VALID_MSR` | check for valid MSR value 0x40000000 | Windows | 35% | | | | -| `VM::QEMU_PROC` | Check for QEMU processes | Windows | 30% | | | | -| `VM::VPC_PROC` | Check for VPC processes | Windows | 30% | | | | -| `VM::VPC_INVALID` | Check for official VPC method | Windows | 75% | | | 32-bit | -| `VM::SIDT` | Check for sidt instruction method | Linux, Windows | 30% | | | | -| `VM::SGDT` | Check for sgdt instruction method | Windows | 30% | | | 32-bit | -| `VM::SLDT` | Check for sldt instruction method | Windows | 15% | | | 32-bit | -| `VM::OFFSEC_SIDT` | Check for Offensive Security SIDT method | Windows | 60% | | | 32-bit | -| `VM::OFFSEC_SGDT` | Check for Offensive Security SGDT method | Windows | 60% | | | 32-bit | -| `VM::OFFSEC_SLDT` | Check for Offensive Security SLDT method | Windows | 20% | | | 32-bit | -| `VM::HYPERV_BOARD` | Check for Hyper-V specific string in motherboard | Windows | 45% | | | | -| `VM::VM_FILES_EXTRA` | Check for VPC and Parallels files | Windows | 70% | | | | -| `VM::VPC_SIDT` | Check for sidt method with VPC's 0xE8XXXXXX range | Windows | 15% | | | 32-bit | -| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | Linux | 65% | | | | -| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | Linux | 70% | | | | -| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | Linux | 40% | | | | -| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | Linux | 65% | Admin | | | -| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | Windows | 35% | | | | -| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | Windows | 100% | | | 32-bit | -| `VM::VMWARE_PORT_MEM` | Check for VMware memory using IO port backdoor | Windows | 85% | | | 32-bit | -| `VM::SMSW` | Check for SMSW assembly instruction technique | Windows | 30% | | | 32-bit | -| `VM::MUTEX` | Check for mutex strings of VM brands | Windows | 85% | | | | -| `VM::UPTIME` | Check if uptime is less than or equal to 2 minutes | | 10% | | | | -| `VM::ODD_CPU_THREADS` | Check for odd CPU threads, usually a sign of modification through VM setting because 99% of CPUs have even numbers of threads | | 80% | | | | -| `VM::INTEL_THREAD_MISMATCH` | Check for Intel CPU thread count database if it matches the system's thread count | | 85% | | | | -| `VM::XEON_THREAD_MISMATCH` | Same as above, but for Xeon Intel CPUs | | 85% | | | | -| `VM::NETTITUDE_VM_MEMORY` | Check for memory regions to detect VM-specific brands | Windows | 75% | | | | -| `VM::CPUID_BITSET` | Check for CPUID technique by checking whether all the bits equate to more than 4000 | | 20% | | | | -| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | Windows | 15% | | | | -| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | Windows | 20% | | | | -| `VM::HYPERV_HOSTNAME` | Check for default Azure hostname format regex (Azure uses Hyper-V as their base VM brand) | Windows, Linux | 50% | | | | -| `VM::GENERAL_HOSTNAME` | Check for commonly set hostnames by certain VM brands | Windows, Linux | 20% | | | | -| `VM::SCREEN_RESOLUTION` | Check for pre-set screen resolutions commonly found in VMs | Windows | 10% | | | | -| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | Windows | 25% | | | | -| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | Linux | 15% | | | | -| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | | 95% | | | | -| `VM::HYPERV_BITMASK` | Check for Hyper-V CPUID bitmask range for reserved values | | 20% | | | | -| `VM::KVM_BITMASK` | Check for KVM CPUID bitmask range for reserved values | | 40% | | | | -| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | | 80% | | | | -| `VM::VMWARE_DMI` | Check for VMware DMI strings in BIOS serial number | Windows | 30% | | | | +| Flag alias | Description | Cross-platform? (empty = yes) | Certainty | Admin? | GPL-3.0? | 32-bit? | Notes | +| ---------- | ----------- | ----------------------------- | --------- | ------ | -------- | ------- | ----- | +| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 | | 100% | | | | | +| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | | 50% | | | | | +| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | | 100% | | | | | +| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | | 45% | | | | | +| `VM::RDTSC` | Benchmark RDTSC and evaluate its speed, usually it's very slow in VMs | Linux and Windows | 10% | | | | Disabled by default | +| `VM::THREADCOUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings (nowadays physical CPUs should have at least 4 threads for modern CPUs) | | 35% | | | | | +| `VM::MAC` | Check if mac address starts with certain VM designated values | Linux and Windows | 60% | | | | | +| `VM::TEMPERATURE` | Check if thermal directory in linux is present, might not be present in VMs | Linux | 15% | | | | +| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | Linux | 70% | | | | | +| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | Linux | 65% | | | | | +| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | Linux | 10% | | | | | +| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | Linux | 80% | | | | | +| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | Linux | 55% | Admin | | | | +| `VM::DMESG` | Check if dmesg output matches a VM brand | Linux | 55% | Admin | | | | +| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | Linux | 75% | | | | | +| `VM::SIDT5` | Check if the 5th byte after sidt is null | Linux | 45% | | | | | +| `VM::CURSOR` | Check if cursor isn't active for 5 seconds (sign of automated VM environment) | Windows | 5% | | | | Disabled by default | +| `VM::VMWARE_REG` | Check for VBox RdrDN | Windows | 65% | | | | | +| `VM::VBOX_REG` | Look for any VirtualBox-specific registry data | Windows | 65% | | | | | +| `VM::USER` | checks for default usernames, often a sign of a VM | Windows | 35% | | | | | +| `VM::DLL` | Check for VM-specific DLLs | Windows | 50% | | | | | +| `VM::REGISTRY` | Check for VM-specific registry values | Windows | 75% | | | | | +| `VM::CWSANDBOX_VM` | Check if CWSandbox-specific file exists | Windows | 10% | | | | | +| `VM::VM_FILES` | Find for VMware and VBox specific files | Windows | 10% | | | | | +| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | MacOS | 75% | | | | | +| `VM::DISK_SIZE` | Check if disk size is under or equal to 50GB | Linux | 60% | | | | | +| `VM::VBOX_DEFAULT` | Check for default RAM and DISK sizes set by VirtualBox | Linux and Windows | 55% | Admin | | | | +| `VM::VBOX_NETWORK` | Check for VirtualBox network provider string | Windows | 70% | | | | | +| `VM::COMPUTER_NAME` | Check if the computer name (not username to be clear) is VM-specific | Windows | 40% | | GPL | | | +| `VM::WINE_CHECK` | Check wine_get_unix_file_name file for Wine | Windows | 85% | | GPL | | | +| `VM::HOSTNAME` | Check if hostname is specific | Windows | 25% | | GPL | | | +| `VM::MEMORY` | Check if memory space is far too low for a physical machine | Windows | 35% | | GPL | | | +| `VM::VBOX_WINDOW_CLASS` | Check for the window class for VirtualBox | Windows | 10% | | GPL | | | +| `VM::LOADED_DLLS` | Check for loaded DLLs in the process | Windows | 75% | | GPL | | | +| `VM::KVM_REG` | Check for KVM-specific registry strings | Windows | 75% | | GPL | | | +| `VM::KVM_DRIVERS` | Check for KVM-specific .sys files in system driver directory | Windows | 55% | | GPL | | | +| `VM::KVM_DIRS` | Check for KVM directory "Virtio-Win" | Windows | 55% | | GPL | | | +| `VM::AUDIO` | Check if audio device is present | Windows | 35% | | GPL | | | +| `VM::QEMU_DIR` | Check for QEMU-specific blacklisted directories | Windows | 45% | | GPL | | | +| `VM::MOUSE_DEVICE` | Check for the presence of a mouse device | Windows | 20% | | GPL | | | +| `VM::VM_PROCESSES` | Check for any VM processes that are active | Windows | 30% | | | | | +| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | Linux | 25% | | | | | +| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | Windows | 40% | | | | | +| `VM::VMID_0X4` | Check if the CPU manufacturer ID matches that of a VM brand with leaf 0x40000000 | | 100% | | | | | +| `VM::PARALLELS_VM` | Check for any indication of Parallels VM through BIOS data | Windows | 50% | | | | | +| `VM::RDTSC_VMEXIT` | check through alternative RDTSC technique with VMEXIT | | 25% | | | | Disabled by default | +| `VM::QEMU_BRAND` | Match for QEMU CPU brands with "QEMU Virtual CPU" string | | 100% | | | | | +| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | | 95% | | | | | +| `VM::VPC_BOARD` | Check through the motherboard and match for VirtualPC-specific string | Windows | 20% | | | | | +| `VM::HYPERV_WMI` | Check WMI query for "Hyper-V RAW" string | Windows | 80% | | | | | +| `VM::HYPERV_REG` | Check presence for Hyper-V specific string in registry | Windows | 80% | | | | | +| `VM::BIOS_SERIAL` | Check if the BIOS serial is valid (null = VM) | Windows | 60% | | | | | +| `VM::VBOX_FOLDERS` | Check for VirtualBox-specific string for shared folder ID | Windows | 45% | | | | | +| `VM::MSSMBIOS` | Check MSSMBIOS registry for VM-specific strings | Windows | 75% | | | | | +| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | MacOS | 30% | | | | | +| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | MacOS | 80% | | | | | +| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | MacOS | 75% | | | | | +| `VM::MAC_SIP` | Check if System Integrity Protection is disabled (likely a VM if it is) | MacOS | 85% | | | | | +| `VM::HKLM_REGISTRIES` | Check HKLM registries for specific VM strings | Windows | 70% | | | | | +| `VM::QEMU_GA` | Check for "qemu-ga" process | Linux | 20% | | | | | +| `VM::VALID_MSR` | check for valid MSR value 0x40000000 | Windows | 35% | | | | | +| `VM::QEMU_PROC` | Check for QEMU processes | Windows | 30% | | | | | +| `VM::VPC_PROC` | Check for VPC processes | Windows | 30% | | | | | +| `VM::VPC_INVALID` | Check for official VPC method | Windows | 75% | | | 32-bit | | +| `VM::SIDT` | Check for sidt instruction method | Linux, Windows | 30% | | | | | +| `VM::SGDT` | Check for sgdt instruction method | Windows | 30% | | | 32-bit | | +| `VM::SLDT` | Check for sldt instruction method | Windows | 15% | | | 32-bit | | +| `VM::OFFSEC_SIDT` | Check for Offensive Security SIDT method | Windows | 60% | | | 32-bit | | +| `VM::OFFSEC_SGDT` | Check for Offensive Security SGDT method | Windows | 60% | | | 32-bit | | +| `VM::OFFSEC_SLDT` | Check for Offensive Security SLDT method | Windows | 20% | | | 32-bit | | +| `VM::HYPERV_BOARD` | Check for Hyper-V specific string in motherboard | Windows | 45% | | | | | +| `VM::VM_FILES_EXTRA` | Check for VPC and Parallels files | Windows | 70% | | | | | +| `VM::VPC_SIDT` | Check for sidt method with VPC's 0xE8XXXXXX range | Windows | 15% | | | 32-bit | | +| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | Linux | 65% | | | | | +| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | Linux | 70% | | | | | +| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | Linux | 40% | | | | | +| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | Linux | 65% | Admin | | | | +| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | Windows | 35% | | | | | +| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | Windows | 100% | | | 32-bit | | +| `VM::VMWARE_PORT_MEM` | Check for VMware memory using IO port backdoor | Windows | 85% | | | 32-bit | | +| `VM::SMSW` | Check for SMSW assembly instruction technique | Windows | 30% | | | 32-bit | | +| `VM::MUTEX` | Check for mutex strings of VM brands | Windows | 85% | | | | | +| `VM::UPTIME` | Check if uptime is less than or equal to 2 minutes | | 10% | | | | | +| `VM::ODD_CPU_THREADS` | Check for odd CPU threads, usually a sign of modification through VM setting because 99% of CPUs have even numbers of threads | | 80% | | | | | +| `VM::INTEL_THREAD_MISMATCH` | Check for Intel CPU thread count database if it matches the system's thread count | | 85% | | | | | +| `VM::XEON_THREAD_MISMATCH` | Same as above, but for Xeon Intel CPUs | | 85% | | | | | +| `VM::NETTITUDE_VM_MEMORY` | Check for memory regions to detect VM-specific brands | Windows | 75% | | | | | +| `VM::CPUID_BITSET` | Check for CPUID technique by checking whether all the bits equate to more than 4000 | | 20% | | | | | +| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | Windows | 15% | | | | | +| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | Windows | 20% | | | | | +| `VM::HYPERV_HOSTNAME` | Check for default Azure hostname format regex (Azure uses Hyper-V as their base VM brand) | Windows, Linux | 50% | | | | | +| `VM::GENERAL_HOSTNAME` | Check for commonly set hostnames by certain VM brands | Windows, Linux | 20% | | | | | +| `VM::SCREEN_RESOLUTION` | Check for pre-set screen resolutions commonly found in VMs | Windows | 10% | | | | | +| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | Windows | 25% | | | | | +| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | Linux | 15% | | | | | +| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | | 95% | | | | | +| `VM::HYPERV_BITMASK` | Check for Hyper-V CPUID bitmask range for reserved values | | 20% | | | | | +| `VM::KVM_BITMASK` | Check for KVM CPUID bitmask range for reserved values | | 40% | | | | | +| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | | 80% | | | | | +| `VM::VMWARE_DMI` | Check for VMware DMI strings in BIOS serial number | Windows | 30% | | | | |
@@ -436,7 +436,6 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | `VM::ALL` | This will enable all the technique flags, including the cursor check that's disabled by default. | | `VM::NO_MEMO` | This will disable memoization, meaning the result will not be fetched through a previous computation of the `VM::detect()` function. Use this if you're only using a single function from the `VM` struct for a performance boost. | | `VM::DEFAULT` | This represents a range of flags which are enabled if no default argument is provided. | -| `VM::ENABLE_HYPERV_HOST` | Windows 11 (and 10 if enabled manually) may have Hyper-V as a default virtualisation solution for any host program even if the OS is running as host. There isn't a way to detect whether the host program is ran in default virtualisation mode, or manually intended virtualisation. This is a Hyper-V specific problem, and the library will use heuristical methods to discard Hyper-V's host virtualiser as not running in a VM by default. But if this flag is enabled then it will still count it regardless of the risk that it might be Hyper-V's default host virtualisation for every host program. So basically this flag means that "I'm aware this program might be running in a default virtualised environment on host, but I'll still count this as running in a VM anyway whether it's default virtualisation or manually intended virtualisation". | | `VM::MULTIPLE` | This is specific to `VM::brand()`. This will basically return a `std::string` message of what brands could be involved. For example, it could return "`VMware or VirtualBox`" instead of having a single brand string output. This has no effect if applied to any other functions than `VM::brand()`. | | `VM::HIGH_THRESHOLD` | This is specific to `VM::detect()` and `VM::percentage()`, which will set the threshold bar to confidently detect a VM by 3x higher. | | `VM::SPOOFABLE` | This will enable all the "spoofable" techniques (which are 1/3 of the total amount of techniques) | diff --git a/src/cli.cpp b/src/cli.cpp index 898e236..1497802 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -556,6 +556,7 @@ void general() { checker(VM::KVM_BITMASK, "KVM CPUID reserved bitmask"); checker(VM::KGT_SIGNATURE, "Intel KGT signature"); checker(VM::VMWARE_DMI, "VMware DMI"); + checker(VM::EVENT_LOGS, "Hyper-V event logs"); std::printf("\n"); diff --git a/src/vmaware.hpp b/src/vmaware.hpp index d8e53e3..051b77b 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -408,6 +408,7 @@ struct VM { KVM_BITMASK, KGT_SIGNATURE, VMWARE_DMI, + EVENT_LOGS, // start of non-technique flags (THE ORDERING IS VERY SPECIFIC HERE AND MIGHT BREAK SOMETHING IF RE-ORDERED) NO_MEMO, @@ -2183,6 +2184,131 @@ struct VM { return is_vm; } + + /** + * @brief Retrieves the last error message from the Windows API. Useful for __VMAWARE_DEBUG__ + * + * @author Requiem (https://github.com/NotRequiem) + * + * @return A std::wstring containing the error message. + */ + [[nodiscard]] std::wstring GetLastErrorString() { + DWORD error = GetLastError(); + LPWSTR messageBuffer = nullptr; + size_t size = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error, 0, (LPWSTR)&messageBuffer, 0, nullptr + ); + + std::wstring message(messageBuffer, size); + LocalFree(messageBuffer); + return message; + } + + + /** + * @brief Searches for specific strings within events in a Windows Event Log. + * + * @param logName The name or path of the event log to search (e.g., "System", "Application", "Security", or a custom path). + * @param searchStrings A vector of strings to search for within the event messages. + * @param flags Query flags that define the direction of the search; default is EvtQueryReverseDirection. + * @param timeout The maximum amount of time (in milliseconds) to wait for events; default is INFINITE. + * @param maxEvents The maximum number of events to process; default is 1000. + * + * @author Requiem (https://github.com/NotRequiem) + * + * @return True if any of the search strings are found in the events; otherwise, false. + */ + [[nodiscard]] bool query_event_logs(const std::wstring& logName, + const std::vector& searchStrings, + DWORD flags = EvtQueryReverseDirection, + DWORD timeout = INFINITE, + DWORD maxEvents = 1000) { + + EVT_HANDLE hLog = EvtOpenLog(nullptr, logName.c_str(), EvtOpenChannelPath); + if (!hLog) { + std::wcerr << L"Failed to open event log: " << logName << L". Error: " << GetLastErrorString() << std::endl; + return false; + } + + EVT_HANDLE hResults = EvtQuery(nullptr, logName.c_str(), nullptr, flags); + if (!hResults) { + std::wcerr << L"Failed to query event log: " << logName << L". Error: " << GetLastErrorString() << std::endl; + EvtClose(hLog); + return false; + } + + EVT_HANDLE hEvent = nullptr; + DWORD bufferUsed = 0; + DWORD bufferSize = 0; + DWORD count = 0; + WCHAR* pBuffer = nullptr; + + // Iterate over events up to the maximum number specified + for (DWORD eventCount = 0; eventCount < maxEvents; ++eventCount) { + if (!EvtNext(hResults, 1, &hEvent, timeout, 0, &count)) { + if (GetLastError() == ERROR_NO_MORE_ITEMS) { + break; // No more events to process + } + std::wcerr << L"EvtNext failed. Error: " << GetLastErrorString() << std::endl; + EvtClose(hResults); + EvtClose(hLog); + return false; + } + + if (!EvtRender(nullptr, hEvent, EvtRenderEventXml, 0, nullptr, &bufferUsed, &count) && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + bufferSize = bufferUsed; + pBuffer = new WCHAR[bufferSize]; + if (!pBuffer) { + std::wcerr << L"Memory allocation failed." << std::endl; + EvtClose(hResults); + EvtClose(hLog); + return false; + } + + if (!EvtRender(nullptr, hEvent, EvtRenderEventXml, bufferSize, pBuffer, &bufferUsed, &count)) { + std::wcerr << L"EvtRender failed. Error: " << GetLastErrorString() << std::endl; + delete[] pBuffer; + EvtClose(hResults); + EvtClose(hLog); + return false; + } + } + else { + std::wcerr << L"EvtRender failed. Error: " << GetLastErrorString() << std::endl; + EvtClose(hResults); + EvtClose(hLog); + return false; + } + + std::wstring eventMessage(pBuffer); + delete[] pBuffer; + + // Check if any of the search strings are found in the event message, not in the event name + bool found = false; + for (const auto& searchString : searchStrings) { + if (eventMessage.find(searchString) != std::wstring::npos) { + found = true; + break; + } + } + + if (found) { + EvtClose(hResults); + EvtClose(hLog); + return true; + } + + EvtClose(hEvent); + } + + EvtClose(hResults); + EvtClose(hLog); + + return false; + } + #endif }; @@ -7958,7 +8084,7 @@ struct VM { return false; // returned false because we want the most feature leafs as possible for Hyper-V } -/* this is just a tool to check if all the arrows (^) are aligned correctly, think of it as a ruler and ignore this lol +/* this is just an ascii tool to check if all the arrows (^) are aligned correctly based on bit position, think of it as a ruler. (ignore this btw) ||||||||||||||||||||||9876543210 |||||||||||||||||||||10 ||||||||||||||||||||11 @@ -8289,10 +8415,33 @@ struct VM { } + /** + * @brief Check for presence of Hyper-V in the Windows Event Logs + * + * @author Requiem (https://github.com/NotRequiem) + * + * @category Windows + */ + [[nodiscard]] static bool hyperv_event_logs() try { +#if (!MSVC) + return false; +#else + // Define the log name and search strings + std::wstring logName = L"Microsoft-Windows-Kernel-PnP/Configuration"; // Example: "System", "Application", "Security", or a custom path. In this case, we use Microsoft-Windows-Kernel-PnP/Configuration as a Hyper-V VM artifact + std::vector searchStrings = { L"Virtual_Machine", L"VMBUS" }; + const bool found = query_event_logs(logName, searchStrings); + if (found) { + return core::add(HYPERV); + } - + return false; +#endif + } catch (...) { + debug("EVENT_LOGS: caught error, returned false"); + return false; + } @@ -9262,6 +9411,8 @@ VM::flagset VM::DEFAULT = []() -> flagset { // disable all the non-default flags tmp.flip(NO_MEMO); tmp.flip(CURSOR); + tmp.flip(RDTSC); + tmp.flip(RDTSC_VMEXIT); tmp.flip(HIGH_THRESHOLD); tmp.flip(ENABLE_HYPERV_HOST); tmp.flip(SPOOFABLE); @@ -9320,104 +9471,105 @@ std::vector VM::core::custom_table = { const std::map VM::core::technique_table = { // FORMAT: VM:: = { certainty%, function pointer, is spoofable? } - { VM::VMID, { 100, VM::vmid, false }}, - { VM::CPU_BRAND, { 50, VM::cpu_brand, false }}, - { VM::HYPERVISOR_BIT, { 100, VM::hypervisor_bit , false}}, - { VM::HYPERVISOR_STR, { 45, VM::hypervisor_str, false }}, - { VM::RDTSC, { 10, VM::rdtsc_check, false }}, - { VM::THREADCOUNT, { 35, VM::thread_count, false }}, - { VM::MAC, { 60, VM::mac_address_check, true }}, - { VM::TEMPERATURE, { 15, VM::temperature, false }}, - { VM::SYSTEMD, { 70, VM::systemd_virt, false }}, - { VM::CVENDOR, { 65, VM::chassis_vendor, false }}, - { VM::CTYPE, { 10, VM::chassis_type, false }}, - { VM::DOCKERENV, { 80, VM::dockerenv, true }}, - { VM::DMIDECODE, { 55, VM::dmidecode, false }}, - { VM::DMESG, { 55, VM::dmesg, false }}, - { VM::HWMON, { 75, VM::hwmon, true }}, - { VM::SIDT5, { 45, VM::sidt5, false }}, - { VM::CURSOR, { 5, VM::cursor_check, true }}, - { VM::VMWARE_REG, { 65, VM::vmware_registry, true }}, - { VM::VBOX_REG, { 65, VM::vbox_registry, true }}, - { VM::USER, { 35, VM::user_check, true }}, - { VM::DLL, { 50, VM::DLL_check, true }}, - { VM::REGISTRY, { 75, VM::registry_key, true }}, - { VM::CWSANDBOX_VM, { 10, VM::cwsandbox_check, true }}, - { VM::VM_FILES, { 60, VM::vm_files, true }}, - { VM::HWMODEL, { 75, VM::hwmodel, true }}, - { VM::DISK_SIZE, { 60, VM::disk_size, false }}, - { VM::VBOX_DEFAULT, { 55, VM::vbox_default_specs, false }}, - { VM::VBOX_NETWORK, { 70, VM::vbox_network_share, false }}, - { VM::WINE_CHECK, { 85, VM::wine, false }}, // GPL - { VM::COMPUTER_NAME, { 15, VM::computer_name_match, true }}, // GPL - { VM::HOSTNAME, { 25, VM::hostname_match, true }}, // GPL - { VM::MEMORY, { 35, VM::low_memory_space, false }}, // GPL - { VM::VBOX_WINDOW_CLASS, { 10, VM::vbox_window_class, false }}, // GPL - { VM::KVM_REG, { 75, VM::kvm_registry, true }}, // GPL - { VM::KVM_DRIVERS, { 55, VM::kvm_drivers, true }}, // GPL - { VM::KVM_DIRS, { 55, VM::kvm_directories, true }}, // GPL - { VM::LOADED_DLLS, { 75, VM::loaded_dlls, true }}, // GPL - { VM::AUDIO, { 35, VM::check_audio, false }}, // GPL - { VM::QEMU_DIR, { 45, VM::qemu_dir, true }}, // GPL - { VM::MOUSE_DEVICE, { 20, VM::mouse_device, true }}, // GPL - { VM::VM_PROCESSES, { 30, VM::vm_processes, true }}, - { VM::LINUX_USER_HOST, { 25, VM::linux_user_host, true }}, - { VM::GAMARUE, { 40, VM::gamarue, false }}, - { VM::VMID_0X4, { 90, VM::vmid_0x4, false }}, - { VM::PARALLELS_VM, { 50, VM::parallels, false }}, - { VM::RDTSC_VMEXIT, { 25, VM::rdtsc_vmexit, false }}, - { VM::QEMU_BRAND, { 100, VM::cpu_brand_qemu, false }}, - { VM::BOCHS_CPU, { 95, VM::bochs_cpu, false }}, - { VM::VPC_BOARD, { 20, VM::vpc_board, false }}, - { VM::HYPERV_WMI, { 80, VM::hyperv_wmi, false }}, - { VM::HYPERV_REG, { 80, VM::hyperv_registry, true }}, - { VM::BIOS_SERIAL, { 60, VM::bios_serial, false }}, - { VM::VBOX_FOLDERS, { 45, VM::vbox_shared_folders, false }}, - { VM::MSSMBIOS, { 75, VM::mssmbios, false }}, - { VM::MAC_MEMSIZE, { 30, VM::hw_memsize, true }}, - { VM::MAC_IOKIT, { 80, VM::io_kit, true }}, - { VM::IOREG_GREP, { 75, VM::ioreg_grep, true }}, - { VM::MAC_SIP, { 85, VM::mac_sip, true }}, - { VM::HKLM_REGISTRIES, { 70, VM::hklm_registries, true }}, - { VM::QEMU_GA, { 20, VM::qemu_ga, true }}, - { VM::VALID_MSR, { 35, VM::valid_msr, false }}, - { VM::QEMU_PROC, { 30, VM::qemu_processes, true }}, - { VM::VPC_PROC, { 30, VM::vpc_proc, true }}, - { VM::VPC_INVALID, { 75, VM::vpc_invalid, false }}, - { VM::SIDT, { 30, VM::sidt, false }}, - { VM::SGDT, { 30, VM::sgdt, false }}, - { VM::SLDT, { 15, VM::sldt, false }}, - { VM::OFFSEC_SIDT, { 60, VM::offsec_sidt, false }}, - { VM::OFFSEC_SGDT, { 60, VM::offsec_sgdt, false }}, - { VM::OFFSEC_SLDT, { 20, VM::offsec_sldt, false }}, - { VM::VPC_SIDT, { 15, VM::vpc_sidt, false }}, - { VM::HYPERV_BOARD, { 45, VM::hyperv_board, false }}, - { VM::VM_FILES_EXTRA, { 70, VM::vm_files_extra, true }}, - { VM::VMWARE_IOMEM, { 65, VM::vmware_iomem, false }}, - { VM::VMWARE_IOPORTS, { 70, VM::vmware_ioports, false }}, - { VM::VMWARE_SCSI, { 40, VM::vmware_scsi, false }}, - { VM::VMWARE_DMESG, { 65, VM::vmware_dmesg, false }}, - { VM::VMWARE_STR, { 35, VM::vmware_str, false }}, - { VM::VMWARE_BACKDOOR, { 100, VM::vmware_backdoor, false }}, - { VM::VMWARE_PORT_MEM, { 85, VM::vmware_port_memory, false }}, - { VM::SMSW, { 30, VM::smsw, false }}, - { VM::MUTEX, { 85, VM::mutex, false }}, - { VM::UPTIME, { 10, VM::uptime, true }}, - { VM::ODD_CPU_THREADS, { 80, VM::odd_cpu_threads, false }}, - { VM::INTEL_THREAD_MISMATCH, { 85, VM::intel_thread_mismatch, false }}, - { VM::XEON_THREAD_MISMATCH, { 85, VM::xeon_thread_mismatch, false }}, - { VM::NETTITUDE_VM_MEMORY, { 75, VM::nettitude_vm_memory, false }}, - { VM::CPUID_BITSET, { 20, VM::cpuid_bitset, false }}, - { VM::CUCKOO_DIR, { 15, VM::cuckoo_dir, true }}, - { VM::CUCKOO_PIPE, { 20, VM::cuckoo_pipe, true }}, - { VM::HYPERV_HOSTNAME, { 50, VM::hyperv_hostname, true }}, - { VM::GENERAL_HOSTNAME, { 20, VM::general_hostname, true }}, - { VM::SCREEN_RESOLUTION, { 30, VM::screen_resolution, false }}, - { VM::DEVICE_STRING, { 25, VM::device_string, false }}, - { VM::BLUESTACKS_FOLDERS, { 15, VM::bluestacks, true }}, - { VM::CPUID_SIGNATURE, { 95, VM::cpuid_signature, false }}, - { VM::HYPERV_BITMASK, { 20, VM::hyperv_bitmask, false }}, - { VM::KVM_BITMASK, { 40, VM::kvm_bitmask, false }}, - { VM::KGT_SIGNATURE, { 80, VM::intel_kgt_signature, false }}, - { VM::VMWARE_DMI, { 30, VM::vmware_dmi, false }} + { VM::VMID, { 100, VM::vmid, false } }, + { VM::CPU_BRAND, { 50, VM::cpu_brand, false } }, + { VM::HYPERVISOR_BIT, { 100, VM::hypervisor_bit , false}} , + { VM::HYPERVISOR_STR, { 45, VM::hypervisor_str, false } }, + { VM::RDTSC, { 10, VM::rdtsc_check, false } }, + { VM::THREADCOUNT, { 35, VM::thread_count, false } }, + { VM::MAC, { 60, VM::mac_address_check, true } }, + { VM::TEMPERATURE, { 15, VM::temperature, false } }, + { VM::SYSTEMD, { 70, VM::systemd_virt, false } }, + { VM::CVENDOR, { 65, VM::chassis_vendor, false } }, + { VM::CTYPE, { 10, VM::chassis_type, false } }, + { VM::DOCKERENV, { 80, VM::dockerenv, true } }, + { VM::DMIDECODE, { 55, VM::dmidecode, false } }, + { VM::DMESG, { 55, VM::dmesg, false } }, + { VM::HWMON, { 75, VM::hwmon, true } }, + { VM::SIDT5, { 45, VM::sidt5, false } }, + { VM::CURSOR, { 5, VM::cursor_check, true } }, + { VM::VMWARE_REG, { 65, VM::vmware_registry, true } }, + { VM::VBOX_REG, { 65, VM::vbox_registry, true } }, + { VM::USER, { 35, VM::user_check, true } }, + { VM::DLL, { 50, VM::DLL_check, true } }, + { VM::REGISTRY, { 75, VM::registry_key, true } }, + { VM::CWSANDBOX_VM, { 10, VM::cwsandbox_check, true } }, + { VM::VM_FILES, { 60, VM::vm_files, true } }, + { VM::HWMODEL, { 75, VM::hwmodel, true } }, + { VM::DISK_SIZE, { 60, VM::disk_size, false } }, + { VM::VBOX_DEFAULT, { 55, VM::vbox_default_specs, false } }, + { VM::VBOX_NETWORK, { 70, VM::vbox_network_share, false } }, + { VM::WINE_CHECK, { 85, VM::wine, false } }, // GPL + { VM::COMPUTER_NAME, { 15, VM::computer_name_match, true } }, // GPL + { VM::HOSTNAME, { 25, VM::hostname_match, true } }, // GPL + { VM::MEMORY, { 35, VM::low_memory_space, false } }, // GPL + { VM::VBOX_WINDOW_CLASS, { 10, VM::vbox_window_class, false } }, // GPL + { VM::KVM_REG, { 75, VM::kvm_registry, true } }, // GPL + { VM::KVM_DRIVERS, { 55, VM::kvm_drivers, true } }, // GPL + { VM::KVM_DIRS, { 55, VM::kvm_directories, true } }, // GPL + { VM::LOADED_DLLS, { 75, VM::loaded_dlls, true } }, // GPL + { VM::AUDIO, { 35, VM::check_audio, false } }, // GPL + { VM::QEMU_DIR, { 45, VM::qemu_dir, true } }, // GPL + { VM::MOUSE_DEVICE, { 20, VM::mouse_device, true } }, // GPL + { VM::VM_PROCESSES, { 30, VM::vm_processes, true } }, + { VM::LINUX_USER_HOST, { 25, VM::linux_user_host, true } }, + { VM::GAMARUE, { 40, VM::gamarue, false } }, + { VM::VMID_0X4, { 90, VM::vmid_0x4, false } }, + { VM::PARALLELS_VM, { 50, VM::parallels, false } }, + { VM::RDTSC_VMEXIT, { 15, VM::rdtsc_vmexit, false } }, + { VM::QEMU_BRAND, { 100, VM::cpu_brand_qemu, false } }, + { VM::BOCHS_CPU, { 95, VM::bochs_cpu, false } }, + { VM::VPC_BOARD, { 20, VM::vpc_board, false } }, + { VM::HYPERV_WMI, { 80, VM::hyperv_wmi, false } }, + { VM::HYPERV_REG, { 80, VM::hyperv_registry, true } }, + { VM::BIOS_SERIAL, { 60, VM::bios_serial, false } }, + { VM::VBOX_FOLDERS, { 45, VM::vbox_shared_folders, false } }, + { VM::MSSMBIOS, { 75, VM::mssmbios, false } }, + { VM::MAC_MEMSIZE, { 30, VM::hw_memsize, true } }, + { VM::MAC_IOKIT, { 80, VM::io_kit, true } }, + { VM::IOREG_GREP, { 75, VM::ioreg_grep, true } }, + { VM::MAC_SIP, { 85, VM::mac_sip, true } }, + { VM::HKLM_REGISTRIES, { 70, VM::hklm_registries, true } }, + { VM::QEMU_GA, { 20, VM::qemu_ga, true } }, + { VM::VALID_MSR, { 35, VM::valid_msr, false } }, + { VM::QEMU_PROC, { 30, VM::qemu_processes, true } }, + { VM::VPC_PROC, { 30, VM::vpc_proc, true } }, + { VM::VPC_INVALID, { 75, VM::vpc_invalid, false } }, + { VM::SIDT, { 30, VM::sidt, false } }, + { VM::SGDT, { 30, VM::sgdt, false } }, + { VM::SLDT, { 15, VM::sldt, false } }, + { VM::OFFSEC_SIDT, { 60, VM::offsec_sidt, false } }, + { VM::OFFSEC_SGDT, { 60, VM::offsec_sgdt, false } }, + { VM::OFFSEC_SLDT, { 20, VM::offsec_sldt, false } }, + { VM::VPC_SIDT, { 15, VM::vpc_sidt, false } }, + { VM::HYPERV_BOARD, { 45, VM::hyperv_board, false } }, + { VM::VM_FILES_EXTRA, { 70, VM::vm_files_extra, true } }, + { VM::VMWARE_IOMEM, { 65, VM::vmware_iomem, false } }, + { VM::VMWARE_IOPORTS, { 70, VM::vmware_ioports, false } }, + { VM::VMWARE_SCSI, { 40, VM::vmware_scsi, false } }, + { VM::VMWARE_DMESG, { 65, VM::vmware_dmesg, false } }, + { VM::VMWARE_STR, { 35, VM::vmware_str, false } }, + { VM::VMWARE_BACKDOOR, { 100, VM::vmware_backdoor, false } }, + { VM::VMWARE_PORT_MEM, { 85, VM::vmware_port_memory, false } }, + { VM::SMSW, { 30, VM::smsw, false } }, + { VM::MUTEX, { 85, VM::mutex, false } }, + { VM::UPTIME, { 10, VM::uptime, true } }, + { VM::ODD_CPU_THREADS, { 80, VM::odd_cpu_threads, false } }, + { VM::INTEL_THREAD_MISMATCH, { 85, VM::intel_thread_mismatch, false } }, + { VM::XEON_THREAD_MISMATCH, { 85, VM::xeon_thread_mismatch, false } }, + { VM::NETTITUDE_VM_MEMORY, { 75, VM::nettitude_vm_memory, false } }, + { VM::CPUID_BITSET, { 20, VM::cpuid_bitset, false } }, + { VM::CUCKOO_DIR, { 15, VM::cuckoo_dir, true } }, + { VM::CUCKOO_PIPE, { 20, VM::cuckoo_pipe, true } }, + { VM::HYPERV_HOSTNAME, { 50, VM::hyperv_hostname, true } }, + { VM::GENERAL_HOSTNAME, { 20, VM::general_hostname, true } }, + { VM::SCREEN_RESOLUTION, { 30, VM::screen_resolution, false } }, + { VM::DEVICE_STRING, { 25, VM::device_string, false } }, + { VM::BLUESTACKS_FOLDERS, { 15, VM::bluestacks, true } }, + { VM::CPUID_SIGNATURE, { 95, VM::cpuid_signature, false } }, + { VM::HYPERV_BITMASK, { 20, VM::hyperv_bitmask, false } }, + { VM::KVM_BITMASK, { 40, VM::kvm_bitmask, false } }, + { VM::KGT_SIGNATURE, { 80, VM::intel_kgt_signature, false } }, + { VM::VMWARE_DMI, { 30, VM::vmware_dmi, false } }, + { VM::EVENT_LOGS, { 30, VM::hyperv_event_logs, true } } }; \ No newline at end of file From 9745cab728b4ea7acb1aa79a0c616a29c6355d5b Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Sat, 10 Aug 2024 04:23:48 +0100 Subject: [PATCH 14/16] added missing headers --- src/vmaware.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 051b77b..5d0e85b 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -214,6 +214,7 @@ #include #include #include +#include #pragma comment(lib, "wbemuuid.lib") #pragma comment(lib, "iphlpapi.lib") @@ -225,6 +226,8 @@ #pragma comment(lib, "strmiids.lib") #pragma comment(lib, "uuid.lib") #pragma comment(lib, "ntdll.lib") +#pragma comment(lib, "wevtapi.lib") + #ifdef _UNICODE #define tregex std::wregex From 1522c71e3a7247876b58ad6af50c360dd1aaf623 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Sat, 10 Aug 2024 04:28:35 +0100 Subject: [PATCH 15/16] added missing headers 2 --- src/vmaware.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 5d0e85b..8e05f79 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -8433,7 +8433,7 @@ struct VM { std::wstring logName = L"Microsoft-Windows-Kernel-PnP/Configuration"; // Example: "System", "Application", "Security", or a custom path. In this case, we use Microsoft-Windows-Kernel-PnP/Configuration as a Hyper-V VM artifact std::vector searchStrings = { L"Virtual_Machine", L"VMBUS" }; - const bool found = query_event_logs(logName, searchStrings); + const bool found = util::query_event_logs(logName, searchStrings); if (found) { return core::add(HYPERV); From 8fbd6fb087f1e4f23f9ab0443d661b6bacd0930b Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Sat, 10 Aug 2024 04:44:51 +0100 Subject: [PATCH 16/16] fixed static --- src/vmaware.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 8e05f79..8e50122 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -2195,7 +2195,7 @@ struct VM { * * @return A std::wstring containing the error message. */ - [[nodiscard]] std::wstring GetLastErrorString() { + [[nodiscard]] static std::wstring GetLastErrorString() { DWORD error = GetLastError(); LPWSTR messageBuffer = nullptr; size_t size = FormatMessageW( @@ -2222,7 +2222,7 @@ struct VM { * * @return True if any of the search strings are found in the events; otherwise, false. */ - [[nodiscard]] bool query_event_logs(const std::wstring& logName, + [[nodiscard]] static bool query_event_logs(const std::wstring& logName, const std::vector& searchStrings, DWORD flags = EvtQueryReverseDirection, DWORD timeout = INFINITE, @@ -8433,7 +8433,7 @@ struct VM { std::wstring logName = L"Microsoft-Windows-Kernel-PnP/Configuration"; // Example: "System", "Application", "Security", or a custom path. In this case, we use Microsoft-Windows-Kernel-PnP/Configuration as a Hyper-V VM artifact std::vector searchStrings = { L"Virtual_Machine", L"VMBUS" }; - const bool found = util::query_event_logs(logName, searchStrings); + bool found = util::query_event_logs(logName, searchStrings); if (found) { return core::add(HYPERV);