From 30c1c3fc13b3525c859e33c185c255b3f2cbd0a3 Mon Sep 17 00:00:00 2001 From: kernel <77142078+kernelwernel@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:36:31 +0100 Subject: [PATCH] 1.7 release --- src/cli.cpp | 4 +- src/vmaware.hpp | 20 +- src/vmaware_MIT.hpp | 1953 ++++++++++++++++++++++++++----------------- 3 files changed, 1183 insertions(+), 794 deletions(-) diff --git a/src/cli.cpp b/src/cli.cpp index cb73ced..d3458a0 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -37,8 +37,8 @@ #include "vmaware.hpp" -constexpr const char* ver = "1.6"; -constexpr const char* date = "July 2024"; +constexpr const char* ver = "1.7"; +constexpr const char* date = "August 2024"; constexpr const char* bold = "\033[1m"; constexpr const char* ansi_exit = "\x1B[0m"; diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 57a53e8..d60d37c 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -4,7 +4,7 @@ * ██║ ██║██╔████╔██║███████║██║ █╗ ██║███████║██████╔╝█████╗ * ╚██╗ ██╔╝██║╚██╔╝██║██╔══██║██║███╗██║██╔══██║██╔══██╗██╔══╝ * ╚████╔╝ ██║ ╚═╝ ██║██║ ██║╚███╔███╔╝██║ ██║██║ ██║███████╗ - * ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ 1.6 (July 2024) + * ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ 1.7 (August 2024) * * C++ VM detection library * @@ -22,14 +22,14 @@ * * * ================================ SECTIONS ================================== - * - enums for publicly accessible techniques => line 302 - * - struct for internal cpu operations => line 519 - * - struct for internal memoization => line 891 - * - struct for internal utility functions => line 996 - * - struct for internal core components => line 7781 - * - start of internal VM detection techniques => line 1815 - * - start of public VM detection functions => line 8277 - * - start of externally defined variables => line 8672 + * - enums for publicly accessible techniques => line 309 + * - struct for internal cpu operations => line 534 + * - struct for internal memoization => line 954 + * - struct for internal utility functions => line 1059 + * - struct for internal core components => line 8150 + * - start of internal VM detection techniques => line 1879 + * - start of public VM detection functions => line 8618 + * - start of externally defined variables => line 9044 * * * ================================ EXAMPLE ================================== @@ -3610,7 +3610,7 @@ struct VM { } /* removed due to potential false positives - + if ( check_proc(_T("vmtoolsd.exe")) || check_proc(_T("vmwaretrat.exe")) || diff --git a/src/vmaware_MIT.hpp b/src/vmaware_MIT.hpp index c6f7e5d..d842940 100644 --- a/src/vmaware_MIT.hpp +++ b/src/vmaware_MIT.hpp @@ -4,7 +4,7 @@ * ██║ ██║██╔████╔██║███████║██║ █╗ ██║███████║██████╔╝█████╗ * ╚██╗ ██╔╝██║╚██╔╝██║██╔══██║██║███╗██║██╔══██║██╔══██╗██╔══╝ * ╚████╔╝ ██║ ╚═╝ ██║██║ ██║╚███╔███╔╝██║ ██║██║ ██║███████╗ - * ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ 1.6 (July 2024) + * ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ 1.7 (August 2024) * * C++ VM detection library * @@ -19,7 +19,7 @@ * - Docs: https://github.com/kernelwernel/VMAware/docs/documentation.md * - Full credits: https://github.com/kernelwernel/VMAware#credits-and-contributors-%EF%B8%8F * - License: MIT - * + * * MIT License * * Copyright (c) 2024 kernelwernel @@ -41,23 +41,22 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. - * - * + * * ================================ SECTIONS ================================== - * - enums for publicly accessible techniques => line 324 - * - struct for internal cpu operations => line 528 - * - struct for internal memoization => line 900 - * - struct for internal utility functions => line 1005 - * - struct for internal core components => line 7298 - * - start of internal VM detection techniques => line 1824 - * - start of public VM detection functions => line 7794 - * - start of externally defined variables => line 8189 - * - * + * - enums for publicly accessible techniques => line 330 + * - struct for internal cpu operations => line 543 + * - struct for internal memoization => line 963 + * - struct for internal utility functions => line 1068 + * - struct for internal core components => line 7679 + * - start of internal VM detection techniques => line 1888 + * - start of public VM detection functions => line 8147 + * - start of externally defined variables => line 8573 + * + * * ================================ EXAMPLE ================================== * #include "vmaware.hpp" * #include - * + * * int main() { * if (VM::detect()) { * std::cout << "Virtual machine detected!" << std::endl; @@ -65,7 +64,7 @@ * } else { * std::cout << "Running in baremetal" << std::endl; * } - * + * * std::cout << "VM certainty: " << (int)VM::percentage() << "%" << std::endl; * } */ @@ -89,7 +88,7 @@ #define APPLE 0 #endif - // shorter and succinct macros +// shorter and succinct macros #if __cplusplus > 202100L #define CPP 23 #ifdef __VMAWARE_DEBUG__ @@ -184,7 +183,13 @@ #endif #if (MSVC) +#pragma warning(disable : 4244) +#include +#pragma warning(default : 4244) + #pragma warning(push, 0) // disable the windows SDK errors temporarily +#else +#include #endif #include @@ -205,6 +210,7 @@ #include #include + #if (MSVC) #include #include @@ -326,7 +332,6 @@ struct VM { VMID = 0, CPU_BRAND, HYPERVISOR_BIT, - CPUID_0X4, HYPERVISOR_STR, RDTSC, THREADCOUNT, @@ -399,7 +404,7 @@ struct VM { INTEL_THREAD_MISMATCH, XEON_THREAD_MISMATCH, NETTITUDE_VM_MEMORY, - HYPERV_CPUID, + CPUID_BITSET, CUCKOO_DIR, CUCKOO_PIPE, HYPERV_HOSTNAME, @@ -407,35 +412,37 @@ struct VM { SCREEN_RESOLUTION, DEVICE_STRING, BLUESTACKS_FOLDERS, + CPUID_SIGNATURE, + HYPERV_BITMASK, + KVM_BITMASK, + KGT_SIGNATURE, + VMWARE_DMI, - // start of non-technique flags (THE ORDERING IS VERY SPECIFIC AND MIGHT BREAK SOMETHING IDK) - EXTREME, + // start of non-technique flags (THE ORDERING IS VERY SPECIFIC HERE AND MIGHT BREAK SOMETHING IF RE-ORDERED) NO_MEMO, HIGH_THRESHOLD, ENABLE_HYPERV_HOST, - WIN_HYPERV_DEFAULT [[deprecated("Use VM::ENABLE_HYPERV_HOST instead")]], MULTIPLE }; private: static constexpr u8 enum_size = MULTIPLE; // get enum size through value of last element - static constexpr u8 non_technique_count = MULTIPLE - EXTREME + 1; // get number of non-technique flags like VM::NO_MEMO for example - static constexpr u8 WIN_HYPERV_DEFAULT_MACRO = ENABLE_HYPERV_HOST + 1; // can't use orignal enum value because that would give compiler warnings of deprecated usage + static constexpr u8 non_technique_count = MULTIPLE - NO_MEMO + 1; // get number of non-technique flags like VM::NO_MEMO for example static constexpr u8 INVALID = 255; // explicit invalid technique macro static constexpr u16 maximum_points = 4765; // theoretical total points if all VM detections returned true (which is practically impossible) - static constexpr u16 high_threshold_score = 350; // new threshold score from 100 to 350 if VM::HIGH_THRESHOLD flag is enabled + static constexpr u16 high_threshold_score = 300; // new threshold score from 100 to 350 if VM::HIGH_THRESHOLD flag is enabled static constexpr bool SHORTCUT = true; // macro for whether VM::core::run_all() should take a shortcut by skipping the rest of the techniques if the threshold score is already met // intended for loop indexes static constexpr u8 enum_begin = 0; static constexpr u8 enum_end = enum_size + 1; static constexpr u8 technique_begin = enum_begin; - static constexpr u8 technique_end = EXTREME; - static constexpr u8 non_technique_begin = EXTREME; + static constexpr u8 technique_end = NO_MEMO; + static constexpr u8 non_technique_begin = NO_MEMO; static constexpr u8 non_technique_end = enum_end; public: - static constexpr u8 technique_count = EXTREME; // get total number of techniques + static constexpr u8 technique_count = NO_MEMO; // get total number of techniques static std::vector technique_vector; #ifdef __VMAWARE_DEBUG__ static u16 total_points; @@ -483,6 +490,7 @@ struct VM { static constexpr const char* VMWARE_ESX = "VMware ESX"; static constexpr const char* VMWARE_GSX = "VMware GSX"; static constexpr const char* VMWARE_WORKSTATION = "VMware Workstation"; + static constexpr const char* VMWARE_FUSION = "VMware Fusion"; static constexpr const char* KVM = "KVM"; static constexpr const char* BHYVE = "bhyve"; static constexpr const char* QEMU = "QEMU"; @@ -500,18 +508,25 @@ struct VM { static constexpr const char* VPC = "Virtual PC"; static constexpr const char* ANUBIS = "Anubis"; static constexpr const char* JOEBOX = "JoeBox"; - static constexpr const char* THREADEXPERT = "Thread Expert"; + static constexpr const char* THREATEXPERT = "ThreatExpert"; static constexpr const char* CWSANDBOX = "CWSandbox"; static constexpr const char* COMODO = "Comodo"; static constexpr const char* BOCHS = "Bochs"; static constexpr const char* KVM_HYPERV = "KVM Hyper-V Enlightenment"; - static constexpr const char* NVMM = "NVMM"; + static constexpr const char* NVMM = "NetBSD NVMM"; static constexpr const char* BSD_VMM = "OpenBSD VMM"; static constexpr const char* INTEL_HAXM = "Intel HAXM"; static constexpr const char* UNISYS = "Unisys s-Par"; static constexpr const char* LMHS = "Lockheed Martin LMHS"; // yes, you read that right. The library can now detect VMs running on US military fighter jets, apparently. static constexpr const char* CUCKOO = "Cuckoo"; static constexpr const char* BLUESTACKS = "BlueStacks"; + static constexpr const char* JAILHOUSE = "Jailhouse"; + static constexpr const char* APPLE_VZ = "Apple VZ"; + static constexpr const char* INTEL_KGT = "Intel KGT (Trusty)"; + 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"; + // macro for bypassing unused parameter/variable warnings #define UNUSED(x) ((void)(x)) @@ -694,6 +709,44 @@ struct VM { #endif } + struct stepping_struct { + u8 model; + u8 family; + u8 extmodel; + }; + + [[nodiscard]] static stepping_struct fetch_steppings() { + struct stepping_struct steps {}; + + u32 unused, eax = 0; + cpu::cpuid(eax, unused, unused, unused, 1); + UNUSED(unused); + + steps.model = ((eax >> 4) & 0b1111); + steps.family = ((eax >> 8) & 0b1111); + steps.extmodel = ((eax >> 16) & 0b1111); + + return steps; + } + + // check if the CPU is an intel celeron + [[nodiscard]] static bool is_celeron(const stepping_struct steps) { + if (!cpu::is_intel()) { + return false; + } + + constexpr u8 celeron_model = 0xA; + constexpr u8 celeron_family = 0x6; + constexpr u8 celeron_extmodel = 0x2; + + return ( + steps.model == celeron_model && + steps.family == celeron_family && + steps.extmodel == celeron_extmodel + ); + } + + struct model_struct { bool found; bool is_xeon; @@ -784,7 +837,10 @@ struct VM { intel_haxm = "HAXMHAXMHAXM", virtapple = "VirtualApple", unisys = "UnisysSpar64", - lmhs = "SRESRESRESRE"; + lmhs = "SRESRESRESRE", + jailhouse = "Jailhouse\0\0\0", + apple_vz = "Apple VZ", + intel_kgt = "EVMMEVMMEVMM"; auto cpuid_thingy = [](const u32 p_leaf, u32* regs, std::size_t start = 0, std::size_t end = 4) -> bool { u32 x[4]{}; @@ -857,12 +913,12 @@ struct VM { if (tmp_brand == intel_haxm) { return core::add(INTEL_HAXM); } if (tmp_brand == unisys) { return core::add(UNISYS); } if (tmp_brand == lmhs) { return core::add(LMHS); } + if (tmp_brand == jailhouse) { return core::add(JAILHOUSE); } + if (tmp_brand == intel_kgt) { return core::add(INTEL_KGT); } // both Hyper-V and VirtualPC have the same string value if (tmp_brand == hyperv) { - bool tmp = core::add(VPC); - UNUSED(tmp); - return core::add(HYPERV); + return core::add(HYPERV, VPC); } /** @@ -878,19 +934,26 @@ struct VM { /** * i'm honestly not sure about this one, - * it's supposed to have 12 characters but - * Wikipedia tells me it has 8, so i'm just + * they're supposed to have 12 characters but + * Wikipedia tells me it these brands have + * less characters (both 8), so i'm just * going to scan for the entire string ig */ #if (CPP >= 17) const char* qnx_sample = qnx2.data(); + const char* applevz_sample = apple_vz.data(); #else const char* qnx_sample = qnx2.c_str(); + const char* applevz_sample = apple_vz.c_str(); #endif if (util::find(tmp_brand, qnx_sample)) { return core::add(QNX); } + + if (util::find(tmp_brand, applevz_sample)) { + return core::add(APPLE); + } } return false; @@ -1142,7 +1205,7 @@ struct VM { print_to_stream(std::cout, message...); #endif - std::cout << "\n"; + std::cout << std::dec << "\n"; } template @@ -1167,7 +1230,7 @@ struct VM { print_to_stream(std::cout, message...); #endif - std::cout << "\n"; + std::cout << std::dec << "\n"; } #endif @@ -1469,7 +1532,7 @@ struct VM { continue; } - std::cout << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nNOT SKIPPED\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + //std::cout << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nNOT SKIPPED\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; const std::size_t slash_index = line.find_last_of('/'); @@ -1822,9 +1885,10 @@ struct VM { #endif }; + private: // START OF PRIVATE VM DETECTION TECHNIQUE DEFINITIONS /** - * @brief Check CPUID output of manufacturer ID for known VMs/hypervisors + * @brief Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 * @category x86 */ [[nodiscard]] static bool vmid() try { @@ -1845,27 +1909,7 @@ struct VM { /** - * @brief Check CPUID output of manufacturer ID for known VMs/hypervisors with leaf value 0x40000000 - * @category x86 - */ - [[nodiscard]] static bool vmid_0x4() try { -#if (!x86) - return false; -#else - if (!core::cpuid_supported) { - return false; - } - - return cpu::vmid_template(cpu::leaf::hypervisor, "VMID_0x4: "); -#endif - } - catch (...) { - debug("VMID_0x4: caught error, returned false"); - return false; - } - - /** - * @brief Check if CPU brand is a VM brand + * @brief Check if CPU brand model contains any VM-specific string snippets * @category x86 */ [[nodiscard]] static bool cpu_brand() try { @@ -1913,42 +1957,13 @@ struct VM { #endif } catch (...) { - debug("BRAND_KEYWORDS: caught error, returned false"); - return false; - } - - - /** - * @brief Match for QEMU CPU brand - * @category x86 - */ - [[nodiscard]] static bool cpu_brand_qemu() try { -#if (!x86) - return false; -#else - if (!core::cpuid_supported) { - return false; - } - - std::string brand = cpu::get_brand(); - - std::regex pattern("QEMU Virtual CPU", std::regex_constants::icase); - - if (std::regex_match(brand, pattern)) { - return core::add(QEMU); - } - - return false; -#endif - } - catch (...) { - debug("QEMU_BRAND: caught error, returned false"); + debug("CPU_BRANDS: caught error, returned false"); return false; } /** - * @brief Check if hypervisor feature bit in CPUID is enabled (always false for physical CPUs) + * @brief Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) * @category x86 */ [[nodiscard]] static bool hypervisor_bit() try { @@ -1973,37 +1988,6 @@ struct VM { } - /** - * @brief Check if 0x40000000~0x400000FF cpuid input is present (mostly present in VMs, according to VMware) - * @link https://kb.vmware.com/s/article/1009458 - * @category x86 - */ - [[nodiscard]] static bool cpuid_0x4() try { -#if (!x86) - return false; -#else - if (!core::cpuid_supported) { - return false; - } - - u32 a, b, c, d = 0; - - for (u8 i = 0; i < 0xFF; ++i) { - cpu::cpuid(a, b, c, d, (cpu::leaf::hypervisor + i)); - if ((a + b + c + d) != 0) { - return true; - } - } - - return false; -#endif - } - catch (...) { - debug("CPUID_0x4: caught error, returned false"); - return false; - } - - /** * @brief Check for hypervisor brand string length (would be around 2 characters in a host machine) * @category x86 @@ -2031,7 +2015,7 @@ struct VM { /** - * @brief Check if RDTSC is slow, if yes then it might be a VM + * @brief Benchmark RDTSC and evaluate its speed, usually it's very slow in VMs * @category x86 */ [[nodiscard]] @@ -2099,49 +2083,23 @@ struct VM { /** - * @brief Check if the 5th byte after sidt is null - * @author Matteo Malvica - * @link https://www.matteomalvica.com/blog/2018/12/05/detecting-vmware-on-64-bit-systems/ - * @category x86 - */ - [[nodiscard]] static bool sidt5() try { -#if (!x86 || !LINUX || GCC) - return false; -#else - u8 values[10]; - std::memset(values, 0, 10); - - fflush(stdout); - __asm__ __volatile__("sidt %0" : "=m"(values)); - -#ifdef __VMAWARE_DEBUG__ - u32 result = 0; - - for (u8 i = 0; i < 10; i++) { - result <<= 8; - result |= values[i]; - } - - debug("SIDT5: ", "values = 0x", std::hex, std::setw(16), std::setfill('0'), result); -#endif - - return (values[5] == 0x00); -#endif - } - catch (...) { - debug("SIDT5: caught error, returned false"); - return false; - } - - - /** - * @brief Check if processor count is 1 or 2 (some VMs only have a single core) - * @category All systems + * @brief 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 + * @category x86 (ARM might have very low thread counts, which si why it should be only for x86) */ [[nodiscard]] static bool thread_count() try { +#if (x86) debug("THREADCOUNT: ", "threads = ", std::thread::hardware_concurrency()); + + struct cpu::stepping_struct steps = cpu::fetch_steppings(); + + if (cpu::is_celeron(steps)) { + return false; + } return (std::thread::hardware_concurrency() <= 2); +#else + return false; +#endif } catch (...) { debug("THREADCOUNT: caught error, returned false"); @@ -2234,10 +2192,11 @@ struct VM { ss << std::setw(2) << std::setfill('0') << std::hex << static_cast(mac[0]) << ":" << static_cast(mac[1]) << ":" - << static_cast(mac[2]) << ":" - << static_cast(mac[3]) << ":" - << static_cast(mac[4]) << ":" - << static_cast(mac[5]); + << static_cast(mac[2]) << ":"; + // removed for privacy reasons, cuz only the first 3 bytes are needed + //<< static_cast(mac[3]) << ":" + //<< static_cast(mac[4]) << ":" + //<< static_cast(mac[5]); debug("MAC: ", ss.str()); #endif @@ -2267,9 +2226,13 @@ struct VM { return core::add(PARALLELS); } - //if (compare(0x0A, 0x00, 0x27)) { - // return core::add(HYBRID); - //} + /* + see https://github.com/kernelwernel/VMAware/issues/105 + + if (compare(0x0A, 0x00, 0x27)) { + return core::add(HYBRID); + } + */ return false; } @@ -2280,7 +2243,7 @@ struct VM { /** - * @brief Check if thermal directory is present, might not be present in VMs + * @brief Check if thermal directory in linux is present, might not be present in VMs * @category Linux */ [[nodiscard]] static bool temperature() try { @@ -2328,7 +2291,7 @@ struct VM { /** - * @brief Check if chassis vendor is a VM vendor + * @brief Check if the chassis vendor is a VM vendor * @category Linux */ [[nodiscard]] static bool chassis_vendor() try { @@ -2450,7 +2413,7 @@ struct VM { /** - * @brief Check if dmesg command output matches a VM brand + * @brief Check if dmesg output matches a VM brand * @category Linux */ [[nodiscard]] static bool dmesg() try { @@ -2507,182 +2470,151 @@ struct VM { /** - * @brief Check for tons of VM-specific registry values - * @category Windows + * @brief Check if the 5th byte after sidt is null + * @author Matteo Malvica + * @link https://www.matteomalvica.com/blog/2018/12/05/detecting-vmware-on-64-bit-systems/ + * @category x86 */ - [[nodiscard]] static bool registry_key() try { -#if (!MSVC) + [[nodiscard]] static bool sidt5() try { +#if (!x86 || !LINUX || GCC) return false; #else - u8 score = 0; + u8 values[10]; + std::memset(values, 0, 10); - auto key = [&score](const char* p_brand, const char* regkey_s) -> void { - HKEY regkey; - LONG ret; + fflush(stdout); + __asm__ __volatile__("sidt %0" : "=m"(values)); - if (util::is_wow64()) { - wchar_t wRegKey[MAX_PATH]; - MultiByteToWideChar(CP_ACP, 0, regkey_s, -1, wRegKey, MAX_PATH); +#ifdef __VMAWARE_DEBUG__ + u32 result = 0; - ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wRegKey, 0, KEY_READ | KEY_WOW64_64KEY, ®key); - } else { - wchar_t wRegKey[MAX_PATH]; - MultiByteToWideChar(CP_ACP, 0, regkey_s, -1, wRegKey, MAX_PATH); + for (u8 i = 0; i < 10; i++) { + result <<= 8; + result |= values[i]; + } - ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wRegKey, 0, KEY_READ, ®key); - } + debug("SIDT5: ", "values = 0x", std::hex, std::setw(16), std::setfill('0'), result); +#endif - if (ret == ERROR_SUCCESS) { - RegCloseKey(regkey); - score++; + return (values[5] == 0x00); +#endif + } + catch (...) { + debug("SIDT5: caught error, returned false"); + return false; + } - if (std::string(p_brand) != "") { - debug("REGISTRY: ", "detected = ", p_brand); - core::add(p_brand); - } - } - }; - // general - key("", "HKLM\\Software\\Classes\\Folder\\shell\\sandbox"); + /** + * @brief Check if the mouse coordinates have changed after 5 seconds + * @note Some VMs are automatic without a human due to mass malware scanning being a thing + * @note Disabled by default due to performance reasons + * @category Windows + */ + [[nodiscard]] static bool cursor_check() try { +#if (!MSVC) + return false; +#else + POINT pos1, pos2; + GetCursorPos(&pos1); - // hyper-v - key(HYPERV, "HKLM\\SOFTWARE\\Microsoft\\Hyper-V"); - key(HYPERV, "HKLM\\SOFTWARE\\Microsoft\\VirtualMachine"); - key(HYPERV, "HKLM\\SOFTWARE\\Microsoft\\Virtual Machine\\Guest\\Parameters"); - key(HYPERV, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmicheartbeat"); - key(HYPERV, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmicvss"); - key(HYPERV, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmicshutdown"); - key(HYPERV, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmicexchange"); + debug("CURSOR: pos1.x = ", pos1.x); + debug("CURSOR: pos1.y = ", pos1.y); - // parallels - key(PARALLELS, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_1AB8*"); + Sleep(5000); + GetCursorPos(&pos2); - // sandboxie - key(SANDBOXIE, "HKLM\\SYSTEM\\CurrentControlSet\\Services\\SbieDrv"); - key(SANDBOXIE, "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Sandboxie"); + debug("CURSOR: pos1.x = ", pos1.x); + debug("CURSOR: pos1.y = ", pos1.y); + debug("CURSOR: pos2.x = ", pos2.x); + debug("CURSOR: pos2.y = ", pos2.y); - // virtualbox - key(VBOX, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_80EE*"); - key(VBOX, "HKLM\\HARDWARE\\ACPI\\DSDT\\VBOX__"); - key(VBOX, "HKLM\\HARDWARE\\ACPI\\FADT\\VBOX__"); - key(VBOX, "HKLM\\HARDWARE\\ACPI\\RSDT\\VBOX__"); - key(VBOX, "HKLM\\SOFTWARE\\Oracle\\VirtualBox Guest Additions"); - key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxGuest"); - key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxMouse"); - key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxService"); - key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxSF"); - key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxVideo"); + return ((pos1.x == pos2.x) && (pos1.y == pos2.y)); +#endif + } + catch (...) { + debug("CURSOR: caught error, returned false"); + return false; + } - // virtualpc - key(VPC, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_5333*"); - key(VPC, "HKLM\\SYSTEM\\ControlSet001\\Services\\vpcbus"); - key(VPC, "HKLM\\SYSTEM\\ControlSet001\\Services\\vpc-s3"); - key(VPC, "HKLM\\SYSTEM\\ControlSet001\\Services\\vpcuhub"); - key(VPC, "HKLM\\SYSTEM\\ControlSet001\\Services\\msvmmouf"); - // vmware - key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_15AD*"); - key(VMWARE, "HKCU\\SOFTWARE\\VMware, Inc.\\VMware Tools"); - key(VMWARE, "HKLM\\SOFTWARE\\VMware, Inc.\\VMware Tools"); - key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmdebug"); - key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmmouse"); - key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\VMTools"); - key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\VMMEMCTL"); - key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmware"); - key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmci"); - key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmx86"); - key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\IDE\\CdRomNECVMWar_VMware_IDE_CD*"); - key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\IDE\\CdRomNECVMWar_VMware_SATA_CD*"); - key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\IDE\\DiskVMware_Virtual_IDE_Hard_Drive*"); - key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\IDE\\DiskVMware_Virtual_SATA_Hard_Drive*"); - - // wine - key(WINE, "HKCU\\SOFTWARE\\Wine"); - key(WINE, "HKLM\\SOFTWARE\\Wine"); + /** + * @brief Find for registries of VMware tools + * @category Windows + */ + [[nodiscard]] static bool vmware_registry() try { +#if (!MSVC) + return false; +#else + HKEY hKey; + // Use wide string literal + bool result = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\VMware, Inc.\\VMware Tools", 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS); - // xen - key(XEN, "HKLM\\HARDWARE\\ACPI\\DSDT\\xen"); - key(XEN, "HKLM\\HARDWARE\\ACPI\\FADT\\xen"); - key(XEN, "HKLM\\HARDWARE\\ACPI\\RSDT\\xen"); - key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xenevtchn"); - key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xennet"); - key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xennet6"); - key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xensvc"); - key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xenvdb"); + debug("VMWARE_REG: result = ", result); - debug("REGISTRY: ", "score = ", static_cast(score)); + if (result == true) { + return core::add(VMWARE); + } - return (score >= 1); + return result; #endif } catch (...) { - debug("REGISTRY: caught error, returned false"); + debug("VMWARE_REG: caught error, returned false"); return false; } /** - * @brief checks for default usernames, often a sign of a VM - * @author: Some guy in a russian underground forum from a screenshot I saw, idk I don't speak russian ¯\_(ツ)_/¯ + * @brief Check for VBox RdrDN * @category Windows */ - [[nodiscard]] static bool user_check() try { + [[nodiscard]] static bool vbox_registry() try { #if (!MSVC) return false; #else - TCHAR user[UNLEN + 1]{}; - DWORD user_len = UNLEN + 1; - GetUserName(user, &user_len); - - //TODO Ansi: debug("USER: ", "output = ", user); - - if (0 == _tcscmp(user, _T("username"))) { - return core::add(THREADEXPERT); - } - - if (0 == _tcscmp(user, _T("vmware"))) { - return core::add(VMWARE); - } + HANDLE handle = CreateFile(_T("\\\\.\\VBoxMiniRdrDN"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (0 == _tcscmp(user, _T("user"))) { - return core::add(SANDBOXIE); + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + return core::add(VBOX); } - return ( - (0 == _tcscmp(user, _T("USER"))) || // Sandbox - (0 == _tcscmp(user, _T("currentuser"))) // Normal - ); + return false; #endif } catch (...) { - debug("USER: caught error, returned false"); + debug("VBOX_REG: caught error, returned false"); return false; } /** - * @brief Check if CWSandbox-specific file exists - * @author same russian guy as above. Whoever you are, ty + * @brief checks for default usernames, often a sign of a VM * @category Windows */ - [[nodiscard]] static bool cwsandbox_check() try { + [[nodiscard]] static bool user_check() try { #if (!MSVC) return false; #else - if (util::exists(_T("C:\\analysis"))) { - return core::add(CWSANDBOX); + TCHAR user[UNLEN + 1]{}; + DWORD user_len = UNLEN + 1; + GetUserName(user, &user_len); + + //TODO Ansi: debug("USER: ", "output = ", user); + + if (0 == _tcscmp(user, _T("vmware"))) { + return core::add(VMWARE); } return false; #endif } catch (...) { - debug("CWSANDBOX_VM: caught error, returned false"); + debug("USER: caught error, returned false"); return false; } - /** * @brief Check for VM-specific DLLs * @category Windows @@ -2732,85 +2664,138 @@ struct VM { /** - * @brief Check VBox RdrDN + * @brief Check for VM-specific registry values * @category Windows */ - [[nodiscard]] static bool vbox_registry() try { + [[nodiscard]] static bool registry_key() try { #if (!MSVC) return false; #else - HANDLE handle = CreateFile(_T("\\\\.\\VBoxMiniRdrDN"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + u8 score = 0; - if (handle != INVALID_HANDLE_VALUE) { - CloseHandle(handle); - return core::add(VBOX); - } + auto key = [&score](const char* p_brand, const char* regkey_s) -> void { + HKEY regkey; + LONG ret; - return false; -#endif - } - catch (...) { - debug("VBOX_REG: caught error, returned false"); - return false; - } + if (util::is_wow64()) { + wchar_t wRegKey[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, regkey_s, -1, wRegKey, MAX_PATH); + ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wRegKey, 0, KEY_READ | KEY_WOW64_64KEY, ®key); + } else { + wchar_t wRegKey[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, regkey_s, -1, wRegKey, MAX_PATH); - /** - * @brief Find VMware tools presence - * @category Windows - */ - [[nodiscard]] static bool vmware_registry() try { -#if (!MSVC) - return false; -#else - HKEY hKey; - // Use wide string literal - bool result = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\VMware, Inc.\\VMware Tools", 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS); + ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wRegKey, 0, KEY_READ, ®key); + } - debug("VMWARE_REG: result = ", result); + if (ret == ERROR_SUCCESS) { + RegCloseKey(regkey); + score++; - if (result == true) { - return core::add(VMWARE); - } + if (std::string(p_brand) != "") { + debug("REGISTRY: ", "detected = ", p_brand); + core::add(p_brand); + } + } + }; - return result; + // general + key("", "HKLM\\Software\\Classes\\Folder\\shell\\sandbox"); + + // hyper-v + key(HYPERV, "HKLM\\SOFTWARE\\Microsoft\\Hyper-V"); + key(HYPERV, "HKLM\\SOFTWARE\\Microsoft\\VirtualMachine"); + key(HYPERV, "HKLM\\SOFTWARE\\Microsoft\\Virtual Machine\\Guest\\Parameters"); + key(HYPERV, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmicheartbeat"); + key(HYPERV, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmicvss"); + key(HYPERV, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmicshutdown"); + key(HYPERV, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmicexchange"); + + // parallels + key(PARALLELS, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_1AB8*"); + + // sandboxie + key(SANDBOXIE, "HKLM\\SYSTEM\\CurrentControlSet\\Services\\SbieDrv"); + key(SANDBOXIE, "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Sandboxie"); + + // virtualbox + key(VBOX, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_80EE*"); + key(VBOX, "HKLM\\HARDWARE\\ACPI\\DSDT\\VBOX__"); + key(VBOX, "HKLM\\HARDWARE\\ACPI\\FADT\\VBOX__"); + key(VBOX, "HKLM\\HARDWARE\\ACPI\\RSDT\\VBOX__"); + key(VBOX, "HKLM\\SOFTWARE\\Oracle\\VirtualBox Guest Additions"); + key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxGuest"); + key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxMouse"); + key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxService"); + key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxSF"); + key(VBOX, "HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxVideo"); + + // virtualpc + key(VPC, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_5333*"); + key(VPC, "HKLM\\SYSTEM\\ControlSet001\\Services\\vpcbus"); + key(VPC, "HKLM\\SYSTEM\\ControlSet001\\Services\\vpc-s3"); + key(VPC, "HKLM\\SYSTEM\\ControlSet001\\Services\\vpcuhub"); + key(VPC, "HKLM\\SYSTEM\\ControlSet001\\Services\\msvmmouf"); + + // vmware + key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_15AD*"); + key(VMWARE, "HKCU\\SOFTWARE\\VMware, Inc.\\VMware Tools"); + key(VMWARE, "HKLM\\SOFTWARE\\VMware, Inc.\\VMware Tools"); + key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmdebug"); + key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmmouse"); + key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\VMTools"); + key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\VMMEMCTL"); + key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmware"); + key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmci"); + key(VMWARE, "HKLM\\SYSTEM\\ControlSet001\\Services\\vmx86"); + key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\IDE\\CdRomNECVMWar_VMware_IDE_CD*"); + key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\IDE\\CdRomNECVMWar_VMware_SATA_CD*"); + key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\IDE\\DiskVMware_Virtual_IDE_Hard_Drive*"); + key(VMWARE, "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\IDE\\DiskVMware_Virtual_SATA_Hard_Drive*"); + + // wine + key(WINE, "HKCU\\SOFTWARE\\Wine"); + key(WINE, "HKLM\\SOFTWARE\\Wine"); + + // xen + key(XEN, "HKLM\\HARDWARE\\ACPI\\DSDT\\xen"); + key(XEN, "HKLM\\HARDWARE\\ACPI\\FADT\\xen"); + key(XEN, "HKLM\\HARDWARE\\ACPI\\RSDT\\xen"); + key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xenevtchn"); + key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xennet"); + key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xennet6"); + key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xensvc"); + key(XEN, "HKLM\\SYSTEM\\ControlSet001\\Services\\xenvdb"); + + debug("REGISTRY: ", "score = ", static_cast(score)); + + return (score >= 1); #endif } catch (...) { - debug("VMWARE_REG: caught error, returned false"); + debug("REGISTRY: caught error, returned false"); return false; } /** - * @brief Check if the mouse coordinates have changed after 5 seconds - * @note Some VMs are automatic without a human due to mass malware scanning being a thing - * @note Disabled by default due to performance reasons + * @brief Check if CWSandbox-specific file exists * @category Windows */ - [[nodiscard]] static bool cursor_check() try { + [[nodiscard]] static bool cwsandbox_check() try { #if (!MSVC) return false; #else - POINT pos1, pos2; - GetCursorPos(&pos1); - - debug("CURSOR: pos1.x = ", pos1.x); - debug("CURSOR: pos1.y = ", pos1.y); - - Sleep(5000); - GetCursorPos(&pos2); - - debug("CURSOR: pos1.x = ", pos1.x); - debug("CURSOR: pos1.y = ", pos1.y); - debug("CURSOR: pos2.x = ", pos2.x); - debug("CURSOR: pos2.y = ", pos2.y); + if (util::exists(_T("C:\\analysis"))) { + return core::add(CWSANDBOX); + } - return ((pos1.x == pos2.x) && (pos1.y == pos2.y)); + return false; #endif } catch (...) { - debug("CURSOR: caught error, returned false"); + debug("CWSANDBOX_VM: caught error, returned false"); return false; } @@ -2903,7 +2888,7 @@ struct VM { /** - * @brief Check for sysctl hardware model + * @brief Check if the sysctl for the hwmodel does not contain the "Mac" string * @author MacRansom ransomware * @category MacOS */ @@ -2943,7 +2928,7 @@ struct VM { /** - * @brief Check if disk size is too low + * @brief Check if disk size is under or equal to 50GB * @category Linux (for now) */ [[nodiscard]] static bool disk_size() try { @@ -2964,7 +2949,7 @@ struct VM { /** - * @brief Check for match with default RAM and disk size (VBOX-specific) + * @brief Check for default RAM and DISK sizes set by VirtualBox * @note RAM DISK * WINDOWS 11: 4096MB, 80GB * WINDOWS 10: 2048MB, 50GB @@ -3071,7 +3056,7 @@ struct VM { /** - * @brief Check VBox network provider string + * @brief Check for VirtualBox network provider string * @category Windows */ [[nodiscard]] static bool vbox_network_share() try { @@ -3152,7 +3137,9 @@ struct VM { if (check_proc(_T("vmsrvc.exe")) || check_proc(_T("vmusrvc.exe"))) { return core::add(VPC); } - +/* + removed due to potential false positives + if ( check_proc(_T("vmtoolsd.exe")) || check_proc(_T("vmwaretrat.exe")) || @@ -3164,6 +3151,7 @@ struct VM { ) { return core::add(VMWARE); } +*/ if (check_proc(_T("xenservice.exe")) || check_proc(_T("xsvc_depriv.exe"))) { return core::add(XEN); @@ -3209,7 +3197,7 @@ struct VM { /** - * @brief Gamarue ransomware check + * @brief Check for Gamarue ransomware technique which compares VM-specific Window product IDs * @category Windows */ [[nodiscard]] static bool gamarue() try { @@ -3233,7 +3221,7 @@ struct VM { hMod = GetModuleHandleW(L"dbghelp.dll"); // Thread Expert if (hMod != 0) { free(szBuff); - return core::add(THREADEXPERT); + return core::add(THREATEXPERT); } nRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion", 0L, KEY_QUERY_VALUE, &hOpen); @@ -3275,47 +3263,28 @@ struct VM { /** - * @brief Check if the BIOS serial is valid - * @category Linux + * @brief Check if the CPU manufacturer ID matches that of a VM brand with leaf 0x40000000 + * @category x86 */ - [[nodiscard]] static bool bios_serial() try { -#if (!MSVC) + [[nodiscard]] static bool vmid_0x4() try { +#if (!x86) return false; #else - std::unique_ptr info = util::make_unique(); - - const std::string str = info->get_serialnumber(); - const std::size_t nl_pos = str.find('\n'); - - if (nl_pos == std::string::npos) { + if (!core::cpuid_supported) { return false; } - debug("BIOS_SERIAL: ", str); - - const std::string extract = str.substr(nl_pos + 1); - - const bool all_digits = std::all_of(extract.cbegin(), extract.cend(), [](const char c) { - return std::isdigit(c); - }); - - if (all_digits) { - if (extract == "0") { - return true; - } - } - - return false; + return cpu::vmid_template(cpu::leaf::hypervisor, "VMID_0x4: "); #endif } catch (...) { - debug("BIOS_SERIAL: caught error, returned false"); + debug("VMID_0x4: caught error, returned false"); return false; } /** - * @brief check for any indication of parallels through BIOS stuff + * @brief Check for any indication of Parallels VM through BIOS data * @link https://stackoverflow.com/questions/1370586/detect-if-windows-is-running-from-within-parallels * @category Windows */ @@ -3364,7 +3333,7 @@ struct VM { /** - * @brief check VM through alternative RDTSC technique with VMEXIT + * @brief check through alternative RDTSC technique with VMEXIT * @category x86 */ [[nodiscard]] @@ -3389,19 +3358,48 @@ struct VM { avg += (tsc2 - tsc1); } - avg /= 10; + avg /= 10; + + return (avg >= 1500 || avg == 0); +#endif + } + catch (...) { + debug("RDTSC_VMEXIT:", "caught error, returned false"); + return false; + } + + + /** + * @brief Match for QEMU CPU brands with "QEMU Virtual CPU" string + * @category x86 + */ + [[nodiscard]] static bool cpu_brand_qemu() try { +#if (!x86) + return false; +#else + if (!core::cpuid_supported) { + return false; + } + + std::string brand = cpu::get_brand(); + + std::regex qemu_pattern("QEMU Virtual CPU", std::regex_constants::icase); + + if (std::regex_match(brand, qemu_pattern)) { + return core::add(QEMU); + } - return (avg >= 1500 || avg == 0); + return false; #endif } catch (...) { - debug("RDTSC_VMEXIT:", "caught error, returned false"); + debug("QEMU_BRAND: caught error, returned false"); return false; } /** - * @brief Do various Bochs-related CPU stuff + * @brief Check for various Bochs-related emulation oversights through CPU checks * @category x86 * @note Discovered by Peter Ferrie, Senior Principal Researcher, Symantec Advanced Threat Research peter_ferrie@symantec.com */ @@ -3466,7 +3464,7 @@ struct VM { /** - * @brief Go through the motherboard and match for VPC-specific string + * @brief Check through the motherboard and match for VirtualPC-specific string * @category Windows */ [[nodiscard]] static bool vpc_board() try { @@ -3617,7 +3615,7 @@ struct VM { /** - * @brief get WMI query for HYPERV name + * @brief Check WMI query for "Hyper-V RAW" string * @category Windows * @note idea is from nettitude * @link https://labs.nettitude.com/blog/vm-detection-tricks-part-3-hyper-v-raw-network-protocol/ @@ -3756,7 +3754,7 @@ struct VM { /** - * @brief compare for hyperv-specific string in registry + * @brief Check presence for Hyper-V specific string in registry * @category Windows * @note idea is from nettitude * @link https://labs.nettitude.com/blog/vm-detection-tricks-part-3-hyper-v-raw-network-protocol/ @@ -3816,6 +3814,46 @@ struct VM { } + /** + * @brief Check if the BIOS serial is valid (null = VM) + * @category Windows + */ + [[nodiscard]] static bool bios_serial() try { +#if (!MSVC) + return false; +#else + std::unique_ptr info = util::make_unique(); + + const std::string str = info->get_serialnumber(); + const std::size_t nl_pos = str.find('\n'); + + if (nl_pos == std::string::npos) { + return false; + } + + debug("BIOS_SERIAL: ", str); + + const std::string extract = str.substr(nl_pos + 1); + + const bool all_digits = std::all_of(extract.cbegin(), extract.cend(), [](const char c) { + return std::isdigit(c); + }); + + if (all_digits) { + if (extract == "0") { + return true; + } + } + + return false; +#endif + } + catch (...) { + debug("BIOS_SERIAL: caught error, returned false"); + return false; + } + + /** * @brief Check for VirtualBox-specific string for shared folder ID * @category Windows @@ -4189,7 +4227,7 @@ struct VM { /** - * @brief Fetch HKLM registries for specific VM strings + * @brief Check HKLM registries for specific VM strings * @category Windows */ [[nodiscard]] static bool hklm_registries() try { @@ -4295,7 +4333,7 @@ struct VM { /** - * @brief Check for qemu-ga process + * @brief Check for "qemu-ga" process * @category Linux */ [[nodiscard]] static bool qemu_ga() try { @@ -4318,7 +4356,7 @@ struct VM { /** - * @brief check for valid MSR value + * @brief check for valid MSR value 0x40000000 * @category Windows * @author LukeGoule * @link https://github.com/LukeGoule/compact_vm_detector/tree/main @@ -4454,7 +4492,7 @@ struct VM { /** - * @brief Check for sidt method + * @brief Check for sidt instruction method * @category Linux, Windows, x86 */ [[nodiscard]] static bool sidt() try { @@ -4527,7 +4565,30 @@ struct VM { /** - * @brief Check for sldt + * @brief Check for sgdt instruction method + * @category Windows, x86 + */ + [[nodiscard]] static bool sgdt() try { +#if (x86_32 && MSVC) + u8 gdtr[6]{}; + u32 gdt = 0; + + _asm sgdt gdtr + gdt = *((unsigned long*)&gdtr[2]); + + return ((gdt >> 24) == 0xFF); +#else + return false; +#endif + } + catch (...) { + debug("SGDT: ", "caught error, returned false"); + return false; + } + + + /** + * @brief Check for sldt instruction method * @category Windows, x86 */ [[nodiscard]] static bool sldt() try { @@ -4550,30 +4611,82 @@ struct VM { /** - * @brief Check for sgdt + * @brief Check for Offensive Security sidt method * @category Windows, x86 + * @author Danny Quist (chamuco@gmail.com) + * @author Val Smith (mvalsmith@metasploit.com) + * @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf */ - [[nodiscard]] static bool sgdt() try { -#if (x86_32 && MSVC) - u8 gdtr[6]{}; - u32 gdt = 0; + [[nodiscard]] static bool offsec_sidt() try { +#if (!MSVC || !x86) + return false; +#elif (x86_32) + unsigned char m[6]{}; + __asm sidt m; - _asm sgdt gdtr - gdt = *((unsigned long*)&gdtr[2]); + return (m[5] > 0xD0); +#else + return false; +#endif + } + catch (...) { + debug("OFFSEC_SIDT: ", "caught error, returned false"); + return false; + } - return ((gdt >> 24) == 0xFF); + + /** + * @brief Check for Offensive Security sgdt method + * @category Windows, x86 + * @author Danny Quist (chamuco@gmail.com) + * @author Val Smith (mvalsmith@metasploit.com) + * @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf + */ + [[nodiscard]] static bool offsec_sgdt() try { +#if (!MSVC || !x86) + return false; +#elif (x86_32) + unsigned char m[6]{}; + __asm sgdt m; + + return (m[5] > 0xD0); #else return false; #endif } catch (...) { - debug("SGDT: ", "caught error, returned false"); + debug("OFFSEC_SGDT: ", "caught error, returned false"); + return false; + } + + + /** + * @brief Check for Offensive Security sldt method + * @category Windows, x86 + * @author Danny Quist (chamuco@gmail.com) + * @author Val Smith (mvalsmith@metasploit.com) + * @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf + */ + [[nodiscard]] static bool offsec_sldt() try { +#if (!MSVC || !x86) + return false; +#elif (x86_32) + unsigned short m[6]{}; + __asm sldt m; + + return (m[0] != 0x00 && m[1] != 0x00); +#else + return false; +#endif + } + catch (...) { + debug("OFFSEC_SLDT: ", "caught error, returned false"); return false; } /** - * @brief Go through the motherboard and match for Hyper-V string + * @brief Check for Hyper-V specific string in motherboard * @category Windows */ [[nodiscard]] static bool hyperv_board() try { @@ -4724,76 +4837,36 @@ struct VM { /** - * @brief Check for offensive security sidt method - * @category Windows, x86 - * @author Danny Quist (chamuco@gmail.com) - * @author Val Smith (mvalsmith@metasploit.com) - * @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf - */ - [[nodiscard]] static bool offsec_sidt() try { -#if (!MSVC || !x86) - return false; -#elif (x86_32) - unsigned char m[6]{}; - __asm sidt m; - - return (m[5] > 0xD0); -#else - return false; -#endif - } - catch (...) { - debug("OFFSEC_SIDT: ", "caught error, returned false"); - return false; - } - - - /** - * @brief Check for offensive security sgdt method - * @category Windows, x86 - * @author Danny Quist (chamuco@gmail.com) - * @author Val Smith (mvalsmith@metasploit.com) - * @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf + * @brief Check for VPC and Parallels files + * @category Windows */ - [[nodiscard]] static bool offsec_sgdt() try { -#if (!MSVC || !x86) + [[nodiscard]] static bool vm_files_extra() try { +#if (!MSVC) return false; -#elif (x86_32) - unsigned char m[6]{}; - __asm sgdt m; - - return (m[5] > 0xD0); #else - return false; -#endif - } - catch (...) { - debug("OFFSEC_SGDT: ", "caught error, returned false"); - return false; - } - + constexpr std::array, 9> files = {{ + { VPC, "c:\\windows\\system32\\drivers\\vmsrvc.sys" }, + { VPC, "c:\\windows\\system32\\drivers\\vpc-s3.sys" }, + { PARALLELS, "c:\\windows\\system32\\drivers\\prleth.sys" }, + { PARALLELS, "c:\\windows\\system32\\drivers\\prlfs.sys" }, + { PARALLELS, "c:\\windows\\system32\\drivers\\prlmouse.sys" }, + { PARALLELS, "c:\\windows\\system32\\drivers\\prlvideo.sys" }, + { PARALLELS, "c:\\windows\\system32\\drivers\\prltime.sys" }, + { PARALLELS, "c:\\windows\\system32\\drivers\\prl_pv32.sys" }, + { PARALLELS, "c:\\windows\\system32\\drivers\\prl_paravirt_32.sys" } + }}; - /** - * @brief Check for Offensive Security sldt method - * @category Windows, x86 - * @author Danny Quist (chamuco@gmail.com) - * @author Val Smith (mvalsmith@metasploit.com) - * @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf - */ - [[nodiscard]] static bool offsec_sldt() try { -#if (!MSVC || !x86) - return false; -#elif (x86_32) - unsigned short m[6]{}; - __asm sldt m; + for (const auto& file_pair : files) { + if (util::exists(file_pair.second)) { + return core::add(file_pair.first); + } + } - return (m[0] != 0x00 && m[1] != 0x00); -#else return false; #endif } catch (...) { - debug("OFFSEC_SLDT: ", "caught error, returned false"); + debug("VM_FILES_EXTRA: caught error, returned false"); return false; } @@ -4805,67 +4878,32 @@ struct VM { * @note Paper situated at /papers/ThwartingVMDetection_Liston_Skoudis.pdf */ [[nodiscard]] static bool vpc_sidt() try { -#if (!MSVC || !x86) - return false; -#elif (x86_32) - u8 idtr[6]{}; - u32 idt = 0; - - _asm sidt idtr - idt = *((unsigned long*)&idtr[2]); - - if ((idt >> 24) == 0xE8) { - return core::add(VPC); - } - - return false; -#else - return false; -#endif - } - catch (...) { - debug("VPC_SIDT: ", "caught error, returned false"); - return false; - } - - - /** - * @brief Find for VPC and Parallels specific VM files - * @category Windows - */ - [[nodiscard]] static bool vm_files_extra() try { -#if (!MSVC) - return false; -#else - constexpr std::array, 9> files = {{ - { VPC, "c:\\windows\\system32\\drivers\\vmsrvc.sys" }, - { VPC, "c:\\windows\\system32\\drivers\\vpc-s3.sys" }, - { PARALLELS, "c:\\windows\\system32\\drivers\\prleth.sys" }, - { PARALLELS, "c:\\windows\\system32\\drivers\\prlfs.sys" }, - { PARALLELS, "c:\\windows\\system32\\drivers\\prlmouse.sys" }, - { PARALLELS, "c:\\windows\\system32\\drivers\\prlvideo.sys" }, - { PARALLELS, "c:\\windows\\system32\\drivers\\prltime.sys" }, - { PARALLELS, "c:\\windows\\system32\\drivers\\prl_pv32.sys" }, - { PARALLELS, "c:\\windows\\system32\\drivers\\prl_paravirt_32.sys" } - }}; +#if (!MSVC || !x86) + return false; +#elif (x86_32) + u8 idtr[6]{}; + u32 idt = 0; - for (const auto& file_pair : files) { - if (util::exists(file_pair.second)) { - return core::add(file_pair.first); - } + _asm sidt idtr + idt = *((unsigned long*)&idtr[2]); + + if ((idt >> 24) == 0xE8) { + return core::add(VPC); } + return false; +#else return false; #endif } catch (...) { - debug("VM_FILES_EXTRA: caught error, returned false"); + debug("VPC_SIDT: ", "caught error, returned false"); return false; } /** - * @brief Find for VMware string in /proc/iomem + * @brief Check for VMware string in /proc/iomem * @category Linux * @note idea from ScoopyNG by Tobias Klein */ @@ -4889,7 +4927,7 @@ struct VM { /** - * @brief Find for VMware string in /proc/ioports + * @brief Check for VMware string in /proc/ioports * @category Windows * @note idea from ScoopyNG by Tobias Klein */ @@ -4913,7 +4951,7 @@ struct VM { /** - * @brief Find for VMware string in /proc/scsi/scsi + * @brief Check for VMware string in /proc/scsi/scsi * @category Windows * @note idea from ScoopyNG by Tobias Klein */ @@ -4937,7 +4975,7 @@ struct VM { /** - * @brief Find for VMware-specific device name in dmesg output + * @brief Check for VMware-specific device name in dmesg output * @category Windows * @note idea from ScoopyNG by Tobias Klein */ @@ -4974,7 +5012,7 @@ struct VM { /** - * @brief Check using str assembly instruction + * @brief Check str assembly instruction method for VMware * @note Alfredo Omella's (S21sec) STR technique * @note paper describing this technique is located at /papers/www.s21sec.com_vmware-eng.pdf (2006) * @category Windows @@ -5196,7 +5234,7 @@ struct VM { /** - * @brief Check for uptime of less than or equal to 2 minutes + * @brief Check if uptime is less than or equal to 2 minutes * @category Windows, Linux * @note https://stackoverflow.com/questions/30095439/how-do-i-get-system-up-time-in-milliseconds-in-c */ @@ -5256,43 +5294,12 @@ struct VM { #else const u32 threads = std::thread::hardware_concurrency(); - struct stepping_struct { - u8 model; - u8 family; - u8 extmodel; - }; - - struct stepping_struct steps {}; - - u32 unused, eax = 0; - cpu::cpuid(eax, unused, unused, unused, 1); - UNUSED(unused); - - steps.model = ((eax >> 4) & 0b1111); - steps.family = ((eax >> 8) & 0b1111); - steps.extmodel = ((eax >> 16) & 0b1111); + struct cpu::stepping_struct steps = cpu::fetch_steppings(); debug("ODD_CPU_THREADS: model = ", static_cast(steps.model)); debug("ODD_CPU_THREADS: family = ", static_cast(steps.family)); debug("ODD_CPU_THREADS: extmodel = ", static_cast(steps.extmodel)); - // check if the CPU is an intel celeron - auto is_celeron = [&steps]() -> bool { - if (!cpu::is_intel()) { - return false; - } - - constexpr u8 celeron_family = 0x6; - constexpr u8 celeron_extmodel = 0x2; - constexpr u8 celeron_model = 0xA; - - return ( - steps.family == celeron_family && - steps.extmodel == celeron_extmodel && - steps.model == celeron_model - ); - }; - // check if the microarchitecture was made before 2006, which was around the time multi-core processors were implemented auto old_microarchitecture = [&steps]() -> bool { constexpr std::array, 32> old_archs = {{ @@ -5361,7 +5368,7 @@ struct VM { } // intel celeron CPUs are relatively modern, but they can contain a single or odd thread count - if (is_celeron()) { + if (cpu::is_celeron(steps)) { return false; } @@ -5381,7 +5388,7 @@ struct VM { /** - * @brief Check for CPUs that don't match their thread count + * @brief Check for Intel CPU thread count database if it matches the system's thread count * @category All, x86 * @link https://en.wikipedia.org/wiki/List_of_Intel_Core_processors */ @@ -6388,7 +6395,7 @@ struct VM { /** - * @brief Check for Intel Xeon CPUs that don't match their thread count + * @brief Same as above, but for Xeon Intel CPUs * @category All, x86 * @link https://en.wikipedia.org/wiki/List_of_Intel_Core_processors */ @@ -6934,13 +6941,13 @@ struct VM { /** - * @brief Check for Hyper-V CPUID technique by checking whether all the bits equate to more than 4000 (not sure how this works if i'm honest) + * @brief Check for CPUID technique by checking whether all the bits equate to more than 4000 (not sure how this works if i'm honest) * @category x86 * @author 一半人生 * @link https://unprotect.it/snippet/vmcpuid/195/ * @copyright MIT */ - [[nodiscard]] static bool hyperv_cpuid() try { + [[nodiscard]] static bool cpuid_bitset() try { #if (!x86) return false; #else @@ -6989,20 +6996,20 @@ struct VM { vid = (i32)cpu_info[0]; if (vid >= 4000) { - return core::add(HYPERV); + return true; } return false; #endif } catch (...) { - debug("HYPERV_CPUID: caught error, returned false"); + debug("CPUID_BITSET: caught error, returned false"); return false; } /** - * @brief Check for cuckoo directory using crt and win api directory functions + * @brief Check for cuckoo directory using crt and WIN API directory functions * @category Windows * @author 一半人生 * @link https://unprotect.it/snippet/checking-specific-folder-name/196/ @@ -7079,7 +7086,7 @@ struct VM { /** - * @brief Check for cuckoo pipe + * @brief Check for Cuckoo specific piping mechanism * @category Windows * @author Thomas Roccia (fr0gger) * @link https://unprotect.it/snippet/checking-specific-folder-name/196/ @@ -7112,7 +7119,7 @@ struct VM { /** - * @brief Check for Azure Hyper-V utilisation through hostname regex check + * @brief Check for default Azure hostname format regex (Azure uses Hyper-V as their base VM brand) * @category Windows, Linux */ [[nodiscard]] static bool hyperv_hostname() try { @@ -7121,10 +7128,375 @@ struct VM { #else std::string hostname = util::get_hostname(); - // most Hyper-V hostnames under Azure have the hostname format of fv-azXXX-XXX where the X is a digit - std::regex pattern("fv-az\\d+-\\d+"); + // most Hyper-V hostnames under Azure have the hostname format of fv-azXXX-XXX where the X is a digit + std::regex pattern("fv-az\\d+-\\d+"); + + if (std::regex_match(hostname, pattern)) { + return core::add(AZURE_HYPERV); + } + + return false; +#endif + } + catch (...) { + debug("HYPERV_HOSTNAME: caught error, returned false"); + return false; + } + + + /** + * @brief Check for commonly set hostnames by certain VM brands + * @category Windows, Linux + * @note Idea from Thomas Roccia (fr0gger) + * @link https://unprotect.it/technique/detecting-hostname-username/ + * @copyright MIT + */ + [[nodiscard]] static bool general_hostname() try { +#if (!(MSVC || LINUX)) + return false; +#else + std::string hostname = util::get_hostname(); + + auto cmp = [&](const char* str2) -> bool { + return (hostname == str2); + }; + + if ( + cmp("Sandbox") || + cmp("Maltest") || + cmp("Malware") || + cmp("malsand") || + cmp("ClonePC") + ) { + return true; + } + + if (cmp("Cuckoo")) { + return core::add(CUCKOO); + } + + return false; +#endif + } + catch (...) { + debug("GENERAL_HOSTNAME: caught error, returned false"); + return false; + } + + + /** + * @brief Check for pre-set screen resolutions commonly found in VMs + * @category Windows + * @note Idea from Thomas Roccia (fr0gger) + * @link https://unprotect.it/technique/checking-screen-resolution/ + * @copyright MIT + */ + [[nodiscard]] static bool screen_resolution() try { +#if (!MSVC) + return false; +#else + RECT desktop; + const HWND hDesktop = GetDesktopWindow(); + GetWindowRect(hDesktop, &desktop); + const i32 horiz = desktop.right; + const i32 verti = desktop.bottom; + + debug("SCREEN_RESOLUTION: horizontal = ", horiz, ", vertical = ", verti); + + if ( + (horiz == 1024 && verti == 768) || + (horiz == 800 && verti == 600) || + (horiz == 640 && verti == 480) + ) { + return true; + } + + return false; +#endif + } + catch (...) { + debug("SCREEN_RESOLUTION: caught error, returned false"); + return false; + } + + + /** + * @brief Check if bogus device string would be accepted + * @category Windows + * @author Huntress Research Team + * @link https://unprotect.it/technique/buildcommdcbandtimeouta/ + * @copyright MIT + */ + [[nodiscard]] static bool device_string() try { +#if (!MSVC) + return false; +#else + DCB dcb = { 0 }; + COMMTIMEOUTS timeouts = { 0 }; + + if (BuildCommDCBAndTimeouts("jhl46745fghb", &dcb, &timeouts)) { + return true; + } else { + debug("DEVICE_STRING: BuildCommDCBAndTimeouts failed"); + return false; + } +#endif + } + catch (...) { + debug("DEVICE_STRING: caught error, returned false"); + return false; + } + + + /** + * @brief Check for the presence of BlueStacks-specific folders + * @category ARM, Linux + */ + [[nodiscard]] static bool bluestacks() try { +#if (!(ARM && LINUX)) + return false; +#else + if ( + util::exists("/mnt/windows/BstSharedFolder") || + util::exists("/sdcard/windows/BstSharedFolder") + ) { + return core::add(BLUESTACKS); + } + + return false; +#endif + } + catch (...) { + debug("BLUESTACKS_FOLDERS: caught error, returned false"); + return false; + } + + + /** + * @brief Check for signatures in leaf 0x40000001 in CPUID + * @link https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/shared/hvgdk_mini/hv_hypervisor_interface.htm + * @link https://github.com/ionescu007/SimpleVisor/blob/master/shvvp.c + * @category x86 + */ + [[nodiscard]] static bool cpuid_signature() try { +#if (!x86) + return false; +#else + u32 eax, unused = 0; + cpu::cpuid(eax, unused, unused, unused, 0x40000001); + UNUSED(unused); + + constexpr u32 hyperv = 0x31237648; // "Hv#1" + constexpr u32 nanovisor = 0x766E6258; // "Xbnv" + constexpr u32 simplevisor = 0x00766853; // " vhS" + + debug("CPUID_SIGNATURE: eax = ", eax); + + switch (eax) { + case hyperv: return core::add(HYPERV); + case nanovisor: return core::add(NANOVISOR); + case simplevisor: return core::add(SIMPLEVISOR); + } + + return false; +#endif + } + catch (...) { + debug("CPUID_SIGNATURE: caught error, returned false"); + return false; + } + + + /** + * @brief Check for Hyper-V CPUID bitmask range for reserved values + * @category x86 + */ + [[nodiscard]] static bool hyperv_bitmask() try { +#if (!x86) + return false; +#else + enum registers : u8 { + EAX = 1, + EBX, + ECX, + EDX + }; + + auto fetch_register = [](const registers register_id, const u32 leaf) -> u32 { + u32 eax, ebx, ecx, edx = 0; + cpu::cpuid(eax, ebx, ecx, edx, leaf); + + switch (register_id) { + case EAX: return eax; + case EBX: return ebx; + case ECX: return ecx; + case EDX: return edx; + } + + return 0; + }; + + const u32 max_leaf = fetch_register(EAX, 0x40000000); + + debug("HYPERV_BITMASK: max leaf = ", std::hex, max_leaf); + + if (max_leaf < 0x4000000A) { + return false; // returned false because we want the most feature leafs as possible for Hyper-V + } + + auto leaf_03 = [&]() -> bool { + const u32 ecx = fetch_register(ECX, 0x40000003); + const u32 edx = fetch_register(EDX, 0x40000003); + + debug("03 ecx = ", std::bitset<31>(ecx)); + debug("03 edx = ", std::bitset<31>(edx)); + + if (ecx == 0 || edx == 0) { + return false; + } else { + return ( + ((ecx & 0b11111) == 0) && + ((ecx >> 9) == 0) && + ((edx & (1 << 16)) == 0) && + ((edx & (1 << 22)) == 0) && + (((edx >> 24) & 0b11) == 0) && + ((edx >> 27) == 0) + ); + } + }; + + auto leaf_04 = [&]() -> bool { + const u32 eax = fetch_register(EAX, 0x40000004); + const u32 ecx = fetch_register(ECX, 0x40000004); + const u32 edx = fetch_register(EDX, 0x40000004); + + debug("04 eax = ", std::bitset<31>(eax)); + debug("04 ecx = ", std::bitset<31>(ecx)); + debug("04 edx = ", std::bitset<31>(edx)); + + if ( + eax == 0 || + ecx == 0 || + edx != 0 // edx is supposed to be null + ) { + return false; + } else { + return ( + ((eax & (1 << 8)) == 0) && + ((eax & (1 << 16)) == 0) && + ((eax >> 19) == 0) && + ((ecx >> 7) == 0) && + (edx == 0) + ); + } + }; + + auto leaf_05 = [&]() -> bool { + const u32 edx = fetch_register(EDX, 0x40000005); + debug("05 edx = ", std::bitset<31>(edx)); + return (edx == 0); + }; + + auto leaf_06 = [&]() -> bool { + u32 eax, ebx, ecx, edx = 0; + cpu::cpuid(eax, ebx, ecx, edx, 0x40000006); + + debug("06 eax = ", std::bitset<31>(eax)); + debug("06 ebx = ", std::bitset<31>(ebx)); + debug("06 ecx = ", std::bitset<31>(ecx)); + debug("06 edx = ", std::bitset<31>(edx)); + + if ( + eax == 0 || + ebx != 0 || + ecx != 0 || + edx != 0 // edx is supposed to be null + ) { + return false; + } else { + return ( + ((eax & (1 << 15)) == 0) && + ((eax >> 25) == 0) && + (ebx == 0) && + (ecx == 0) && + (edx == 0) + ); + } + }; + + auto leaf_09 = [&]() -> bool { + u32 eax, ebx, ecx, edx = 0; + cpu::cpuid(eax, ebx, ecx, edx, 0x40000009); + + debug("09 eax = ", std::bitset<31>(eax)); + debug("09 ebx = ", std::bitset<31>(ebx)); + debug("09 ecx = ", std::bitset<31>(ecx)); + debug("09 edx = ", std::bitset<31>(edx)); + + if ( + eax == 0 || + ebx != 0 || + ecx != 0 || + edx == 0 + ) { + return false; + } else { + return ( + ((eax & 0b11) == 0) && + ((eax & (1 << 3)) == 0) && + (((eax >> 7) & 0b11111) == 0) && + ((eax >> 13) == 0) && + (ebx == 0) && + (ecx == 0) && + ((edx & 0b1111) == 0) && + (((edx >> 5) & 0b1111111111) == 0) && + ((edx & (1 << 16)) == 0) && + ((edx >> 18) == 0) + ); + } + }; + + auto leaf_0A = [&]() -> bool { + const u32 eax = fetch_register(EAX, 0x4000000A); + const u32 ecx = fetch_register(ECX, 0x4000000A); + const u32 edx = fetch_register(EDX, 0x4000000A); - if (std::regex_match(hostname, pattern)) { + debug("0A eax = ", std::bitset<31>(eax)); + debug("0A ecx = ", std::bitset<31>(ecx)); + debug("0A edx = ", std::bitset<31>(edx)); + + if ( + eax == 0 || + ecx != 0 || + edx != 0 + ) { + return false; + } else { + return ( + // ebx is left out on purpose due to how likely it can result the overall result to be a false negative + ((eax & (1 << 16)) == 0) && + ((eax >> 21) == 0) && + (ecx == 0) && + (edx == 0) + ); + } + }; + + debug("03: ", leaf_03()); + debug("04: ", leaf_04()); + debug("05: ", leaf_05()); + debug("06: ", leaf_06()); + debug("09: ", leaf_09()); + debug("0A: ", leaf_0A()); + + if ( + leaf_03() && + leaf_04() && + leaf_05() && + leaf_06() && + leaf_09() && + leaf_0A() + ) { return core::add(HYPERV); } @@ -7132,163 +7504,172 @@ struct VM { #endif } catch (...) { - debug("HYPERV_HOSTNAME: caught error, returned false"); + debug("HYPERV_BITMASK: caught error, returned false"); return false; } /** - * @brief Check for commonly set hostnames by certain VM brands - * @category Windows, Linux - * @note Idea from Thomas Roccia (fr0gger) - * @link https://unprotect.it/technique/detecting-hostname-username/ - * @copyright MIT + * @brief Check for KVM CPUID bitmask range for reserved values + * @category x86 */ - [[nodiscard]] static bool general_hostname() try { -#if (!(MSVC || LINUX)) + [[nodiscard]] static bool kvm_bitmask() try { +#if (!x86) return false; #else - std::string hostname = util::get_hostname(); + u32 eax, ebx, ecx, edx = 0; + cpu::cpuid(eax, ebx, ecx, edx, 0x40000000); + + // KVM brand and max leaf check + if (!( + (eax == 0x40000001) && + (ebx == 0x4b4d564b) && + (ecx == 0x564b4d56) && + (edx == 0x4d) + )) { + return false; + } - auto cmp = [&](const char* str2) -> bool { - return (hostname == str2); - }; + cpu::cpuid(eax, ebx, ecx, edx, 0x40000001); if ( - cmp("Sandbox") || - cmp("Maltest") || - cmp("Malware") || - cmp("malsand") || - cmp("ClonePC") + (eax & (1 << 8)) && + (((eax >> 13) & 0b1111111111) == 0) && + ((eax >> 24) == 0) ) { - return true; - } - - if (cmp("Cuckoo")) { - return core::add(CUCKOO); + return core::add(KVM); } return false; #endif } catch (...) { - debug("GENERAL_HOSTNAME: caught error, returned false"); + debug("KVM_BITMASK: caught error, returned false"); return false; } /** - * @brief Check for known window resolutions in VMs - * @category Windows - * @note Idea from Thomas Roccia (fr0gger) - * @link https://unprotect.it/technique/checking-screen-resolution/ - * @copyright MIT + * @brief Check for Intel KGT (Trusty branch) hypervisor signature in CPUID + * @link https://github.com/intel/ikgt-core/blob/7dfd4d1614d788ec43b02602cce7a272ef8d5931/vmm/vmexit/vmexit_cpuid.c + * @category x86 */ - [[nodiscard]] static bool screen_resolution() try { -#if (!MSVC) + [[nodiscard]] static bool intel_kgt_signature() try { +#if (!x86) return false; #else - RECT desktop; - const HWND hDesktop = GetDesktopWindow(); - GetWindowRect(hDesktop, &desktop); - const i32 horiz = desktop.right; - const i32 verti = desktop.bottom; - - debug("SCREEN_RESOLUTION: horizontal = ", horiz, ", vertical = ", verti); + u32 unused, ecx, edx = 0; + cpu::cpuid(unused, unused, ecx, edx, 3); if ( - (horiz == 1024 && verti == 768) || - (horiz == 800 && verti == 600) || - (horiz == 640 && verti == 480) + // ecx should be "EVMM" and edx is "INTC". + // Not sure if it's little endian or big endian, so i'm comparing both + ((ecx == 0x4D4D5645) && (edx == 0x43544E49)) || + ((ecx == 0x45564D4D) && (edx == 0x494E5443)) ) { - return true; + return core::add(INTEL_KGT); } - + return false; #endif } catch (...) { - debug("SCREEN_RESOLUTION: caught error, returned false"); + debug("KGT_SIGNATURE: caught error, returned false"); return false; } /** - * @brief Check if bogus device string would succeed + * @brief Check for VMware DMI strings in BIOS serial number + * @link https://knowledge.broadcom.com/external/article?legacyId=1009458 * @category Windows - * @author Huntress Research Team - * @link https://unprotect.it/technique/buildcommdcbandtimeouta/ - * @copyright MIT */ - [[nodiscard]] static bool device_string() try { + [[nodiscard]] static bool vmware_dmi() try { #if (!MSVC) return false; #else - DCB dcb = { 0 }; - COMMTIMEOUTS timeouts = { 0 }; + std::unique_ptr info = util::make_unique(); - if (BuildCommDCBAndTimeouts("jhl46745fghb", &dcb, &timeouts)) { - return true; - } else { - debug("DEVICE_STRING: BuildCommDCBAndTimeouts failed"); - return false; + const std::string str = info->get_serialnumber(); + + if (util::find(str, "VMware-")) { + return core::add(VMWARE); + } + + if (util::find(str, "VMW")) { + return core::add(VMWARE_FUSION); } -#endif - } - catch (...) { - debug("DEVICE_STRING: caught error, returned false"); - return false; - } - /** - * @brief Check for the presence of a mouse device - * @category Windows - * @author a0rtega - * @link https://github.com/a0rtega/pafish/blob/master/pafish/rtt.c - * @note from pafish project - * @copyright GPL - */ - [[nodiscard]] static bool mouse_device() try { -#if (!MSVC) return false; -#else - int res; - res = GetSystemMetrics(SM_MOUSEPRESENT); - return (res == 0); #endif - } - catch (...) { - debug("MOUSE_DEVICE: caught error, returned false"); + } catch (...) { + debug("VMWARE_DMI: caught error, returned false"); return false; } - /** - * @brief Check for the presence of a mouse device - * @category Windows - * @author a0rtega - * @link https://github.com/a0rtega/pafish/blob/master/pafish/rtt.c - * @note from pafish project - * @copyright GPL - */ - [[nodiscard]] static bool bluestacks() try { -#if (!ARM) - return false; -#else - if ( - util::exists("/mnt/windows/BstSharedFolder") || - util::exists("/sdcard/windows/BstSharedFolder") - ) { - return core::add(BLUESTACKS); - } - return false; -#endif - } - catch (...) { - debug("BLUESTACKS_FOLDERS: caught error, returned false"); - return false; - } + + + + + + +// https://medium.com/@matterpreter/hypervisor-detection-with-systemhypervisordetailinformation-26e44a57f80e + +// idea: maybe try to get the hyper-v version and check for those values in cpuid + +/* +EAX=21h: Reserved for TDX enumerationWhen Intel TDX (Trust Domain Extensions) is active, attempts to execute the CPUID instruction by a TD (Trust Domain) guest will be intercepted by the TDX module. This module will, when CPUID is invoked with EAX=21h and ECX=0 (leaf 21h, sub-leaf 0), return the index of the highest supported sub-leaf for leaf 21h in EAX and a TDX module vendor ID string as a 12-byte ASCII string in EBX,EDX,ECX (in that order). Intel's own module implementation returns the vendor ID string "IntelTDX " (with four trailing spaces)[102] - for this module, additional feature information is not available through CPUID and must instead be obtained through the TDX-specific TDCALL instruction. +*/ + + +// https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/feature-discovery + + +// https://github.com/systemd/systemd/blob/main/src/basic/virt.c + + +/* +In the same way, a lot of these virtual files can provide information on the environment, including – +but not limited to – /proc/sysinfo (in which some distribution expose data about virtual machines), +/proc/device-tree (that lists the devices on the machine), /proc/xen (a file created by the Xen +Server) or /proc/modules (that contains information about the loaded kernel modules, modules +that are used by hypervisors to optimize the guests). +Like procfs (mounted in /proc), sysfs can be useful. Its role is to provide to the user an access to the +devices and their drivers. The file /sys/hypervisor/type, for instance, is sometimes used to store +information about the hypervisor Linux is running on +*/ + + + + + +// https://github.com/torvalds/linux/blob/31cc088a4f5d83481c6f5041bd6eb06115b974af/arch/x86/kernel/cpu/vmware.c + + + + + + + + + + + + + + + + + + + + + + + + @@ -7319,8 +7700,11 @@ struct VM { #elif (LINUX) [[gnu::const]] #endif - static inline bool add(const char* p_brand) noexcept { + static inline bool add(const char* p_brand, const char* extra_brand = "") noexcept { core::brand_scoreboard.at(p_brand)++; + if (std::strcmp(extra_brand, "") != 0) { + core::brand_scoreboard.at(p_brand)++; + } return true; } @@ -7382,11 +7766,9 @@ struct VM { // at this stage, only non-technique flags are asserted to be set if ( - flags.test(EXTREME) || flags.test(NO_MEMO) || flags.test(HIGH_THRESHOLD) || flags.test(ENABLE_HYPERV_HOST) || - flags.test(WIN_HYPERV_DEFAULT_MACRO) || // deprecated flags.test(MULTIPLE) ) { flags |= DEFAULT; @@ -7460,55 +7842,26 @@ struct VM { } - template - static std::vector brand_vector(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); - } - - std::vector tmp_vec; - - for ( - auto it = core::brand_scoreboard.cbegin(); - it != core::brand_scoreboard.cend(); - ++it - ) { - const char* brand = it->first; - const brand_score_t points = it->second; - - if (points > 0) { - tmp_vec.push_back(brand); - } - } - - return tmp_vec; - } - // check if Hyper-V is running - static bool hyperv_host_virtualisation_check(const flagset& flags) { + static bool hyperv_host_virt_check(const flagset& flags) { if ( - core::is_enabled(flags, ENABLE_HYPERV_HOST) || - core::is_enabled(flags, WIN_HYPERV_DEFAULT_MACRO) // deprecated + core::is_enabled(flags, ENABLE_HYPERV_HOST) ) { core_debug("HYPERV_CHECK: returned false through flag check"); return false; } - std::vector brand_vec = brand_vector(flags); + std::vector brand_vec = VM::brand_vector(flags); - bool is_hyperv_present = false; + bool is_hyperv_vpc_present = false; for (const auto p_brand : brand_vec) { - if (p_brand == HYPERV) { - is_hyperv_present = true; + if (p_brand == HYPERV || p_brand == VPC) { + is_hyperv_vpc_present = true; } } - if (is_hyperv_present == false) { + if (is_hyperv_vpc_present == false) { return false; } @@ -7525,7 +7878,7 @@ struct VM { return false; } #endif - + core_debug("HYPERV_HOST_CHECK: valid_version = ", false); return false; }; @@ -7562,9 +7915,8 @@ struct VM { auto technique_check = []() -> bool { std::vector techniques = memo::cache_fetch_all(); - constexpr std::array non_brand_returners = {{ + constexpr std::array non_brand_returners = {{ VM::HYPERVISOR_BIT, - VM::CPUID_0X4, VM::HYPERVISOR_STR, VM::RDTSC, VM::THREADCOUNT, @@ -7598,7 +7950,8 @@ struct VM { VM::INTEL_THREAD_MISMATCH, VM::XEON_THREAD_MISMATCH, VM::SCREEN_RESOLUTION, - VM::DEVICE_STRING + VM::DEVICE_STRING, + VM::CPUID_BITSET }}; bool no_possible_brand = true; @@ -7793,6 +8146,7 @@ struct VM { public: // START OF PUBLIC FUNCTIONS + /** * @brief Check for a specific technique based on flag argument * @param u8 (flags from VM wrapper) @@ -7823,10 +8177,8 @@ struct VM { // check if the bit is a non-technique flag, which shouldn't be allowed if ( (flag_bit == NO_MEMO) || - (flag_bit == EXTREME) || (flag_bit == HIGH_THRESHOLD) || (flag_bit == ENABLE_HYPERV_HOST) || - (flag_bit == WIN_HYPERV_DEFAULT_MACRO) || // deprecated (flag_bit == MULTIPLE) ) { throw_error("Flag argument must be a technique flag and not a settings flag"); @@ -7883,7 +8235,6 @@ struct VM { #define brands core::brand_scoreboard - // check if it's already cached and return that instead if (core::is_disabled(flags, NO_MEMO)) { if (is_multiple) { @@ -7929,9 +8280,12 @@ struct VM { constexpr const char* TMP_ESX = "VMware ESX"; constexpr const char* TMP_GSX = "VMware GSX"; constexpr const char* TMP_WORKSTATION = "VMware Workstation"; + constexpr const char* TMP_FUSION = "VMware Fusion"; constexpr const char* TMP_VPC = "Virtual PC"; constexpr const char* TMP_HYPERV = "Microsoft Hyper-V"; + constexpr const char* TMP_AZURE = "Microsoft Azure Hyper-V"; + constexpr const char* TMP_NANOVISOR = "Xbox NanoVisor (Hyper-V)"; #else constexpr const char* TMP_QEMU = QEMU; constexpr const char* TMP_KVM = KVM; @@ -7942,45 +8296,37 @@ struct VM { constexpr const char* TMP_ESX = VMWARE_ESX; constexpr const char* TMP_GSX = VMWARE_GSX; constexpr const char* TMP_WORKSTATION = VMWARE_WORKSTATION; + constexpr const char* TMP_FUSION = VMWARE_FUSION; constexpr const char* TMP_VPC = VPC; constexpr const char* TMP_HYPERV = HYPERV; + constexpr const char* TMP_AZURE = AZURE_HYPERV; + constexpr const char* TMP_NANOVISOR = NANOVISOR; #endif - if (is_multiple) { - std::vector potential_brands; - - for (auto it = brands.cbegin(); it != brands.cend(); ++it) { - const brand_score_t points = it->second; - const std::string brand = it->first; - - if (points > 0) { - potential_brands.push_back(brand); - } - } - - std::stringstream ss; - u8 i = 1; - - ss << potential_brands.front(); - for (; i < potential_brands.size(); i++) { - ss << " or "; - ss << potential_brands.at(i); - } - current_brand = ss.str(); - } else if ( - brands.at(TMP_KVM) > 0 && - brands.at(TMP_KVM_HYPERV) > 0 + if ( + (brands.at(TMP_KVM) > 0) && + (brands.at(TMP_KVM_HYPERV) > 0) ) { current_brand = TMP_KVM_HYPERV; } else if ( - brands.at(TMP_QEMU) > 0 && - brands.at(TMP_KVM) > 0 + (brands.at(TMP_QEMU) > 0) && + (brands.at(TMP_KVM) > 0) ) { current_brand = "QEMU+KVM"; } else if ( - brands.at(TMP_VPC) > 0 && - brands.at(TMP_HYPERV) > 0 + (brands.at(TMP_AZURE) > 0) && + ((brands.at(TMP_HYPERV) > 0) || (brands.at(TMP_VPC) > 0)) + ) { + current_brand = TMP_AZURE; + } else if ( + (brands.at(TMP_NANOVISOR) > 0) && + ((brands.at(TMP_HYPERV) > 0) || (brands.at(TMP_VPC) > 0)) + ) { + current_brand = TMP_NANOVISOR; + } else if ( + (brands.at(TMP_VPC) > 0) && + (brands.at(TMP_HYPERV) > 0) ) { #if (MSVC) const u8 version = util::get_windows_version(); @@ -7996,16 +8342,42 @@ struct VM { #else current_brand = "Microsoft Virtual PC/Hyper-V"; #endif + } else if ( + // if only VMware Fusion is detected, set it. Otherwise, ignore this (the Fusion detection mechanisms are very weak as of 1.6 release) + (brands.at(TMP_FUSION) > 0) && + (brands.at(TMP_EXPRESS) == 0) && + (brands.at(TMP_ESX) == 0) && + (brands.at(TMP_GSX) == 0) && + (brands.at(TMP_WORKSTATION) == 0) && + (brands.at(TMP_VMWARE) == 0) + ) { + current_brand = TMP_FUSION; } else if (brands.at(TMP_VMWARE) > 0) { - if (brands.at(TMP_EXPRESS) > 0) { - current_brand = TMP_EXPRESS; - } else if (brands.at(TMP_ESX) > 0) { - current_brand = TMP_ESX; - } else if (brands.at(TMP_GSX) > 0) { - current_brand = TMP_GSX; - } else if (brands.at(TMP_WORKSTATION) > 0) { - current_brand = TMP_WORKSTATION; + if (brands.at(TMP_EXPRESS) > 0) { current_brand = TMP_EXPRESS; } + else if (brands.at(TMP_ESX) > 0) { current_brand = TMP_ESX; } + else if (brands.at(TMP_GSX) > 0) { current_brand = TMP_GSX; } + else if (brands.at(TMP_WORKSTATION) > 0) { current_brand = TMP_WORKSTATION; } + } else if (is_multiple) { + std::vector potential_brands; + + for (auto it = brands.cbegin(); it != brands.cend(); ++it) { + const brand_score_t points = it->second; + const std::string brand = it->first; + + if (points > 0) { + potential_brands.push_back(brand); + } + } + + std::stringstream ss; + u8 i = 1; + + ss << potential_brands.front(); + for (; i < potential_brands.size(); i++) { + ss << " or "; + ss << potential_brands.at(i); } + current_brand = ss.str(); } if (core::is_disabled(flags, NO_MEMO)) { @@ -8039,13 +8411,6 @@ struct VM { static bool detect(Args ...args) { flagset flags = core::arg_handler(args...); - if ( - core::is_enabled(flags, EXTREME) && \ - core::is_enabled(flags, HIGH_THRESHOLD) - ) { - throw std::invalid_argument("VM::EXTREME and VM::HIGH_THRESHOLD should not be set at the same time. Use either options instead of both"); - } - const u16 points = core::run_all(flags, SHORTCUT); #if (CPP >= 23) @@ -8054,15 +8419,13 @@ struct VM { bool result = false; - if (core::is_enabled(flags, EXTREME)) { - result = (points > 0); - } else if (core::is_enabled(flags, HIGH_THRESHOLD)) { - result = (points > high_threshold_score); + if (core::is_enabled(flags, HIGH_THRESHOLD)) { + result = (points >= high_threshold_score); } else { result = (points >= 100); } - if (core::hyperv_host_virtualisation_check(flags)) { + if (core::hyperv_host_virt_check(flags)) { core_debug("VM::detect(): returned false due to Hyper-V default check"); return false; } @@ -8081,13 +8444,6 @@ struct VM { static u8 percentage(Args ...args) { flagset flags = core::arg_handler(args...); - if ( - core::is_enabled(flags, EXTREME) && \ - core::is_enabled(flags, HIGH_THRESHOLD) - ) { - throw std::invalid_argument("VM::EXTREME and VM::HIGH_THRESHOLD should not be set at the same time. Use either options instead of both"); - } - const u16 points = core::run_all(flags, SHORTCUT); u8 percent = 0; @@ -8095,16 +8451,12 @@ struct VM { [[assume(points < maximum_points)]]; #endif - u16 threshold = 200; + u16 threshold = 150; if (core::is_enabled(flags, HIGH_THRESHOLD)) { threshold = high_threshold_score; } - if (core::is_enabled(flags, EXTREME)) { - threshold = 0; - } - if (points >= threshold) { percent = 100; } else if (points >= 100) { @@ -8113,7 +8465,7 @@ struct VM { percent = static_cast(points); } - if (core::hyperv_host_virtualisation_check(flags)) { + if (core::hyperv_host_virt_check(flags)) { core_debug("VM::percentage(): returned 0 due to Hyper-V default check"); return 0; } @@ -8174,14 +8526,46 @@ struct VM { flags.flip(); flags.set(NO_MEMO, 0); - flags.set(EXTREME, 0); flags.set(HIGH_THRESHOLD, 0); flags.set(ENABLE_HYPERV_HOST, 0); - flags.set(WIN_HYPERV_DEFAULT_MACRO, 0); // deprecated 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::vector brand_vector(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); + } + + std::vector tmp_vec; + + for ( + auto it = core::brand_scoreboard.cbegin(); + it != core::brand_scoreboard.cend(); + ++it + ) { + const char* brand = it->first; + const brand_score_t points = it->second; + + if (points > 0) { + tmp_vec.push_back(brand); + } + } + + return tmp_vec; + } }; MSVC_ENABLE_WARNING(ASSIGNMENT_OPERATOR NO_INLINE_FUNC SPECTRE) @@ -8200,6 +8584,7 @@ std::map VM::core::brand_scoreboard { { VM::VMWARE_ESX, 0 }, { VM::VMWARE_GSX, 0 }, { VM::VMWARE_WORKSTATION, 0 }, + { VM::VMWARE_FUSION, 0 }, { VM::BHYVE, 0 }, { VM::QEMU, 0 }, { VM::KVM, 0 }, @@ -8218,7 +8603,7 @@ std::map VM::core::brand_scoreboard { { VM::VPC, 0 }, { VM::ANUBIS, 0 }, { VM::JOEBOX, 0 }, - { VM::THREADEXPERT, 0 }, + { VM::THREATEXPERT, 0 }, { VM::CWSANDBOX, 0 }, { VM::COMODO, 0 }, { VM::BOCHS, 0 }, @@ -8228,7 +8613,13 @@ std::map VM::core::brand_scoreboard { { VM::UNISYS, 0 }, { VM::LMHS, 0 }, { VM::CUCKOO, 0 }, - { VM::BLUESTACKS, 0 } + { VM::BLUESTACKS, 0 }, + { VM::JAILHOUSE, 0 }, + { VM::APPLE_VZ, 0 }, + { VM::INTEL_KGT, 0 }, + { VM::AZURE_HYPERV, 0 }, + { VM::NANOVISOR, 0 }, + { VM::SIMPLEVISOR, 0 } }; @@ -8254,12 +8645,10 @@ VM::flagset VM::DEFAULT = []() -> flagset { tmp.set(); // disable all the non-default flags - tmp.flip(EXTREME); tmp.flip(NO_MEMO); tmp.flip(CURSOR); tmp.flip(HIGH_THRESHOLD); tmp.flip(ENABLE_HYPERV_HOST); - tmp.flip(WIN_HYPERV_DEFAULT_MACRO); // deprecated tmp.flip(MULTIPLE); return tmp; @@ -8316,11 +8705,10 @@ const std::map VM::core::technique_table = { { VM::VMID, { 100, VM::vmid }}, { VM::CPU_BRAND, { 50, VM::cpu_brand }}, { VM::HYPERVISOR_BIT, { 100, VM::hypervisor_bit }}, - { VM::CPUID_0X4, { 70, VM::cpuid_0x4 }}, { VM::HYPERVISOR_STR, { 45, VM::hypervisor_brand }}, { VM::RDTSC, { 10, VM::rdtsc_check }}, { VM::THREADCOUNT, { 35, VM::thread_count }}, - { VM::MAC, { 90, VM::mac_address_check }}, + { VM::MAC, { 60, VM::mac_address_check }}, { VM::TEMPERATURE, { 15, VM::temperature }}, { VM::SYSTEMD, { 70, VM::systemd_virt }}, { VM::CVENDOR, { 65, VM::chassis_vendor }}, @@ -8389,16 +8777,17 @@ const std::map VM::core::technique_table = { { VM::INTEL_THREAD_MISMATCH, { 85, VM::intel_thread_mismatch }}, { VM::XEON_THREAD_MISMATCH, { 85, VM::xeon_thread_mismatch }}, { VM::NETTITUDE_VM_MEMORY, { 75, VM::nettitude_vm_memory }}, - { VM::HYPERV_CPUID, { 35, VM::hyperv_cpuid }}, + { VM::CPUID_BITSET, { 20, VM::cpuid_bitset }}, { VM::CUCKOO_DIR, { 15, VM::cuckoo_dir }}, { VM::CUCKOO_PIPE, { 20, VM::cuckoo_pipe }}, { VM::HYPERV_HOSTNAME, { 50, VM::hyperv_hostname }}, { VM::GENERAL_HOSTNAME, { 20, VM::general_hostname }}, { VM::SCREEN_RESOLUTION, { 30, VM::screen_resolution }}, { VM::DEVICE_STRING, { 25, VM::device_string }}, - { VM::BLUESTACKS_FOLDERS, { 15, VM::bluestacks }} - - // __TABLE_LABEL, add your technique above - // { VM::FUNCTION, { POINTS, FUNCTION_POINTER }} - // ^ template + { VM::BLUESTACKS_FOLDERS, { 15, VM::bluestacks }}, + { VM::CPUID_SIGNATURE, { 95, VM::cpuid_signature }}, + { VM::HYPERV_BITMASK, { 40, VM::hyperv_bitmask }}, + { VM::KVM_BITMASK, { 40, VM::kvm_bitmask }}, + { VM::KGT_SIGNATURE, { 80, VM::intel_kgt_signature }}, + { VM::VMWARE_DMI, { 30, VM::vmware_dmi }} }; \ No newline at end of file