diff --git a/CMakeLists.txt b/CMakeLists.txt index 84f303d..fe4f665 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,10 +91,12 @@ add_test(executable, "${BUILD_DIR}/${TARGET}") # release stuff if (NOT MSVC) if(CMAKE_BUILD_TYPE MATCHES "Release") - install(TARGETS ${TARGET} DESTINATION ${CMAKE_SOURCE_DIR}) + install(TARGETS ${TARGET} DESTINATION /usr/local/bin) install(FILES "src/vmaware.hpp" DESTINATION /usr/include) + else() + install(TARGETS ${TARGET} DESTINATION ${CMAKE_SOURCE_DIR}) endif() elseif(MSVC) set(CMAKE_INSTALL_PREFIX "C:\\Program Files\\YourApplication") install(TARGETS ${TARGET} DESTINATION .) -endif() +endif() \ No newline at end of file diff --git a/TODO.md b/TODO.md index 453c5d1..52cd355 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,3 @@ -- [ ] revise sidt check - [ ] analyse the UUID check technique's efficiency - [X] fix c++11 debug function param error - [X] completely remove std::system() grep commands @@ -20,7 +19,8 @@ - [X] add a python script to automatically set the lines of the seperate sections in the header - [ ] add C++20 concepts for the VM::add_custom() function - [ ] check for valid monitor technique - +- [ ] fix the is_admin code for windows +- [ ] test it on compiler explorer with windows 32-bit settings # Distant plans - add ARM support diff --git a/assets/demo.png b/assets/demo.png index fb8682d..18d6304 100644 Binary files a/assets/demo.png and b/assets/demo.png differ diff --git a/docs/documentation.md b/docs/documentation.md index 9b9588d..4035888 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -230,7 +230,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | `VM::HYPERVISOR_BIT` | Check if the hypervisor bit is set (always false on physical CPUs) | Yes | 100% | | | |`VM::CPUID_0X4` | Check if there are any leaf values between 0x40000000 and 0x400000FF that changes the CPUID output | Yes | 70% | | | | `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::RDTSC` | Benchmark RDTSC and evaluate its speed, usually it's very slow in VMs (WARNING: this may return a false positive if you use sanitizers like libasan) | Linux and Windows | 20% | | | | `VM::SIDT5` | Check if the 5th byte after sidt is null | Linux | 45% | | | | `VM::THREADCOUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings (nowadays physical CPUs should have at least 4 threads for modern CPUs) | Yes | 35% | | | | `VM::MAC` | Check if the system's MAC address matches with preset values for certain VMs | Linux and Windows | 90% | | | @@ -265,7 +265,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | Windows | 40% | | | | `VM::VMID_0X4` | Check if the CPU manufacturer ID matches that of a VM brand with leaf 0x40000000 | Yes | 100% | | | | `VM::PARALLELS_VM` | Check for indications of Parallels VM | Windows | 50% | | | -| `VM::RDTSC_VMEXIT` | Check for RDTSC technique with VMEXIT | Yes | 50% | | | +| `VM::RDTSC_VMEXIT` | Check for RDTSC technique with VMEXIT (WARNING: this may return a false positive if you use sanitizers like libasan) | Yes | 50% | | | | `VM::LOADED_DLLS` | Check for DLLs of multiple VM brands | Windows | 75% | | GPL | | `VM::QEMU_BRAND` | Check for QEMU CPU brand with cpuid | Yes | 100% | | | | `VM::BOCHS_CPU` | Check for Bochs cpuid emulation oversights | Yes | 95% | | | diff --git a/papers/www.s21sec.com_vmware-eng.pdf b/papers/www.s21sec.com_vmware-eng.pdf new file mode 100644 index 0000000..1e1de77 Binary files /dev/null and b/papers/www.s21sec.com_vmware-eng.pdf differ diff --git a/src/vmaware.hpp b/src/vmaware.hpp index e24f50b..dcd79b5 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -751,7 +751,7 @@ struct VM { #endif // self-explanatory - [[nodiscard]] static bool is_root() noexcept { + [[nodiscard]] static bool is_admin() noexcept { #if (LINUX || APPLE) const uid_t uid = getuid(); const uid_t euid = geteuid(); @@ -759,7 +759,7 @@ struct VM { return ( (uid != euid) || (euid == 0) - ); + ); #elif (MSVC) BOOL is_admin = FALSE; HANDLE hToken = NULL; @@ -948,7 +948,7 @@ struct VM { // get physical RAM size in GB [[nodiscard]] static u64 get_physical_ram_size() { #if (LINUX) - if (!util::is_root()) { + if (!util::is_admin()) { debug("private get_physical_ram_size function: ", "not root, returned 0"); return 0; } @@ -2020,8 +2020,8 @@ struct VM { * @category Linux */ [[nodiscard]] static bool dmidecode() try { - if (core::disabled(DMIDECODE) || (util::is_root() == false)) { - debug("DMIDECODE: ", "precondition return called (root = ", util::is_root(), ")"); + if (core::disabled(DMIDECODE) || (util::is_admin() == false)) { + debug("DMIDECODE: ", "precondition return called (root = ", util::is_admin(), ")"); return false; } @@ -2069,7 +2069,7 @@ struct VM { * @category Linux */ [[nodiscard]] static bool dmesg() try { - if (core::disabled(DMESG) || !util::is_root()) { + if (core::disabled(DMESG) || !util::is_admin()) { return false; } @@ -3347,7 +3347,7 @@ struct VM { #if (!LINUX) return false; #else - if (util::is_root()) { + if (util::is_admin()) { return false; } @@ -4721,7 +4721,7 @@ struct VM { ctx->Eip += 4; // skip past the "call VPC" opcodes return static_cast(EXCEPTION_CONTINUE_EXECUTION); // we can safely resume execution since we skipped faulty instruction - }; + }; __try { __asm { @@ -4782,7 +4782,7 @@ struct VM { return false; #elif (LINUX) - if (util::is_root()) { + if (util::is_admin()) { return false; } @@ -5280,13 +5280,17 @@ struct VM { * @note idea from ScoopyNG by Tobias Klein */ [[nodiscard]] static bool vmware_dmesg() try { - if (core::disabled(VMWARE_DMESG) || !util::is_root()) { + if (core::disabled(VMWARE_DMESG)) { return false; } #if (!LINUX) return false; #else + if (!util::is_admin()) { + return false; + } + auto dmesg_output = util::sys_result("dmesg"); const std::string dmesg = *dmesg_output; @@ -5314,6 +5318,7 @@ struct VM { /** * @brief Check using str assembly instruction * @note Alfredo Omella's (S21sec) STR technique + * @note paper describing this technique is located at /papers/www.s21sec.com_vmware-eng.pdf (2006) * @category Windows */ [[nodiscard]] static bool vmware_str() { @@ -5324,7 +5329,7 @@ struct VM { #if (!x86) return false; #elif (defined(_WIN32) && defined(__i386__)) - unsigned char mem[4] = {0, 0, 0, 0}; + unsigned char mem[4] = {0, 0, 0, 0}; __asm str mem; diff --git a/src/vmaware_MIT.hpp b/src/vmaware_MIT.hpp index a43893e..8594e58 100644 --- a/src/vmaware_MIT.hpp +++ b/src/vmaware_MIT.hpp @@ -728,7 +728,7 @@ struct VM { #endif // self-explanatory - [[nodiscard]] static bool is_root() noexcept { + [[nodiscard]] static bool is_admin() noexcept { #if (LINUX || APPLE) const uid_t uid = getuid(); const uid_t euid = geteuid(); @@ -925,7 +925,7 @@ struct VM { // get physical RAM size in GB [[nodiscard]] static u64 get_physical_ram_size() { #if (LINUX) - if (!util::is_root()) { + if (!util::is_admin()) { debug("private get_physical_ram_size function: ", "not root, returned 0"); return 0; } @@ -1997,8 +1997,8 @@ struct VM { * @category Linux */ [[nodiscard]] static bool dmidecode() try { - if (core::disabled(DMIDECODE) || (util::is_root() == false)) { - debug("DMIDECODE: ", "precondition return called (root = ", util::is_root(), ")"); + if (core::disabled(DMIDECODE) || (util::is_admin() == false)) { + debug("DMIDECODE: ", "precondition return called (root = ", util::is_admin(), ")"); return false; } @@ -2046,7 +2046,7 @@ struct VM { * @category Linux */ [[nodiscard]] static bool dmesg() try { - if (core::disabled(DMESG) || !util::is_root()) { + if (core::disabled(DMESG) || !util::is_admin()) { return false; } @@ -2853,7 +2853,7 @@ struct VM { #if (!LINUX) return false; #else - if (util::is_root()) { + if (util::is_admin()) { return false; }