Files

20 KiB
Raw Permalink Blame History

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