From 8a2d546201bc4ec19a1e3eb63d393a0edf5bb867 Mon Sep 17 00:00:00 2001 From: kernelwernel Date: Wed, 26 Nov 2025 08:04:53 +0000 Subject: [PATCH 01/17] changed from pragma to include guard --- src/vmaware.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 6970a3d3..5e75b06a 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -214,8 +214,8 @@ * the threshold becomes 300 instead of 150. */ - -#pragma once +#ifndef VMAWARE_HEADER +#define VMAWARE_HEADER #ifndef __VMAWARE_DEBUG__ #if defined(_DEBUG) /* MSVC Debug */ \ @@ -12100,4 +12100,6 @@ table_t VM::core::technique_table = []() -> table_t { table.insert(technique); } return table; -}(); \ No newline at end of file +}(); + +#endif // include guard end \ No newline at end of file From eaa16c78c5679652c2c5c40ceda6ba7b8c2f7af0 Mon Sep 17 00:00:00 2001 From: kernelwernel Date: Wed, 26 Nov 2025 12:34:55 +0000 Subject: [PATCH 02/17] added composable functions for lookups and hashing --- src/txt.txt | 199 ++++++++++++++++++ src/vmaware.hpp | 543 ++++++++++++++++-------------------------------- 2 files changed, 379 insertions(+), 363 deletions(-) create mode 100644 src/txt.txt diff --git a/src/txt.txt b/src/txt.txt new file mode 100644 index 00000000..0f68ea3a --- /dev/null +++ b/src/txt.txt @@ -0,0 +1,199 @@ + for (size_t i = 0; str[i] != '\0'; ) { + const char c = str[i]; + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { + i++; + continue; + } + + u32 current_hash = 0; + size_t current_len = 0; + size_t j = i; + + while (true) { + const char k = str[j]; + const bool is_valid = (k >= '0' && k <= '9') || + (k >= 'A' && k <= 'Z') || + (k >= 'a' && k <= 'z') || + (k == '-'); + if (!is_valid) break; + + if (current_len >= MAX_XEON_MODEL_LEN) { + while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null + break; + } + + /* + models are usually 8 or more bytes long, i.e. i9-10900K + so imagine we want to use u64, you hash the first 8 bytes i9-10900 + but then you are left with K. You have to handle the tail + fetching 8 bytes would include the characters after the token, corrupting the hash + so a byte-by-byte loop is the most optimal choice here + */ + + // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction + // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah + current_hash = hash_func(current_hash, k); + current_len++; + j++; + + // only verify match if the token has ended (next char is not alphanumeric) + const char next = str[j]; + const bool next_is_alnum = (next >= '0' && next <= '9') || + (next >= 'A' && next <= 'Z') || + (next >= 'a' && next <= 'z'); + + if (!next_is_alnum) { + // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast + for (const auto& entry : thread_database) { + if (entry.hash == current_hash) { + if (current_len > best_len) { + best_len = current_len; + expected_threads = entry.threads; + found = true; + } + // since hashing implies uniqueness in this dataset, you might say we could break here, + // but we continue to ensure we find the longest substring match if overlaps exist, + // so like it finds both "i9-11900" and "i9-11900K" i.e. + } + } + } + } + i = j; + } + + + + + + + + + + + + + + for (size_t i = 0; str[i] != '\0'; ) { + const char c = str[i]; + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { + i++; + continue; + } + + u32 current_hash = 0; + size_t current_len = 0; + size_t j = i; + + while (true) { + const char k = str[j]; + const bool is_valid = (k >= '0' && k <= '9') || + (k >= 'A' && k <= 'Z') || + (k >= 'a' && k <= 'z') || + (k == '-'); // models have hyphen + if (!is_valid) break; + + if (current_len >= MAX_INTEL_MODEL_LEN) { + while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null + break; + } + + /* + models are usually 8 or more bytes long, i.e. i9-10900K + so imagine we want to use u64, you hash the first 8 bytes i9-10900 + but then you are left with K. You have to handle the tail + fetching 8 bytes would include the characters after the token, corrupting the hash + so a byte-by-byte loop is the most optimal choice here + */ + + // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction + // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah + current_hash = hash_func(current_hash, k); + current_len++; + j++; + + // only verify match if the token has ended (next char is not alphanumeric) + const char next = str[j]; + const bool next_is_alnum = (next >= '0' && next <= '9') || + (next >= 'A' && next <= 'Z') || + (next >= 'a' && next <= 'z'); + + if (!next_is_alnum) { + // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast + for (const auto& entry : thread_database) { + if (entry.hash == current_hash) { + if (current_len > best_len) { + best_len = current_len; + expected_threads = entry.threads; + found = true; + } + // since hashing implies uniqueness in this dataset, you might say we could break here, + // but we continue to ensure we find the longest substring match if overlaps exist, + // so like it finds both "i9-11900" and "i9-11900K" i.e. + } + } + } + } + i = j; + } + + + + + + + + for (size_t i = 0; str[i] != '\0'; ) { + char c = str[i]; + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { + i++; + continue; + } + + u32 current_hash = 0; + size_t current_len = 0; + size_t j = i; + + while (true) { + char k = str[j]; + const bool is_valid = (k >= '0' && k <= '9') || + (k >= 'A' && k <= 'Z') || + (k >= 'a' && k <= 'z') || + (k == '-'); + if (!is_valid) break; + + if (current_len >= MAX_AMD_TOKEN_LEN) { + while (str[j] != '\0' && str[j] != ' ') j++; + break; + } + + // convert to lowercase on-the-fly to match compile-time keys + if (k >= 'A' && k <= 'Z') k += 32; + + current_hash = hash_func(current_hash, k); + current_len++; + j++; + + // boundary check + const char next = str[j]; + const bool next_is_alnum = (next >= '0' && next <= '9') || + (next >= 'A' && next <= 'Z') || + (next >= 'a' && next <= 'z'); + + if (!next_is_alnum) { + // Check specific Z1 Extreme token + // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 + if (current_hash == 0x3D09D5B4) { z_series_threads = 16; } + + for (const auto& entry : db_entries) { + if (entry.hash == current_hash) { + if (current_len > best_len) { + best_len = current_len; + expected_threads = entry.threads; + found = true; + } + } + } + } + } + i = j; + } \ No newline at end of file diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 4e570f78..92b13eb2 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1353,6 +1353,23 @@ struct VM { file.close(); return data; } + + [[nodiscard]] static bool exists(const char* path) { + #if (VMA_CPP >= 17) + return std::filesystem::exists(path); + #elif (VMA_CPP >= 11) + struct stat buffer; + return (stat(path, &buffer) == 0); + #endif + } + + static bool is_directory(const char* path) { + struct stat info; + if (stat(path, &info) != 0) { + return false; + } + return (info.st_mode & S_IFDIR); // check if directory + }; #endif // fetch the file but in binary form @@ -1377,24 +1394,155 @@ struct VM { return buffer; } -#if (LINUX) - [[nodiscard]] static bool exists(const char* path) { - #if (VMA_CPP >= 17) - return std::filesystem::exists(path); - #elif (VMA_CPP >= 11) - struct stat buffer; - return (stat(path, &buffer) == 0); - #endif - } - static bool is_directory(const char* path) { - struct stat info; - if (stat(path, &info) != 0) { - return false; + // we want to precompute hashes at compile time for C++11 and later, so we need to match the hardware _mm_crc32_u8 + // it is based on CRC32-C (Castagnoli) polynomial + struct constexpr_hash { + static constexpr u32 crc32_bits(u32 crc, int bits) { + return (bits == 0) ? crc : + crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); + } + static constexpr u32 crc32_str(const char* s, u32 crc) { + return (*s == '\0') ? crc : + crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); + } + static constexpr u32 get(const char* s) { + return crc32_str(s, 0); } - return (info.st_mode & S_IFDIR); // check if directory }; -#endif + + + // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible + struct thread_entry { + u32 hash; + u32 threads; + constexpr thread_entry(const char* m, u32 t) : hash(constexpr_hash::get(m)), threads(t) {} + }; + + + struct hasher { + static u32 crc32_sw(u32 crc, char data) { + crc ^= static_cast(data); + for (int i = 0; i < 8; ++i) + crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); + return crc; + } + + #if (CLANG || GCC) + __attribute__((__target__("crc32"))) + #endif + static u32 crc32_hw(u32 crc, char data) { + return _mm_crc32_u8(crc, static_cast(data)); + } + + using hashfc = u32(*)(u32, char); + + static hashfc get() { + // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often + i32 regs[4]; + cpu::cpuid(regs, 1); + const bool has_sse42 = (regs[2] & (1 << 20)) != 0; + + return has_sse42 ? crc32_hw : crc32_sw; + } + }; + + + static void lookup( + bool &found, + const char* str, + const thread_entry* thread_database, + const std::size_t& db_size, + std::size_t best_len, + const u8 max_token_length, + u32* z_series_threads, + u32& expected_threads, + const bool is_amd = false + ) { + const hasher::hashfc hash_func = util::hasher::get(); + + for (size_t i = 0; str[i] != '\0'; ) { + char c = str[i]; + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { + i++; + continue; + } + + u32 current_hash = 0; + size_t current_len = 0; + size_t j = i; + + while (true) { + char k = str[j]; + const bool is_valid = (k >= '0' && k <= '9') || + (k >= 'A' && k <= 'Z') || + (k >= 'a' && k <= 'z') || + (k == '-'); // models have hyphen + if (!is_valid) break; + + if (current_len >= max_token_length) { + while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null + break; + } + + if (is_amd) { + // convert to lowercase on-the-fly to match compile-time keys + if (k >= 'A' && k <= 'Z') k += 32; + } + + /* + models are usually 8 or more bytes long, i.e. i9-10900K + so imagine we want to use u64, you hash the first 8 bytes i9-10900 + but then you are left with K. You have to handle the tail + fetching 8 bytes would include the characters after the token, corrupting the hash + so a byte-by-byte loop is the most optimal choice here + */ + + // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction + // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah + current_hash = hash_func(current_hash, k); + current_len++; + j++; + + // boundary check, only verify match if the token has ended (next char is not alphanumeric) + const char next = str[j]; + const bool next_is_alnum = (next >= '0' && next <= '9') || + (next >= 'A' && next <= 'Z') || + (next >= 'a' && next <= 'z'); + + if (!next_is_alnum) { + // Check specific Z1 Extreme token + // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 + if ( + is_amd && + (z_series_threads != nullptr) && + (current_hash == 0x3D09D5B4) + ) { + *z_series_threads = 16; + } + + // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast + for (std::size_t i = 0; i < db_size; ++i) { + if ( + (thread_database[i].hash == current_hash) && + (current_len > best_len) + ) { + best_len = current_len; + expected_threads = thread_database[i].threads; + found = true; + + // on intel, since hashing implies uniqueness in this dataset, you might say we could break + // here, but we continue to ensure we find the longest substring match if overlaps exist, + // so like it finds both "i9-11900" and "i9-11900K" i.e. + } + } + } + } + i = j; + } + } + + // wrapper for std::make_unique because it's not available for C++11 template @@ -2093,13 +2241,13 @@ struct VM { */ [[nodiscard]] static bool vmid() { #if (!x86) - return false; + return false; #else - return ( - cpu::vmid_template(0) || - cpu::vmid_template(cpu::leaf::hypervisor) || // 0x40000000 - cpu::vmid_template(cpu::leaf::hypervisor + 0x100) // 0x40000100 - ); + return ( + cpu::vmid_template(0) || + cpu::vmid_template(cpu::leaf::hypervisor) || // 0x40000000 + cpu::vmid_template(cpu::leaf::hypervisor + 0x100) // 0x40000100 + ); #endif } @@ -2343,44 +2491,17 @@ struct VM { if (!model.found) { return false; } - + if (!model.is_i_series) { return false; } debug("INTEL_THREAD_MISMATCH: CPU model = ", model.string); - // we want to precompute hashes at compile time for C++11 and later, so we need to match the hardware _mm_crc32_u8 - // it is based on CRC32-C (Castagnoli) polynomial - struct ConstexprHash { - // it does 8 rounds of CRC32-C bit reflection recursively - static constexpr u32 crc32_bits(u32 crc, int bits) { - return (bits == 0) ? crc : - crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); - } - - // over string - static constexpr u32 crc32_str(const char* s, u32 crc) { - return (*s == '\0') ? crc : - crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); - } - - static constexpr u32 get(const char* s) { - return crc32_str(s, 0); - } - }; - - // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible - struct Entry { - u32 hash; - u32 threads; - constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} - }; - // umap is not an option because it cannot be constexpr // constexpr is respected here even in c++ 11 and static solves stack overflow // c arrays have less construction overhead than std::array - static constexpr Entry thread_database[] = { + static constexpr util::thread_entry thread_database[] = { // i3 series { "i3-1000G1", 4 }, { "i3-1000G4", 4 }, @@ -3340,102 +3461,13 @@ struct VM { // to save a few cycles static constexpr size_t MAX_INTEL_MODEL_LEN = 16; - struct hasher { - static u32 crc32_sw(u32 crc, char data) { - crc ^= static_cast(data); - for (int i = 0; i < 8; ++i) - crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); - return crc; - } - - #if (CLANG || GCC) - __attribute__((__target__("crc32"))) - #endif - static u32 crc32_hw(u32 crc, char data) { - return _mm_crc32_u8(crc, static_cast(data)); - } - - using hashfc = u32(*)(u32, char); - - static hashfc get() { - // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often - i32 regs[4]; - cpu::cpuid(regs, 1); - const bool has_sse42 = (regs[2] & (1 << 20)) != 0; - - return has_sse42 ? crc32_hw : crc32_sw; - } - }; - const char* str = model.string.c_str(); u32 expected_threads = 0; bool found = false; size_t best_len = 0; + const std::size_t size = sizeof(thread_database) / sizeof(util::thread_entry); - const auto hash_func = hasher::get(); - - for (size_t i = 0; str[i] != '\0'; ) { - const char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - const char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); // models have hyphen - if (!is_valid) break; - - if (current_len >= MAX_INTEL_MODEL_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null - break; - } - - /* - models are usually 8 or more bytes long, i.e. i9-10900K - so imagine we want to use u64, you hash the first 8 bytes i9-10900 - but then you are left with K. You have to handle the tail - fetching 8 bytes would include the characters after the token, corrupting the hash - so a byte-by-byte loop is the most optimal choice here - */ - - // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction - // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // only verify match if the token has ended (next char is not alphanumeric) - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast - for (const auto& entry : thread_database) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - // since hashing implies uniqueness in this dataset, you might say we could break here, - // but we continue to ensure we find the longest substring match if overlaps exist, - // so like it finds both "i9-11900" and "i9-11900K" i.e. - } - } - } - } - i = j; - } + util::lookup(found, str, thread_database, size, best_len, MAX_INTEL_MODEL_LEN, nullptr, expected_threads); if (found) { const u32 actual = memo::threadcount::fetch(); @@ -3473,37 +3505,10 @@ struct VM { debug("XEON_THREAD_MISMATCH: CPU model = ", model.string); - // we want to precompute hashes at compile time for C++11 and later, so we need to match the hardware _mm_crc32_u8 - // it is based on CRC32-C (Castagnoli) polynomial - struct ConstexprHash { - // it does 8 rounds of CRC32-C bit reflection recursively - static constexpr u32 crc32_bits(u32 crc, int bits) { - return (bits == 0) ? crc : - crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); - } - - // over string - static constexpr u32 crc32_str(const char* s, u32 crc) { - return (*s == '\0') ? crc : - crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); - } - - static constexpr u32 get(const char* s) { - return crc32_str(s, 0); - } - }; - - // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible - struct Entry { - u32 hash; - u32 threads; - constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} - }; - // umap is not an option because it cannot be constexpr // constexpr is respected here even in c++ 11 and static solves stack overflow // c arrays have less construction overhead than std::array - static constexpr Entry thread_database[] = { + static constexpr util::thread_entry thread_database[] = { // Xeon D { "D-1518", 8 }, { "D-1520", 8 }, @@ -3642,33 +3647,6 @@ struct VM { // to save a few cycles static constexpr size_t MAX_XEON_MODEL_LEN = 16; - struct hasher { - static u32 crc32_sw(u32 crc, char data) { - crc ^= static_cast(data); - for (int i = 0; i < 8; ++i) - crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); - return crc; - } - - #if (CLANG || GCC) - __attribute__((__target__("crc32"))) - #endif - static u32 crc32_hw(u32 crc, char data) { - return _mm_crc32_u8(crc, static_cast(data)); - } - - using hashfc = u32(*)(u32, char); - - static hashfc get() { - // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often - i32 regs[4]; - cpu::cpuid(regs, 1); - const bool has_sse42 = (regs[2] & (1 << 20)) != 0; - - return has_sse42 ? crc32_hw : crc32_sw; - } - }; - const std::string& cpu_full_name = model.string; if (cpu_full_name.empty()) return false; @@ -3676,71 +3654,9 @@ struct VM { u32 expected_threads = 0; bool found = false; size_t best_len = 0; + const std::size_t size = sizeof(thread_database) / sizeof(util::thread_entry); - const auto hash_func = hasher::get(); - - for (size_t i = 0; str[i] != '\0'; ) { - const char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - const char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); - if (!is_valid) break; - - if (current_len >= MAX_XEON_MODEL_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null - break; - } - - /* - models are usually 8 or more bytes long, i.e. i9-10900K - so imagine we want to use u64, you hash the first 8 bytes i9-10900 - but then you are left with K. You have to handle the tail - fetching 8 bytes would include the characters after the token, corrupting the hash - so a byte-by-byte loop is the most optimal choice here - */ - - // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction - // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // only verify match if the token has ended (next char is not alphanumeric) - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast - for (const auto& entry : thread_database) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - // since hashing implies uniqueness in this dataset, you might say we could break here, - // but we continue to ensure we find the longest substring match if overlaps exist, - // so like it finds both "i9-11900" and "i9-11900K" i.e. - } - } - } - } - i = j; - } + util::lookup(found, str, thread_database, size, best_len, MAX_XEON_MODEL_LEN, nullptr, expected_threads); if (found) { const u32 actual = memo::threadcount::fetch(); @@ -3771,29 +3687,9 @@ struct VM { static constexpr size_t MAX_AMD_TOKEN_LEN = 24; // "threadripper" is long - struct ConstexprHash { - static constexpr u32 crc32_bits(u32 crc, int bits) { - return (bits == 0) ? crc : - crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); - } - static constexpr u32 crc32_str(const char* s, u32 crc) { - return (*s == '\0') ? crc : - crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); - } - static constexpr u32 get(const char* s) { - return crc32_str(s, 0); - } - }; - - struct Entry { - u32 hash; - u32 threads; - constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} - }; - // Database is reduced to identifying suffixes (last token of the original strings) // for example handles "ryzen 5 3600" by matching "3600", which is unique in context - static constexpr Entry db_entries[] = { + static constexpr util::thread_entry thread_database[] = { // 3015/3020 { "3015ce", 4 }, { "3015e", 4 }, @@ -4304,100 +4200,21 @@ struct VM { { "z2", 16 } }; - struct hasher { - static u32 crc32_sw(u32 crc, char data) { - crc ^= static_cast(data); - for (int i = 0; i < 8; ++i) - crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); - return crc; - } - - #if (CLANG || GCC) - __attribute__((__target__("crc32"))) - #endif - static u32 crc32_hw(u32 crc, char data) { - return _mm_crc32_u8(crc, static_cast(data)); - } - - using hashfc = u32(*)(u32, char); - - static hashfc get() { - i32 regs[4]; - cpu::cpuid(regs, 1); - const bool has_sse42 = (regs[2] & (1 << 20)) != 0; - return has_sse42 ? crc32_hw : crc32_sw; - } - }; - debug("AMD_THREAD_MISMATCH: CPU model = ", model_str); const char* str = model_str.c_str(); u32 expected_threads = 0; bool found = false; size_t best_len = 0; - - const auto hash_func = hasher::get(); + const std::size_t size = sizeof(thread_database) / sizeof(util::thread_entry); + const bool is_amd = true; // manual collision fix for Z1 Extreme (16) vs Z1 (12) // this is a special runtime check because "z1" is a substring of "z1 extreme" tokens // and both might be hashed. VMAware should prioritize 'extreme' if found u32 z_series_threads = 0; - for (size_t i = 0; str[i] != '\0'; ) { - char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); - if (!is_valid) break; - - if (current_len >= MAX_AMD_TOKEN_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; - break; - } - - // convert to lowercase on-the-fly to match compile-time keys - if (k >= 'A' && k <= 'Z') k += 32; - - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // boundary check - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // Check specific Z1 Extreme token - // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 - if (current_hash == 0x3D09D5B4) { z_series_threads = 16; } - - for (const auto& entry : db_entries) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - } - } - } - } - i = j; - } + util::lookup(found, str, thread_database, size, best_len, MAX_AMD_TOKEN_LEN, &z_series_threads, expected_threads, is_amd); // Z1 Extreme fix if (z_series_threads != 0 && expected_threads == 12) { From c1e5f2c4c6b9d3696c1deb64fb498e9597af7aac Mon Sep 17 00:00:00 2001 From: Requiem Date: Wed, 26 Nov 2025 20:30:58 +0100 Subject: [PATCH 03/17] fix: VM::EDID false flagging at manufacturer decoding --- src/vmaware.hpp | 226 +++++++++++++++++++++++++++++++----------------- 1 file changed, 145 insertions(+), 81 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 88183477..2e069968 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -9627,76 +9627,101 @@ struct VM { * @implements VM::EDID */ [[nodiscard]] static bool edid() { - auto decodeManufacturerFast = [](const BYTE* edid) __declspec(noinline) -> std::array { - // edid[8..9] big-endian word - const uint16_t word = static_cast((edid[8] << 8) | edid[9]); - const uint8_t c1 = static_cast((word >> 10) & 0x1F); - const uint8_t c2 = static_cast((word >> 5) & 0x1F); - const uint8_t c3 = static_cast((word >> 0) & 0x1F); - std::array out{ {'?','?','?','\0'} }; - if (c1 >= 1 && c1 <= 26) out[0] = static_cast('A' + c1 - 1); - if (c2 >= 1 && c2 <= 26) out[1] = static_cast('A' + c2 - 1); - if (c3 >= 1 && c3 <= 26) out[2] = static_cast('A' + c3 - 1); - return out; + auto decodeManufacturer = [](const BYTE* edid, char out[4]) { + const u16 word = static_cast((edid[8] << 8) | edid[9]); + const u8 c1 = static_cast((word >> 10) & 0x1F); + const u8 c2 = static_cast((word >> 5) & 0x1F); + const u8 c3 = static_cast((word >> 0) & 0x1F); + out[0] = (c1 >= 1 && c1 <= 26) ? static_cast('A' + c1 - 1) : '?'; + out[1] = (c2 >= 1 && c2 <= 26) ? static_cast('A' + c2 - 1) : '?'; + out[2] = (c3 >= 1 && c3 <= 26) ? static_cast('A' + c3 - 1) : '?'; + out[3] = '\0'; }; - auto isThreeUpperAlphaFast = [](const std::array& m) -> bool { + auto isThreeUpperAlpha = [](const char m[4]) -> bool { return (m[0] >= 'A' && m[0] <= 'Z') && (m[1] >= 'A' && m[1] <= 'Z') && (m[2] >= 'A' && m[2] <= 'Z'); }; - auto descHasUpperPrefixMonitorFast = [](const char* desc) -> bool { - if (!desc || desc[0] == '\0') return false; - // Two tails to search: " Monitor" and " Display" (leading space) - const char* tails[] = { " Monitor", " Display" }; - for (const char* tail : tails) { - const char* p = strstr(desc, tail); - while (p) { - // ensure not at position 0 - if (p != desc) { - // walk backwards counting uppercase letters - const char* start = p; - size_t len = 0; - while (start > desc) { - --start; - unsigned char uc = static_cast(*start); - if (uc >= 'A' && uc <= 'Z') { - ++len; - if (len > 8) break; - } - else break; + auto edidChecksumValid = [](const BYTE* edid, size_t len) -> bool { + if (len < 128) return false; + u8 sum = 0; + for (size_t i = 0; i < 128; ++i) sum = static_cast(sum + edid[i]); + return sum == 0; + }; + + auto extractMonitorName = [](const BYTE* edid, size_t len, char out[32]) -> bool { + if (len < 128) { out[0] = '\0'; return false; } + const size_t base = 54; + for (int i = 0; i < 4; ++i) { + size_t off = base + i * 18; + if (off + 18 > 128) break; + const BYTE* block = edid + off; + // descriptor: bytes 0 to 1 == 0x00, byte3 = tag + if (block[0] == 0x00 && block[1] == 0x00) { + if (block[3] == 0xFC) { // monitor name + int outi = 0; + for (int j = 5; j < 18 && outi < 31; ++j) { + char c = static_cast(block[j]); + if (c == 0x0A || c == 0x0D || c == 0x00) break; + out[outi++] = c; } - if (len >= 4 && len <= 8) { - // verify all are uppercase (we already did while walking) - return true; + out[outi] = '\0'; + // trim trailing spaces + while (outi > 0 && (out[outi - 1] == ' ' || out[outi - 1] == '\t')) { out[--outi] = '\0'; } + return outi > 0; + } + // monitor serial descriptor 0xFF could be used as name fallback + if (block[3] == 0xFF) { + int outi = 0; + for (int j = 5; j < 18 && outi < 31; ++j) { + char c = static_cast(block[j]); + if (c == 0x0A || c == 0x0D || c == '\0') break; + out[outi++] = c; } + out[outi] = '\0'; + while (outi > 0 && (out[outi - 1] == ' ' || out[outi - 1] == '\t')) { out[--outi] = '\0'; } + return outi > 0; } - p = strstr(p + 1, tail); } } + out[0] = '\0'; return false; }; - auto getDevicePropertyA = [](HDEVINFO devInfo, SP_DEVINFO_DATA& devData, DWORD propId, std::string& out) -> bool { - char small[512] = {}; - DWORD needed = 0; - if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devData, propId, nullptr, reinterpret_cast(small), sizeof(small), &needed)) { - out.assign(small); - return true; - } - const DWORD err = GetLastError(); - if (err == ERROR_INSUFFICIENT_BUFFER && needed > 0 && needed < 65536) { - std::vector big(needed + 1); - if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devData, propId, nullptr, reinterpret_cast(big.data()), static_cast(big.size()), &needed)) { - big[big.size() - 1] = '\0'; - out.assign(big.data()); + auto read_le16 = [](const BYTE* p) -> u16 { + return static_cast(p[0] | (p[1] << 8)); + }; + auto read_le32 = [](const BYTE* p) -> u32 { + return static_cast(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); + }; + + auto getDevicePropertyA = [](HDEVINFO devInfo, SP_DEVINFO_DATA& devData, DWORD propId, + char* outBuf, DWORD outBufSize) -> bool { + DWORD needed = 0; + if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devData, propId, nullptr, + reinterpret_cast(outBuf), outBufSize, &needed)) { + outBuf[outBufSize - 1] = '\0'; return true; } - } - out.clear(); - return false; - }; + const DWORD err = GetLastError(); + if (err == ERROR_INSUFFICIENT_BUFFER && needed > 0 && needed < 65536) { + HLOCAL h = LocalAlloc(LMEM_FIXED, static_cast(needed) + 1); + if (!h) return false; + if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devData, propId, nullptr, + reinterpret_cast(h), needed, &needed)) { + DWORD toCopy = (needed < outBufSize - 1) ? needed : outBufSize - 1; + memcpy(outBuf, h, toCopy); + outBuf[toCopy] = '\0'; + LocalFree(h); + return true; + } + LocalFree(h); + } + outBuf[0] = '\0'; + return false; + }; const HDEVINFO devInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_MONITOR, nullptr, nullptr, DIGCF_PRESENT); if (devInfo == INVALID_HANDLE_VALUE) return false; @@ -9704,47 +9729,86 @@ struct VM { SP_DEVINFO_DATA devData{}; devData.cbSize = sizeof(devData); + const int threshold = 3; + for (DWORD index = 0; SetupDiEnumDeviceInfo(devInfo, index, &devData); ++index) { - // open registry key for device const HKEY hDevKey = SetupDiOpenDevRegKey(devInfo, &devData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); - if (hDevKey == INVALID_HANDLE_VALUE) continue; + if (hDevKey == INVALID_HANDLE_VALUE) { + devData = {}; + devData.cbSize = sizeof(devData); + continue; + } - BYTE buffer[2048]; - DWORD bufSize = static_cast(sizeof(buffer)); - const LONG rc = RegQueryValueExA(hDevKey, "EDID", nullptr, nullptr, buffer, &bufSize); + BYTE edid_stack[256]; + DWORD bufSize = static_cast(sizeof(edid_stack)); + LONG rc = RegQueryValueExA(hDevKey, "EDID", nullptr, nullptr, edid_stack, &bufSize); RegCloseKey(hDevKey); - if (rc != ERROR_SUCCESS || bufSize < 128) continue; - const BYTE* edid = buffer; - // standard header - if (!(edid[0] == 0x00 && edid[1] == 0xFF && edid[2] == 0xFF && edid[3] == 0xFF && - edid[4] == 0xFF && edid[5] == 0xFF && edid[6] == 0xFF && edid[7] == 0x00)) { + BYTE* edid = nullptr; + bool used_heap = false; + BYTE* heap_buf = nullptr; + if (rc == ERROR_SUCCESS && bufSize >= 128) { + edid = edid_stack; + } + else if (rc == ERROR_MORE_DATA) { + if (bufSize > 0 && bufSize < 65536) { + heap_buf = static_cast(LocalAlloc(LMEM_FIXED, bufSize)); + if (heap_buf) { + DWORD bufSize2 = bufSize; + const HKEY hDevKey2 = SetupDiOpenDevRegKey(devInfo, &devData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (hDevKey2 != INVALID_HANDLE_VALUE) { + if (RegQueryValueExA(hDevKey2, "EDID", nullptr, nullptr, heap_buf, &bufSize2) == ERROR_SUCCESS && bufSize2 >= 128) { + edid = heap_buf; + used_heap = true; + bufSize = bufSize2; + } + RegCloseKey(hDevKey2); + } + if (!edid) { LocalFree(heap_buf); heap_buf = nullptr; } + } + } + } + + if (!edid) { + devData = {}; + devData.cbSize = sizeof(devData); continue; } - const uint8_t yearOffset = edid[0x11]; // 1990 + yearOffset - // those don't need device properties - const auto manufacturer = decodeManufacturerFast(edid); - const bool vendor_nonstandard = !isThreeUpperAlphaFast(manufacturer); - const bool year_in_range = (yearOffset >= 25 && yearOffset <= 35); // 2015..2025 + // header check + if (!(edid[0] == 0x00 && edid[1] == 0xFF && edid[2] == 0xFF && edid[3] == 0xFF + && edid[4] == 0xFF && edid[5] == 0xFF && edid[6] == 0xFF && edid[7] == 0x00)) { + if (used_heap) LocalFree(heap_buf); + devData = {}; + devData.cbSize = sizeof(devData); + continue; + } - if (!year_in_range) continue; + int score = 0; - if (vendor_nonstandard) { - SetupDiDestroyDeviceInfoList(devInfo); - return true; - } + if (!edidChecksumValid(edid, bufSize)) score += 1; + + char manu[4]; + decodeManufacturer(edid, manu); + if (!isThreeUpperAlpha(manu)) score += 1; + + u16 product = static_cast(edid[10] | (edid[11] << 8)); // because its little-endian + u32 serial = static_cast(edid[12] | (edid[13] << 8) | (edid[14] << 16) | (edid[15] << 24)); + if (product == 0) score += 1; + if (serial == 0) score += 1; + + char monname[32]; + bool hasName = extractMonitorName(edid, bufSize, monname); + if (!hasName) score += 1; // no way you dont have a EDID monitor name - std::string friendly, devdesc; - // query Friendly name first cuz more likely to be present - getDevicePropertyA(devInfo, devData, SPDRP_FRIENDLYNAME, friendly); - if (friendly.empty()) getDevicePropertyA(devInfo, devData, SPDRP_DEVICEDESC, devdesc); + char propBuf[512]; + bool haveFriendly = getDevicePropertyA(devInfo, devData, SPDRP_FRIENDLYNAME, propBuf, sizeof(propBuf)); // friendly_name is often empty, like in Digital-Flachbildschirm monitors + bool haveDevDesc = getDevicePropertyA(devInfo, devData, SPDRP_DEVICEDESC, propBuf, sizeof(propBuf)); + if (!haveFriendly && !haveDevDesc) score += 1; - const char* descriptor = nullptr; - if (!friendly.empty()) descriptor = friendly.c_str(); - else if (!devdesc.empty()) descriptor = devdesc.c_str(); + if (used_heap) LocalFree(heap_buf); - if (descriptor && descHasUpperPrefixMonitorFast(descriptor)) { + if (score >= threshold) { SetupDiDestroyDeviceInfoList(devInfo); return true; } From a013deb4022e0d7246b28f3b02686b60bd09730e Mon Sep 17 00:00:00 2001 From: Requiem Date: Wed, 26 Nov 2025 21:43:35 +0100 Subject: [PATCH 04/17] fix: false flags due to some OEMs advetising 0x4000xxxx leaves without a hypervisor present --- src/vmaware.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 2e069968..4b32edf2 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -214,7 +214,6 @@ * the threshold becomes 300 instead of 150. */ - #pragma once #ifndef __VMAWARE_DEBUG__ @@ -1793,15 +1792,15 @@ struct VM { } } else { - // normally eax 12 const std::string brand_str = cpu::cpu_manufacturer(0x40000100); - + if (util::find(brand_str, "KVM")) { debug("HYPER_X: Detected Hyper-V enlightenments"); core::add(brands::QEMU_KVM_HYPERV); state = HYPERV_ENLIGHTENMENT; } - else { + // vendors can sometimes advertise 0x4000xxxx leaves for compatibility/enlightenment purposes without running a hypervisor + else if (eax() == 12) { // Windows machine running under Hyper-V type 1 debug("HYPER_X: Detected Hyper-V host machine"); core::add(brands::HYPERV_ARTIFACT); From bc569602d736191bd75a2955f4f88ac79690dfcb Mon Sep 17 00:00:00 2001 From: Requiem Date: Wed, 26 Nov 2025 22:43:02 +0100 Subject: [PATCH 05/17] refactor: thread_database handling --- src/vmaware.hpp | 688 ++++++++++++++++-------------------------------- 1 file changed, 227 insertions(+), 461 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 4b32edf2..1fdb0b88 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -493,7 +493,7 @@ namespace brands { static constexpr const char* AZURE_HYPERV = "Microsoft Azure Hyper-V"; static constexpr const char* NANOVISOR = "Xbox NanoVisor (Hyper-V)"; static constexpr const char* SIMPLEVISOR = "SimpleVisor"; - static constexpr const char* HYPERV_ARTIFACT = "Hyper-V artifact (not an actual VM)"; + static constexpr const char* HYPERV_ARTIFACT = "Hyper-V artifact (host running Hyper-V)"; static constexpr const char* UML = "User-mode Linux"; static constexpr const char* POWERVM = "IBM PowerVM"; static constexpr const char* GCE = "Google Compute Engine (KVM)"; @@ -1639,26 +1639,6 @@ struct VM { } - [[nodiscard]] static std::string get_hostname() { - #if (WINDOWS) - char ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; - DWORD cbComputerName = sizeof(ComputerName); - - if (GetComputerNameA(ComputerName, &cbComputerName)) { - return std::string(ComputerName); - } - #elif (LINUX) - char hostname[HOST_NAME_MAX]; - - if (gethostname(hostname, sizeof(hostname)) == 0) { - return std::string(hostname); - } - #endif - - return std::string(); - } - - [[nodiscard]] static bool is_running_under_translator() { #if (WINDOWS && _WIN32_WINNT >= _WIN32_WINNT_WIN10) const HANDLE hCurrentProcess = reinterpret_cast(-1LL); @@ -1777,7 +1757,7 @@ struct VM { return eax; }; - hyperx_state state; + hyperx_state state = HYPERV_UNKNOWN; if (!is_root_partition()) { if (eax() == 11 && is_hyperv_present()) { @@ -1805,6 +1785,9 @@ struct VM { debug("HYPER_X: Detected Hyper-V host machine"); core::add(brands::HYPERV_ARTIFACT); state = HYPERV_ARTIFACT_VM; + } else { + debug("HYPER_X: Hyper-V is not active"); + state = HYPERV_UNKNOWN; } } @@ -2081,6 +2064,210 @@ struct VM { if (h) cachedNtdll = h; return h; } + + // to search in our databases, we want to precompute hashes at compile time for C++11 and later + // so we need to match the hardware _mm_crc32_u8, it is based on CRC32-C (Castagnoli) polynomial + struct ConstexprHash { + // it does 8 rounds of CRC32-C bit reflection recursively + static constexpr u32 crc32_bits(u32 crc, int bits) { + return (bits == 0) ? crc : + crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); + } + + // over string + static constexpr u32 crc32_str(const char* s, u32 crc) { + return (*s == '\0') ? crc : + crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); + } + + static constexpr u32 get(const char* s) { + return crc32_str(s, 0); + } + }; + + // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible + struct Entry { + u32 hash; + u32 threads; + constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} + }; + + enum class CpuType { + INTEL_I, + INTEL_XEON, + AMD + }; + + // 4 arguments to stay compliant with x64 __fastcall (just in case) + [[nodiscard]] static bool verify_thread_count(const Entry* db, size_t db_size, size_t max_model_len, CpuType type) { + // to save a few cycles + struct hasher { + static u32 crc32_sw(u32 crc, char data) { + crc ^= static_cast(data); + for (int i = 0; i < 8; ++i) + crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); + return crc; + } + + // For strings shorter than 16-32 bytes, the overhead of setting up the _mm_crc32_u64 (or 32) loop, then checking length, handling the tail bytes, and finally handling alignment, + // will always make it slower or equal to a simple unrolled u8 loop, and not every cpu model fits in u32/u64 + #if (CLANG || GCC) + __attribute__((__target__("crc32"))) + #endif + static u32 crc32_hw(u32 crc, char data) { + + return _mm_crc32_u8(crc, static_cast(data)); + } + + using hashfc = u32(*)(u32, char); + + static hashfc get() { + // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often + i32 regs[4]; + cpu::cpuid(regs, 1); + const bool has_sse42 = (regs[2] & (1 << 20)) != 0; + + return has_sse42 ? crc32_hw : crc32_sw; + } + }; + + std::string model_string; + const char* debug_tag = ""; + + if (type == CpuType::AMD) { + if (!cpu::is_amd()) { + return false; + } + model_string = cpu::get_brand(); + debug_tag = "AMD_THREAD_MISMATCH"; + } + else { + if (!cpu::is_intel()) { + return false; + } + + const cpu::model_struct model = cpu::get_model(); + + if (!model.found) { + return false; + } + + if (type == CpuType::INTEL_I) { + if (!model.is_i_series) { + return false; + } + debug_tag = "INTEL_THREAD_MISMATCH"; + } + else { + if (!model.is_xeon) { + return false; + } + debug_tag = "XEON_THREAD_MISMATCH"; + } + model_string = model.string; + } + + if (model_string.empty()) return false; + + debug(debug_tag, ": CPU model = ", model_string); + + const char* str = model_string.c_str(); + u32 expected_threads = 0; + bool found = false; + size_t best_len = 0; + + // manual collision fix for Z1 Extreme (16) vs Z1 (12) + // this is a special runtime check because "z1" is a substring of "z1 extreme" tokens + // and both might be hashed. VMAware should prioritize 'extreme' if found + u32 z_series_threads = 0; + + const auto hash_func = hasher::get(); + + for (size_t i = 0; str[i] != '\0'; ) { + char c = str[i]; + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { + i++; + continue; + } + + u32 current_hash = 0; + size_t current_len = 0; + size_t j = i; + + while (true) { + char k = str[j]; + const bool is_valid = (k >= '0' && k <= '9') || + (k >= 'A' && k <= 'Z') || + (k >= 'a' && k <= 'z') || + (k == '-'); // models have hyphen + if (!is_valid) break; + + if (current_len >= max_model_len) { + while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null + break; + } + + /* + models are usually 8 or more bytes long, i.e. i9-10900K + so imagine we want to use u64, you hash the first 8 bytes i9-10900 + but then you are left with K. You have to handle the tail + fetching 8 bytes would include the characters after the token, corrupting the hash + so a byte-by-byte loop is the most optimal choice here + */ + + // convert to lowercase on-the-fly to match compile-time keys + if (type == CpuType::AMD && (k >= 'A' && k <= 'Z')) k += 32; + + // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction + // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah + current_hash = hash_func(current_hash, k); + current_len++; + j++; + + // only verify match if the token has ended (next char is not alphanumeric) + const char next = str[j]; + const bool next_is_alnum = (next >= '0' && next <= '9') || + (next >= 'A' && next <= 'Z') || + (next >= 'a' && next <= 'z'); + + if (!next_is_alnum) { + // Check specific Z1 Extreme token + // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 + if (type == CpuType::AMD && current_hash == 0x3D09D5B4) { z_series_threads = 16; } + + // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast + for (size_t idx = 0; idx < db_size; ++idx) { + if (db[idx].hash == current_hash) { + if (current_len > best_len) { + best_len = current_len; + expected_threads = db[idx].threads; + found = true; + } + // since hashing implies uniqueness in this dataset, you might say we could break here, + // but we continue to ensure we find the longest substring match if overlaps exist, + // so like it finds both "i9-11900" and "i9-11900K" i.e. + } + } + } + } + i = j; + } + + // Z1 Extreme fix + if (type == CpuType::AMD && z_series_threads != 0 && expected_threads == 12) { + expected_threads = z_series_threads; + } + + if (found) { + const u32 actual = memo::threadcount::fetch(); + if (actual != expected_threads) { + debug(debug_tag, ": Expected threads -> ", expected_threads); + return true; + } + } + + return false; + } #endif }; @@ -2333,53 +2520,10 @@ struct VM { #if (!x86) return false; #else - if (!cpu::is_intel()) { - return false; - } - - const cpu::model_struct model = cpu::get_model(); - - if (!model.found) { - return false; - } - - if (!model.is_i_series) { - return false; - } - - debug("INTEL_THREAD_MISMATCH: CPU model = ", model.string); - - // we want to precompute hashes at compile time for C++11 and later, so we need to match the hardware _mm_crc32_u8 - // it is based on CRC32-C (Castagnoli) polynomial - struct ConstexprHash { - // it does 8 rounds of CRC32-C bit reflection recursively - static constexpr u32 crc32_bits(u32 crc, int bits) { - return (bits == 0) ? crc : - crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); - } - - // over string - static constexpr u32 crc32_str(const char* s, u32 crc) { - return (*s == '\0') ? crc : - crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); - } - - static constexpr u32 get(const char* s) { - return crc32_str(s, 0); - } - }; - - // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible - struct Entry { - u32 hash; - u32 threads; - constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} - }; - // umap is not an option because it cannot be constexpr // constexpr is respected here even in c++ 11 and static solves stack overflow // c arrays have less construction overhead than std::array - static constexpr Entry thread_database[] = { + static constexpr util::Entry thread_database[] = { // i3 series { "i3-1000G1", 4 }, { "i3-1000G4", 4 }, @@ -3336,112 +3480,9 @@ struct VM { { "i9-9990XE", 28 } }; - // to save a few cycles static constexpr size_t MAX_INTEL_MODEL_LEN = 16; - struct hasher { - static u32 crc32_sw(u32 crc, char data) { - crc ^= static_cast(data); - for (int i = 0; i < 8; ++i) - crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); - return crc; - } - - #if (CLANG || GCC) - __attribute__((__target__("crc32"))) - #endif - static u32 crc32_hw(u32 crc, char data) { - return _mm_crc32_u8(crc, static_cast(data)); - } - - using hashfc = u32(*)(u32, char); - - static hashfc get() { - // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often - i32 regs[4]; - cpu::cpuid(regs, 1); - const bool has_sse42 = (regs[2] & (1 << 20)) != 0; - - return has_sse42 ? crc32_hw : crc32_sw; - } - }; - - const char* str = model.string.c_str(); - u32 expected_threads = 0; - bool found = false; - size_t best_len = 0; - - const auto hash_func = hasher::get(); - - for (size_t i = 0; str[i] != '\0'; ) { - const char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - const char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); // models have hyphen - if (!is_valid) break; - - if (current_len >= MAX_INTEL_MODEL_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null - break; - } - - /* - models are usually 8 or more bytes long, i.e. i9-10900K - so imagine we want to use u64, you hash the first 8 bytes i9-10900 - but then you are left with K. You have to handle the tail - fetching 8 bytes would include the characters after the token, corrupting the hash - so a byte-by-byte loop is the most optimal choice here - */ - - // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction - // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // only verify match if the token has ended (next char is not alphanumeric) - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast - for (const auto& entry : thread_database) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - // since hashing implies uniqueness in this dataset, you might say we could break here, - // but we continue to ensure we find the longest substring match if overlaps exist, - // so like it finds both "i9-11900" and "i9-11900K" i.e. - } - } - } - } - i = j; - } - - if (found) { - const u32 actual = memo::threadcount::fetch(); - return actual != expected_threads; - } - - return false; + return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::Entry), MAX_INTEL_MODEL_LEN, util::CpuType::INTEL_I); #endif } @@ -3456,53 +3497,10 @@ struct VM { #if (!x86) return false; #else - if (!cpu::is_intel()) { - return false; - } - - const cpu::model_struct model = cpu::get_model(); - - if (!model.found) { - return false; - } - - if (!model.is_xeon) { - return false; - } - - debug("XEON_THREAD_MISMATCH: CPU model = ", model.string); - - // we want to precompute hashes at compile time for C++11 and later, so we need to match the hardware _mm_crc32_u8 - // it is based on CRC32-C (Castagnoli) polynomial - struct ConstexprHash { - // it does 8 rounds of CRC32-C bit reflection recursively - static constexpr u32 crc32_bits(u32 crc, int bits) { - return (bits == 0) ? crc : - crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); - } - - // over string - static constexpr u32 crc32_str(const char* s, u32 crc) { - return (*s == '\0') ? crc : - crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); - } - - static constexpr u32 get(const char* s) { - return crc32_str(s, 0); - } - }; - - // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible - struct Entry { - u32 hash; - u32 threads; - constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} - }; - // umap is not an option because it cannot be constexpr // constexpr is respected here even in c++ 11 and static solves stack overflow // c arrays have less construction overhead than std::array - static constexpr Entry thread_database[] = { + static constexpr util::Entry thread_database[] = { // Xeon D { "D-1518", 8 }, { "D-1520", 8 }, @@ -3638,116 +3636,9 @@ struct VM { { "w9-3595X", 120 } }; - // to save a few cycles static constexpr size_t MAX_XEON_MODEL_LEN = 16; - struct hasher { - static u32 crc32_sw(u32 crc, char data) { - crc ^= static_cast(data); - for (int i = 0; i < 8; ++i) - crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); - return crc; - } - - #if (CLANG || GCC) - __attribute__((__target__("crc32"))) - #endif - static u32 crc32_hw(u32 crc, char data) { - return _mm_crc32_u8(crc, static_cast(data)); - } - - using hashfc = u32(*)(u32, char); - - static hashfc get() { - // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often - i32 regs[4]; - cpu::cpuid(regs, 1); - const bool has_sse42 = (regs[2] & (1 << 20)) != 0; - - return has_sse42 ? crc32_hw : crc32_sw; - } - }; - - const std::string& cpu_full_name = model.string; - if (cpu_full_name.empty()) return false; - - const char* str = cpu_full_name.c_str(); - u32 expected_threads = 0; - bool found = false; - size_t best_len = 0; - - const auto hash_func = hasher::get(); - - for (size_t i = 0; str[i] != '\0'; ) { - const char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - const char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); - if (!is_valid) break; - - if (current_len >= MAX_XEON_MODEL_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null - break; - } - - /* - models are usually 8 or more bytes long, i.e. i9-10900K - so imagine we want to use u64, you hash the first 8 bytes i9-10900 - but then you are left with K. You have to handle the tail - fetching 8 bytes would include the characters after the token, corrupting the hash - so a byte-by-byte loop is the most optimal choice here - */ - - // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction - // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // only verify match if the token has ended (next char is not alphanumeric) - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast - for (const auto& entry : thread_database) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - // since hashing implies uniqueness in this dataset, you might say we could break here, - // but we continue to ensure we find the longest substring match if overlaps exist, - // so like it finds both "i9-11900" and "i9-11900K" i.e. - } - } - } - } - i = j; - } - - if (found) { - const u32 actual = memo::threadcount::fetch(); - debug("XEON_THREAD_MISMATCH: Expected threads -> ", expected_threads); - return actual != expected_threads; - } - - return false; + return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::Entry), MAX_XEON_MODEL_LEN, util::CpuType::INTEL_XEON); #endif } @@ -3761,38 +3652,10 @@ struct VM { [[nodiscard]] static bool amd_thread_mismatch() { #if (!x86) return false; - #else - if (!cpu::is_amd()) { - return false; - } - - std::string model_str = cpu::get_brand(); - - static constexpr size_t MAX_AMD_TOKEN_LEN = 24; // "threadripper" is long - - struct ConstexprHash { - static constexpr u32 crc32_bits(u32 crc, int bits) { - return (bits == 0) ? crc : - crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); - } - static constexpr u32 crc32_str(const char* s, u32 crc) { - return (*s == '\0') ? crc : - crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); - } - static constexpr u32 get(const char* s) { - return crc32_str(s, 0); - } - }; - - struct Entry { - u32 hash; - u32 threads; - constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} - }; - + #else // Database is reduced to identifying suffixes (last token of the original strings) // for example handles "ryzen 5 3600" by matching "3600", which is unique in context - static constexpr Entry db_entries[] = { + static constexpr util::Entry thread_database[] = { // 3015/3020 { "3015ce", 4 }, { "3015e", 4 }, @@ -4303,112 +4166,9 @@ struct VM { { "z2", 16 } }; - struct hasher { - static u32 crc32_sw(u32 crc, char data) { - crc ^= static_cast(data); - for (int i = 0; i < 8; ++i) - crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); - return crc; - } - - #if (CLANG || GCC) - __attribute__((__target__("crc32"))) - #endif - static u32 crc32_hw(u32 crc, char data) { - return _mm_crc32_u8(crc, static_cast(data)); - } - - using hashfc = u32(*)(u32, char); - - static hashfc get() { - i32 regs[4]; - cpu::cpuid(regs, 1); - const bool has_sse42 = (regs[2] & (1 << 20)) != 0; - return has_sse42 ? crc32_hw : crc32_sw; - } - }; - - debug("AMD_THREAD_MISMATCH: CPU model = ", model_str); - - const char* str = model_str.c_str(); - u32 expected_threads = 0; - bool found = false; - size_t best_len = 0; - - const auto hash_func = hasher::get(); - - // manual collision fix for Z1 Extreme (16) vs Z1 (12) - // this is a special runtime check because "z1" is a substring of "z1 extreme" tokens - // and both might be hashed. VMAware should prioritize 'extreme' if found - u32 z_series_threads = 0; - - for (size_t i = 0; str[i] != '\0'; ) { - char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); - if (!is_valid) break; - - if (current_len >= MAX_AMD_TOKEN_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; - break; - } - - // convert to lowercase on-the-fly to match compile-time keys - if (k >= 'A' && k <= 'Z') k += 32; + static constexpr size_t MAX_AMD_MODEL_LEN = 24; // "threadripper" is long - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // boundary check - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // Check specific Z1 Extreme token - // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 - if (current_hash == 0x3D09D5B4) { z_series_threads = 16; } - - for (const auto& entry : db_entries) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - } - } - } - } - i = j; - } - - // Z1 Extreme fix - if (z_series_threads != 0 && expected_threads == 12) { - expected_threads = z_series_threads; - } - - if (found) { - const u32 actual = memo::threadcount::fetch(); - return actual != expected_threads; - } - - return false; + return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::Entry), MAX_AMD_MODEL_LEN, util::CpuType::INTEL_XEON); #endif } @@ -5864,8 +5624,15 @@ struct VM { * @category Windows, Linux * @implements VM::HYPERV_HOSTNAME */ - static bool hyperv_hostname() { - const std::string hostname = util::get_hostname(); + [[nodiscard]] static bool hyperv_hostname() { + char buf[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD len = sizeof(buf); + + if (!GetComputerNameA(buf, &len)) { + return false; + } + + std::string hostname(buf, len); const char* prefix = "runnervm"; const std::size_t prefix_len = std::strlen(prefix); @@ -5881,8 +5648,7 @@ struct VM { } for (std::size_t i = prefix_len; i < hostname.size(); ++i) { - unsigned char c = static_cast(hostname[i]); - if (!std::isalnum(c)) { + if (!std::isalnum(static_cast(hostname[i]))) { return false; } } From bf95df8985947356d6995434479ed8cc6a7354e4 Mon Sep 17 00:00:00 2001 From: Requiem Date: Wed, 26 Nov 2025 22:44:20 +0100 Subject: [PATCH 06/17] chore: updated github merge workflow --- .github/workflows/updater.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/updater.yml b/.github/workflows/updater.yml index 0200d507..9d900b49 100644 --- a/.github/workflows/updater.yml +++ b/.github/workflows/updater.yml @@ -3,7 +3,7 @@ name: Python script to update documentations and header details on merge on: push: branches: - - main + - dev jobs: run-script: From 352a5d67b0d5b5069fe03772e83c10dbcb8651d3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Nov 2025 21:44:28 +0000 Subject: [PATCH 07/17] Automated update after merge --- docs/documentation.md | 181 +++++++++++++++++++++--------------------- src/vmaware.hpp | 14 ++-- 2 files changed, 97 insertions(+), 98 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index 3251e6e0..8565e725 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -499,97 +499,96 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | Flag alias | Description | Supported platforms | Certainty | Admin? | 32-bit only? | Notes | Code implementation | | ---------- | ----------- | ------------------- | --------- | ------ | ------------ | ----- | ------------------- | -| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2082) | -| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2100) | -| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2174) | -| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2205) | -| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4269) | -| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6513) | -| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4668) | -| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5518) | -| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4549) | -| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4573) | -| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4598) | -| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4616) | -| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4631) | -| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4774) | -| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4815) | -| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6809) | -| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6537) | -| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6840) | -| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6879) | -| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5529) | -| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4825) | -| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6939) | -| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2233) | -| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6570) | -| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6603) | -| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6700) | -| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6757) | -| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7037) | -| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5556) | -| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7088) | -| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7156) | -| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7211) | -| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4854) | -| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5364) | -| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5163) | -| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5182) | -| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7238) | -| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7263) | -| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7324) | -| `VM::INTEL_THREAD_MISMATCH` | Check for Intel CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2321) | -| `VM::XEON_THREAD_MISMATCH` | Same as above, but for Xeon Intel CPUs | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3356) | -| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3570) | -| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7403) | -| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7459) | -| `VM::HYPERV_HOSTNAME` | Check for default Azure hostname format (Azure uses Hyper-V as their base VM brand) | 🐧🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5656) | -| `VM::GENERAL_HOSTNAME` | Check for commonly set hostnames by certain VM brands | 🐧🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5690) | -| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7515) | -| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7550) | -| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4870) | -| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4216) | -| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4245) | -| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4951) | -| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4980) | -| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5008) | -| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5056) | -| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5086) | -| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5140) | -| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5216) | -| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5238) | -| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5319) | -| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5349) | -| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5381) | -| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7567) | -| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7665) | -| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7884) | -| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7983) | -| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8021) | -| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5409) | -| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8117) | -| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8147) | -| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4893) | -| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8208) | -| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5724) | -| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5439) | -| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8293) | -| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5466) | -| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6148) | -| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8391) | -| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8536) | -| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8293) | -| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8760) | -| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8807) | -| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8925) | -| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6784) | -| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9017) | -| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9186) | -| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9433) | -| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9444) | -| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9581) | -| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9846) | -| `VM::LBR` | Check if Last Branch Record MSRs are correctly virtualized | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9939) | +| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2278) | +| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2296) | +| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2370) | +| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2401) | +| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4234) | +| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6461) | +| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4637) | +| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5487) | +| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4518) | +| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4542) | +| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4567) | +| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4585) | +| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4600) | +| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4743) | +| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4784) | +| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6757) | +| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6485) | +| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6788) | +| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6827) | +| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5498) | +| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4794) | +| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6887) | +| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2429) | +| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6518) | +| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6551) | +| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6648) | +| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6705) | +| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6985) | +| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5525) | +| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7036) | +| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7104) | +| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7159) | +| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4823) | +| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5333) | +| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5132) | +| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5151) | +| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7186) | +| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7211) | +| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7272) | +| `VM::INTEL_THREAD_MISMATCH` | Check for Intel I-series CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2517) | +| `VM::XEON_THREAD_MISMATCH` | Check for Intel Xeon CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3494) | +| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3650) | +| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7351) | +| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7407) | +| `VM::AZURE` | | | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L1) | +| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7463) | +| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7498) | +| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4839) | +| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4181) | +| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4210) | +| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4920) | +| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4949) | +| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4977) | +| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5025) | +| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5055) | +| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5109) | +| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5185) | +| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5207) | +| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5288) | +| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5318) | +| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5350) | +| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7515) | +| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7613) | +| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7832) | +| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7931) | +| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7969) | +| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5378) | +| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8065) | +| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8095) | +| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4862) | +| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8156) | +| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5665) | +| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5408) | +| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8241) | +| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5435) | +| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6089) | +| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8339) | +| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8484) | +| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8241) | +| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8708) | +| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8755) | +| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8873) | +| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6732) | +| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8965) | +| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9134) | +| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9381) | +| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9392) | +| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9593) | +| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10084) | +| `VM::LBR` | Check if Last Branch Record MSRs are correctly virtualized | 🪟 | 95% | | | Currently investigating possible false flags with this | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10177) |
diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 1fdb0b88..87829870 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -53,13 +53,13 @@ * * ============================== SECTIONS ================================== * - enums for publicly accessible techniques => line 534 - * - struct for internal cpu operations => line 717 - * - struct for internal memoization => line 1150 - * - struct for internal utility functions => line 1280 - * - struct for internal core components => line 10141 - * - start of VM detection technique list => line 2077 - * - start of public VM detection functions => line 10634 - * - start of externally defined variables => line 11615 + * - struct for internal cpu operations => line 716 + * - struct for internal memoization => line 1159 + * - struct for internal utility functions => line 1289 + * - struct for internal core components => line 10381 + * - start of VM detection technique list => line 2273 + * - start of public VM detection functions => line 10874 + * - start of externally defined variables => line 11854 * * * ============================== EXAMPLE =================================== From 5b147f5ff9fe15ebd9ac4e771fe31f6de2f177ea Mon Sep 17 00:00:00 2001 From: Requiem Date: Wed, 26 Nov 2025 22:48:02 +0100 Subject: [PATCH 08/17] refactor: made verify_thread_count visible to all platforms --- src/vmaware.hpp | 410 ++++++++++++++++++++++++------------------------ 1 file changed, 205 insertions(+), 205 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 1fdb0b88..f6c0c524 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1796,6 +1796,210 @@ struct VM { return state; #endif } + + // to search in our databases, we want to precompute hashes at compile time for C++11 and later + // so we need to match the hardware _mm_crc32_u8, it is based on CRC32-C (Castagnoli) polynomial + struct ConstexprHash { + // it does 8 rounds of CRC32-C bit reflection recursively + static constexpr u32 crc32_bits(u32 crc, int bits) { + return (bits == 0) ? crc : + crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); + } + + // over string + static constexpr u32 crc32_str(const char* s, u32 crc) { + return (*s == '\0') ? crc : + crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); + } + + static constexpr u32 get(const char* s) { + return crc32_str(s, 0); + } + }; + + // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible + struct Entry { + u32 hash; + u32 threads; + constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} + }; + + enum class CpuType { + INTEL_I, + INTEL_XEON, + AMD + }; + + // 4 arguments to stay compliant with x64 __fastcall (just in case) + [[nodiscard]] static bool verify_thread_count(const Entry* db, size_t db_size, size_t max_model_len, CpuType type) { + // to save a few cycles + struct hasher { + static u32 crc32_sw(u32 crc, char data) { + crc ^= static_cast(data); + for (int i = 0; i < 8; ++i) + crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); + return crc; + } + + // For strings shorter than 16-32 bytes, the overhead of setting up the _mm_crc32_u64 (or 32) loop, then checking length, handling the tail bytes, and finally handling alignment, + // will always make it slower or equal to a simple unrolled u8 loop, and not every cpu model fits in u32/u64 + #if (CLANG || GCC) + __attribute__((__target__("crc32"))) + #endif + static u32 crc32_hw(u32 crc, char data) { + + return _mm_crc32_u8(crc, static_cast(data)); + } + + using hashfc = u32(*)(u32, char); + + static hashfc get() { + // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often + i32 regs[4]; + cpu::cpuid(regs, 1); + const bool has_sse42 = (regs[2] & (1 << 20)) != 0; + + return has_sse42 ? crc32_hw : crc32_sw; + } + }; + + std::string model_string; + const char* debug_tag = ""; + + if (type == CpuType::AMD) { + if (!cpu::is_amd()) { + return false; + } + model_string = cpu::get_brand(); + debug_tag = "AMD_THREAD_MISMATCH"; + } + else { + if (!cpu::is_intel()) { + return false; + } + + const cpu::model_struct model = cpu::get_model(); + + if (!model.found) { + return false; + } + + if (type == CpuType::INTEL_I) { + if (!model.is_i_series) { + return false; + } + debug_tag = "INTEL_THREAD_MISMATCH"; + } + else { + if (!model.is_xeon) { + return false; + } + debug_tag = "XEON_THREAD_MISMATCH"; + } + model_string = model.string; + } + + if (model_string.empty()) return false; + + debug(debug_tag, ": CPU model = ", model_string); + + const char* str = model_string.c_str(); + u32 expected_threads = 0; + bool found = false; + size_t best_len = 0; + + // manual collision fix for Z1 Extreme (16) vs Z1 (12) + // this is a special runtime check because "z1" is a substring of "z1 extreme" tokens + // and both might be hashed. VMAware should prioritize 'extreme' if found + u32 z_series_threads = 0; + + const auto hash_func = hasher::get(); + + for (size_t i = 0; str[i] != '\0'; ) { + char c = str[i]; + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { + i++; + continue; + } + + u32 current_hash = 0; + size_t current_len = 0; + size_t j = i; + + while (true) { + char k = str[j]; + const bool is_valid = (k >= '0' && k <= '9') || + (k >= 'A' && k <= 'Z') || + (k >= 'a' && k <= 'z') || + (k == '-'); // models have hyphen + if (!is_valid) break; + + if (current_len >= max_model_len) { + while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null + break; + } + + /* + models are usually 8 or more bytes long, i.e. i9-10900K + so imagine we want to use u64, you hash the first 8 bytes i9-10900 + but then you are left with K. You have to handle the tail + fetching 8 bytes would include the characters after the token, corrupting the hash + so a byte-by-byte loop is the most optimal choice here + */ + + // convert to lowercase on-the-fly to match compile-time keys + if (type == CpuType::AMD && (k >= 'A' && k <= 'Z')) k += 32; + + // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction + // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah + current_hash = hash_func(current_hash, k); + current_len++; + j++; + + // only verify match if the token has ended (next char is not alphanumeric) + const char next = str[j]; + const bool next_is_alnum = (next >= '0' && next <= '9') || + (next >= 'A' && next <= 'Z') || + (next >= 'a' && next <= 'z'); + + if (!next_is_alnum) { + // Check specific Z1 Extreme token + // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 + if (type == CpuType::AMD && current_hash == 0x3D09D5B4) { z_series_threads = 16; } + + // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast + for (size_t idx = 0; idx < db_size; ++idx) { + if (db[idx].hash == current_hash) { + if (current_len > best_len) { + best_len = current_len; + expected_threads = db[idx].threads; + found = true; + } + // since hashing implies uniqueness in this dataset, you might say we could break here, + // but we continue to ensure we find the longest substring match if overlaps exist, + // so like it finds both "i9-11900" and "i9-11900K" i.e. + } + } + } + } + i = j; + } + + // Z1 Extreme fix + if (type == CpuType::AMD && z_series_threads != 0 && expected_threads == 12) { + expected_threads = z_series_threads; + } + + if (found) { + const u32 actual = memo::threadcount::fetch(); + if (actual != expected_threads) { + debug(debug_tag, ": Expected threads -> ", expected_threads); + return true; + } + } + + return false; + } #if (WINDOWS) // retrieves the addresses of specified functions from a loaded module using the export directory, manual implementation of GetProcAddress @@ -2063,211 +2267,7 @@ struct VM { const HMODULE h = GetModuleHandleW(L"ntdll.dll"); if (h) cachedNtdll = h; return h; - } - - // to search in our databases, we want to precompute hashes at compile time for C++11 and later - // so we need to match the hardware _mm_crc32_u8, it is based on CRC32-C (Castagnoli) polynomial - struct ConstexprHash { - // it does 8 rounds of CRC32-C bit reflection recursively - static constexpr u32 crc32_bits(u32 crc, int bits) { - return (bits == 0) ? crc : - crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); - } - - // over string - static constexpr u32 crc32_str(const char* s, u32 crc) { - return (*s == '\0') ? crc : - crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); - } - - static constexpr u32 get(const char* s) { - return crc32_str(s, 0); - } - }; - - // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible - struct Entry { - u32 hash; - u32 threads; - constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} - }; - - enum class CpuType { - INTEL_I, - INTEL_XEON, - AMD - }; - - // 4 arguments to stay compliant with x64 __fastcall (just in case) - [[nodiscard]] static bool verify_thread_count(const Entry* db, size_t db_size, size_t max_model_len, CpuType type) { - // to save a few cycles - struct hasher { - static u32 crc32_sw(u32 crc, char data) { - crc ^= static_cast(data); - for (int i = 0; i < 8; ++i) - crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); - return crc; - } - - // For strings shorter than 16-32 bytes, the overhead of setting up the _mm_crc32_u64 (or 32) loop, then checking length, handling the tail bytes, and finally handling alignment, - // will always make it slower or equal to a simple unrolled u8 loop, and not every cpu model fits in u32/u64 - #if (CLANG || GCC) - __attribute__((__target__("crc32"))) - #endif - static u32 crc32_hw(u32 crc, char data) { - - return _mm_crc32_u8(crc, static_cast(data)); - } - - using hashfc = u32(*)(u32, char); - - static hashfc get() { - // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often - i32 regs[4]; - cpu::cpuid(regs, 1); - const bool has_sse42 = (regs[2] & (1 << 20)) != 0; - - return has_sse42 ? crc32_hw : crc32_sw; - } - }; - - std::string model_string; - const char* debug_tag = ""; - - if (type == CpuType::AMD) { - if (!cpu::is_amd()) { - return false; - } - model_string = cpu::get_brand(); - debug_tag = "AMD_THREAD_MISMATCH"; - } - else { - if (!cpu::is_intel()) { - return false; - } - - const cpu::model_struct model = cpu::get_model(); - - if (!model.found) { - return false; - } - - if (type == CpuType::INTEL_I) { - if (!model.is_i_series) { - return false; - } - debug_tag = "INTEL_THREAD_MISMATCH"; - } - else { - if (!model.is_xeon) { - return false; - } - debug_tag = "XEON_THREAD_MISMATCH"; - } - model_string = model.string; - } - - if (model_string.empty()) return false; - - debug(debug_tag, ": CPU model = ", model_string); - - const char* str = model_string.c_str(); - u32 expected_threads = 0; - bool found = false; - size_t best_len = 0; - - // manual collision fix for Z1 Extreme (16) vs Z1 (12) - // this is a special runtime check because "z1" is a substring of "z1 extreme" tokens - // and both might be hashed. VMAware should prioritize 'extreme' if found - u32 z_series_threads = 0; - - const auto hash_func = hasher::get(); - - for (size_t i = 0; str[i] != '\0'; ) { - char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); // models have hyphen - if (!is_valid) break; - - if (current_len >= max_model_len) { - while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null - break; - } - - /* - models are usually 8 or more bytes long, i.e. i9-10900K - so imagine we want to use u64, you hash the first 8 bytes i9-10900 - but then you are left with K. You have to handle the tail - fetching 8 bytes would include the characters after the token, corrupting the hash - so a byte-by-byte loop is the most optimal choice here - */ - - // convert to lowercase on-the-fly to match compile-time keys - if (type == CpuType::AMD && (k >= 'A' && k <= 'Z')) k += 32; - - // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction - // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // only verify match if the token has ended (next char is not alphanumeric) - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // Check specific Z1 Extreme token - // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 - if (type == CpuType::AMD && current_hash == 0x3D09D5B4) { z_series_threads = 16; } - - // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast - for (size_t idx = 0; idx < db_size; ++idx) { - if (db[idx].hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = db[idx].threads; - found = true; - } - // since hashing implies uniqueness in this dataset, you might say we could break here, - // but we continue to ensure we find the longest substring match if overlaps exist, - // so like it finds both "i9-11900" and "i9-11900K" i.e. - } - } - } - } - i = j; - } - - // Z1 Extreme fix - if (type == CpuType::AMD && z_series_threads != 0 && expected_threads == 12) { - expected_threads = z_series_threads; - } - - if (found) { - const u32 actual = memo::threadcount::fetch(); - if (actual != expected_threads) { - debug(debug_tag, ": Expected threads -> ", expected_threads); - return true; - } - } - - return false; - } + } #endif }; From 8dff22a4b3269aa5037a1bc0a38f666ae23968c3 Mon Sep 17 00:00:00 2001 From: Requiem Date: Wed, 26 Nov 2025 22:59:18 +0100 Subject: [PATCH 09/17] remove: LBR virtualization checks --- src/cli.cpp | 1 - src/vmaware.hpp | 202 ------------------------------------------------ 2 files changed, 203 deletions(-) diff --git a/src/cli.cpp b/src/cli.cpp index fbce6532..973ddc22 100755 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -828,7 +828,6 @@ static void general() { checker(VM::EDID, "EDID"); checker(VM::CPU_HEURISTIC, "CPU heuristics"); checker(VM::CLOCK, "system timers"); - checker(VM::LBR, "LBR virtualization"); // ADD NEW TECHNIQUE CHECKER HERE const auto t2 = std::chrono::high_resolution_clock::now(); diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 88dd3200..54605025 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -570,7 +570,6 @@ struct VM { EDID, CPU_HEURISTIC, CLOCK, - LBR, // Linux and Windows SIDT, @@ -10169,205 +10168,6 @@ struct VM { SetupDiDestroyDeviceInfoList(devs); return !found; } - - - /** - * @brief Check if Last Branch Record MSRs are correctly virtualized - * @category Windows - * @implements VM::LBR - * @note Currently investigating possible false flags with this - */ - [[nodiscard]] static bool lbr() { - #if (x86) - const HMODULE ntdll = util::get_ntdll(); - if (!ntdll) return false; - - const char* names[] = { - "NtAllocateVirtualMemory", - "NtFreeVirtualMemory", - "NtFlushInstructionCache", - "RtlAddVectoredExceptionHandler", - "RtlRemoveVectoredExceptionHandler", - "NtCreateThreadEx", - "NtGetContextThread", - "NtSetContextThread", - "NtResumeThread", - "NtWaitForSingleObject", - "NtClose", - "NtProtectVirtualMemory" - }; - void* funcs[ARRAYSIZE(names)] = {}; - util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); - - using NtAllocateVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, ULONG_PTR, PSIZE_T, ULONG, ULONG); - using NtFreeVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG); - using NtFlushInstructionCache_t = NTSTATUS(__stdcall*)(HANDLE, PVOID, SIZE_T); - using RtlAddVectoredExceptionHandler_t = PVOID(__stdcall*)(ULONG, PVECTORED_EXCEPTION_HANDLER); - using RtlRemoveVectoredExceptionHandler_t = ULONG(__stdcall*)(PVOID); - using NtCreateThreadEx_t = NTSTATUS(__stdcall*)(PHANDLE, ACCESS_MASK, PVOID, HANDLE, PVOID, PVOID, BOOLEAN, ULONG_PTR, SIZE_T, SIZE_T, PVOID); - using NtGetContextThread_t = NTSTATUS(__stdcall*)(HANDLE, PCONTEXT); - using NtSetContextThread_t = NTSTATUS(__stdcall*)(HANDLE, PCONTEXT); - using NtResumeThread_t = NTSTATUS(__stdcall*)(HANDLE, PULONG); - using NtWaitForSingleObject_t = NTSTATUS(__stdcall*)(HANDLE, BOOLEAN, PLARGE_INTEGER); - using NtClose_t = NTSTATUS(__stdcall*)(HANDLE); - using NtProtectVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG, PULONG); - - const auto pNtAllocateVirtualMemory = reinterpret_cast(funcs[0]); - const auto pNtFreeVirtualMemory = reinterpret_cast(funcs[1]); - const auto pNtFlushInstructionCache = reinterpret_cast(funcs[2]); - const auto pRtlAddVectoredExceptionHandler = reinterpret_cast(funcs[3]); - const auto pRtlRemoveVectoredExceptionHandler = reinterpret_cast(funcs[4]); - const auto pNtCreateThreadEx = reinterpret_cast(funcs[5]); - const auto pNtGetContextThread = reinterpret_cast(funcs[6]); - const auto pNtSetContextThread = reinterpret_cast(funcs[7]); - const auto pNtResumeThread = reinterpret_cast(funcs[8]); - const auto pNtWaitForSingleObject = reinterpret_cast(funcs[9]); - const auto pNtClose = reinterpret_cast(funcs[10]); - const auto pNtProtectVirtualMemory = reinterpret_cast(funcs[11]); - - if (!pNtAllocateVirtualMemory || !pNtFreeVirtualMemory || !pNtFlushInstructionCache || - !pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || - !pNtCreateThreadEx || !pNtGetContextThread || !pNtSetContextThread || - !pNtResumeThread || !pNtWaitForSingleObject || !pNtClose || !pNtProtectVirtualMemory) { - return false; - } - - // ICEBP because the kernel interrupt handler that inserts the LastBranchFromIp into EXCEPTION_RECORD->ExceptionInformation[0] is the INT 01 handler - constexpr unsigned char codeBytes[] = { 0xE8,0x00,0x00,0x00,0x00, 0xF1, 0xC3 }; // CALL next ; ICEBP ; RET - const SIZE_T codeSize = sizeof(codeBytes); - const HANDLE hCurrentProcess = reinterpret_cast(-1LL); - - PVOID controlBase = nullptr; - SIZE_T controlSize = sizeof(PVOID); - NTSTATUS st = pNtAllocateVirtualMemory(hCurrentProcess, &controlBase, 0, &controlSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (st != 0 || !controlBase) return false; - *reinterpret_cast(controlBase) = nullptr; - - PVOID execBase = nullptr; - SIZE_T allocSize = codeSize; - st = pNtAllocateVirtualMemory(hCurrentProcess, &execBase, 0, &allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (st != 0 || !execBase) { - SIZE_T tmp = controlSize; - pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); - return false; - } - - unsigned char* dst = reinterpret_cast(execBase); - for (SIZE_T i = 0; i < codeSize; ++i) dst[i] = codeBytes[i]; - - ULONG oldProtect = 0; - SIZE_T protectSize = allocSize; - PVOID protectBase = execBase; - st = pNtProtectVirtualMemory(hCurrentProcess, &protectBase, &protectSize, PAGE_EXECUTE_READ, &oldProtect); - if (st != 0) { - SIZE_T tmpExec = allocSize; - pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmpExec, MEM_RELEASE); - SIZE_T tmpControl = controlSize; - pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmpControl, MEM_RELEASE); - return false; - } - - pNtFlushInstructionCache(hCurrentProcess, execBase, codeSize); - - // local static pointer to control slot so lambda can access a stable address - static PVOID g_control_slot = nullptr; - g_control_slot = controlBase; - - auto veh_lambda = [](PEXCEPTION_POINTERS ep) -> LONG { - if (!ep || !ep->ExceptionRecord) return EXCEPTION_CONTINUE_SEARCH; - if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP) return EXCEPTION_CONTINUE_SEARCH; - - ULONG_PTR info0 = 0; - if (ep->ExceptionRecord->NumberParameters > 0) info0 = ep->ExceptionRecord->ExceptionInformation[0]; - if (info0 && g_control_slot) { - PVOID expected = nullptr; - _InterlockedCompareExchangePointer(reinterpret_cast(g_control_slot), reinterpret_cast(info0), expected); - } - return EXCEPTION_CONTINUE_EXECUTION; - }; - - // Register VEH - const PVECTORED_EXCEPTION_HANDLER veh_fn = static_cast(veh_lambda); - const PVOID vehHandle = pRtlAddVectoredExceptionHandler(1, veh_fn); - if (!vehHandle) { - SIZE_T tmp = allocSize; - pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); - tmp = controlSize; - pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); - return false; - } - - // create suspended thread - HANDLE hThread = nullptr; - NTSTATUS ntres = pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, nullptr, hCurrentProcess, execBase, nullptr, TRUE, 0, 0, 0, nullptr); - if (ntres != 0 || !hThread) { - pRtlRemoveVectoredExceptionHandler(vehHandle); - SIZE_T tmp = allocSize; - pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); - tmp = controlSize; - pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); - return false; - } - - // set debug bits + TF on suspended thread - CONTEXT ctx; - ZeroMemory(&ctx, sizeof(ctx)); - ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS; - ntres = pNtGetContextThread(hThread, &ctx); - if (ntres != 0) { - pNtClose(hThread); - pRtlRemoveVectoredExceptionHandler(vehHandle); - SIZE_T tmp = allocSize; - pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); - tmp = controlSize; - pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); - return false; - } - ctx.Dr7 |= (1ull << 8) | (1ull << 9); // LBR only would be enough - ctx.EFlags |= 0x100; - ntres = pNtSetContextThread(hThread, &ctx); - if (ntres != 0) { - pNtClose(hThread); - pRtlRemoveVectoredExceptionHandler(vehHandle); - SIZE_T tmp = allocSize; - pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); - tmp = controlSize; - pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); - return false; - } - - // resume and wait - ULONG suspendCount = 0; - ntres = pNtResumeThread(hThread, &suspendCount); - if (ntres != 0) { - pNtClose(hThread); - pRtlRemoveVectoredExceptionHandler(vehHandle); - SIZE_T tmp = allocSize; - pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); - tmp = controlSize; - pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); - return false; - } - ntres = pNtWaitForSingleObject(hThread, FALSE, nullptr); - - // read slot (pointer-sized) so if null then no LBR observed - const PVOID slot_val = *reinterpret_cast(controlBase); - - // cleanup - pRtlRemoveVectoredExceptionHandler(vehHandle); - pNtClose(hThread); - SIZE_T tmpSize = allocSize; - pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmpSize, MEM_RELEASE); - tmpSize = controlSize; - pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmpSize, MEM_RELEASE); - g_control_slot = nullptr; - - // a breakpoint set anywhere in this function before slot_val is read will cause the kernel to not deliver any LBR info, thereby returning true - return (slot_val == nullptr); - #else - return false; - #endif - } // ADD NEW TECHNIQUE FUNCTION HERE #endif @@ -11457,7 +11257,6 @@ struct VM { case EDID: return "EDID"; case CPU_HEURISTIC: return "CPU_HEURISTIC"; case CLOCK: return "CLOCK"; - case LBR: return "LBR"; // END OF TECHNIQUE LIST case DEFAULT: return "setting flag, error"; case ALL: return "setting flag, error"; @@ -11996,7 +11795,6 @@ std::pair VM::core::technique_list[] = { std::make_pair(VM::CLOCK, VM::core::technique(100, VM::clock)), std::make_pair(VM::POWER_CAPABILITIES, VM::core::technique(45, VM::power_capabilities)), std::make_pair(VM::CPU_HEURISTIC, VM::core::technique(90, VM::cpu_heuristic)), - std::make_pair(VM::LBR, VM::core::technique(95, VM::lbr)), std::make_pair(VM::EDID, VM::core::technique(100, VM::edid)), std::make_pair(VM::BOOT_LOGO, VM::core::technique(100, VM::boot_logo)), std::make_pair(VM::GPU_CAPABILITIES, VM::core::technique(45, VM::gpu_capabilities)), From 32cbc8d7e349cc4b0d10cf0ce8744fe8423551d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Nov 2025 21:59:27 +0000 Subject: [PATCH 10/17] Automated update after merge --- docs/documentation.md | 177 +++++++++++++++++++++--------------------- src/vmaware.hpp | 14 ++-- 2 files changed, 95 insertions(+), 96 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index 8565e725..ad417402 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -499,96 +499,95 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | Flag alias | Description | Supported platforms | Certainty | Admin? | 32-bit only? | Notes | Code implementation | | ---------- | ----------- | ------------------- | --------- | ------ | ------------ | ----- | ------------------- | -| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2278) | -| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2296) | -| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2370) | -| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2401) | -| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4234) | -| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6461) | -| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4637) | -| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5487) | -| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4518) | -| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4542) | -| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4567) | -| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4585) | -| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4600) | -| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4743) | -| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4784) | -| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6757) | -| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6485) | -| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6788) | -| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6827) | -| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5498) | -| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4794) | -| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6887) | -| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2429) | -| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6518) | -| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6551) | -| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6648) | -| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6705) | -| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6985) | -| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5525) | -| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7036) | -| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7104) | -| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7159) | -| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4823) | -| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5333) | -| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5132) | -| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5151) | -| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7186) | -| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7211) | -| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7272) | -| `VM::INTEL_THREAD_MISMATCH` | Check for Intel I-series CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2517) | -| `VM::XEON_THREAD_MISMATCH` | Check for Intel Xeon CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3494) | -| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3650) | -| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7351) | -| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7407) | +| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2277) | +| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2295) | +| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2369) | +| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2400) | +| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4233) | +| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6460) | +| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4636) | +| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5486) | +| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4517) | +| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4541) | +| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4566) | +| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4584) | +| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4599) | +| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4742) | +| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4783) | +| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6756) | +| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6484) | +| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6787) | +| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6826) | +| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5497) | +| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4793) | +| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6886) | +| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2428) | +| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6517) | +| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6550) | +| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6647) | +| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6704) | +| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6984) | +| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5524) | +| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7035) | +| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7103) | +| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7158) | +| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4822) | +| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5332) | +| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5131) | +| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5150) | +| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7185) | +| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7210) | +| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7271) | +| `VM::INTEL_THREAD_MISMATCH` | Check for Intel I-series CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2516) | +| `VM::XEON_THREAD_MISMATCH` | Check for Intel Xeon CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3493) | +| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3649) | +| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7350) | +| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7406) | | `VM::AZURE` | | | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L1) | -| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7463) | -| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7498) | -| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4839) | -| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4181) | -| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4210) | -| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4920) | -| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4949) | -| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4977) | -| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5025) | -| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5055) | -| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5109) | -| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5185) | -| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5207) | -| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5288) | -| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5318) | -| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5350) | -| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7515) | -| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7613) | -| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7832) | -| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7931) | -| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7969) | -| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5378) | -| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8065) | -| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8095) | -| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4862) | -| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8156) | -| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5665) | -| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5408) | -| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8241) | -| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5435) | -| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6089) | -| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8339) | -| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8484) | -| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8241) | -| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8708) | -| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8755) | -| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8873) | -| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6732) | -| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8965) | -| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9134) | -| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9381) | -| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9392) | -| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9593) | -| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10084) | -| `VM::LBR` | Check if Last Branch Record MSRs are correctly virtualized | 🪟 | 95% | | | Currently investigating possible false flags with this | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10177) | +| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7462) | +| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7497) | +| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4838) | +| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4180) | +| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4209) | +| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4919) | +| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4948) | +| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4976) | +| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5024) | +| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5054) | +| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5108) | +| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5184) | +| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5206) | +| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5287) | +| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5317) | +| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5349) | +| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7514) | +| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7612) | +| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7831) | +| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7930) | +| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7968) | +| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5377) | +| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8064) | +| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8094) | +| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4861) | +| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8155) | +| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5664) | +| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5407) | +| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8240) | +| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5434) | +| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6088) | +| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8338) | +| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8483) | +| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8240) | +| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8707) | +| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8754) | +| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8872) | +| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6731) | +| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8964) | +| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9133) | +| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9380) | +| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9391) | +| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9592) | +| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10083) |
diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 54605025..1a3ba2a3 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -53,13 +53,13 @@ * * ============================== SECTIONS ================================== * - enums for publicly accessible techniques => line 534 - * - struct for internal cpu operations => line 716 - * - struct for internal memoization => line 1159 - * - struct for internal utility functions => line 1289 - * - struct for internal core components => line 10381 - * - start of VM detection technique list => line 2273 - * - start of public VM detection functions => line 10874 - * - start of externally defined variables => line 11854 + * - struct for internal cpu operations => line 715 + * - struct for internal memoization => line 1158 + * - struct for internal utility functions => line 1288 + * - struct for internal core components => line 10181 + * - start of VM detection technique list => line 2272 + * - start of public VM detection functions => line 10674 + * - start of externally defined variables => line 11653 * * * ============================== EXAMPLE =================================== From 76653f2995e2bdba45f4fca3e35b8d5e37452ef8 Mon Sep 17 00:00:00 2001 From: Requiem Date: Wed, 26 Nov 2025 23:45:51 +0100 Subject: [PATCH 11/17] fix: made VM::HYPERV_HOSTNAME cross-platform --- src/vmaware.hpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 54605025..1957b3ac 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -4240,7 +4240,7 @@ struct VM { } u16 cycleThreshold = 1200; if (util::hyper_x() == HYPERV_ARTIFACT_VM) { - cycleThreshold = 15000; // if we're running under Hyper-V, attempt to detect nested virtualization only + cycleThreshold = 8000; // if we're running under Hyper-V, attempt to detect nested virtualization only } #if (WINDOWS) @@ -5624,14 +5624,28 @@ struct VM { * @implements VM::HYPERV_HOSTNAME */ [[nodiscard]] static bool hyperv_hostname() { + std::string hostname; + + #if (WINDOWS) char buf[MAX_COMPUTERNAME_LENGTH + 1]; DWORD len = sizeof(buf); - if (!GetComputerNameA(buf, &len)) { + if (GetComputerNameA(buf, &len)) { + hostname.assign(buf, len); + } + else { return false; } + #elif (LINUX) + char buf[HOST_NAME_MAX]; - std::string hostname(buf, len); + if (gethostname(buf, sizeof(buf)) == 0) { + hostname = buf; + } + else { + return false; + } + #endif const char* prefix = "runnervm"; const std::size_t prefix_len = std::strlen(prefix); From c344068ec396642001f5efba475f10eb24c2f163 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Nov 2025 22:46:02 +0000 Subject: [PATCH 12/17] Automated update after merge --- docs/documentation.md | 90 +++++++++++++++++++++---------------------- src/vmaware.hpp | 6 +-- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index ad417402..5587e81f 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -504,7 +504,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2369) | | `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2400) | | `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4233) | -| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6460) | +| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6474) | | `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4636) | | `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5486) | | `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4517) | @@ -514,38 +514,38 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4599) | | `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4742) | | `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4783) | -| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6756) | -| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6484) | -| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6787) | -| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6826) | +| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6770) | +| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6498) | +| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6801) | +| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6840) | | `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5497) | | `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4793) | -| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6886) | +| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6900) | | `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2428) | -| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6517) | -| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6550) | -| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6647) | -| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6704) | -| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6984) | +| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6531) | +| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6564) | +| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6661) | +| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6718) | +| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6998) | | `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5524) | -| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7035) | -| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7103) | -| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7158) | +| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7049) | +| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7117) | +| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7172) | | `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4822) | | `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5332) | | `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5131) | | `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5150) | -| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7185) | -| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7210) | -| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7271) | +| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7199) | +| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7224) | +| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7285) | | `VM::INTEL_THREAD_MISMATCH` | Check for Intel I-series CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2516) | | `VM::XEON_THREAD_MISMATCH` | Check for Intel Xeon CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3493) | | `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3649) | -| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7350) | -| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7406) | +| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7364) | +| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7420) | | `VM::AZURE` | | | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L1) | -| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7462) | -| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7497) | +| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7476) | +| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7511) | | `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4838) | | `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4180) | | `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4209) | @@ -560,34 +560,34 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5287) | | `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5317) | | `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5349) | -| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7514) | -| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7612) | -| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7831) | -| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7930) | -| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7968) | +| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7528) | +| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7626) | +| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7845) | +| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7944) | +| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7982) | | `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5377) | -| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8064) | -| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8094) | +| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8078) | +| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8108) | | `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4861) | -| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8155) | -| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5664) | +| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8169) | +| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5678) | | `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5407) | -| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8240) | +| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8254) | | `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5434) | -| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6088) | -| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8338) | -| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8483) | -| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8240) | -| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8707) | -| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8754) | -| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8872) | -| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6731) | -| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8964) | -| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9133) | -| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9380) | -| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9391) | -| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9592) | -| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10083) | +| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6102) | +| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8352) | +| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8497) | +| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8254) | +| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8721) | +| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8768) | +| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8886) | +| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6745) | +| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8978) | +| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9147) | +| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9394) | +| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9405) | +| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9606) | +| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10097) |
diff --git a/src/vmaware.hpp b/src/vmaware.hpp index bd376f94..7bf82faf 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -56,10 +56,10 @@ * - struct for internal cpu operations => line 715 * - struct for internal memoization => line 1158 * - struct for internal utility functions => line 1288 - * - struct for internal core components => line 10181 + * - struct for internal core components => line 10195 * - start of VM detection technique list => line 2272 - * - start of public VM detection functions => line 10674 - * - start of externally defined variables => line 11653 + * - start of public VM detection functions => line 10688 + * - start of externally defined variables => line 11667 * * * ============================== EXAMPLE =================================== From 6b4d8e90f51000a25c6119adfcb04de165fcd104 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Nov 2025 22:56:55 +0000 Subject: [PATCH 13/17] Automated update after merge --- docs/documentation.md | 176 +++++++++++++++++++++--------------------- src/vmaware.hpp | 16 ++-- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index 5587e81f..19a9d5b0 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -499,95 +499,95 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | Flag alias | Description | Supported platforms | Certainty | Admin? | 32-bit only? | Notes | Code implementation | | ---------- | ----------- | ------------------- | --------- | ------ | ------------ | ----- | ------------------- | -| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2277) | -| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2295) | -| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2369) | -| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2400) | -| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4233) | -| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6474) | -| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4636) | -| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5486) | -| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4517) | -| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4541) | -| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4566) | -| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4584) | -| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4599) | -| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4742) | -| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4783) | -| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6770) | -| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6498) | -| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6801) | -| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6840) | -| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5497) | -| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4793) | -| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6900) | -| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2428) | -| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6531) | -| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6564) | -| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6661) | -| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6718) | -| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6998) | -| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5524) | -| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7049) | -| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7117) | -| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7172) | -| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4822) | -| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5332) | -| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5131) | -| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5150) | -| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7199) | -| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7224) | -| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7285) | -| `VM::INTEL_THREAD_MISMATCH` | Check for Intel I-series CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2516) | -| `VM::XEON_THREAD_MISMATCH` | Check for Intel Xeon CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3493) | -| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3649) | -| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7364) | -| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7420) | +| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2426) | +| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2444) | +| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2518) | +| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2549) | +| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4382) | +| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6623) | +| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4785) | +| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5635) | +| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4666) | +| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4690) | +| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4715) | +| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4733) | +| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4748) | +| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4891) | +| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4932) | +| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6919) | +| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6647) | +| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6950) | +| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6989) | +| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5646) | +| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4942) | +| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7049) | +| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2577) | +| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6680) | +| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6713) | +| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6810) | +| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6867) | +| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7147) | +| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5673) | +| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7198) | +| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7266) | +| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7321) | +| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4971) | +| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5481) | +| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5280) | +| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5299) | +| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7348) | +| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7373) | +| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7434) | +| `VM::INTEL_THREAD_MISMATCH` | Check for Intel I-series CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2665) | +| `VM::XEON_THREAD_MISMATCH` | Check for Intel Xeon CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3642) | +| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3798) | +| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7513) | +| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7569) | | `VM::AZURE` | | | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L1) | -| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7476) | -| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7511) | -| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4838) | -| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4180) | -| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4209) | -| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4919) | -| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4948) | -| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4976) | -| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5024) | -| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5054) | -| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5108) | -| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5184) | -| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5206) | -| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5287) | -| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5317) | -| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5349) | -| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7528) | -| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7626) | -| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7845) | -| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7944) | -| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7982) | -| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5377) | -| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8078) | -| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8108) | -| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4861) | -| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8169) | -| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5678) | -| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5407) | -| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8254) | -| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5434) | -| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6102) | -| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8352) | -| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8497) | -| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8254) | -| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8721) | -| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8768) | -| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8886) | -| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6745) | -| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8978) | -| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9147) | -| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9394) | -| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9405) | -| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9606) | -| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10097) | +| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7625) | +| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7660) | +| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4987) | +| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4329) | +| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4358) | +| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5068) | +| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5097) | +| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5125) | +| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5173) | +| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5203) | +| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5257) | +| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5333) | +| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5355) | +| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5436) | +| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5466) | +| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5498) | +| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7677) | +| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7775) | +| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7994) | +| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8093) | +| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8131) | +| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5526) | +| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8227) | +| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8257) | +| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5010) | +| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8318) | +| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5827) | +| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5556) | +| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8403) | +| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5583) | +| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6251) | +| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8501) | +| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8646) | +| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8403) | +| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8870) | +| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8917) | +| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9035) | +| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6894) | +| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9127) | +| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9296) | +| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9543) | +| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9554) | +| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9755) | +| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10246) |
diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 88bae47f..16a9db10 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -52,14 +52,14 @@ * * * ============================== SECTIONS ================================== - * - enums for publicly accessible techniques => line 534 - * - struct for internal cpu operations => line 715 - * - struct for internal memoization => line 1158 - * - struct for internal utility functions => line 1288 - * - struct for internal core components => line 10195 - * - start of VM detection technique list => line 2272 - * - start of public VM detection functions => line 10688 - * - start of externally defined variables => line 11667 + * - enums for publicly accessible techniques => line 535 + * - struct for internal cpu operations => line 716 + * - struct for internal memoization => line 1159 + * - struct for internal utility functions => line 1289 + * - struct for internal core components => line 10344 + * - start of VM detection technique list => line 2421 + * - start of public VM detection functions => line 10837 + * - start of externally defined variables => line 11816 * * * ============================== EXAMPLE =================================== From 61307265ac7091339c2a5eccf389db5f7058b0e4 Mon Sep 17 00:00:00 2001 From: Requiem Date: Thu, 27 Nov 2025 00:02:09 +0100 Subject: [PATCH 14/17] style: changed struct name from ::Entry to ::thread_entry --- src/vmaware.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index bd376f94..86e27bf5 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1817,10 +1817,10 @@ struct VM { }; // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible - struct Entry { + struct thread_entry { u32 hash; u32 threads; - constexpr Entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} + constexpr thread_entry(const char* m, u32 t) : hash(ConstexprHash::get(m)), threads(t) {} }; enum class CpuType { @@ -1830,7 +1830,7 @@ struct VM { }; // 4 arguments to stay compliant with x64 __fastcall (just in case) - [[nodiscard]] static bool verify_thread_count(const Entry* db, size_t db_size, size_t max_model_len, CpuType type) { + [[nodiscard]] static bool verify_thread_count(const thread_entry* db, size_t db_size, size_t max_model_len, CpuType type) { // to save a few cycles struct hasher { static u32 crc32_sw(u32 crc, char data) { @@ -2522,7 +2522,7 @@ struct VM { // umap is not an option because it cannot be constexpr // constexpr is respected here even in c++ 11 and static solves stack overflow // c arrays have less construction overhead than std::array - static constexpr util::Entry thread_database[] = { + static constexpr util::thread_entry thread_database[] = { // i3 series { "i3-1000G1", 4 }, { "i3-1000G4", 4 }, @@ -3481,7 +3481,7 @@ struct VM { static constexpr size_t MAX_INTEL_MODEL_LEN = 16; - return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::Entry), MAX_INTEL_MODEL_LEN, util::CpuType::INTEL_I); + return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::thread_entry), MAX_INTEL_MODEL_LEN, util::CpuType::INTEL_I); #endif } @@ -3499,7 +3499,7 @@ struct VM { // umap is not an option because it cannot be constexpr // constexpr is respected here even in c++ 11 and static solves stack overflow // c arrays have less construction overhead than std::array - static constexpr util::Entry thread_database[] = { + static constexpr util::thread_entry thread_database[] = { // Xeon D { "D-1518", 8 }, { "D-1520", 8 }, @@ -3637,7 +3637,7 @@ struct VM { static constexpr size_t MAX_XEON_MODEL_LEN = 16; - return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::Entry), MAX_XEON_MODEL_LEN, util::CpuType::INTEL_XEON); + return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::thread_entry), MAX_XEON_MODEL_LEN, util::CpuType::INTEL_XEON); #endif } @@ -3654,7 +3654,7 @@ struct VM { #else // Database is reduced to identifying suffixes (last token of the original strings) // for example handles "ryzen 5 3600" by matching "3600", which is unique in context - static constexpr util::Entry thread_database[] = { + static constexpr util::thread_entry thread_database[] = { // 3015/3020 { "3015ce", 4 }, { "3015e", 4 }, @@ -4167,7 +4167,7 @@ struct VM { static constexpr size_t MAX_AMD_MODEL_LEN = 24; // "threadripper" is long - return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::Entry), MAX_AMD_MODEL_LEN, util::CpuType::INTEL_XEON); + return util::verify_thread_count(thread_database, sizeof(thread_database) / sizeof(util::thread_entry), MAX_AMD_MODEL_LEN, util::CpuType::INTEL_XEON); #endif } From 326d42cc39d7cca65e3fcba24a8cb7ac08b155eb Mon Sep 17 00:00:00 2001 From: Requiem Date: Thu, 27 Nov 2025 00:05:35 +0100 Subject: [PATCH 15/17] remove: code duplications --- src/vmaware.hpp | 149 ------------------------------------------------ 1 file changed, 149 deletions(-) diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 7650af68..cf495821 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -1394,155 +1394,6 @@ struct VM { } - // we want to precompute hashes at compile time for C++11 and later, so we need to match the hardware _mm_crc32_u8 - // it is based on CRC32-C (Castagnoli) polynomial - struct constexpr_hash { - static constexpr u32 crc32_bits(u32 crc, int bits) { - return (bits == 0) ? crc : - crc32_bits((crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0), bits - 1); - } - static constexpr u32 crc32_str(const char* s, u32 crc) { - return (*s == '\0') ? crc : - crc32_str(s + 1, crc32_bits(crc ^ static_cast(*s), 8)); - } - static constexpr u32 get(const char* s) { - return crc32_str(s, 0); - } - }; - - - // this forces the compiler to calculate the hash when initializing the array while staying C++11 compatible - struct thread_entry { - u32 hash; - u32 threads; - constexpr thread_entry(const char* m, u32 t) : hash(constexpr_hash::get(m)), threads(t) {} - }; - - - struct hasher { - static u32 crc32_sw(u32 crc, char data) { - crc ^= static_cast(data); - for (int i = 0; i < 8; ++i) - crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78u : 0); - return crc; - } - - #if (CLANG || GCC) - __attribute__((__target__("crc32"))) - #endif - static u32 crc32_hw(u32 crc, char data) { - return _mm_crc32_u8(crc, static_cast(data)); - } - - using hashfc = u32(*)(u32, char); - - static hashfc get() { - // yes, vmaware runs on dinosaur cpus without sse4.2 pretty often - i32 regs[4]; - cpu::cpuid(regs, 1); - const bool has_sse42 = (regs[2] & (1 << 20)) != 0; - - return has_sse42 ? crc32_hw : crc32_sw; - } - }; - - - static void lookup( - bool &found, - const char* str, - const thread_entry* thread_database, - const std::size_t& db_size, - std::size_t best_len, - const u8 max_token_length, - u32* z_series_threads, - u32& expected_threads, - const bool is_amd = false - ) { - const hasher::hashfc hash_func = util::hasher::get(); - - for (size_t i = 0; str[i] != '\0'; ) { - char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); // models have hyphen - if (!is_valid) break; - - if (current_len >= max_token_length) { - while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null - break; - } - - if (is_amd) { - // convert to lowercase on-the-fly to match compile-time keys - if (k >= 'A' && k <= 'Z') k += 32; - } - - /* - models are usually 8 or more bytes long, i.e. i9-10900K - so imagine we want to use u64, you hash the first 8 bytes i9-10900 - but then you are left with K. You have to handle the tail - fetching 8 bytes would include the characters after the token, corrupting the hash - so a byte-by-byte loop is the most optimal choice here - */ - - // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction - // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // boundary check, only verify match if the token has ended (next char is not alphanumeric) - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // Check specific Z1 Extreme token - // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 - if ( - is_amd && - (z_series_threads != nullptr) && - (current_hash == 0x3D09D5B4) - ) { - *z_series_threads = 16; - } - - // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast - for (std::size_t i = 0; i < db_size; ++i) { - if ( - (thread_database[i].hash == current_hash) && - (current_len > best_len) - ) { - best_len = current_len; - expected_threads = thread_database[i].threads; - found = true; - - // on intel, since hashing implies uniqueness in this dataset, you might say we could break - // here, but we continue to ensure we find the longest substring match if overlaps exist, - // so like it finds both "i9-11900" and "i9-11900K" i.e. - } - } - } - } - i = j; - } - } - - - // wrapper for std::make_unique because it's not available for C++11 template [[nodiscard]] static std::unique_ptr make_unique(Args&&... args) { From fa33bc6088d3a37e9f3b09ed1f5e40aac7d79413 Mon Sep 17 00:00:00 2001 From: Requiem <114197630+NotRequiem@users.noreply.github.com> Date: Thu, 27 Nov 2025 00:05:56 +0100 Subject: [PATCH 16/17] remove: temporary text file --- src/txt.txt | 199 ---------------------------------------------------- 1 file changed, 199 deletions(-) delete mode 100644 src/txt.txt diff --git a/src/txt.txt b/src/txt.txt deleted file mode 100644 index 0f68ea3a..00000000 --- a/src/txt.txt +++ /dev/null @@ -1,199 +0,0 @@ - for (size_t i = 0; str[i] != '\0'; ) { - const char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - const char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); - if (!is_valid) break; - - if (current_len >= MAX_XEON_MODEL_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null - break; - } - - /* - models are usually 8 or more bytes long, i.e. i9-10900K - so imagine we want to use u64, you hash the first 8 bytes i9-10900 - but then you are left with K. You have to handle the tail - fetching 8 bytes would include the characters after the token, corrupting the hash - so a byte-by-byte loop is the most optimal choice here - */ - - // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction - // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // only verify match if the token has ended (next char is not alphanumeric) - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast - for (const auto& entry : thread_database) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - // since hashing implies uniqueness in this dataset, you might say we could break here, - // but we continue to ensure we find the longest substring match if overlaps exist, - // so like it finds both "i9-11900" and "i9-11900K" i.e. - } - } - } - } - i = j; - } - - - - - - - - - - - - - - for (size_t i = 0; str[i] != '\0'; ) { - const char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - const char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); // models have hyphen - if (!is_valid) break; - - if (current_len >= MAX_INTEL_MODEL_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; // fast forward to space/null - break; - } - - /* - models are usually 8 or more bytes long, i.e. i9-10900K - so imagine we want to use u64, you hash the first 8 bytes i9-10900 - but then you are left with K. You have to handle the tail - fetching 8 bytes would include the characters after the token, corrupting the hash - so a byte-by-byte loop is the most optimal choice here - */ - - // since this technique is cross-platform, we cannot use a standard C++ try-catch block to catch a missing CPU instruction - // we could use preprocessor directives and add an exception handler (VEH/SEH or SIGHANDLER) but nah - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // only verify match if the token has ended (next char is not alphanumeric) - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // since it's a contiguous block of integers in .rodata/.rdata, this is extremely fast - for (const auto& entry : thread_database) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - // since hashing implies uniqueness in this dataset, you might say we could break here, - // but we continue to ensure we find the longest substring match if overlaps exist, - // so like it finds both "i9-11900" and "i9-11900K" i.e. - } - } - } - } - i = j; - } - - - - - - - - for (size_t i = 0; str[i] != '\0'; ) { - char c = str[i]; - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { - i++; - continue; - } - - u32 current_hash = 0; - size_t current_len = 0; - size_t j = i; - - while (true) { - char k = str[j]; - const bool is_valid = (k >= '0' && k <= '9') || - (k >= 'A' && k <= 'Z') || - (k >= 'a' && k <= 'z') || - (k == '-'); - if (!is_valid) break; - - if (current_len >= MAX_AMD_TOKEN_LEN) { - while (str[j] != '\0' && str[j] != ' ') j++; - break; - } - - // convert to lowercase on-the-fly to match compile-time keys - if (k >= 'A' && k <= 'Z') k += 32; - - current_hash = hash_func(current_hash, k); - current_len++; - j++; - - // boundary check - const char next = str[j]; - const bool next_is_alnum = (next >= '0' && next <= '9') || - (next >= 'A' && next <= 'Z') || - (next >= 'a' && next <= 'z'); - - if (!next_is_alnum) { - // Check specific Z1 Extreme token - // Hash for "extreme" (CRC32-C) is 0x3D09D5B4 - if (current_hash == 0x3D09D5B4) { z_series_threads = 16; } - - for (const auto& entry : db_entries) { - if (entry.hash == current_hash) { - if (current_len > best_len) { - best_len = current_len; - expected_threads = entry.threads; - found = true; - } - } - } - } - } - i = j; - } \ No newline at end of file From 5043b58b73db1766baf7a41fc37d26033b112a45 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Nov 2025 23:06:18 +0000 Subject: [PATCH 17/17] Automated update after merge --- docs/documentation.md | 176 +++++++++++++++++++++--------------------- src/vmaware.hpp | 8 +- 2 files changed, 92 insertions(+), 92 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index 19a9d5b0..5587e81f 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -499,95 +499,95 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | Flag alias | Description | Supported platforms | Certainty | Admin? | 32-bit only? | Notes | Code implementation | | ---------- | ----------- | ------------------- | --------- | ------ | ------------ | ----- | ------------------- | -| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2426) | -| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2444) | -| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2518) | -| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2549) | -| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4382) | -| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6623) | -| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4785) | -| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5635) | -| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4666) | -| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4690) | -| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4715) | -| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4733) | -| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4748) | -| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4891) | -| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4932) | -| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6919) | -| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6647) | -| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6950) | -| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6989) | -| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5646) | -| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4942) | -| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7049) | -| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2577) | -| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6680) | -| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6713) | -| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6810) | -| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6867) | -| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7147) | -| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5673) | -| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7198) | -| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7266) | -| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7321) | -| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4971) | -| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5481) | -| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5280) | -| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5299) | -| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7348) | -| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7373) | -| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7434) | -| `VM::INTEL_THREAD_MISMATCH` | Check for Intel I-series CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2665) | -| `VM::XEON_THREAD_MISMATCH` | Check for Intel Xeon CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3642) | -| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3798) | -| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7513) | -| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7569) | +| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2277) | +| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2295) | +| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2369) | +| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2400) | +| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4233) | +| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6474) | +| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4636) | +| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5486) | +| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4517) | +| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4541) | +| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4566) | +| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4584) | +| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4599) | +| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4742) | +| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4783) | +| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6770) | +| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6498) | +| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6801) | +| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6840) | +| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5497) | +| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4793) | +| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6900) | +| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2428) | +| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6531) | +| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6564) | +| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6661) | +| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6718) | +| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6998) | +| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5524) | +| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7049) | +| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7117) | +| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7172) | +| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4822) | +| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5332) | +| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5131) | +| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🪟 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5150) | +| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7199) | +| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7224) | +| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7285) | +| `VM::INTEL_THREAD_MISMATCH` | Check for Intel I-series CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2516) | +| `VM::XEON_THREAD_MISMATCH` | Check for Intel Xeon CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3493) | +| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3649) | +| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7364) | +| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7420) | | `VM::AZURE` | | | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L1) | -| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7625) | -| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7660) | -| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4987) | -| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4329) | -| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4358) | -| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5068) | -| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5097) | -| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5125) | -| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5173) | -| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5203) | -| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5257) | -| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5333) | -| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5355) | -| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5436) | -| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5466) | -| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5498) | -| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7677) | -| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7775) | -| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7994) | -| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8093) | -| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8131) | -| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5526) | -| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8227) | -| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8257) | -| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5010) | -| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8318) | -| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5827) | -| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5556) | -| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8403) | -| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5583) | -| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6251) | -| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8501) | -| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8646) | -| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8403) | -| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8870) | -| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8917) | -| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9035) | -| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6894) | -| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9127) | -| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9296) | -| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9543) | -| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9554) | -| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9755) | -| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10246) | +| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7476) | +| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7511) | +| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4838) | +| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4180) | +| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4209) | +| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4919) | +| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4948) | +| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4976) | +| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5024) | +| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5054) | +| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5108) | +| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5184) | +| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5206) | +| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5287) | +| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5317) | +| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5349) | +| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7528) | +| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7626) | +| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7845) | +| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7944) | +| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7982) | +| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5377) | +| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8078) | +| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8108) | +| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4861) | +| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8169) | +| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5678) | +| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5407) | +| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8254) | +| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5434) | +| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6102) | +| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8352) | +| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8497) | +| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8254) | +| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8721) | +| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8768) | +| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8886) | +| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6745) | +| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8978) | +| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9147) | +| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9394) | +| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9405) | +| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9606) | +| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10097) |
diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 339ef48e..c2979d08 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -56,10 +56,10 @@ * - struct for internal cpu operations => line 716 * - struct for internal memoization => line 1159 * - struct for internal utility functions => line 1289 - * - struct for internal core components => line 10344 - * - start of VM detection technique list => line 2421 - * - start of public VM detection functions => line 10837 - * - start of externally defined variables => line 11816 + * - struct for internal core components => line 10195 + * - start of VM detection technique list => line 2272 + * - start of public VM detection functions => line 10688 + * - start of externally defined variables => line 11667 * * * ============================== EXAMPLE ===================================