20 KiB
Anti-Analysis Bypass Guide
Reference for identifying and bypassing malware evasion techniques during dynamic analysis.
Overview
Anti-analysis techniques fall into four categories:
| Category | Goal | MITRE ATT&CK |
|---|---|---|
| VM/sandbox detection | Avoid executing in automated analysis environments | T1497 |
| Debugger detection | Prevent live debugging and breakpoint analysis | T1622 |
| Timing-based evasion | Delay execution past sandbox timeout windows | T1497.003 |
| Environmental keying | Execute only when specific conditions are met | T1480 |
VM and Sandbox Detection
How Malware Detects VMs
Registry artifact checks (MITRE T1497.001):
HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions
HKLM\SOFTWARE\VMware, Inc.\VMware Tools
HKLM\SYSTEM\CurrentControlSet\Services\VBoxGuest
HKLM\SYSTEM\CurrentControlSet\Services\vmhgfs
HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0\Identifier
→ "VBOX HARDDISK", "VMWARE VIRTUAL IDE HARD DRIVE"
Process enumeration:
vboxservice.exe VirtualBox guest service
vboxtray.exe VirtualBox system tray
vmtoolsd.exe VMware Tools daemon
vmwaretray.exe VMware system tray
vmacthlp.exe VMware activation helper
vmsrvc.exe VirtualPC service
df5serv.exe Parallels service
prl_tools.exe Parallels Tools
Driver/device checks:
\\.\VBoxMiniRdrDN
\\.\VBoxGuest
\\.\vmci
\\.\HGFS
\Device\VBoxGuest
\Device\vmhgfs
CPUID hypervisor bit (EAX=1, ECX bit 31):
Malware executes CPUID and checks bit 31 of ECX. If set, a hypervisor is present. Subsequent CPUID with EAX=0x40000000 returns a vendor string: "VMwareVMware", "KVMKVMKVM", "VBoxVBoxVBox", "Microsoft Hv".
MAC address OUI checks:
08:00:27 VirtualBox
00:0C:29 VMware
00:50:56 VMware (vSphere)
00:1C:14 VMware
00:05:69 VMware
00:03:FF Microsoft Hyper-V
Screen resolution / user interaction:
Screen size exactly 1024x768 → sandbox default
No mouse movement history
No browser history / recent files
Uptime < 10 minutes
Single CPU core
Total RAM < 2 GB
Identifying VM detection in static analysis:
Look for these API calls in import tables or dynamic calls:
GetSystemInfo → CPU count check
GlobalMemoryStatusEx → RAM check
EnumProcesses → process list scan
CreateFile(\\.\<device>) → device existence check
RegOpenKeyEx / RegQueryValueEx → registry artifact checks
GetAdaptersInfo → MAC address check
GetTickCount / GetSystemTime → uptime / time checks
Bypass Strategies
Remove VirtualBox Guest Additions:
# Uninstall via Control Panel or:
Start-Process "C:\Program Files\Oracle\VirtualBox Guest Additions\uninstall.exe" -Wait
# Remove leftover registry keys
Remove-Item "HKLM:\SOFTWARE\Oracle\VirtualBox Guest Additions" -Recurse -ErrorAction SilentlyContinue
Remove-Item "HKLM:\SYSTEM\CurrentControlSet\Services\VBoxGuest" -Recurse -ErrorAction SilentlyContinue
Remove-Item "HKLM:\SYSTEM\CurrentControlSet\Services\VBoxSF" -Recurse -ErrorAction SilentlyContinue
Remove-Item "HKLM:\SYSTEM\CurrentControlSet\Services\VBoxMouse" -Recurse -ErrorAction SilentlyContinue
# Stop and delete VBox services
sc.exe stop VBoxGuest
sc.exe delete VBoxGuest
Spoof BIOS/DMI strings (VirtualBox, run on host):
VBoxManage setextradata "VM-Name" \
"VBoxInternal/Devices/pcbios/0/Config/DmiBIOSVendor" "American Megatrends Inc."
VBoxManage setextradata "VM-Name" \
"VBoxInternal/Devices/pcbios/0/Config/DmiBIOSVersion" "F10"
VBoxManage setextradata "VM-Name" \
"VBoxInternal/Devices/pcbios/0/Config/DmiSystemProduct" "MS-7B86"
VBoxManage setextradata "VM-Name" \
"VBoxInternal/Devices/pcbios/0/Config/DmiSystemVendor" "MSI"
VBoxManage setextradata "VM-Name" \
"VBoxInternal/Devices/pcbios/0/Config/DmiBoardProduct" "MAG Z390 TOMAHAWK"
VBoxManage setextradata "VM-Name" \
"VBoxInternal/Devices/pcbios/0/Config/DmiChassisVendor" "MSI"
Suppress CPUID hypervisor bit (VirtualBox):
VBoxManage modifyvm "VM-Name" --paravirtprovider none
VMware: suppress CPUID and hypervisor detection:
Add to the .vmx file:
hypervisor.cpuid.v0 = "FALSE"
SMBIOS.reflectHost = "TRUE"
board.vendor = "MSI"
board.product = "MAG Z390 TOMAHAWK"
Spoof MAC address:
# VirtualBox (use a real vendor OUI)
VBoxManage modifyvm "VM-Name" --macaddress1 "8C8D28AABBCC"
# Intel OUIs: 8C:8D:28, 00:1B:21, F4:4D:30
# Realtek OUI: 00:E0:4C
Add realistic user artifacts:
# Create browser history (requires Chrome installed)
# Open URLs from PowerShell
Start-Process "chrome.exe" "https://www.gmail.com"
Start-Process "chrome.exe" "https://www.stackoverflow.com"
# Create recent documents
$docs = @("budget_2024.xlsx","notes.txt","presentation.pptx")
foreach ($d in $docs) {
New-Item -Path "$env:USERPROFILE\Documents\$d" -ItemType File -Force
}
# Set realistic uptime by running VM for 30+ minutes before analysis
Use VBoxHardenedLoader (advanced):
VBoxHardenedLoader patches the VirtualBox kernel driver to pass CPUID and other checks transparently without removing guest additions.
# https://github.com/hfiref0x/VBoxHardenedLoader
# Load driver: VBoxHLinstall.cmd
# Provides transparent VM hiding while keeping shared folders functional
Debugger Detection
How Malware Detects Debuggers (MITRE T1622)
IsDebuggerPresent / CheckRemoteDebuggerPresent:
Most common check. Reads the PEB (Process Environment Block) BeingDebugged flag directly.
PEB offset 0x2 (32-bit) / 0x2 (64-bit): BeingDebugged byte
PEB offset 0x68 (32-bit) / 0xBC (64-bit): NtGlobalFlag (set to 0x70 under debugger)
NtQueryInformationProcess:
ProcessDebugPort (class 7) → non-zero if debugged
ProcessDebugObjectHandle (class 30) → valid handle if debugged
ProcessDebugFlags (class 31) → 0 if debugged
Heap flags (PEB.ProcessHeap):
Heap.Flags: normally 0x2, under debugger 0x50000062
Heap.ForceFlags: normally 0x0, under debugger 0x40000060
Hardware breakpoint detection:
Malware reads the debug registers DR0–DR3 via GetThreadContext. If any are non-zero, a hardware breakpoint is set.
Timing-based debugger detection:
DWORD t1 = GetTickCount();
// execute a few instructions
DWORD t2 = GetTickCount();
if ((t2 - t1) > threshold) { /* debugger detected */ }
When single-stepping in a debugger, even a few instructions take milliseconds vs. nanoseconds normally.
Exception-based detection:
Malware raises an exception (INT3, divide-by-zero) and checks if the debugger swallows it instead of passing it to the exception handler.
Window/process name enumeration:
FindWindow("OLLYDBG", NULL)
FindWindow("WinDbgFrameClass", NULL)
FindWindow("ID", NULL) → Immunity Debugger
FindWindow("Qt5QWindowIcon", NULL) → x64dbg
EnumProcesses → scan for:
ollydbg.exe, x32dbg.exe, x64dbg.exe, windbg.exe,
ida.exe, ida64.exe, idag.exe, idaq.exe,
immunitydebugger.exe, dnspy.exe, cheatengine.exe
Bypass Strategies
ScyllaHide (primary tool):
ScyllaHide is an anti-anti-debug plugin for x64dbg, OllyDbg, and IDA Pro. It patches all common detection methods transparently.
# Download: https://github.com/x64dbg/ScyllaHide
# Install in x64dbg:
# Copy ScyllaHide.dp32 / ScyllaHide.dp64 to x64dbg\plugins\
# Restart x64dbg
# Plugins → ScyllaHide → Options
# Enable all protections for evasive samples:
[x] PEB BeingDebugged
[x] PEB NtGlobalFlag
[x] PEB HeapFlags
[x] NtQueryInformationProcess
[x] Unhandled Exception Handler
[x] Hardware Breakpoint Protection
[x] GetTickCount Hook
[x] NtSetInformationThread
TitanHide (kernel-level, for advanced evasion):
TitanHide is a kernel driver that hides debuggers at the OS level, defeating NtQueryInformationProcess and similar kernel-query-based checks.
# Download: https://github.com/mrexodia/TitanHide
# Requires: Windows with test signing enabled or TESTSIGNING boot flag
# Load driver: TitanHide.sys
# Use x64dbg plugin interface for configuration
Manual PEB patching in x64dbg:
# In x64dbg, after attaching:
# Open Memory Map → find PEB
# Manually zero BeingDebugged byte:
# 32-bit:
# FS:[0x30] → PEB base
# PEB+0x2 → BeingDebugged (set to 0x00)
# PEB+0x68 → NtGlobalFlag (set to 0x00)
# Use x64dbg script:
mov byte ptr [peb()]:2, 0 # Clear BeingDebugged
mov dword ptr [peb()]:0x68, 0 # Clear NtGlobalFlag
Handle timing checks — NOP the sleep/delay:
# In x64dbg, find GetTickCount/QueryPerformanceCounter calls
# Identify the comparison instruction after the delta calculation
# NOP the conditional jump (JNE, JA, JBE, etc.)
# Or patch the threshold to an impossibly large value
Handle exception-based detection:
# In x64dbg: Options → Preferences → Exceptions
# Add exception codes to "pass to program":
# 0x80000003 (INT3 / STATUS_BREAKPOINT)
# 0xC0000094 (divide by zero)
# 0xC0000005 (access violation, if used as detection)
Timing-Based Evasion
How Malware Uses Timing (MITRE T1497.003)
Sleep-based evasion:
Malware calls Sleep(600000) (10 minutes) or longer. Automated sandboxes that have a 2-5 minute timeout never observe the payload.
Sleep(DWORD dwMilliseconds)
SleepEx(DWORD dwMilliseconds, BOOL bAlertable)
WaitForSingleObject(HANDLE, INFINITE)
NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval)
Date/time checks:
Malware compares the current date against a hardcoded activation date. If before that date, it does nothing (making sandbox captures useless). If after an expiry date, it also does nothing (anti-forensics).
Idle/user activity checks:
GetLastInputInfo() → returns milliseconds since last user input
GetCursorPos() → called twice, checks if cursor moved
GetForegroundWindow() → checks for active user window
If no mouse movement or keyboard input for N seconds, the malware assumes it is in a sandbox.
Bypass Strategies
Patch Sleep calls in x64dbg:
# Find calls to Sleep / SleepEx / NtDelayExecution
# Option 1: NOP the entire Sleep call
# Option 2: Patch the millisecond argument to 0 or 1
# In x64dbg, set a breakpoint on Sleep:
# Breakpoints → Add → "Sleep"
# When hit, change RCX (x64) or stack arg (x86) to 0 before continuing
# x64dbg script to auto-patch:
bp Sleep
loopSleep:
erun
mov rcx, 0 // Zero the sleep duration (x64 first arg)
erun
goto loopSleep
Accelerate sandbox clock (Cuckoo/CAPE):
# In cuckoo.conf:
[timeouts]
critical = 60
vm_state = 60
# Enable clock skipping in analysis options:
options: "clock=20200101120000"
Manipulate system time on Windows VM:
# Set date forward to trigger time-gated payloads
Set-Date -Date "2025-06-15 12:00:00"
# Use faketime (Linux / Wine)
faketime '2025-06-15 12:00:00' wine sample.exe
Simulate user activity:
# Move mouse programmatically
Add-Type -AssemblyName System.Windows.Forms
while ($true) {
$x = Get-Random -Minimum 100 -Maximum 1800
$y = Get-Random -Minimum 100 -Maximum 900
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point($x, $y)
Start-Sleep -Milliseconds 500
}
Extend sandbox timeout:
For CAPE/Cuckoo, increase the analysis timeout:
python3 utils/submit.py --file sample.exe --timeout 300
For manual analysis, simply wait — set observation window to 60+ minutes for suspected sleep-evasive samples.
Environmental Keying
How Malware Implements Environmental Checks (MITRE T1480)
Environmental keying means the malware will only execute its payload if specific environmental conditions are met. This is distinct from VM detection — the malware is looking for a specific victim configuration.
Domain/hostname checks:
GetComputerNameEx(ComputerNameDnsDomain, ...)
NetGetJoinInformation()
LookupAccountSid()
→ Malware may require a specific AD domain name or NETBIOS name
Username checks:
GetUserName() → compare against hardcoded victim username
WNetGetUser()
Installed software checks:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ → enumerate installed apps
HKLM\SOFTWARE\ → check for specific vendor keys
Language/locale checks:
GetSystemDefaultLangID()
GetUserDefaultLCID()
→ Some malware won't run if locale is English or matches researcher's profile
→ Others skip execution if locale is Russian/Ukrainian (avoiding domestic prosecution)
Network environment checks:
GetAdaptersInfo() → check for specific IP ranges
DnsQuery() → verify domain membership via DNS
NetServerGetInfo() → check for domain controller presence
Bypass Strategies
Domain/hostname spoofing:
# Change computer name (requires restart)
Rename-Computer -NewName "WORKSTATION-01" -Force
# Join a fake workgroup matching expected domain name
# (Does not require actual domain controller)
Add-Computer -WorkgroupName "TARGETCORP"
Username spoofing:
# Create a user matching expected victim name
New-LocalUser -Name "jsmith" -NoPassword
Add-LocalGroupMember -Group "Administrators" -Member "jsmith"
# Log in as that user, then execute sample
Locale patching:
# In x64dbg, break on GetSystemDefaultLangID / GetUserDefaultLCID
# Patch EAX return value to match expected locale
# Russian: 0x0419, Romanian: 0x0418, Ukrainian: 0x0422
Registry keying bypass:
# If malware checks for a specific product registry key:
# Identify the expected key via static analysis (strings, IDA)
# Create a fake registry entry before execution:
New-Item -Path "HKLM:\SOFTWARE\TargetVendor\TargetProduct" -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\TargetVendor\TargetProduct" `
-Name "Version" -Value "1.0" -PropertyType String
Tools for Anti-Analysis Bypass
ScyllaHide
Purpose: Plugin for x64dbg, OllyDbg, IDA Pro, TitanHide. Patches all common anti-debug checks transparently.
Download: https://github.com/x64dbg/ScyllaHide/releases
Platforms: x32dbg, x64dbg, OllyDbg 1.10, OllyDbg 2.01, IDA Pro
Install: Copy DLL to debugger plugins folder
Key options:
- PEB patch (BeingDebugged, NtGlobalFlag, HeapFlags)
- NtQueryInformationProcess hooks
- Timing hooks (GetTickCount, QueryPerformanceCounter)
- NtSetInformationThread (hide from debugger)
- Unhandled exception fix
TitanHide
Purpose: Kernel-mode driver that hides debugger presence from NtQueryInformationProcess and related syscalls.
Download: https://github.com/mrexodia/TitanHide
Requires: Windows with test signing or driver certificate
Use case: When ScyllaHide user-mode hooks are insufficient
(malware uses direct syscalls to bypass user-mode hooks)
al-khaser
Purpose: Open-source tool that tests over 170 anti-analysis checks. Use to validate VM hardening before analyzing evasive samples.
Download: https://github.com/LordNoteworthy/al-khaser
Build: Visual Studio solution (.sln)
Run: al-khaser.exe
Output: Pass/fail for each check category:
- Anti-VM
- Anti-debug
- Anti-sandbox
- Anti-disassembly
- Timing
pafish
Purpose: Lightweight VM/sandbox detection tester. Faster than al-khaser for quick pre-analysis validation.
Download: https://github.com/a0rtega/pafish/releases
Run: pafish.exe
Green: Check passed (not detected)
Red: Check failed (artifact detected — fix before analyzing evasive samples)
hollows_hunter / pe-sieve
Purpose: Scan a running process for injected code, hollowed sections, and unpacked payloads in memory. Useful after malware defeats initial analysis controls.
Download: https://github.com/hasherezade/hollows_hunter
Run: hollows_hunter.exe /pid <malware_pid>
Output: Dumps hollowed/injected regions to disk for static analysis
x64dbg Plugins for Anti-Analysis
ScyllaHide Anti-debug bypass (essential)
xAnalyzer Extended analysis and comment generation
xHotConvert Quick conversion utilities
OllyDumpEx Process dumper for unpacking
Identifying Anti-Analysis at a Glance
During static analysis, flag these patterns for targeted bypass:
String indicators:
"IsDebuggerPresent" → debugger detection
"CheckRemoteDebugger" → remote debugger detection
"NtQueryInformationProcess" → advanced debugger/VM detection
"VBOX", "VMWARE", "QEMU", "VIRTUAL" → VM string checks
"GetTickCount" → timing-based detection
"GetLastInputInfo" → user interaction check
"GetSystemDefaultLangID" → locale-based keying
"GetComputerName" → hostname keying
"EnumProcesses" → process-list scanning
Import table red flags:
kernel32.dll: IsDebuggerPresent, CheckRemoteDebuggerPresent,
GetTickCount, QueryPerformanceCounter,
GetLastInputInfo, GetCursorPos
ntdll.dll: NtQueryInformationProcess, NtSetInformationThread,
NtDelayExecution, RtlQueryProcessHeapInformation
advapi32.dll: RegOpenKeyEx (combined with SYSTEM\CurrentControlSet\Services)
iphlpapi.dll: GetAdaptersInfo (MAC address check)
Behavioral indicators during dynamic analysis:
Process exits immediately (< 2 seconds) → VM or debugger detected
No network activity despite expected C2 → sandbox/network detection
Sleep calls with long durations (> 30s) → timing evasion
Repeated GetTickCount/RDTSC in tight loop → timing check
CreateFile on \\.\VBoxGuest or \\.\vmci → device probing
MITRE ATT&CK Reference
| Technique | ID | Description |
|---|---|---|
| Virtualization/Sandbox Evasion | T1497 | Parent technique |
| System Checks | T1497.001 | Registry, filesystem, hardware artifacts |
| User Activity Based Checks | T1497.002 | Mouse movement, input, idle time |
| Time Based Evasion | T1497.003 | Sleep calls, date checks, RDTSC |
| Debugger Evasion | T1622 | IsDebuggerPresent, NtQueryInformationProcess |
| Execution Guardrails | T1480 | Environmental keying, domain/locale checks |
| Obfuscated Files or Information | T1027 | Packed/encrypted payloads that unpack in memory |
Analysis Decision Tree
Sample exits immediately?
├─ Yes → Check: is VM artifact present? → Fix VM hardening, retry
│ Check: debugger detected? → Enable ScyllaHide, retry
│ Check: locale/domain check? → Spoof environment, retry
└─ No → Sample is executing
No network activity?
├─ Check: INetSim running and configured?
├─ Check: DNS resolving to INetSim IP?
├─ Check: malware performing HTTPS certificate pinning?
│ → Use Proxifier + mitmproxy to intercept
└─ Check: malware waiting for sleep delay?
→ Patch Sleep calls, extend timeout
Behavior stops after initial activity?
├─ Check: C2 response expected? → Configure INetSim response templates
├─ Check: time-gated payload? → Advance system clock
└─ Check: requires second-stage download? → Serve fake C2 response via INetSim