# How to Debug VMAware When It Incorrectly Detects or Misses Virtual Machines

> Debug VMAware VM detection issues. Enable debug macros, use custom flagsets with VM::detect(), inspect cache, and disable shortcuts for accurate results.

- Repository: [Louis/vmaware](https://github.com/kernelwernel/vmaware)
- Tags: how-to-guide
- Published: 2026-03-05

---

**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`](https://github.com/kernelwernel/vmaware/blob/main/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_THRESHOLD` is 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:**

```bash

# 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:

```text
[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:**

```cpp
#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:**

```cpp
// 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:**

```cpp
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:**

```cpp
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:

```cpp
#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`](https://github.com/kernelwernel/vmaware/blob/main/src/vmaware.hpp) to identify malfunctioning checks.

## Summary

- **Enable `__VMAWARE_DEBUG__`** to trace which techniques execute and their point contributions via the `debug_msg` helper at lines 434-442.
- **Isolate techniques** using `VM::flagset` with `core::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 call `VM::memo::clear()` to reset state.
- **Disable the shortcut** by passing `false` to `core::run_all()` (lines 11895-11900) to force complete execution and reveal hidden detection points.
- **Adjust thresholds** using `VM::HIGH_THRESHOLD` to 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`](https://github.com/kernelwernel/vmaware/blob/main/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).