#!/usr/bin/env python3 """Detect anti-analysis and sandbox evasion techniques in binary samples. Scans PE files and other binaries for indicators of VM detection, anti-debugging, timing checks, and environment fingerprinting commonly used by malware to evade analysis environments. """ from __future__ import annotations import argparse import json import sys from pathlib import Path from typing import Any # Evasion indicator databases organized by category EVASION_INDICATORS: dict[str, dict[str, list[str]]] = { "vm_detection": { "vm_vendor_strings": [ "vmware", "virtualbox", "vbox", "qemu", "xen", "hyper-v", "parallels", "bochs", "sandboxie", "virtual hd", "vmtoolsd", "vboxservice", "vboxtray", "vmwaretray", "vmmouse", "vmhgfs", "vboxguest", ], "vm_registry_keys": [ r"SYSTEM\CurrentControlSet\Services\VMTools", r"SOFTWARE\VMware, Inc.", r"SYSTEM\CurrentControlSet\Services\VBoxGuest", r"SOFTWARE\Oracle\VirtualBox", r"HARDWARE\ACPI\DSDT\VBOX__", ], "vm_files": [ "vmguestlib.dll", "vboxdisp.dll", "vboxhook.dll", "vboxmrxnp.dll", "vmhgfs.sys", "vmmemctl.sys", ], "vm_instructions": [ "cpuid", "sidt", "sldt", "sgdt", "str", "in (vmware port 0x5658)", ], }, "anti_debugging": { "debug_api_calls": [ "IsDebuggerPresent", "CheckRemoteDebuggerPresent", "NtQueryInformationProcess", "OutputDebugString", "NtSetInformationThread", "NtQueryObject", "NtClose (invalid handle)", "CloseHandle (invalid handle)", ], "debug_flags": [ "PEB.BeingDebugged", "PEB.NtGlobalFlag", "HEAP_TAIL_CHECKING_ENABLED", "ProcessDebugPort", "ProcessDebugObjectHandle", "ProcessDebugFlags", ], "debug_detection": [ "GetThreadContext", "SetUnhandledExceptionFilter", "RaiseException", "DebugActiveProcess", "INT 2D", "INT 3", "TRAP FLAG", ], }, "timing_checks": { "timing_apis": [ "GetTickCount", "GetTickCount64", "QueryPerformanceCounter", "QueryPerformanceFrequency", "timeGetTime", "NtQuerySystemTime", "GetSystemTimeAsFileTime", ], "timing_instructions": [ "rdtsc", "rdtscp", ], "sleep_evasion": [ "Sleep", "WaitForSingleObject", "WaitForMultipleObjects", "NtDelayExecution", "SetTimer", "CreateTimerQueueTimer", ], }, "environment": { "user_checks": [ "GetUserName", "GetComputerName", "admin", "sandbox", "malware", "analyst", "virus", "sample", "test", ], "system_checks": [ "GetSystemInfo", "GlobalMemoryStatusEx", "GetDiskFreeSpaceEx", "GetSystemMetrics", "EnumDisplaySettings", "GetForegroundWindow", ], "process_checks": [ "CreateToolhelp32Snapshot", "Process32First", "Process32Next", "EnumProcesses", "wireshark", "procmon", "procexp", "ollydbg", "x64dbg", "idaq", "ida64", "fiddler", "autoruns", "tcpview", "regmon", "filemon", ], "network_checks": [ "GetAdaptersInfo", "GetAdaptersAddresses", "00:0C:29:", "00:50:56:", "08:00:27:", ], }, "delayed_execution": { "scheduling": [ "schtasks", "at.exe", "ITaskScheduler", "ITaskService", "Schedule.Service", ], "wmi": [ "Win32_Process", "Win32_ComputerSystem", "ExecQuery", "ManagementObjectSearcher", ], }, "geolocation": { "geo_apis": [ "ipinfo.io", "ipapi.co", "geoip", "maxmind", "ip-api.com", "freegeoip", ], "locale_checks": [ "GetLocaleInfo", "GetSystemDefaultLangID", "GetUserDefaultLangID", "GetKeyboardLayoutList", ], }, } # RDTSC opcode bytes RDTSC_OPCODE = bytes([0x0F, 0x31]) def read_binary(file_path: Path) -> bytes: """Read the binary content of the target file.""" with open(file_path, "rb") as f: return f.read() def extract_strings(data: bytes, min_length: int = 4) -> list[str]: """Extract ASCII strings from binary data.""" strings: list[str] = [] current: list[str] = [] for byte in data: if 0x20 <= byte < 0x7F: current.append(chr(byte)) else: if len(current) >= min_length: strings.append("".join(current)) current = [] if len(current) >= min_length: strings.append("".join(current)) return strings def scan_for_rdtsc(data: bytes) -> list[str]: """Scan binary for RDTSC instruction opcodes (0F 31).""" offsets: list[str] = [] idx = 0 while idx < len(data) - 1: if data[idx] == 0x0F and data[idx + 1] == 0x31: offsets.append(hex(idx)) idx += 1 return offsets def detect_evasion( file_path: Path, category_filter: str | None = None, verbose: bool = False, ) -> dict[str, Any]: """Detect evasion techniques in the given binary file.""" data = read_binary(file_path) file_strings = extract_strings(data) file_strings_lower = [s.lower() for s in file_strings] joined_lower = "\n".join(file_strings_lower) results: dict[str, Any] = { "file": str(file_path), "file_size": len(data), "evasion_techniques": {}, "recommended_countermeasures": [], } categories_to_scan = EVASION_INDICATORS if category_filter: categories_to_scan = { k: v for k, v in EVASION_INDICATORS.items() if k == category_filter } countermeasures_map = { "vm_detection": "Hide VM artifacts (rename VM tools, change BIOS strings, spoof MAC address)", "anti_debugging": "Use anti-anti-debug plugin (ScyllaHide, TitanHide)", "timing_checks": "Hook timing APIs to return consistent values", "environment": "Set realistic hostname, username, and install decoy applications", "delayed_execution": "Hook Sleep to skip delays; extend sandbox timeout", "geolocation": "Configure matching locale, timezone, and route through target region VPN", } for category, subcategories in categories_to_scan.items(): indicators_found: list[str] = [] for subcat_name, patterns in subcategories.items(): for pattern in patterns: if pattern.lower() in joined_lower: indicator_desc = f"{pattern} ({subcat_name})" indicators_found.append(indicator_desc) # Special check for RDTSC opcode if category == "timing_checks": rdtsc_offsets = scan_for_rdtsc(data) if rdtsc_offsets: indicators_found.append( f"RDTSC instruction at offsets: {', '.join(rdtsc_offsets[:5])}" ) found = len(indicators_found) > 0 if found and category in countermeasures_map: results["recommended_countermeasures"].append( countermeasures_map[category] ) severity = "none" if len(indicators_found) >= 5: severity = "high" elif len(indicators_found) >= 2: severity = "medium" elif len(indicators_found) >= 1: severity = "low" results["evasion_techniques"][category] = { "found": found, "indicators": indicators_found if verbose or len(indicators_found) <= 10 else indicators_found[:10], "indicator_count": len(indicators_found), "severity": severity, } return results def analyze(input_path: Path, output_format: str = "json") -> dict[str, Any]: """Analyze the given file and return evasion detection results.""" return detect_evasion(input_path, verbose=True) def main() -> None: """Entry point for the evasion detector CLI.""" parser = argparse.ArgumentParser( description="Detect sandbox evasion and anti-analysis techniques in binary files." ) parser.add_argument( "--input", "--file", type=Path, required=True, dest="input", help="Path to the binary file to analyze", ) parser.add_argument( "--output", type=Path, help="Path to output file (stdout if omitted)" ) parser.add_argument( "--format", default="json", choices=["json", "text"], help="Output format (default: json)", ) parser.add_argument( "--category", choices=list(EVASION_INDICATORS.keys()), help="Scan only a specific evasion category", ) parser.add_argument( "--verbose", action="store_true", help="Include all indicator details in output", ) args = parser.parse_args() if not args.input.exists(): print(f"Error: File not found: {args.input}", file=sys.stderr) sys.exit(1) result = detect_evasion( args.input, category_filter=args.category, verbose=args.verbose, ) if args.format == "text": output_text = _format_text(result) else: output_text = json.dumps(result, indent=2) if args.output: args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(output_text, encoding="utf-8") print(f"Results written to {args.output}", file=sys.stderr) else: print(output_text) def _format_text(result: dict[str, Any]) -> str: """Format results as human-readable text.""" lines: list[str] = [ f"=== Evasion Detection Report: {result['file']} ===", f"File size: {result['file_size']} bytes", "", ] for category, info in result["evasion_techniques"].items(): status = "DETECTED" if info["found"] else "Not found" lines.append(f"[{info['severity'].upper()}] {category}: {status}") if info["found"]: for indicator in info["indicators"]: lines.append(f" - {indicator}") lines.append("") if result["recommended_countermeasures"]: lines.append("Recommended Countermeasures:") for cm in result["recommended_countermeasures"]: lines.append(f" * {cm}") return "\n".join(lines) if __name__ == "__main__": main()