diff --git a/README.md b/README.md index a7184c55..1e180a17 100755 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The library is: - Very easy to use - Cross-platform (Windows + MacOS + Linux) -- Features around 100 unique VM detection techniques [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#flag-table)] +- Features around 90 unique VM detection techniques [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#flag-table)] - Features the most cutting-edge techniques - Able to detect over 70 VM brands including VMware, VirtualBox, QEMU, Hyper-V, and much more [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#brand-table)] - Able to beat VM hardeners diff --git a/src/cli.cpp b/src/cli.cpp index 03b74f83..724aa830 100755 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -751,10 +751,7 @@ static void general( checker(VM::AUDIO, "audio devices"); checker(VM::DEVICE_HANDLES, "device handles"); checker(VM::VPC_INVALID, "VPC invalid instructions"); - checker(VM::SIDT, "SIDT"); - checker(VM::SGDT, "SGDT"); - checker(VM::SLDT, "SLDT"); - checker(VM::SMSW, "SMSW"); + checker(VM::SYSTEM_REGISTERS, "Task segment and descriptor tables"); checker(VM::VMWARE_IOMEM, "/proc/iomem file"); checker(VM::VMWARE_IOPORTS, "/proc/ioports file"); checker(VM::VMWARE_SCSI, "/proc/scsi/scsi file"); diff --git a/src/vmaware.hpp b/src/vmaware.hpp index f1f2891d..962d0b7e 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -551,9 +551,6 @@ struct VM { POWER_CAPABILITIES, DISK_SERIAL, IVSHMEM, - SGDT, - SLDT, - SMSW, DRIVERS, DEVICE_HANDLES, VIRTUAL_PROCESSORS, @@ -584,7 +581,7 @@ struct VM { CLOCK, // Linux and Windows - SIDT, + SYSTEM_REGISTERS, FIRMWARE, PCI_DEVICES, AZURE, @@ -677,7 +674,7 @@ struct VM { // for platform compatibility ranges static constexpr u8 WINDOWS_START = VM::GPU_CAPABILITIES; static constexpr u8 WINDOWS_END = VM::AZURE; - static constexpr u8 LINUX_START = VM::SIDT; + static constexpr u8 LINUX_START = VM::SYSTEM_REGISTERS; static constexpr u8 LINUX_END = VM::THREAD_COUNT; static constexpr u8 MACOS_START = VM::THREAD_COUNT; static constexpr u8 MACOS_END = VM::MAC_SYS; @@ -3025,18 +3022,20 @@ struct VM { bool result; u8 points; bool cached; + const char* brand_name; }; struct cache_entry { bool result; u8 points; bool has_value; + const char* brand_name; }; static std::array cache_table; - static void cache_store(u16 flag, bool result, u8 points) { + static void cache_store(u16 flag, bool result, u8 points, const char* brand = nullptr) { if (flag <= enum_size) { - cache_table[flag] = { result, points, true }; + cache_table[flag] = { result, points, true, brand }; } } @@ -3049,14 +3048,15 @@ struct VM { static data_t cache_fetch(u16 flag) { if (flag <= enum_size && cache_table[flag].has_value) { - return { cache_table[flag].result, cache_table[flag].points, true }; + return { cache_table[flag].result, cache_table[flag].points, true, cache_table[flag].brand_name }; } - return { false, 0, false }; + return { false, 0, false, nullptr }; } static void uncache(u16 flag) { if (flag <= enum_size) { cache_table[flag].has_value = false; + cache_table[flag].brand_name = nullptr; } } @@ -3191,6 +3191,11 @@ struct VM { static const char* fetch_manufacturer() noexcept { return manufacturer; } static const char* fetch_model() noexcept { return model; } }; + + struct hardened { + static bool result; + static bool cached; + }; }; // miscellaneous functionalities @@ -6048,54 +6053,74 @@ struct VM { #if (LINUX || WINDOWS) /** - * @brief Check for uncommon IDT virtual addresses + * @brief Check for Task Segment and Descriptor Table instructions (SGDT, SLDT, SMSW, SIDT) + * @category Windows, Linux, x86, x86_32 + * @implements VM::DESCRIPTOR_TABLES + * + * --- SGDT --- + * @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) + * + * --- SLDT --- + * @author Danny Quist (chamuco@gmail.com), ldtr_buf signature + * @author Val Smith (mvalsmith@metasploit.com), ldtr_buf signature + * @author code documentation paper in /papers/www.offensivecomputing.net_vm.pdf for ldtr_buf signature and in https://www.aldeid.com/wiki/X86-assembly/Instructions/sldt for ldt signature + * + * --- SMSW --- + * @author Danny Quist from Offensive Computing + * + * --- SIDT --- * @author Matteo Malvica * @author Idea to check VPC's range from Tom Liston and Ed Skoudis' paper "On the Cutting Edge: Thwarting Virtual Machine Detection" (Windows) * @link https://www.matteomalvica.com/blog/2018/12/05/detecting-vmware-on-64-bit-systems/ (Linux) - * @category Windows, Linux, x86 - * @implements VM::SIDT + * */ - [[nodiscard]] static bool sidt() { + [[nodiscard]] static bool system_registers() { + // Even though SMSW queries a status register (CR0), it is historically grouped with descriptor table checks in virtualization detection + // (often called "Red Pill" techniques) + bool found = false; + + // Linux Implementation (SIDT only) #if (LINUX && (GCC || CLANG) && x86) u8 values[10] = { 0 }; fflush(stdout); #if (x86_64) - // 64-bit Linux: IDT descriptor is 10 bytes (2-byte limit + 8-byte base) - __asm__ __volatile__("sidt %0" : "=m"(values)); - - #ifdef __VMAWARE_DEBUG__ - debug("SIDT: values = "); - for (u8 i = 0; i < 10; ++i) { - debug(std::hex, std::setw(2), std::setfill('0'), static_cast(values[i])); - if (i < 9) debug(" "); - } - #endif + // 64-bit Linux: IDT descriptor is 10 bytes (2-byte limit + 8-byte base) + __asm__ __volatile__("sidt %0" : "=m"(values)); - return (values[9] == 0x00); // 10th byte in x64 mode + #ifdef __VMAWARE_DEBUG__ + debug("SIDT: values = "); + for (u8 i = 0; i < 10; ++i) { + debug(std::hex, std::setw(2), std::setfill('0'), static_cast(values[i])); + if (i < 9) debug(" "); + } + #endif + + if (values[9] == 0x00) found = true; // 10th byte in x64 mode #elif (x86_32) - // 32-bit Linux: IDT descriptor is 6 bytes (2-byte limit + 4-byte base) - __asm__ __volatile__("sidt %0" : "=m"(values)); - - #ifdef __VMAWARE_DEBUG__ - debug("SIDT: values = "); - for (u8 i = 0; i < 6; ++i) { - debug(std::hex, std::setw(2), std::setfill('0'), static_cast(values[i])); - if (i < 5) debug(" "); - } - #endif + // 32-bit Linux: IDT descriptor is 6 bytes (2-byte limit + 4-byte base) + __asm__ __volatile__("sidt %0" : "=m"(values)); - return (values[5] == 0x00); // 6th byte in x86 mode - #else - return false; + #ifdef __VMAWARE_DEBUG__ + debug("SIDT: values = "); + for (u8 i = 0; i < 6; ++i) { + debug(std::hex, std::setw(2), std::setfill('0'), static_cast(values[i])); + if (i < 5) debug(" "); + } + #endif + + if (values[5] == 0x00) found = true; // 6th byte in x86 mode #endif + + // Windows Implementation (SGDT, SLDT, SIDT, SMSW) #elif (WINDOWS && x86) SYSTEM_INFO si; GetNativeSystemInfo(&si); DWORD_PTR originalMask = 0; const HANDLE hCurrentThread = reinterpret_cast(-2LL); + // Iterating processors for SGDT, SLDT, and SIDT for (DWORD i = 0; i < si.dwNumberOfProcessors; ++i) { const DWORD_PTR mask = (DWORD_PTR)1 << i; const DWORD_PTR previousMask = SetThreadAffinityMask(hCurrentThread, mask); @@ -6108,52 +6133,135 @@ struct VM { originalMask = previousMask; } - #if (x86_64) - u8 idtr_buffer[10] = { 0 }; - #else - u8 idtr_buffer[6] = { 0 }; - #endif - - __try { - #if (CLANG || GCC) - __asm__ volatile("sidt %0" : "=m"(idtr_buffer)); - #elif (MSVC) && (x86_32) - __asm { sidt idtr_buffer } - #elif (MSVC) && (x86_64) - #pragma pack(push, 1) - struct { - USHORT Limit; - ULONG_PTR Base; - } idtr; - #pragma pack(pop) - __sidt(&idtr); - memcpy(idtr_buffer, &idtr, sizeof(idtr)); + // Technique 1: SGDT (x86 & x64) + { + #if (x86_64) + u8 gdtr[10] = { 0 }; + #else + u8 gdtr[6] = { 0 }; #endif - } - __except (EXCEPTION_EXECUTE_HANDLER) {} // CR4.UMIP - ULONG_PTR idt_base = 0; - memcpy(&idt_base, &idtr_buffer[2], sizeof(idt_base)); + __try { + #if (CLANG || GCC) + __asm__ volatile("sgdt %0" : "=m"(gdtr)); + #elif (MSVC && x86_32) + __asm { sgdt gdtr } + #else + #pragma pack(push,1) + struct { + u16 limit; + u64 base; + } _gdtr = {}; + #pragma pack(pop) + _sgdt(&_gdtr); + memcpy(gdtr, &_gdtr, sizeof(_gdtr)); + #endif + } + __except (EXCEPTION_EXECUTE_HANDLER) {} // CR4.UMIP - // Check for the 0xE8 signature (VPC/Hyper-V) in the high byte - if ((idt_base >> 24) == 0xE8) { - debug("SIDT: VPC/Hyper-V signature detected on core %u", i); + ULONG_PTR gdt_base = 0; + memcpy(&gdt_base, &gdtr[2], sizeof(gdt_base)); - if (originalMask != 0) { - SetThreadAffinityMask(hCurrentThread, originalMask); + if ((gdt_base >> 24) == 0xFF) { + debug("SGDT: 0xFF signature detected on core %u", i); + found = true; } - return core::add(brands::VPC); } + + // Technique 2: SLDT (x86_32 only) + #if (x86_32) + if (!found) { + u8 ldtr_buf[4] = { 0xEF, 0xBE, 0xAD, 0xDE }; + u32 ldt_val = 0; + + __try { + #if (CLANG || GCC) + __asm__ volatile("sldt %0" : "=m"(*(u16*)ldtr_buf)); + #else // MSVC + __asm { + sldt ax + mov word ptr[ldtr_buf], ax + } + #endif + } + __except (EXCEPTION_EXECUTE_HANDLER) {} // CR4.UMIP + + memcpy(&ldt_val, ldtr_buf, sizeof(ldt_val)); + if (ldtr_buf[0] != 0x00 && ldtr_buf[1] != 0x00) { + debug("SLDT: ldtr_buf signature detected"); + found = true; + } + if (ldt_val != 0xDEAD0000) { + debug("SLDT: 0xDEAD0000 signature detected"); + found = true; + } + } + #endif + + // Technique 3: SIDT (x86 & x64) + if (!found) { + #if (x86_64) + u8 idtr_buffer[10] = { 0 }; + #else + u8 idtr_buffer[6] = { 0 }; + #endif + + __try { + #if (CLANG || GCC) + __asm__ volatile("sidt %0" : "=m"(idtr_buffer)); + #elif (MSVC) && (x86_32) + __asm { sidt idtr_buffer } + #elif (MSVC) && (x86_64) + #pragma pack(push, 1) + struct { + USHORT Limit; + ULONG_PTR Base; + } idtr; + #pragma pack(pop) + __sidt(&idtr); + memcpy(idtr_buffer, &idtr, sizeof(idtr)); + #endif + } + __except (EXCEPTION_EXECUTE_HANDLER) {} // CR4.UMIP + + ULONG_PTR idt_base = 0; + memcpy(&idt_base, &idtr_buffer[2], sizeof(idt_base)); + + // Check for the 0xE8 signature (VPC/Hyper-V) in the high byte + if ((idt_base >> 24) == 0xE8) { + debug("SIDT: VPC/Hyper-V signature detected on core %u", i); + found = true; + } + } + + if (found) break; } if (originalMask != 0) { SetThreadAffinityMask(hCurrentThread, originalMask); } - return false; - #else - return false; + // Technique 4: SMSW (x86_32 only), no affinity pinning needed + #if (x86_32) + if (!found) { + u32 reax = 0; + __asm + { + mov eax, 0xCCCCCCCC; + smsw eax; + mov DWORD PTR[reax], eax; + } + + if ((((reax >> 24) & 0xFF) == 0xCC) && (((reax >> 16) & 0xFF) == 0xCC)) { + debug("SMSW: Signature detected"); + found = true; + } + } + #endif + #endif + + return found; } @@ -6709,6 +6817,7 @@ struct VM { #endif } + /** * @brief Check for PCI vendor and device IDs that are VM-specific * @link https://www.pcilookup.com/?ven=&dev=&action=submit @@ -7671,159 +7780,6 @@ struct VM { } - /** - * @brief Check for sgdt instruction method - * @category Windows, x86 - * @note code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) - * @implements VM::SGDT - */ - [[nodiscard]] static bool sgdt() { - bool found = false; - #if (x86) - SYSTEM_INFO si; - GetNativeSystemInfo(&si); - DWORD_PTR originalMask = 0; - const HANDLE hCurrentThread = reinterpret_cast(-2LL); - - for (DWORD i = 0; i < si.dwNumberOfProcessors; ++i) { - const DWORD_PTR mask = (DWORD_PTR)1 << i; - const DWORD_PTR previousMask = SetThreadAffinityMask(hCurrentThread, mask); - - if (previousMask == 0) { - continue; - } - - if (originalMask == 0) { - originalMask = previousMask; - } - - #if (x86_64) - u8 gdtr[10] = { 0 }; - #else - u8 gdtr[6] = { 0 }; - #endif - - __try { - #if (CLANG || GCC) - __asm__ volatile("sgdt %0" : "=m"(gdtr)); - #elif (MSVC && x86_32) - __asm { sgdt gdtr } - #else - #pragma pack(push,1) - struct { - u16 limit; - u64 base; - } _gdtr = {}; - #pragma pack(pop) - _sgdt(&_gdtr); - memcpy(gdtr, &_gdtr, sizeof(_gdtr)); - #endif - } - __except (EXCEPTION_EXECUTE_HANDLER) {} // CR4.UMIP - - ULONG_PTR gdt_base = 0; - memcpy(&gdt_base, &gdtr[2], sizeof(gdt_base)); - - if ((gdt_base >> 24) == 0xFF) { - debug("SGDT: 0xFF signature detected on core %u", i); - found = true; - } - - if (found) break; - } - - if (originalMask != 0) { - SetThreadAffinityMask(hCurrentThread, originalMask); - } - #endif - return found; - } - - - /** - * @brief Check for sldt instruction method - * @category Windows, x86_32 - * @author Danny Quist (chamuco@gmail.com), ldtr_buf signature - * @author Val Smith (mvalsmith@metasploit.com), ldtr_buf signature - * @author code documentation paper in /papers/www.offensivecomputing.net_vm.pdf for ldtr_buf signature and in https://www.aldeid.com/wiki/X86-assembly/Instructions/sldt for ldt signature - * @implements VM::SLDT - */ - [[nodiscard]] static bool sldt() { - bool found = false; - #if (x86_32) - SYSTEM_INFO si; - GetNativeSystemInfo(&si); - const HANDLE hCurrentThread = reinterpret_cast(-2LL); - const DWORD_PTR origMask = SetThreadAffinityMask(hCurrentThread, 1); - SetThreadAffinityMask(hCurrentThread, origMask); - - for (DWORD i = 0; i < si.dwNumberOfProcessors; ++i) { - const DWORD_PTR mask = (DWORD_PTR)1 << i; - if (SetThreadAffinityMask(hCurrentThread, mask) == 0) - continue; - - u8 ldtr_buf[4] = { 0xEF, 0xBE, 0xAD, 0xDE }; - u32 ldt_val = 0; - - __try { - #if (CLANG || GCC) - __asm__ volatile("sldt %0" : "=m"(*(u16*)ldtr_buf)); - #else // MSVC - __asm { - sldt ax - mov word ptr[ldtr_buf], ax - } - #endif - } - __except (EXCEPTION_EXECUTE_HANDLER) {} // CR4.UMIP - - memcpy(&ldt_val, ldtr_buf, sizeof(ldt_val)); - if (ldtr_buf[0] != 0x00 && ldtr_buf[1] != 0x00) { - debug("SLDT: ldtr_buf signature detected"); - found = true; - } - if (ldt_val != 0xDEAD0000) { - debug("SLDT: 0xDEAD0000 signature detected"); - found = true; - } - - if (found) - break; - } - - SetThreadAffinityMask(hCurrentThread, origMask); - #endif - return found; - } - - - /** - * @brief Check for SMSW assembly instruction technique - * @category Windows, x86_32 - * @author Danny Quist from Offensive Computing - * @implements VM::SMSW - */ - [[nodiscard]] static bool smsw() { - #if (x86_32) - u32 reax = 0; - - __asm - { - mov eax, 0xCCCCCCCC; - smsw eax; - mov DWORD PTR[reax], eax; - } - - return ( - (((reax >> 24) & 0xFF) == 0xCC) && - (((reax >> 16) & 0xFF) == 0xCC) - ); - #else - return false; - #endif - } - - /** * @brief Check str assembly instruction method for VMware * @author Alfredo Omella's (S21sec) STR technique, paper describing this technique is located in /papers/ @@ -9197,7 +9153,7 @@ struct VM { */ [[nodiscard]] static bool trap() { bool hypervisorCaught = false; -#if (x86_64) + #if (x86_64) // when a single - step(TF) and hardware breakpoint(DR0) collide, Intel CPUs set both DR6.BS and DR6.B0 to report both events, which help make this detection trick // AMD CPUs prioritize the breakpoint, setting only its corresponding bit in DR6 and clearing the single-step bit, which is why this technique is not compatible with AMD if (!cpu::is_intel()) { @@ -9389,7 +9345,7 @@ struct VM { PVOID freeBase = execMem; SIZE_T freeSize = trampSize; pNtFreeVirtualMemory(hCurrentProcess, &freeBase, &freeSize, MEM_RELEASE); -#endif + #endif return hypervisorCaught; } @@ -11157,7 +11113,10 @@ struct VM { &propertyType, nullptr, 0, &required); if (required > bufBytes) { BYTE* newBuf = static_cast(realloc(buffer, required)); - if (!newBuf) { found = false; break; } + if (!newBuf) { + found = false; + break; + } buffer = newBuf; bufBytes = required; } @@ -11187,10 +11146,6 @@ struct VM { free(buffer); SetupDiDestroyDeviceInfoList(devs); - if (!found) { - debug("CLOCK: PIT/AT (PNP0100) timer not found"); - } - return !found; } // ADD NEW TECHNIQUE FUNCTION HERE @@ -11244,8 +11199,14 @@ struct VM { static std::array brand_scoreboard; static size_t brand_count; + // Temporary storage to capture which brand was detected by the currently running technique + static const char* last_detected_brand; + // directly return when adding a brand to the scoreboard for a more succint expression static inline bool add(const char* p_brand, const char* extra_brand = "") noexcept { + // capture the brand being added so we can cache it later + last_detected_brand = p_brand; + for (size_t i = 0; i < brand_count; ++i) { // pointer comparison is sufficient as we use the static constants from brands:: namespace if (brand_scoreboard[i].name == p_brand) { @@ -11331,9 +11292,15 @@ struct VM { continue; } + // reset the last detected brand before running + last_detected_brand = nullptr; + // run the technique const bool result = technique_data.run(); + // retrieve the brand that was set during execution (if any) + const char* detected_brand = (result && last_detected_brand) ? last_detected_brand : brands::NULL_BRAND; + if (result) { points += technique_data.points; @@ -11343,7 +11310,7 @@ struct VM { } // store the current technique result to the cache - memo::cache_store(technique_macro, result, technique_data.points); + memo::cache_store(technique_macro, result, technique_data.points, detected_brand); // for things like VM::detect() and VM::percentage(), // a score of 150+ is guaranteed to be a VM, so @@ -11564,7 +11531,7 @@ struct VM { #endif ) { if (util::is_unsupported(flag_bit)) { - memo::cache_store(flag_bit, false, 0); + memo::cache_store(flag_bit, false, 0, brands::NULL_BRAND); return false; } @@ -11603,13 +11570,18 @@ struct VM { if (flag_bit < technique_end) { const core::technique& pair = core::technique_table[flag_bit]; - if (auto run_fn = pair.run) { + if (auto run_fn = pair.run) { + core::last_detected_brand = nullptr; + bool result = run_fn(); if (result) detected_count_num++; #ifdef __VMAWARE_DEBUG__ total_points += pair.points; #endif - memo::cache_store(flag_bit, result, pair.points); + // determine the brand string associated with this result + const char* detected_brand = (result && core::last_detected_brand) ? core::last_detected_brand : brands::NULL_BRAND; + + memo::cache_store(flag_bit, result, pair.points, detected_brand); return result; } else { @@ -11903,8 +11875,7 @@ struct VM { // this is added as a last ditch attempt to detect a VM, // because if there are indications of hardening then logically - // it should in fact be a VM. It's doubtful if this can actually - // return true, but it's better than nothing + // it should in fact be a VM. return (is_hardened()); } @@ -12050,10 +12021,7 @@ struct VM { case IOREG_GREP: return "IOREG_GREP"; case MAC_SIP: return "MAC_SIP"; case VPC_INVALID: return "VPC_INVALID"; - case SIDT: return "SIDT"; - case SGDT: return "SGDT"; - case SLDT: return "SLDT"; - case SMSW: return "SMSW"; + case SYSTEM_REGISTERS: return "TASK_SEGMENT"; case VMWARE_IOMEM: return "VMWARE_IOMEM"; case VMWARE_IOPORTS: return "VMWARE_IOPORTS"; case VMWARE_SCSI: return "VMWARE_SCSI"; @@ -12434,56 +12402,63 @@ struct VM { * @return bool */ static bool is_hardened() { - // this lambda basically detects whether a technique has found a brand. - // Might rewrite this or redesign the whole concept behind it, this is really janky. - auto detected_brand = [](const enum_flags flag) -> const char* { - memo::uncache(flag); + if (memo::hardened::cached) { + return memo::hardened::result; + } - std::array old_scoreboard_snapshot{}; - std::copy(core::brand_scoreboard.begin(), core::brand_scoreboard.end(), old_scoreboard_snapshot.begin()); + auto hardened_logic = []() -> bool { + // Helper to get the specific brand associated with a technique using the cache. + auto detected_brand = [](const enum_flags flag) -> const char* { + if (!check(flag)) { + return brands::NULL_BRAND; + } + if (memo::cache_table[flag].has_value) { + const char* b = memo::cache_table[flag].brand_name; + return (b != nullptr) ? b : brands::NULL_BRAND; + } + return brands::NULL_BRAND; + }; - check(flag); + const bool hv_present = (check(VM::HYPERVISOR_BIT) || check(VM::HYPERVISOR_STR)); - for (size_t i = 0; i < core::brand_count; ++i) { - if (old_scoreboard_snapshot[i].score < core::brand_scoreboard[i].score) { - return core::brand_scoreboard[i].name; - } + // rule 1: if VM::FIRMWARE is detected, so should VM::HYPERVISOR_BIT or VM::HYPERVISOR_STR + const char* firmware_brand = detected_brand(VM::FIRMWARE); + if (firmware_brand != brands::NULL_BRAND && !hv_present) { + return true; } - return brands::NULL_BRAND; - }; - const bool hv_present = (check(VM::HYPERVISOR_BIT) || check(VM::HYPERVISOR_STR)); + #if (LINUX) + // rule 2: if VM::FIRMWARE is detected, so should VM::CVENDOR (QEMU or VBOX) + if (firmware_brand == brands::QEMU || firmware_brand == brands::VBOX) { + const char* cvendor_brand = detected_brand(VM::CVENDOR); + if (firmware_brand != cvendor_brand) { + return true; + } + } + #endif - // rule 1: if VM::FIRMWARE is detected, so should VM::HYPERVISOR_BIT or VM::HYPERVISOR_STR - const char* firmware_brand = detected_brand(VM::FIRMWARE); - if (firmware_brand != brands::NULL_BRAND && !hv_present) { - return true; - } + #if (WINDOWS) + // rule 3: if VM::ACPI_SIGNATURE (QEMU) is detected, so should VM::FIRMWARE (QEMU) + const char* acpi_brand = detected_brand(VM::ACPI_SIGNATURE); + if (acpi_brand == brands::QEMU && firmware_brand != brands::QEMU) { + return true; + } - #if (LINUX) - // rule 2: if VM::FIRMWARE is detected, so should VM::CVENDOR (QEMU or VBOX) - if (firmware_brand == brands::QEMU || firmware_brand == brands::VBOX) { - const char* cvendor_brand = detected_brand(VM::CVENDOR); - if (firmware_brand != cvendor_brand) { + // rule 4: if VM::TRAP or VM::NVRAM is detected, so should VM::HYPERVISOR_BIT or VM::HYPERVISOR_STR + if ((check(VM::TRAP) || check(VM::NVRAM)) && !hv_present) { return true; } - } - #endif + #endif - #if (WINDOWS) - // rule 3: if VM::ACPI_SIGNATURE (QEMU) is detected, so should VM::FIRMWARE (QEMU) - const char* acpi_brand = detected_brand(VM::ACPI_SIGNATURE); - if (acpi_brand == brands::QEMU && firmware_brand != brands::QEMU) { - return true; - } + return false; + }; - // rule 4: if VM::TRAP or VM::NVRAM is detected, so should VM::HYPERVISOR_BIT or VM::HYPERVISOR_STR - if ((check(VM::TRAP) || check(VM::NVRAM)) && !hv_present) { - return true; - } - #endif + const bool result = hardened_logic(); - return false; + memo::hardened::result = result; + memo::hardened::cached = true; + + return result; } @@ -12654,11 +12629,14 @@ bool VM::memo::multi_brand::cached = false; bool VM::memo::cpu_brand::cached = false; bool VM::memo::bios_info::cached = false; bool VM::memo::hyperx::cached = false; +bool VM::memo::hardened::result = false; +bool VM::memo::hardened::cached = false; VM::u32 VM::memo::threadcount::threadcount_cache = 0; VM::hyperx_state VM::memo::hyperx::state = VM::HYPERV_UNKNOWN; std::array VM::memo::leaf_cache::table{}; std::size_t VM::memo::leaf_cache::count = 0; std::size_t VM::memo::leaf_cache::next_index = 0; +const char* VM::core::last_detected_brand = nullptr; #ifdef __VMAWARE_DEBUG__ VM::u16 VM::total_points = 0; @@ -12709,9 +12687,6 @@ std::array VM::core::technique_table = [ {VM::SMBIOS_INTEGRITY, {50, VM::smbios_integrity}}, {VM::DISK_SERIAL, {100, VM::disk_serial_number}}, {VM::IVSHMEM, {100, VM::ivshmem}}, - {VM::SGDT, {50, VM::sgdt}}, - {VM::SLDT, {50, VM::sldt}}, - {VM::SMSW, {50, VM::smsw}}, {VM::DRIVERS, {100, VM::drivers}}, {VM::DEVICE_HANDLES, {100, VM::device_handles}}, {VM::VIRTUAL_PROCESSORS, {100, VM::virtual_processors}}, @@ -12738,7 +12713,7 @@ std::array VM::core::technique_table = [ #if (LINUX || WINDOWS) {VM::FIRMWARE, {100, VM::firmware}}, {VM::PCI_DEVICES, {95, VM::pci_devices}}, - {VM::SIDT, {50, VM::sidt}}, + {VM::SYSTEM_REGISTERS, {50, VM::system_registers}}, {VM::AZURE, {30, VM::azure}}, #endif