Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
The library is:
- Very easy to use
- Cross-platform (Windows + MacOS + Linux)
- Features up to 115+ unique VM detection techniques [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#flag-table)]
- Features up to 115 unique VM detection techniques [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#flag-table)]
- Features the most cutting-edge techniques
- Able to detect 65+ VM brands including VMware, VirtualBox, QEMU, Hyper-V, and much more [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#brand-table)]
- Able to beat VM hardeners
Expand Down Expand Up @@ -239,7 +239,7 @@ You can view the full docs [here](docs/documentation.md). All the details such a

> I would've made it strictly MIT so proprietary software can make use of the library, but some of the techniques employed are from GPL projects, and I have no choice but to use the same license for legal reasons.
>
> This gave me an idea to make an MIT version without all of the GPL code so it can also be used without forcing your code to be open source. It should be noted that the MIT version removes <b>7</b> techniques out of 116 (as of 2.0 version), and the lesser the number of techniques, the less accurate the overall result might be.
> This gave me an idea to make an MIT version without all of the GPL code so it can also be used without forcing your code to be open source. It should be noted that the MIT version removes <b>7</b> techniques out of 115 (as of 2.0 version), and the lesser the number of techniques, the less accurate the overall result might be.

</details>

Expand Down
3 changes: 1 addition & 2 deletions docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,6 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | MacOS | 100% | | | | |
| `VM::MAC_SIP` | Check if System Integrity Protection is disabled (likely a VM if it is) | MacOS | 40% | | | | |
| `VM::HKLM_REGISTRIES` | Check HKLM registries for specific VM strings | Windows | 25% | | | | |
| `VM::QEMU_GA` | Check for "qemu-ga" process | Linux | 10% | | | | |
| `VM::VPC_INVALID` | Check for official VPC method | Windows | 75% | | | 32-bit | |
| `VM::SIDT` | Check for sidt instruction method | Windows | 25% | | | | |
| `VM::SGDT` | Check for sgdt instruction method | Windows | 30% | | | 32-bit | |
Expand Down Expand Up @@ -514,7 +513,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
| `VM::ANYRUN_DIRECTORY` | Check for any.run directory and handle the status code | Windows | 35% | | | | | Removed from the lib, only available in the CLI |
| `VM::DRIVER_NAMES` | Check for VM-specific names for drivers | Windows | 100% | | | | |
| `VM::VM_SIDT` | Check for unknown IDT base address | Windows | 100% | | | | |
| `VM::HDD_SERIAL` | Check for serial numbers of virtual disks | Windows | 100% | | | | |
| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | Windows | 100% | | | | |
| `VM::PORT_CONNECTORS` | Check for physical connection ports | Windows | 25% | | | | This technique is known to false flag on devices like Surface Pro |
| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | Windows | 100% | Admin | | | Admin only needed for some heuristics |
| `VM::GPU_VM_STRINGS` | Check for specific GPU string signatures related to VMs | Windows | 100% | | | | If GPU_CAPABILITIES also flags, the overall score will be 50 instead of 100 |
Expand Down
2 changes: 1 addition & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
|------|---------|
| `cli.cpp` | Entire CLI tool code |
| `vmaware.hpp` | Official and original library header in GPL-3.0, most likely what you're looking for. |
| `vmaware_MIT.hpp` | Same as above but in MIT. But this removes 7 techniques out of 116 |
| `vmaware_MIT.hpp` | Same as above but in MIT. But this removes 7 techniques out of 115 |

<br>

Expand Down
7 changes: 3 additions & 4 deletions src/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,9 @@ bool is_unsupported(VM::enum_flags flag) {
case VM::SIDT5:
case VM::DISK_SIZE:
case VM::VBOX_DEFAULT:
case VM::VM_PROCESSES:
case VM::LINUX_USER_HOST:
case VM::BOCHS_CPU:
case VM::QEMU_GA:
case VM::SIDT:
case VM::VMWARE_IOMEM:
case VM::VMWARE_IOPORTS:
Expand Down Expand Up @@ -451,7 +451,7 @@ bool is_unsupported(VM::enum_flags flag) {
case VM::KGT_SIGNATURE:
case VM::DRIVER_NAMES:
case VM::VM_SIDT:
case VM::HDD_SERIAL:
case VM::DISK_SERIAL:
case VM::PORT_CONNECTORS:
case VM::GPU_VM_STRINGS:
case VM::GPU_CAPABILITIES:
Expand Down Expand Up @@ -907,7 +907,6 @@ void general() {
checker(VM::KVM_DIRS, "KVM directories");
checker(VM::HKLM_REGISTRIES, "registry values");
checker(VM::AUDIO, "audio device");
checker(VM::QEMU_GA, "qemu-ga process");
checker(VM::QEMU_DIR, "QEMU directories");
checker(VM::VPC_INVALID, "VPC invalid instructions");
checker(VM::SIDT, "SIDT");
Expand Down Expand Up @@ -957,7 +956,7 @@ void general() {
checker(anyrun_directory, "ANY.RUN directory");
checker(VM::DRIVER_NAMES, "driver names");
checker(VM::VM_SIDT, "VM SIDT");
checker(VM::HDD_SERIAL, "HDD serial number");
checker(VM::DISK_SERIAL, "disk serial number");
checker(VM::PORT_CONNECTORS, "physical connection ports");
checker(VM::GPU_CAPABILITIES, "GPU capabilities");
checker(VM::GPU_VM_STRINGS, "GPU strings");
Expand Down
118 changes: 59 additions & 59 deletions src/vmaware.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,6 @@ struct VM {
IOREG_GREP,
MAC_SIP,
HKLM_REGISTRIES,
QEMU_GA,
VPC_INVALID,
SIDT,
SGDT,
Expand Down Expand Up @@ -636,7 +635,7 @@ struct VM {
WSL_PROC,
DRIVER_NAMES,
VM_SIDT,
HDD_SERIAL,
DISK_SERIAL,
PORT_CONNECTORS,
GPU_VM_STRINGS,
GPU_CAPABILITIES,
Expand Down Expand Up @@ -1743,7 +1742,7 @@ struct VM {
#endif
}

// get available memory space
// Get available memory space
[[nodiscard]] static u64 get_memory_space() {
#if (WINDOWS)
MEMORYSTATUSEX statex = { 0 };
Expand Down Expand Up @@ -3655,9 +3654,7 @@ struct VM {
* @implements VM::VM_PROCESSES
*/
[[nodiscard]] static bool vm_processes() {
#if (!WINDOWS)
return false;
#else
#if (WINDOWS)
const auto runningProcesses = util::get_running_process_names();

if (runningProcesses.count("joeboxserver.exe") || runningProcesses.count("joeboxcontrol.exe")) {
Expand All @@ -3670,7 +3667,7 @@ struct VM {
return core::add(brands::PARALLELS);
}

if (runningProcesses.count("vboxservice.exe") || runningProcesses.count("vboxtray.exe")) {
if (runningProcesses.count("vboxservice.exe") || runningProcesses.count("vboxtray.exe") || runningProcesses.count("VBoxControl.exe")) {
debug("VM_PROCESSES: Detected VBox process.");
return core::add(brands::VBOX);
}
Expand All @@ -3694,13 +3691,23 @@ struct VM {

if (runningProcesses.count("vdagent.exe") ||
runningProcesses.count("vdservice.exe") ||
runningProcesses.count("qemuwmi.exe")) {
runningProcesses.count("qemuwmi.exe") ||
runningProcesses.count("looking-glass-host.exe")) {
debug("VM_PROCESSES: Detected QEMU process.");
return core::add(brands::QEMU);
}

if (runningProcesses.count("VDDSysTray.exe")) {
return true;
}

#elif (LINUX)
if (util::is_proc_running("qemu_ga")) {
debug("VM_PROCESSES: Detected QEMU guest agent process.");
return core::add(brands::QEMU);
}
#endif
return false;
#endif
}


Expand Down Expand Up @@ -4332,26 +4339,6 @@ struct VM {
}


/**
* @brief Check for "qemu-ga" process
* @category Linux
* @implements VM::QEMU_GA
*/
[[nodiscard]] static bool qemu_ga() {
#if (!LINUX)
return false;
#else
constexpr const char* process = "qemu-ga";

if (util::is_proc_running(process)) {
return core::add(brands::QEMU);
}

return false;
#endif
}


/**
* @brief Check for official VPC method
* @category Windows, x86
Expand Down Expand Up @@ -7398,9 +7385,9 @@ struct VM {
* @category Windows
* @author Requiem (https://github.com/NotRequiem)
* @note VMware can't be flagged without also flagging legitimate devices
* @implements VM::HDD_SERIAL
* @implements VM::DISK_SERIAL
*/
[[nodiscard]] static bool hdd_serial_number() {
[[nodiscard]] static bool disk_serial_number() {
#if (!WINDOWS)
return false;
#else
Expand Down Expand Up @@ -7849,10 +7836,12 @@ struct VM {
* @implements VM::TIMER
*/
#if defined(MSVC)
#pragma optimize("", off)
#elif defined(__GNUC__)
#pragma GCC push_options
#pragma GCC optimize("O0")
#pragma optimize("", off)
#elif defined(CLANG)
#pragma clang optimize off
#elif defined(GCC)
#pragma GCC push_options
#pragma GCC optimize("O0")
#endif
[[nodiscard]]
#if (LINUX)
Expand All @@ -7862,17 +7851,21 @@ struct VM {
#if (ARM || !x86)
return false;
#else
constexpr u8 classicIterations = 10; // Number of iterations for the classic RDTSC check
constexpr u16 classicThreshold = 20000u; // Cycle threshold per iteration for classic RDTSC check
constexpr u8 requiredClassicSpikes = classicIterations / 2; // At least 50% of iterations must spike
if (util::hyper_x() == HYPERV_ARTIFACT_VM) {
return false;
}

constexpr u8 rdtscIterations = 10; // Number of iterations for the classic RDTSC check
constexpr u16 rdtscThreshold = 6000; // Cycle threshold per iteration for classic RDTSC check
constexpr u8 requiredClassicSpikes = rdtscIterations / 2; // At least 50% of iterations must spike

constexpr u16 spammerIterations = 1000; // Iterations for the multi-CPU/spammer check
constexpr u16 spammerAvgThreshold = 20000u; // Average cycle threshold for the spammer check
constexpr u16 spammerAvgThreshold = 1500; // Average cycle threshold for the spammer check

#if (WINDOWS)
constexpr u16 qpcRatioThreshold = 3000; // QPC ratio threshold
constexpr u16 qpcRatioThreshold = 70; // QPC ratio threshold
constexpr u8 tscIterations = 10; // Number of iterations for the TSC synchronization check
constexpr u16 tscSyncDiffThreshold = 500; // TSC difference threshold
constexpr u16 tscSyncDiffThreshold = 5000; // TSC difference threshold
#endif

// to minimize context switching/scheduling
Expand Down Expand Up @@ -7903,7 +7896,7 @@ struct VM {
#else
sched_setscheduler(0, oldPolicy, &oldParam);
#endif
};
};

// --- 1. Classic Timing Check (rdtsc + cpuid + rdtsc) ---
#ifdef __VMAWARE_DEBUG__
Expand All @@ -7920,8 +7913,10 @@ struct VM {
#endif

#if (WINDOWS)
bool notaligned = false;
flushBuffer = (char*)_aligned_malloc(kBufferSize, kAlignment);
if (!flushBuffer) {
notaligned = true;
flushBuffer = new (std::nothrow) char[kBufferSize];
}
#elif (LINUX || APPLE)
Expand All @@ -7937,7 +7932,7 @@ struct VM {
constexpr size_t segmentsCount = 8; // basically 1/8 of the buffer per iteration
constexpr size_t segmentSize = kBufferSize / segmentsCount;
int spikeCount = 0;
for (int i = 0; i < classicIterations; i++) {
for (int i = 0; i < rdtscIterations; i++) {
u64 start = __rdtsc();
#if (WINDOWS)
int cpu_info[4];
Expand All @@ -7953,7 +7948,7 @@ struct VM {
#ifdef __VMAWARE_DEBUG__
totalCycles += cycles;
#endif
if (cycles >= classicThreshold) {
if (cycles >= rdtscThreshold) {
spikeCount++;
}
// Instead of flushing the entire buffer every iteration (which would decrease performance a lot),
Expand All @@ -7963,25 +7958,30 @@ struct VM {
size_t offsetEnd = offsetStart + segmentSize;

// this detection works better when inducing cache flushing without thread sleeps
for (size_t j = offsetStart; j < offsetEnd; j += 64) {
flushBuffer[j] = static_cast<char>(j);
if (flushBuffer) {
for (size_t j = offsetStart; j < offsetEnd; j += 64) {
flushBuffer[j] = static_cast<char>(j);
#if defined(x86) && (defined(GCC) || defined(CLANG) || defined(MSVC))
COMPILER_BARRIER();
// _mm_clflushopt not available on some systems
_mm_clflush(reinterpret_cast<const void*>(&flushBuffer[j]));
COMPILER_BARRIER();
// _mm_clflushopt not available on some systems
_mm_clflush(reinterpret_cast<const void*>(&flushBuffer[j]));
#endif
}
}
}
#if (WINDOWS)
_aligned_free((void*)flushBuffer);
if (notaligned)
delete[] flushBuffer;
else
_aligned_free((void*)flushBuffer);
#else
free((void*)flushBuffer);
#endif

#ifdef __VMAWARE_DEBUG__
const double averageCycles = static_cast<double>(totalCycles) / classicIterations;
const double averageCycles = static_cast<double>(totalCycles) / rdtscIterations;
debug("TIMER: RDTSC check - Average cycles: ", averageCycles,
" (Threshold per sample: ", classicThreshold,
" (Threshold per sample: ", rdtscThreshold,
") - Spike count: ", spikeCount);
#endif

Expand Down Expand Up @@ -8189,9 +8189,11 @@ struct VM {
#endif
}
#if defined(MSVC)
#pragma optimize("", on)
#elif defined(__GNUC__)
#pragma GCC pop_options
#pragma optimize("", on)
#elif defined(CLANG)
#pragma clang optimize on
#elif defined(GCC)
#pragma GCC pop_options
#endif


Expand Down Expand Up @@ -11290,7 +11292,6 @@ struct VM {
case IOREG_GREP: return "IOREG_GREP";
case MAC_SIP: return "MAC_SIP";
case HKLM_REGISTRIES: return "HKLM_REGISTRIES";
case QEMU_GA: return "QEMU_GA";
case VPC_INVALID: return "VPC_INVALID";
case SIDT: return "SIDT";
case SGDT: return "SGDT";
Expand Down Expand Up @@ -11337,7 +11338,7 @@ struct VM {
case WSL_PROC: return "WSL_PROC";
case DRIVER_NAMES: return "DRIVER_NAMES";
case VM_SIDT: return "VM_SIDT";
case HDD_SERIAL: return "HDD_SERIAL";
case DISK_SERIAL: return "DISK_SERIAL";
case PORT_CONNECTORS: return "PORT_CONNECTORS";
case GPU_VM_STRINGS: return "GPU_STRINGS";
case GPU_CAPABILITIES: return "GPU_CAPABILITIES";
Expand Down Expand Up @@ -11850,7 +11851,6 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
std::make_pair(VM::IOREG_GREP, VM::core::technique(100, VM::ioreg_grep)),
std::make_pair(VM::MAC_SIP, VM::core::technique(40, VM::mac_sip)),
std::make_pair(VM::HKLM_REGISTRIES, VM::core::technique(25, VM::hklm_registries)),
std::make_pair(VM::QEMU_GA, VM::core::technique(10, VM::qemu_ga)),
std::make_pair(VM::VPC_INVALID, VM::core::technique(75, VM::vpc_invalid)),
std::make_pair(VM::SIDT, VM::core::technique(25, VM::sidt)),
std::make_pair(VM::SGDT, VM::core::technique(30, VM::sgdt)),
Expand Down Expand Up @@ -11898,7 +11898,7 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
std::make_pair(VM::WSL_PROC, VM::core::technique(30, VM::wsl_proc_subdir)),
std::make_pair(VM::DRIVER_NAMES, VM::core::technique(100, VM::driver_names)),
std::make_pair(VM::VM_SIDT, VM::core::technique(100, VM::vm_sidt)),
std::make_pair(VM::HDD_SERIAL, VM::core::technique(100, VM::hdd_serial_number)),
std::make_pair(VM::DISK_SERIAL, VM::core::technique(100, VM::disk_serial_number)),
std::make_pair(VM::PORT_CONNECTORS, VM::core::technique(25, VM::port_connectors)),
std::make_pair(VM::GPU_VM_STRINGS, VM::core::technique(100, VM::gpu_vm_strings)),
std::make_pair(VM::GPU_CAPABILITIES, VM::core::technique(100, VM::gpu_capabilities)),
Expand Down
Loading