How to Debug VMAware When It Incorrectly Detects or Misses Virtual Machines
Enable the __VMAWARE_DEBUG__ macro to trace technique execution, use VM::detect() with a custom flagset to isolate specific checks, inspect VM::memo::cache_fetch() for cached results, and force a full scan by disabling the shortcut flag when VMAware returns unexpected boolean results.
VMAware is a comprehensive C++ library that detects virtual machines by aggregating scores from dozens of hardware and software checks in src/vmaware.hpp. When the library reports false positives on bare metal or misses a hypervisor entirely, you need targeted debugging strategies to identify which techniques are misbehaving. This guide walks through precise debugging steps using the actual source code implementation in kernelwernel/vmaware.
Understanding VMAware's Detection Scoring System
VMAware operates on a point-based accumulation model defined in core::run_all() (lines 11733-11802). The library iterates through a table of technique functions, accumulates points for positive detections, and compares the total against a threshold.
Default thresholds:
- 150 points: Standard detection threshold
- 300 points: High-threshold mode when
VM::HIGH_THRESHOLDis set (lines 11736-11743)
The main entry point VM::detect() builds a std::bitset of enabled techniques via core::arg_handler() (lines 12081-12089), then executes checks until the accumulated score meets the current threshold. If the threshold is never reached, the function falls back to a hardened-environment check at lines 12112-12133.
Enabling Internal Debug Output
The fastest way to identify which techniques fire is compiling with the __VMAWARE_DEBUG__ macro. This activates the debug_msg helper function (lines 434-442), which prints a unique one-line message the first time each technique runs, showing the contributing points and detected brand.
Compilation flags:
# GCC/Clang
g++ -std=c++20 -DDEBUG your_file.cpp
# MSVC (automatically defined in Debug builds)
cl /DDEBUG your_file.cpp
When enabled, the debug stream outputs lines like:
[DEBUG] CPU: GenuineIntel
[DEBUG] Hypervisor bit: Microsoft Hv
[DEBUG] VM detection points: 172
These messages are emitted only once per technique thanks to the internal printed_messages set tracked at lines 434-472.
Isolating Specific Techniques
When a particular technique produces noise or false positives, isolate it using the VM::detect() overload that accepts a custom flagset. This eliminates interaction effects from other checks.
Step-by-step isolation:
#include "vmaware.hpp"
#include <iostream>
int main() {
// Generate a flagset with all techniques enabled
VM::flagset fs = VM::core::generate_all();
// Disable everything except the suspect technique
fs.reset();
fs.set(VM::HYPERVISOR_BIT); // Test only the hypervisor bit check
bool result = VM::detect(fs);
std::cout << "Hypervisor bit technique returned: " << result << "\n";
}
This approach uses the core::arg_handler() logic at lines 12081-12089 to execute only the specified bit in the flagset.
Inspecting the Memo Cache
VMAware caches technique results to avoid redundant execution. When debugging stale or incorrect results, verify the cache contents using the memo API (lines 11755-11788).
Cache inspection methods:
// After running detection
bool vm = VM::detect();
// Check if a specific technique is cached
if (VM::memo::is_cached(VM::HYPERVISOR_BIT)) {
auto data = VM::memo::cache_fetch(VM::HYPERVISOR_BIT);
std::cout << "Cached result: " << data.result << "\n";
std::cout << "Points: " << static_cast<int>(data.points) << "\n";
}
The cache stores the raw boolean result, point value, and detected brand string. Call VM::memo::clear() to force fresh execution on the next detect() call.
Disabling Early Exit for Full Scans
False negatives often occur because the shortcut optimization stops execution early once the threshold is met. Disable this to reveal hidden points that would push the score higher.
Force complete execution:
VM::flagset all = VM::core::generate_all();
// Pass false as the shortcut argument to run_all()
// Located at lines 11895-11900 in vmaware.hpp
bool full_result = VM::core::run_all(all, false) >= VM::threshold_score;
This ensures every technique runs regardless of the current point total, helping identify under-scoring issues.
Adjusting Detection Thresholds
For borderline environments (such as sandboxes that reveal only minimal hints), switch to high-threshold mode to determine if the library is under-scoring rather than over-scoring.
Enable high-threshold mode:
VM::flagset fs = VM::core::generate_all();
fs.set(VM::HIGH_THRESHOLD); // Raises requirement from 150 to 300 points
bool strict_result = VM::detect(fs);
This uses the threshold selection logic at lines 11738-11743, requiring stronger evidence before reporting a positive detection.
Complete Debugging Workflow
Combine these techniques into a systematic debugging routine:
#include "vmaware.hpp"
#include <iostream>
int main() {
// Step 1: Compile with -DDEBUG to enable __VMAWARE_DEBUG__ prints
// Step 2: Run full detection with debug output
bool vm = VM::detect();
std::cout << "Standard result: " << vm << "\n\n";
// Step 3: Clear cache and isolate suspect technique
VM::memo::clear();
VM::flagset only_hv = VM::core::generate_all();
only_hv.reset();
only_hv.set(VM::HYPERVISOR_BIT);
bool hv = VM::detect(only_hv);
std::cout << "Hypervisor bit alone: " << hv << "\n";
// Step 4: Check cache state
if (VM::memo::is_cached(VM::HYPERVISOR_BIT)) {
auto data = VM::memo::cache_fetch(VM::HYPERVISOR_BIT);
std::cout << "Cached points: " << static_cast<int>(data.points) << "\n";
}
// Step 5: Force full scan without shortcut
VM::flagset all = VM::core::generate_all();
int full_score = VM::core::run_all(all, false);
std::cout << "Full scan score: " << full_score
<< " (threshold: " << VM::threshold_score << ")\n";
return 0;
}
Compile with debug symbols and the DEBUG macro to see the internal technique trace, then correlate the output with the specific line ranges in src/vmaware.hpp to identify malfunctioning checks.
Summary
- Enable
__VMAWARE_DEBUG__to trace which techniques execute and their point contributions via thedebug_msghelper at lines 434-442. - Isolate techniques using
VM::flagsetwithcore::generate_all()and specific bit resets to test individual checks without interference. - Inspect the memo cache through
VM::memo::cache_fetch()to verify cached results at lines 11755-11788, and callVM::memo::clear()to reset state. - Disable the shortcut by passing
falsetocore::run_all()(lines 11895-11900) to force complete execution and reveal hidden detection points. - Adjust thresholds using
VM::HIGH_THRESHOLDto switch between 150-point and 300-point modes (lines 11736-11743).
Frequently Asked Questions
Why does VMAware detect a VM when I'm running on bare metal?
False positives typically occur when specific hardware or software configurations mimic hypervisor signatures. Enable __VMAWARE_DEBUG__ to see which techniques contribute points, then isolate each technique using a custom flagset to identify the false positive source. Check the memo cache with VM::memo::cache_fetch() to verify if stale data is causing the incorrect result.
How do I permanently disable a noisy technique?
Construct a VM::flagset using VM::core::generate_all(), then call fs.reset(VM::TECHNIQUE_NAME) for each technique you want to disable. Pass this filtered flagset to VM::detect(fs). For permanent removal, modify the library source at src/vmaware.hpp lines 12081-12089 where core::arg_handler() processes technique bits.
What does the memo cache store and when should I clear it?
The memo cache stores a struct containing the raw boolean result, point value, and detected brand string for each technique ID (lines 11755-11788). Clear the cache by calling VM::memo::clear() when you suspect stale results between detection runs, or when debugging to ensure fresh execution of all techniques.
How do I interpret the point values in debug output?
Each technique contributes a specific weight (typically 50-100 points) defined in the technique table at lines 11733-11802. The debug output shows the running total. If the total reaches 150 (or 300 in high-threshold mode), detect() returns true. Compare the debug output against the threshold to determine if the environment is under-scoring (few techniques fire) or over-scoring (noisy techniques fire incorrectly).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →