Sign in to track your watch progress.
Five categories of self-defence: debugger detection, VM/sandbox detection, data protection, code misdirection, and advanced unpacking. The mechanism behind each, with concrete bypasses.
The techniques covered in earlier lessons assume a cooperative target: the malware runs, the analyst observes, and the tools produce accurate results. Real-world malware does not cooperate. Professionally developed samples — particularly those from organised crime groups and state-sponsored actors — contain layers of self-defence designed to detect analysis environments, corrupt debugger output, mislead the reverse engineer, and protect embedded secrets.
Analysing defended malware is like interrogating a spy who has been trained to resist questioning. They check if the room is bugged, feed false information, speak in code, and have a cyanide pill ready if cornered. The analyst's job is to spot the counter-surveillance, decode the lies, and get the intel before they swallow the pill.
The presence of anti-analysis techniques is itself a signal. Commodity malware rarely invests in elaborate defences. If a sample fights back, you are likely dealing with a professional operation — and the payload is worth the effort to extract.
| Defence | Goal | Counter |
|---|---|---|
| Debugger detection | Detect attached debuggers; alter behaviour or exit | Patch checks, ScyllaHide, TitanHide |
| VM/Sandbox detection | Identify virtualised environments | Remove artefacts, bare-metal analysis |
| Data protection | Encrypt config, C2, payloads | Find decryption routine, dump at runtime |
| Code misdirection | Confuse disassembler and analyst | Manual analysis, symbolic execution |
| Advanced unpacking | Multi-layer packing, stolen bytes | Layered unpacking, OEP reconstruction |
When a debugger attaches to a process, the OS modifies several internal data structures. Malware queries these structures to decide whether it is being analysed.
The simplest checks call Windows API functions that directly report debugger presence.
if (IsDebuggerPresent()) { exit(0); }
if (CheckRemoteDebuggerPresent()) { exit(0); }
NTSTATUS status = NtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugPort,
&debugPort, sizeof(debugPort), NULL);
if (debugPort != 0) exit(0);
These calls are equivalent to looking straight in the rear-view mirror. Bypass: patch the call to return 0, or set a hardware breakpoint and edit the return value as it executes.
The Process Environment Block is the OS's per-process metadata. The debug field at PEB+0x02 (32-bit) or PEB+0x02 (64-bit) is set to 1 when a debugger is attached. Malware reads it directly:
mov eax, fs:[30h] ; FS:[30h] points to the PEB on x86
mov al, [eax+2] ; BeingDebugged byte
test al, al
jne detected
Bypass: clear the PEB flag manually, or use ScyllaHide which intercepts these reads.
RDTSC (Read Time-Stamp Counter) returns the CPU cycle count. Code wraps a small operation in two RDTSC reads and computes the elapsed cycles:
rdtsc
mov esi, eax
; ... operation that should take a few thousand cycles ...
rdtsc
sub eax, esi
cmp eax, 100000
ja detected ; took too long → debugger
A debugger paused on a breakpoint or single-stepping makes the operation take vastly longer than normal. Bypass: ScyllaHide also intercepts RDTSC and returns plausible values.
Software breakpoints (INT 3, opcode 0xCC) are easy to find by scanning code for 0xCC bytes that should not be there. Hardware breakpoints (DR0–DR3) can be detected by reading the debug registers via GetThreadContext. Some malware sets its own SEH handler and triggers an exception, expecting the OS to deliver it; if a debugger intercepts first, the malware notices.
Sandboxes are virtualised — and virtual environments leave fingerprints.
| Indicator | What it reveals |
|---|---|
Registry: HKLM\SOFTWARE\Oracle\VirtualBox or \VMware\ | VirtualBox or VMware host |
Drivers: vboxsf.sys, vmtoolsd.exe | VirtualBox / VMware guest tools |
MAC address prefix 08:00:27: | VirtualBox default NIC OUI |
MAC address prefix 00:0C:29: or 00:50:56: | VMware default NICs |
| BIOS strings containing "VirtualBox", "VMware", "QEMU" | Hypervisor BIOS |
User name sandbox, malware, test | Common sandbox usernames |
| Disk size < 60 GB | Suspiciously small for a normal user system |
| Recently created files < 100 | Empty filesystem typical of fresh sandbox |
| Mouse cursor unmoved for N seconds | No real user present |
A sample checks several of these and exits silently if any match.
The most elegant: the malware stalls execution for a long time before doing anything malicious. Many sandboxes time out after 60–120 seconds. The malware sleeps for 10 minutes, then runs. The sandbox has already declared the sample benign and moved on.
Variants: timer manipulation (GetTickCount followed by a long sleep, then GetTickCount again — if the elapsed wall time does not match, the sandbox is accelerating clocks), CPU stress loops (waste cycles to consume the sandbox's allocated budget).
Bypass: bare-metal analysis (a real machine reset to a known image after each sample), or a sandbox that detects and counters time-acceleration tricks.
Malware encrypts its configuration, C2 endpoints, and payloads. The encryption keeps strings off-disk and complicates static analysis. Common approaches:
K[64] constants, SHA-256's initial hash values. signsrch and findcrypt plugins for IDA/Ghidra recognise these constant tables and identify the algorithm.The bypass is universal: find the decryption routine, set a breakpoint after it returns, and dump the now-cleartext data from memory.
The most analyst-frustrating category.
if ((x*x + x) % 2 == 0) is always true (consecutive integers always have even product), so the false branch is unreachable. The disassembler shows both branches; the analyst wastes time on the dead one.JMP EAX where EAX is computed at runtime. The disassembler cannot resolve the target.The bypass is patience. Run the code in a debugger; at each junction, observe which path actually executes. Annotate the dead branches in your disassembler. Move on.
Multi-layered packing — output of one stub feeds another. Stolen bytes — the packer copies the first few instructions of the original program to a different memory region, then jumps there; dumping at the OEP misses these instructions. Anticipatory unpacking — packers detect dumping attempts and corrupt their own memory to defeat the dump. Each requires a tailored response.
Themida, VMProtect, and Enigma Protector are commercial-grade examples. Generic unpackers fail against them. Manual analysis is required.
IsDebuggerPresent and PEB checks in disassemblySection 03
Universal anti-anti-debug plugin for x64dbg, OllyDbg, IDA. The first thing to install.
toolOpen-source PE that exercises hundreds of anti-analysis techniques. Useful for testing analysis lab fidelity.
toolSandbox detection harness. Run inside your lab to see what artefacts you have failed to hide.
toolAnti-analysis chapters, with worked examples of every technique covered here.
referenceSection 04
Run al-khaser inside your standard analysis VM. Document which checks succeed in detecting the VM. Each success is a hardening task for your lab.
Take any sample that calls IsDebuggerPresent. In x64dbg, set a breakpoint on the API. When it fires, modify EAX to return 0 before continuing. Verify the malware proceeds past the check.
Pick a sample with encrypted strings. Run signsrch (or the IDA findcrypt plugin). Identify which algorithm's constants appear. Trace the decryption routine; set a breakpoint after it; dump the decrypted strings.