diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4472657..ea88cae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,7 +56,7 @@ To add your own function, follow the format below: return false; } - #if (!MSVC) // This is a filter in case your function only works for a specific platform. There are many macros such as LINUX, MSVC, APPLE, and x86. It's also case sensitive, so don't make any typos! + #if (!MSVC) // This is a filter in case your function only works for a specific platform. There are many macros such as "LINUX", "MSVC", "APPLE", and "x86". It's also case sensitive, so don't make any typos! return false; #else diff --git a/README.md b/README.md index 3cad8ae..d5ea358 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,15 @@ **VMAware** (not to be confused with VMware) is an open-source, cross-platform, and incredibly simple C++ library for virtual machine detection. -It utilises a comprehensive list of low-level and high-level anti-VM techniques that gets accounted in a scoring system. The library is meant to be stupidly easy to use, designed for anybody wanting to integrate the library to their project. +It utilises a comprehensive list of low-level and high-level anti-VM techniques that gets accounted in a scoring system. The library is meant to be stupidly easy to use, designed for anybody wanting to integrate the library to their project without a hassle. The library is: -- Very easy to use, with only 3 functions in its public interface -- Very flexible, with total fine-grained control -- Cross-platform **(NOTE: MACOS AND MSVC ARE NOT EFFECTIVE FOR NOW)** +- Very easy to use, with only 4 functions in its public interface +- Very flexible, with total fine-grained control over what gets executed +- Cross-platform **(NOTE: MACOS AND WINDOWS ARE NOT EFFECTIVE FOR NOW)** - Header-only - Available with C++11 and above +- Features up to 50+ techniques - Able to detect VMware, VirtualBox, QEMU, KVM, Parallels, and much more - Able to detect semi-VM technologies like hypervisors, docker, and wine - Able to guess the VM brand @@ -26,8 +27,11 @@ The library is: **IMPORTANT:** The library is currently a beta, so more improvements and cross-compatibility fixes are planned (especially for Windows which I'm currently working on improving). I don't recommend using this for any serious projects for now. +Also, this library doesn't guarantee it'll be accurate. If you found a false negative then please create an issue with information on what your VM is, what OS you're using, and what . + - - - +
## Example ๐Ÿงช ```cpp @@ -38,18 +42,21 @@ int main() { if (VM::detect()) { std::cout << "Virtual machine detected!" << std::endl; std::cout << "VM name: " << VM::brand() << std::endl; + std::cout << "VM certainty: " << VM::percentage() << "%" << std::endl; } else { std::cout << "Running in baremetal" << std::endl; } } ``` +
## CLI tool ๐Ÿ”ง This project also provides a tiny, but handy CLI tool utilising the full potential of what the library can do. Also, running the CLI as root would give better results. +
## Installation ๐Ÿ“ฅ To install the library, download or copy paste the `vmaware.hpp` file in the [release section](https://github.com/kernelwernel/VMAware/releases/) to your project. No CMake or shared object linkages are necessary, it's literally that simple. @@ -74,10 +81,12 @@ cmake -S . -B build/ -G "Visual Studio 16 2019" ``` > NOTE: I'm most likely going to change my username in the future. If the github link doesn't exist, search for the VMAware project and you should find it. +
## Documentation ๐Ÿ“’ You can view the full docs [here](docs/documentation.md). Trust me, it's not too intimidating. +
## Q&A โ“ - Who is this library for? @@ -90,11 +99,12 @@ You can view the full docs [here](docs/documentation.md). Trust me, it's not too > Yes. There are some techniques that are trivially spoofable, and there's nothing the library can do about it whether it's a deliberate false negative or even a false positive. This is a problem that every VM detection project is facing, which is why the library is trying to test every technique possible to get the best result based on the environment it's running under. - Can I use this for malware? -> This project is not soliciting the development of malware for any malicious intentions. Even if you intend to use it that way, it'll most likely be flagged by antiviruses anyway. +> This project is not soliciting the development of malware for obvious reasons. Even if you intend to use it for concealment purposes, it'll most likely be flagged by antiviruses anyway. - When will a 1.0 be available? > Pretty soon, maybe around january 2024 (I just started university, so I can't guarantee anything) +
## Issues and pull requests ๐Ÿ“ฌ If you have any suggestions, ideas, or any sort of contribution, feel free to ask! I'll be more than happy to discuss. If you want to personally ask something in private, my discord is `kr.nl` @@ -103,6 +113,7 @@ Contribution guidelines can be found [here](CONTRIBUTING.md). If you found this project useful, a star would be appreciated :) +
## Credits โœ’๏ธ - [Check Point Research](https://research.checkpoint.com/) @@ -112,7 +123,9 @@ If you found this project useful, a star would be appreciated :) - [Matteo Malvica](https://www.matteomalvica.com) - N. Rin, EP_X0FF - [Peter Ferrie, Symantec](https://github.com/peterferrie) +- [Graham Sutherland, LRQA Nettitude](https://www.nettitude.com/uk/) +
## Legal ๐Ÿ“œ I am not responsible nor liable for any damage you cause through any malicious usage of this project. diff --git a/TODO.md b/TODO.md index fe8b53c..836c589 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,18 @@ - [ ] revise sidt check - [ ] analyse the UUID check technique's efficiency -- [ ] fix c++11 debug function param error +- [X] fix c++11 debug function param error - [ ] completely remove std::system() grep commands - [ ] add github metrics for license (looks cool af) -- [ ] add CTest before alpha release +- [X] add CTest before alpha release - [ ] maybe add a threadpool - [ ] test for processor type and stuff like that for cpuid EAX=1 - [ ] grep everywhere for QEMU shit -- [ ] +- [ ] implement VM::EXTREME flag +- [ ] update the cli tool image in the readme +- [X] focus on hyperv detection (https://labs.nettitude.com/blog/vm-detection-tricks-part-3-hyper-v-raw-network-protocol/) +- [X] add percentage overload functionality for VM::detect() +- [ ] maybe document the project in codeproject.com when a 1.0 is ready + # Distant plans - add ARM support diff --git a/docs/documentation.md b/docs/documentation.md index 50236ec..c045ca1 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -1,9 +1,11 @@ # Documentation # `VM::detect()` -This is basically the only thing you need, which returns a bool. If the parameter is set to default, all the recommended checks will be performed. But you can optionally set what techniques are used: +This is basically the main function you're looking for, which returns a bool. If the parameter is set to nothing, all the recommended checks will be performed. But you can optionally set what techniques are used. ```cpp +#include "vmaware.hpp" + int main() { /** * The basic way to detect a VM where most checks will be @@ -16,7 +18,8 @@ int main() { * Essentially means only the brand, MAC, and hypervisor bit techniques * should be performed. Note that the less flags you provide, the more * likely the result will not be accurate. If you just want to check for - * a single technique, use VM::check() instead. + * a single technique, use VM::check() instead. Also, read the flag table + * at the end of this doc file for a full list of technique flags. */ bool is_vm2 = VM::detect(VM::BRAND | VM::MAC | VM::HYPERV_BIT); @@ -37,13 +40,22 @@ int main() { * Keep in mind that this could take a performance hit. */ bool is_vm4 = VM::detect(VM::ALL | VM::NO_MEMO); + + + /** + * If you want to treat any technique that was detected as positive, + * you can enable the VM::EXTREME flag which will return true if any + * technique has detected a hit despite the certainty score. This is + * not recommended for obvious reasons. + */ + bool is_vm5 = VM::detect(VM::EXTREME); } ```
# `VM::brand()` -This will essentially return the VM brand as a `std::string`. The brand string return values are: +This will essentially return the VM brand as a `std::string`. The exact possible brand string return values are: - `VMware` - `VirtualBox` - `KVM` @@ -70,9 +82,12 @@ This will essentially return the VM brand as a `std::string`. The brand string r - `Bochs` -If none were detected, it will return `Unknown`. It's often NOT going to produce a satisfying result due to technical difficulties with accomplishing this, on top of being highly dependant on what mechanisms detected a VM. Don't rely on this function for critical operations as if it's your golden bullet. 75% of the time it'll simply return `Unknown`. +If none were detected, it will return `Unknown`. It's often NOT going to produce a satisfying result due to technical difficulties with accomplishing this, on top of being highly dependent on what mechanisms detected a VM. Don't rely on this function for critical operations as if it's your golden bullet. Roughly 75% of the time it'll simply return `Unknown`. ```cpp +#include "vmaware.hpp" +#include + int main() { const std::string result = VM::brand(); @@ -94,16 +109,48 @@ This takes a single flag argument and returns a `bool`. It's essentially the sam `VM::detect()` is meant for a range of techniques to be evaluated in the bigger picture with weights and biases in its scoring system, while `VM::check()` is meant for a single technique to be evaluated without any points or anything extra. It just gives you what the technique has found on its own. For example: ```cpp -if (VM::check(VM::VMID)) { - std::cout << "VMID technique detected a VM!\n"; -} +#include "vmaware.hpp" +#include + +int main() { + if (VM::check(VM::VMID)) { + std::cout << "VMID technique detected a VM!\n"; + } + + if (VM::check(VM::HYPERVISOR_BIT)) { + std::cout << "Hypervisor bit is set, most definitely a VM!\n"; + } -if (VM::check(VM::HYPERV_BIT)) { - std::cout << "Hypervisor bit is set, most definitely a VM!\n"; + // invalid, will throw an std::invalid_argument exception + bool result = VM::check(VM::VMID | VM::HYPERVISOR_BIT; } +``` + +
+ +# `VM::percentage()` +This will return a std::uint8_t between 0 and 100. It'll return the certainty of whether it has detected a VM based on all the techniques available as a percentage. The lower the value, the less chance it's a VM. The higher the value, the more likely chance it is. The parameters are treated the exact same way with the VM::detect() function. + +```cpp +#include "vmaware.hpp" +#include +#include -// invalid -bool result = VM::check(VM::SIDT | VM::RDTSC); +int main() { + // uint8_t and unsigned char works too + const std::uint8_t percent = VM::percentage(); + + if (percent == 100) { + std::cout << "Definitely a VM!\n"; + } else if (percent == 0) { + std::cout << "Definitely NOT a VM"; + } else { + std::cout << "Unsure if it's a VM"; + } + + // converted to std::uint32_t for console character encoding reasons + std::cout << "percentage: " << static_cast(percent) << "%\n"; +} ```
@@ -116,9 +163,9 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | ---------- | ----------- | --------------- | --------- | -------------- | | `VM::VMID` | Check if the CPU manufacturer ID matches that of a VM brand | Yes | 100% | | | `VM::BRAND` | Check if the CPU brand string contains any indications of VM keywords | Yes | 50% | | -| `VM::HYPERV_BIT` | Check if the hypervisor bit is set (always false on physical CPUs) | Yes | 95% | | +| `VM::HYPERVISOR_BIT` | Check if the hypervisor bit is set (always false on physical CPUs) | Yes | 95% | | |`VM::CPUID_0X4` | Check if there are any leaf values between 0x40000000 and 0x400000FF that changes the CPUID output | Yes | 70% | | -| `VM::HYPERV_STR` | Check if brand string length is long enough (would be around 2 characters in a host machine while it's longer in a hypervisor) | Yes | 45% | | +| `VM::HYPERVISOR_STR` | Check if brand string length is long enough (would be around 2 characters in a host machine while it's longer in a hypervisor) | Yes | 45% | | | `VM::RDTSC` | Benchmark RDTSC and evaluate its speed, usually it's very slow in VMs | Linux and Windows | 20% | | | `VM::SIDT` | Check if SIDT instructions does anything to the interrupt descriptor table | Linux | 65% | | | `VM::SIDT5` | Check if the 5th byte after sidt is null | Linux | 45% | | @@ -169,5 +216,6 @@ VMAware provides a convenient way to not only check for VMs, but also have the f # Non-technique flags | Flag | Description | |------|-------------| -| `VM::ALL` | This will enable all the technique flags, including the cursor check. | +| `VM::ALL` | This will enable all the technique flags, including the cursor check that's disabled by default. | | `VM::NO_MEMO` | This will disable memoization, meaning the result will not be fetched through a previous computation of the VM::detect() function. Note that this can take a performance hit. | +| `VM::EXTREME` | This will disregard the weights/biases and its scoring system. It will essentially treat any technique that found a hit as a VM detection no matter how low that technique's certainty is, so if a single technique is positive then it will return true. | \ No newline at end of file diff --git a/papers/attacks_on_VMs.pdf b/papers/attacks_on_VMs.pdf new file mode 100644 index 0000000..feb3d52 Binary files /dev/null and b/papers/attacks_on_VMs.pdf differ diff --git a/papers/vmde.pdf b/papers/vmde.pdf new file mode 100644 index 0000000..295df40 Binary files /dev/null and b/papers/vmde.pdf differ diff --git a/resources/attacks_on_VMs.pdf b/resources/attacks_on_VMs.pdf new file mode 100644 index 0000000..feb3d52 Binary files /dev/null and b/resources/attacks_on_VMs.pdf differ diff --git a/src/cli.cpp b/src/cli.cpp index f534056..12996e7 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #if (defined(__GNUC__) || defined(__linux__)) #include @@ -12,7 +13,10 @@ constexpr const char* date = "September 2023"; constexpr const char* bold = "\033[1m"; constexpr const char* ansi_exit = "\x1B[0m"; constexpr const char* red = "\x1B[38;2;239;75;75m"; +constexpr const char* orange = "\x1B[38;2;255;180;5m"; constexpr const char* green = "\x1B[38;2;94;214;114m"; +constexpr const char* red_orange = "\x1B[38;2;247;127;40m"; +constexpr const char* green_orange = "\x1B[38;2;174;197;59m"; void help(void) { std::cout << @@ -42,7 +46,7 @@ int main(int argc, char* argv[]) { const std::string not_detected = ("[" + std::string(red) + "NOT DETECTED" + std::string(ansi_exit) + "]"); const std::string note = ("[ NOTE ]"); - auto checker = [=](const std::uint64_t flag, const char* message) -> void { + auto checker = [&](const std::uint64_t flag, const char* message) -> void { std::cout << (VM::check(flag) ? detected : not_detected) << " Checking " << message << "...\n"; }; @@ -62,9 +66,9 @@ int main(int argc, char* argv[]) { checker(VM::VMID, "VMID"); checker(VM::BRAND, "CPU brand"); - checker(VM::HYPERV_BIT, "CPUID hypervisor bit"); + checker(VM::HYPERVISOR_BIT, "CPUID hypervisor bit"); checker(VM::CPUID_0X4, "CPUID 0x4 leaf"); - checker(VM::HYPERV_STR, "hypervisor brand"); + checker(VM::HYPERVISOR_STR, "hypervisor brand"); checker(VM::RDTSC, "RDTSC"); checker(VM::SIDT, "sidt"); checker(VM::SIDT5, "sidt null byte"); @@ -100,7 +104,7 @@ int main(int argc, char* argv[]) { checker(VM::LINUX_USER_HOST, "default Linux user/host"); checker(VM::VBOX_WINDOW_CLASS, "VBox window class"); checker(VM::GAMARUE, "gamarue ransomware technique"); - checker(VM::WINDOWS_NUMBER, "Windows number"); + checker(VM::WMIC, "WMIC outputs"); checker(VM::VMID_0X4, "0x4 leaf of VMID"); checker(VM::VPC_BACKDOOR, "VPC backdoor"); checker(VM::PARALLELS_VM, "Parallels techniques"); @@ -109,18 +113,71 @@ int main(int argc, char* argv[]) { checker(VM::QEMU_BRAND, "QEMU CPU brand"); checker(VM::BOCHS_CPU, "BOCHS CPU techniques"); checker(VM::VPC_BOARD, "VirtualPC motherboard"); + checker(VM::BIOS_SERIAL, "BIOS serial number"); std::printf("\n"); - std::cout << "VM brand: " << (std::string(VM::brand()) == "Unknown" ? red : green) << VM::brand() << ansi_exit << "\n\n"; + std::cout << "VM brand: " << (std::string(VM::brand()) == "Unknown" ? red : green) << VM::brand() << ansi_exit << "\n"; - const std::string baremetal = (std::string(red) + "Running in baremetal " + std::string(ansi_exit)); - const std::string vmachine = (std::string(green) + "Running inside a VM " + std::string(ansi_exit)); + const char* percent_color = ""; + std::uint8_t percent = VM::percentage(); + + if (percent < 20) { + percent_color = red; + } else if (percent > 75) { + percent_color = green; + } else { + percent_color = orange; + } + + std::cout << "VM certainty: " << percent_color << static_cast(VM::percentage()) << "%" << ansi_exit << "\n"; + + const bool is_detected = VM::detect(); + + std::cout << "VM confirmation: " << (is_detected ? green : red) << std::boolalpha << is_detected << std::noboolalpha << ansi_exit << "\n\n"; + + const char* conclusion_color = ""; + const char* conclusion_message = ""; + + constexpr const char* baremetal = "Running in baremetal"; + constexpr const char* very_unlikely = "Very unlikely a VM"; + constexpr const char* unlikely = "Unlikely a VM"; + constexpr const char* potentially = "Potentially a VM"; + constexpr const char* might = "Might be a VM"; + constexpr const char* likely = "Likely a VM"; + constexpr const char* very_likely = "Very likely a VM"; + constexpr const char* inside_vm = "Running inside a VM"; + + if (percent == 0) { + conclusion_color = red; + conclusion_message = baremetal; + } else if (percent <= 12) { + conclusion_color = red; + conclusion_message = very_unlikely; + } else if (percent <= 25) { + conclusion_color = red_orange; + conclusion_message = unlikely; + } else if (percent < 50) { // not <= on purpose + conclusion_color = red_orange; + conclusion_message = potentially; + } else if (percent <= 62) { + conclusion_color = orange; + conclusion_message = might; + } else if (percent <= 75) { + conclusion_color = green_orange; + conclusion_message = likely; + } else if (percent < 100) { + conclusion_color = green_orange; + conclusion_message = very_likely; + } else if (percent == 100) { + conclusion_color = green; + conclusion_message = inside_vm; + } std::cout << bold << "====== CONCLUSION: " << ansi_exit - << (VM::detect() ? vmachine : baremetal) + << conclusion_color << conclusion_message << " " << ansi_exit << bold << "======" << ansi_exit @@ -134,7 +191,7 @@ int main(int argc, char* argv[]) { }; if (cmp(arg, "-s") || cmp(arg, "--stdout")) { - return (!VM::detect()); + return (!VM::detect(VM::NO_MEMO)); } else if (cmp(arg, "-h") || cmp(arg, "--help")) { help(); return 0; diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 87d9740..8e4abf5 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -128,109 +128,115 @@ struct VM { using u16 = std::uint16_t; using u32 = std::uint32_t; using u64 = std::uint64_t; + using i8 = std::int8_t; + using i16 = std::int16_t; using i32 = std::int32_t; using i64 = std::int64_t; - #if (LINUX) - // fetch file data - [[nodiscard]] static std::string read_file(const char* dir) { - std::ifstream file{}; - std::string data{}; - file.open(dir); - if (file.is_open()) { - file >> data; - } - file.close(); - return data; - }; + // macro for bypassing unused parameter warnings + #define UNUSED(x) ((void)(x)) - // Basically std::system but it runs in the background with no output - [[nodiscard]] static std::unique_ptr sys_result(const char *cmd) try { - #if (CPP < 14) - std::unique_ptr tmp(nullptr); - return tmp; - #else - #if (LINUX) - std::unique_ptr pipe(popen(cmd, "r"), pclose); + // Basically std::system but it runs in the background with std::string output + [[nodiscard]] static std::unique_ptr sys_result(const char *cmd) try { + #if (CPP < 14) + std::unique_ptr tmp(nullptr); + UNUSED(cmd); + return tmp; + #else + #if (LINUX) + std::unique_ptr pipe(popen(cmd, "r"), pclose); - if (!pipe) { - return nullptr; - } + if (!pipe) { + return nullptr; + } - std::string result{}; - std::array buffer{}; + std::string result{}; + std::array buffer{}; - while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { - result += buffer.data(); - } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } - result.pop_back(); - - return std::make_unique(result); - #elif (MSVC) - // Set up the structures for creating the process - STARTUPINFO si; - PROCESS_INFORMATION pi; - ZeroMemory(&si, sizeof(si)); - ZeroMemory(&pi, sizeof(pi)); - si.cb = sizeof(si); - - // Create a pipe to capture the command output - HANDLE hReadPipe, hWritePipe; - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) { - #if __VMAWARE_DEBUG__ - debug("sys_result: ", "error creating pipe"); - #endif - return nullptr; - } + result.pop_back(); - // Set up the startup information with the write end of the pipe as the standard output - si.hStdError = hWritePipe; - si.hStdOutput = hWritePipe; - si.dwFlags |= STARTF_USESTDHANDLES; + return std::make_unique(result); + #elif (MSVC) + // Set up the structures for creating the process + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + si.cb = sizeof(si); + + // Create a pipe to capture the command output + HANDLE hReadPipe, hWritePipe; + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) { + #if __VMAWARE_DEBUG__ + debug("sys_result: ", "error creating pipe"); + #endif + return nullptr; + } - // Create the process - if (!CreateProcess(NULL, const_cast(command), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { - #if __VMAWARE_DEBUG__ - debug("sys_result: ", "error creating process"); - #endif - CloseHandle(hReadPipe); - CloseHandle(hWritePipe); - return nullptr; - } + // Set up the startup information with the write end of the pipe as the standard output + si.hStdError = hWritePipe; + si.hStdOutput = hWritePipe; + si.dwFlags |= STARTF_USESTDHANDLES; - // Close the write end of the pipe as it's not needed in this process + // Create the process + if (!CreateProcess(NULL, const_cast(command), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { + #if __VMAWARE_DEBUG__ + debug("sys_result: ", "error creating process"); + #endif + CloseHandle(hReadPipe); CloseHandle(hWritePipe); + return nullptr; + } - // Read the output from the pipe - char buffer[4096]; - DWORD bytesRead; - std::string result; + // Close the write end of the pipe as it's not needed in this process + CloseHandle(hWritePipe); - while (ReadFile(hReadPipe, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead > 0) { - result.append(buffer, bytesRead); - } + // Read the output from the pipe + char buffer[4096]; + DWORD bytesRead; + std::string result; - // Close handles - CloseHandle(hReadPipe); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); + while (ReadFile(hReadPipe, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead > 0) { + result.append(buffer, bytesRead); + } - // Return the result as a unique_ptr - return std::make_unique(result); - #endif - #endif - } catch (...) { - #if __VMAWARE_DEBUG__ - debug("sys_result: ", "catched error, returning nullptr"); + // Close handles + CloseHandle(hReadPipe); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + // Return the result as a unique_ptr + return std::make_unique(result); #endif - std::unique_ptr tmp(nullptr); - return tmp; + #endif + } catch (...) { + #if __VMAWARE_DEBUG__ + debug("sys_result: ", "catched error, returning nullptr"); + #endif + std::unique_ptr tmp(nullptr); + return tmp; + } + + #if (LINUX) + // fetch file data + [[nodiscard]] static std::string read_file(const char* dir) { + std::ifstream file{}; + std::string data{}; + file.open(dir); + if (file.is_open()) { + file >> data; + } + file.close(); + return data; } #endif @@ -296,12 +302,12 @@ struct VM { // cpuid leaf values struct leaf { static constexpr u32 - hyperv = 0x40000000, func_ext = 0x80000000, proc_ext = 0x80000001, brand1 = 0x80000002, brand2 = 0x80000003, brand3 = 0x80000004, + hypervisor = 0x40000000, amd_easter_egg = 0x8fffffff; }; @@ -405,7 +411,7 @@ struct VM { #elif (LINUX) [[gnu::const]] #endif - [[nodiscard]] static inline bool add(const char* p_brand) noexcept { + static inline bool add(const char* p_brand) noexcept { scoreboard[p_brand]++; return true; } @@ -751,8 +757,15 @@ struct VM { }; #endif + // memoization structure + struct memo_struct { + bool get_vm; + const char* get_brand; + u8 get_percent; + }; + // memoize the value from VM::detect() in case it's ran again - static std::map> memo; + static std::map memo; // cpuid check value static bool cpuid_supported; @@ -779,6 +792,22 @@ struct VM { return (!(flags & p_flag)); } + // same as above but for checking enabled flags + #if (LINUX && __has_cpp_attribute(gnu::pure)) + [[gnu::pure]] + #endif + [[nodiscard]] static inline bool enabled(const u64 p_flag) noexcept { + return (flags & p_flag); + } + + // easier way to check if the result is memoized + [[nodiscard]] static inline bool is_memoized() noexcept { + return ( + disabled(NO_MEMO) && \ + memo.find(true) != memo.end() + ); + } + public: VM() = delete; // Delete default constructor VM(const VM&) = delete; // Delete copy constructor @@ -787,9 +816,9 @@ struct VM { static constexpr u64 VMID = 1 << 0, BRAND = 1 << 1, - HYPERV_BIT = 1 << 2, + HYPERVISOR_BIT = 1 << 2, CPUID_0X4 = 1 << 3, - HYPERV_STR = 1 << 4, + HYPERVISOR_STR = 1 << 4, RDTSC = 1 << 5, SIDT = 1 << 6, VMWARE_PORT = 1 << 7, @@ -825,7 +854,7 @@ struct VM { LINUX_USER_HOST = 1ULL << 37, VBOX_WINDOW_CLASS = 1ULL << 38, GAMARUE = 1ULL << 39, - WINDOWS_NUMBER = 1ULL << 40, + WMIC = 1ULL << 40, VMID_0X4 = 1ULL << 41, VPC_BACKDOOR = 1ULL << 42, PARALLELS_VM = 1ULL << 43, @@ -834,10 +863,13 @@ struct VM { QEMU_BRAND = 1ULL << 46, BOCHS_CPU = 1ULL << 47, VPC_BOARD = 1ULL << 48, + HYPERV_WMI = 1ULL << 49, + HYPERV_REG = 1ULL << 50, + BIOS_SERIAL = 1ULL << 51, // __UNIQUE_LABEL, ADD YOUR UNIQUE FUNCTION FLAG VALUE ABOVE HERE - + EXTREME = 1ULL << 62, NO_MEMO = 1ULL << 63, #if (MSVC) @@ -917,7 +949,6 @@ struct VM { #else #if (CPP < 17) // bypass compiler warning about unused parameter, ignore this - #define UNUSED(x) ((void)(x)) UNUSED(technique_name); #endif #endif @@ -1059,13 +1090,12 @@ struct VM { const auto regex = std::regex(vmkeywords.at(i), std::regex::icase); const bool match = std::regex_search(brand, regex); - #ifdef __VMAWARE_DEBUG__ - if (match) { + if (match) { + #ifdef __VMAWARE_DEBUG__ debug("BRAND_KEYWORDS: ", "match = ", vmkeywords.at(i)); - } - #endif - - match_count += match; + #endif + match_count++; + } } #ifdef __VMAWARE_DEBUG__ @@ -1105,7 +1135,9 @@ struct VM { #else std::string brand = get_cpu_brand(); - if (brand == "QEMU Virtual CPU") { + std::regex pattern("QEMU Virtual CPU", std::regex_constants::icase); + + if (std::regex_match(brand, pattern)) { return add(QEMU); } @@ -1123,8 +1155,8 @@ struct VM { * @brief Check if hypervisor feature bit in CPUID is enabled (always false for physical CPUs) * @category x86 */ - [[nodiscard]] static bool cpuid_hyperv() try { - if (!cpuid_supported || disabled(HYPERV_BIT)) { + [[nodiscard]] static bool hypervisor_bit() try { + if (!cpuid_supported || disabled(HYPERVISOR_BIT)) { return false; } @@ -1139,7 +1171,7 @@ struct VM { #endif } catch (...) { #ifdef __VMAWARE_DEBUG__ - debug("HYPERV_BIT: catched error, returned false"); + debug("HYPERVISOR_BIT: catched error, returned false"); #endif return false; } @@ -1161,7 +1193,7 @@ struct VM { u32 a, b, c, d = 0; for (u8 i = 0; i < 0xFF; i++) { - cpuid(a, b, c, d, (leaf::hyperv + i)); + cpuid(a, b, c, d, (leaf::hypervisor + i)); if ((a + b + c + d) != 0) { return true; } @@ -1181,8 +1213,8 @@ struct VM { * @brief Check for hypervisor brand string length (would be around 2 characters in a host machine) * @category x86 */ - [[nodiscard]] static bool hyperv_brand() try { - if (disabled(HYPERV_STR)) { + [[nodiscard]] static bool hypervisor_brand() try { + if (disabled(HYPERVISOR_STR)) { return false; } @@ -1190,7 +1222,7 @@ struct VM { return false; #else char out[sizeof(i32) * 4 + 1] = { 0 }; // e*x size + number of e*x registers + null terminator - cpuid((int*)out, leaf::hyperv); + cpuid((int*)out, leaf::hypervisor); #ifdef __VMAWARE_DEBUG__ debug("HYPERV_STR: eax: ", static_cast(out[0]), @@ -1204,7 +1236,7 @@ struct VM { #endif } catch (...) { #ifdef __VMAWARE_DEBUG__ - debug("HYPERV_STR: catched error, returned false"); + debug("HYPERVISOR_STR: catched error, returned false"); #endif return false; } @@ -3012,47 +3044,91 @@ struct VM { /** - * @brief get top-level default window level - * @category Windows + * @brief match WMIC output for + * + * */ - [[nodiscard]] static bool windows_number() try { - return false; // TODO: fix this garbage code - /* - if (disabled(WINDOWS_NUMBER)) { + [[nodiscard]] static bool wmic() try { + if (disabled(WMIC)) { return false; } - #if (!MSVC) + #if (!MSVC) return false; #else - // this definitely doesn't fucking work - auto enumProc = [](HWND, LPARAM lParam) -> bool - { - if (LPDWORD pCnt = reinterpret_cast(lParam)) - *pCnt++; - return true; - }; + std::unique_ptr manufacturer = sys_result("WMIC COMPUTERSYSTEM GET MANUFACTURER"); + + if (*manufacturer == "VirtualBox") { + return add(VBOX); + } - DWORD winCnt = 0; + std::unique_ptr model = sys_result("WMIC COMPUTERSYSTEM GET MODEL"); + + constexpr std::array vmkeywords { + "qemu", "kvm", "virtual", "vm", + "vbox", "virtualbox", "vmm", "monitor", + "bhyve", "hyperv", "hypervisor", "hvisor", + "parallels", "vmware", "hvm", "qnx" + }; - if (!EnumWindows(enumProc,LPARAM(&winCnt))) { - #ifdef __VMAWARE_DEBUG__ - debug("WINDOWS_NUMBER: EnumWindows() failed"); - #endif - return false; + for (std::size_t i = 0; i < vmkeywords.size(); i++) { + const auto regex = std::regex(vmkeywords.at(i), std::regex::icase); + const bool match = std::regex_search(*model, regex); + + if (match) { + #ifdef __VMAWARE_DEBUG__ + debug("WMIC: ", "match = ", vmkeywords.at(i)); + #endif + return true; + } } - return (winCnt < 10); + return false; #endif - */ } catch (...) { #ifdef __VMAWARE_DEBUG__ - debug("WINDOWS_NUMBER: catched error, returned false"); + debug("WMIC: catched error, returned false"); #endif return false; } + /** + * @brief Check if the BIOS serial is valid + * @category Windows + */ + [[nodiscard]] static bool bios_serial() try { + if (disabled(BIOS_SERIAL)) { + return false; + } + + #if (!MSVC) + return false; + #else + std::unique_ptr bios = sys_result("wmic bios get serialnumber"); + + const std::size_t nl_pos = bios->find('\n'); + std::string extract = bios->substr(nl_pos + 1); + + const bool all_digits = std::all_of(extract.cbegin(), extract.cend(), [](const char c) { + return std::isdigit(c); + }); + + if (all_digits) { + if (extract == "0") { + return true; + } + } + + return false; + #endif + } catch (...) { + #ifdef __VMAWARE_DEBUG__ + debug("BIOS_SERIAL: catched error, returned false"); + #endif + return false; + } + /** * @brief Check for semi-documented Virtual PC detection method using illegal instructions * @category Windows x86 @@ -3102,45 +3178,6 @@ struct VM { } // The exception block shouldn't get triggered if VPC is running __except(VPCExceptionHandler(GetExceptionInformation())) { } -/* - - // ========== TEST 2 (query virtual pc device) ========== - if (is_vm == false) { - is_vm = (IsObjectExists(DEVICELINK, DEVICE_VIRTUALPC)); - } - - // ========== TEST 3 (query virtual pc driver, reg. rights elevation) ========== - if (is_vm == false ) { - is_vm = (IsObjectExists(DRIVERLINK, DRIVER_VIRTUALPC)); - } - - // ========== TEST 4 (scan raw firmware for specific string patterns) ========== - if (is_vm == false) { - pSIF = (PSYSTEM_FIRMWARE_TABLE_INFORMATION)GetFirmwareTable(&dwDataSize, FIRM, - 0xC0000); - if (pSIF != NULL && dwDataSize > 0) { - is_vm = ScanDump((CHAR*)pSIF, dwDataSize, VENDOR_VPC, _strlenA(VENDOR_VPC)); - - RtlFreeHeap(RtlProcessHeap(), 0, pSIF); - } - } - - // ========== TEST 5 (scan raw smbios data for specific string patters) ========== - if (is_vm == false) { - pSIF = (PSYSTEM_FIRMWARE_TABLE_INFORMATION)GetFirmwareTable(&dwDataSize, RSMB, 0); - if (pSIF != NULL && dwDataSize > 0) { - is_vm = ScanDump((CHAR*)pSIF, dwDataSize, SMB_VPC, _strlenA(SMB_VPC)); - RtlFreeHeap(RtlProcessHeap(), 0, pSIF); - } - } - - // ========== TEST 6 (query S3 VID on PCI bus devices) ========== - if (is_vm == false) { - if (vIsInList(VID_S3MS) != NULL) { - IsVM = true; - } - } - */ if (is_vm == true) { return add(VPC); @@ -3393,13 +3430,16 @@ struct VM { return add(BOCHS); } - // technique 3: Check for AMD easter egg for K7 and K8 CPUs + // technique 3: Check for absence of AMD easter egg for K7 and K8 CPUs u32 unused, eax = 0; cpuid(eax, unused, unused, unused, 1); + constexpr u8 AMD_K7 = 6; + constexpr u8 AMD_K8 = 15; + const u32 family = ((eax >> 8) & 0xF); - if (family != 6 && family != 15) { // AMD K7 = 6, AMD K8 = 15 + if (family != AMD_K7 && family != AMD_K8) { return false; } @@ -3588,6 +3628,229 @@ struct VM { } + /** + * @brief get WMI query for HYPERV name + * @category Windows + * @note idea is from nettitude + * @link https://labs.nettitude.com/blog/vm-detection-tricks-part-3-hyper-v-raw-network-protocol/ + */ + [[nodiscard]] static bool hyperv_wmi() try { + if (disabled(HYPERV_WMI)) { + return false; + } + + #if (!MSVC) + return false; + #else + HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: Failed to initialize COM library. Error code = ", hres); + #endif + return false; + } + + hres = CoInitializeSecurity( + NULL, + -1, // COM authentication + NULL, // Authentication services + NULL, // Reserved + RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication + RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation + NULL, // Authentication info + EOAC_NONE, // Additional capabilities + NULL // Reserved + ); + + if (FAILED(hres)) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: Failed to initialize security. Error code = ", hres); + #endif + CoUninitialize(); + return false; + } + + // Connect to WMI + IWbemLocator *pLoc = NULL; + hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc); + + if (FAILED(hres)) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: Failed to create IWbemLocator object. Error code = ", hres); + #endif + CoUninitialize(); + return false; + } + + IWbemServices *pSvc = NULL; + hres = pLoc->ConnectServer( + _bstr_t(L"\\\\.\\root\\CIMV2"), // Object path of WMI namespace + NULL, // User name. NULL = current user + NULL, // User password. NULL = current + 0, // Locale. NULL indicates current + NULL, // Security flags. + 0, // Authority (e.g. Kerberos) + 0, // Context object + &pSvc // pointer to IWbemServices proxy + ); + + if (FAILED(hres)) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: Could not connect. Error code = ", hres); + #endif + pLoc->Release(); + CoUninitialize(); + return false; + } + + hres = CoSetProxyBlanket( + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + NULL, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + NULL, // client identity + EOAC_NONE // proxy capabilities + ); + + if (FAILED(hres)) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: Could not set proxy blanket. Error code = ", hres); + #endif + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return false; + } + + IEnumWbemClassObject* pEnumerator = NULL; + hres = pSvc->ExecQuery( + _bstr_t(L"WQL"), + _bstr_t(L"SELECT * FROM Win32_NetworkProtocol"), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &pEnumerator + ); + + if (FAILED(hres)) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: ExecQuery failed. Error code = ", hres); + #endif + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return false; + } + + IWbemClassObject* pclsObj = NULL; + ULONG uReturn = 0; + bool is_vm = false; + + while (pEnumerator) { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if (uReturn == 0 || FAILED(hr)) { + break; + } + + VARIANT vtProp; + hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0); + + if (!FAILED(hr)) { + if (vtProp.vt == VT_BSTR) { + is_vm = (wcscmp(vtProp.bstrVal, L"Hyper-V RAW") == 0); + } + } + + VariantClear(&vtProp); + pclsObj->Release(); + pclsObj = NULL; + } + + pSvc->Release(); + pLoc->Release(); + pEnumerator->Release(); + CoUninitialize(); + + return is_vm; + #endif + } catch (...) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: ", "catched error, returned false"); + #endif + return false; + } + + + /** + * @brief compare for hyperv-specific string in registry + * @category Windows + * @note idea is from nettitude + * @link https://labs.nettitude.com/blog/vm-detection-tricks-part-3-hyper-v-raw-network-protocol/ + */ + [[nodiscard]] static bool hyperv_registry() try { + if (disabled(HYPERV_REG)) { + return false; + } + + #if (!MSVC) + return false; + #else + constexpr const char* registryPath = "SYSTEM\\CurrentControlSet\\Services\\WinSock2\\Parameters\\Protocol_Catalog9\\Catalog_Entries"; + + HKEY hKey; + LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryPath, 0, KEY_READ, &hKey); + + if (result != ERROR_SUCCESS) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: Error opening registry key. Code: ", result); + #endif + return false; + } + + bool is_vm = false; + + DWORD index = 0; + char subkeyName[256]; + DWORD subkeyNameSize = sizeof(subkeyName) / sizeof(subkeyName[0]); + + while (RegEnumKeyEx(hKey, index++, subkeyName, &subkeyNameSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { + HKEY subkey; + result = RegOpenKeyEx(hKey, subkeyName, 0, KEY_READ, &subkey); + + if (result == ERROR_SUCCESS) { + wchar_t protocolName[256]; + DWORD dataSize = sizeof(protocolName); + + // Check if the "ProtocolName" value exists + result = RegQueryValueEx(subkey, "ProtocolName", NULL, NULL, reinterpret_cast(protocolName), &dataSize); + + if (result == ERROR_SUCCESS) { + if (wcscmp(protocolName, L"Hyper-V RAW") == 0) { + is_vm = true; + break; + } + } + + RegCloseKey(subkey); + } + + subkeyNameSize = sizeof(subkeyName) / sizeof(subkeyName[0]); + } + + RegCloseKey(hKey); + + return is_vm; + #endif + } catch (...) { + #ifdef __VMAWARE_DEBUG__ + debug("HYPERV_WMI: ", "catched error, returned false"); + #endif + return false; + } + + // __TECHNIQUE_LABEL, label for adding techniques above this point @@ -3662,68 +3925,63 @@ struct VM { /** * @brief Fetch the VM brand + * @param void * @return std::string * @returns VMware, VirtualBox, KVM, bhyve, QEMU, Microsoft Hyper-V, Microsoft x86-to-ARM, Parallels, Xen HVM, ACRN, QNX hypervisor, Hybrid Analysis, Sandboxie, Docker, Wine, Virtual Apple, Virtual PC, Unknown * @link https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#vmbrand */ [[nodiscard]] static std::string brand(void) { - // check if result hasn't been memoized already - if (memo.find(true) == memo.end()) { - #ifdef __VMAWARE_DEBUG__ - debug("memoization: detect() called in brand"); - #endif + if (is_memoized() == false) { detect(); } - // check if no VM was detected - if (memo[true].first == false) { - return "Unknown"; - } - - return (std::string(memo[true].second)); + memo_struct &tmp = memo[true]; + return tmp.get_brand; } /** * @brief Detect if running inside a VM - * @param u64 (any combination of flags in VM wrapper, can be optional) + * @param std::uint64_t (any combination of flags in VM wrapper, can be optional) * @return bool * @link https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#vmdetect */ static bool detect(const u64 p_flags = DEFAULT) { + VM::flags = p_flags; + /** * load memoized value if it exists from a previous * execution of VM::detect(). This can save around * 5~10x speed depending on the circumstances. */ - if ( - disabled(NO_MEMO) && \ - memo.find(true) != memo.end() - ) { + if (is_memoized()) { #ifdef __VMAWARE_DEBUG__ debug("memoization: returned cached result in detect()"); #endif - return memo[true].first; + memo_struct &lol = memo[true]; + return (lol.get_vm); } - // set local variables within struct scope - VM::flags = p_flags; - - u8 points = 0; + u16 points = 0; #ifdef __VMAWARE_DEBUG__ debug("cpuid: is supported? : ", VM::cpuid_supported); #endif - + for (auto it = table.cbegin(); it != table.cend(); ++it) { const technique &pair = it->second; if (pair.ptr()) { // equivalent to std::invoke, not used bc of C++11 compatibility points += pair.points; - }; + } } - // threshold score - const bool result = (points >= 100); + bool result = false; + + if (enabled(EXTREME)) { + result = (points > 0); + } else { + result = (points >= 100); + } const char* current_brand = ""; @@ -3776,13 +4034,98 @@ struct VM { #endif // memoize the result in case VM::detect() is executed again - if (disabled(NO_MEMO)) { - memo[true].first = result; - memo[true].second = current_brand; + if (is_memoized() == false) { + u8 percent = 0; + + if (points > 100) { + percent = 100; + } else { + percent = static_cast(points); + } + + memo_struct tmp = { + result, // is vm? + current_brand, // brand + percent // self-explanatory + }; + + memo[true] = tmp; } return result; } + + + /** + * @brief Get the percentage of how likely it's a VM + * @param std::uint64_t (any combination of flags, can be optional) + * @return std::uint8_t + * @link https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#vmpercentage + */ + static u8 percentage(const u64 p_flags = DEFAULT) { + VM::flags = p_flags; + + if (is_memoized()) { + #ifdef __VMAWARE_DEBUG__ + debug("memoization: ", "returned cached result in VM::percentage()"); + #endif + + memo_struct &tmp = memo[true]; + return tmp.get_percent; + } + + u16 points = 0; + + for (auto it = table.cbegin(); it != table.cend(); ++it) { + const technique &pair = it->second; + if (pair.ptr()) { // equivalent to std::invoke, not used bc of C++11 compatibility + points += pair.points; + } + } + + u8 percent = 0; + + if (points > 100) { + percent = 100; + } else { + percent = static_cast(points); + } + + if (disabled(NO_MEMO)) { + #ifdef __VMAWARE_DEBUG__ + debug("memoization: ", "cached result to VM::percentage()"); + #endif + const bool result = (points >= 100); + const char* current_brand = ""; + + #if (MSVC) + int max = 0; + #else + u8 max = 0; + #endif + + for (auto it = scoreboard.cbegin(); it != scoreboard.cend(); ++it) { + if (it->second > max) { + current_brand = it->first; + max = it->second; + } + } + + if (max == 0) { + current_brand = "Unknown"; + } + + memo_struct tmp = { + result, // is vm? + current_brand, // brand + percent // percent + }; + + memo[true] = tmp; + } + + return percent; + } }; @@ -3819,11 +4162,11 @@ struct VM { { VM::COMODO, 0 }, { VM::SUNBELT, 0 }, { VM::BOCHS, 0 } -}; +}; VM::u64 VM::flags = 0; -std::map> VM::memo; +std::map VM::memo; bool VM::cpuid_supported = []() -> bool { @@ -3838,7 +4181,7 @@ bool VM::cpuid_supported = []() -> bool { #elif (MSVC) i32 info[4]; __cpuid(info, 0); - return (info[0] >= 1); + return (info[0] > 0); #elif (LINUX) u32 ext = 0; return (__get_cpuid_max(ext, nullptr) > 0); @@ -3851,9 +4194,9 @@ bool VM::cpuid_supported = []() -> bool { const std::map VM::table = { { VM::VMID, { 100, VM::vmid }}, { VM::BRAND, { 50, VM::cpu_brand }}, - { VM::HYPERV_BIT, { 95, VM::cpuid_hyperv }}, + { VM::HYPERVISOR_BIT, { 95, VM::hypervisor_bit }}, { VM::CPUID_0X4, { 70, VM::cpuid_0x4 }}, - { VM::HYPERV_STR, { 45, VM::hyperv_brand }}, + { VM::HYPERVISOR_STR, { 45, VM::hypervisor_brand }}, { VM::RDTSC, { 20, VM::rdtsc_check }}, { VM::SIDT, { 65, VM::sidt_check }}, { VM::VMWARE_PORT, { 80, VM::vmware_port }}, @@ -3889,7 +4232,7 @@ const std::map VM::table = { { VM::LINUX_USER_HOST, { 35, VM::linux_user_host }}, { VM::VBOX_WINDOW_CLASS, { 10, VM::vbox_window_class }}, { VM::GAMARUE, { 40, VM::gamarue }}, - { VM::WINDOWS_NUMBER, { 20, VM::windows_number }}, + { VM::WMIC, { 20, VM::wmic }}, { VM::VMID_0X4, { 90, VM::vmid_0x4 }}, { VM::VPC_BACKDOOR, { 70, VM::vpc_backdoor }}, { VM::PARALLELS_VM, { 50, VM::parallels }}, @@ -3897,7 +4240,10 @@ const std::map VM::table = { { VM::LOADED_DLLS, { 75, VM::loaded_dlls }}, { VM::QEMU_BRAND, { 100, VM::cpu_brand_qemu }}, { VM::BOCHS_CPU, { 95, VM::bochs_cpu }}, - { VM::VPC_BOARD, { 20, VM::vpc_board }} + { VM::VPC_BOARD, { 20, VM::vpc_board }}, + { VM::HYPERV_WMI, { 80, VM::hyperv_wmi }}, + { VM::HYPERV_REG, { 80, VM::hyperv_registry }}, + { VM::BIOS_SERIAL, { 60, VM::bios_serial }}, // __TABLE_LABEL, add your technique above // { VM::YOUR_FUNCTION, { POINTS, FUNCTION POINTER }}