Static AV engines are, at their core, byte-pattern matchers. They load a binary into a buffer and run thousands of regex-like rules against it looking for known bad strings: IP addresses, function names, C2 tokens. The rule for an IP pattern is something like /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ — simple, fast, and completely defeated by encoding. XOR encodes each byte of the string against a key, producing output that looks like random binary garbage. The scanner's rule finds nothing because nothing it expects is there.
The second axis of protection is per-variant keying. If you encrypt your C2 IP with key 0xFC and the AV vendor reverse-engineers one sample and writes a rule for the encrypted form, changing the key to 0xAB produces completely different ciphertext. Each build is unique. This is why commodity malware authors rotate keys per campaign — it's free polymorphism with four lines of Python at build time.
The weak point is runtime. When the payload decrypts to a stack buffer, the plaintext exists in process memory for the duration of that function call. Memory scanners that hook into the process or scan virtual address space can catch it there. The defence is scope: decrypt, use immediately, then the function returns and the stack frame is gone. Never store decrypted strings in globals.
.rdata section.Every Windows executable has an Import Address Table — a section in the PE that lists exactly which DLLs and functions the binary needs at load time. AV products love the IAT because it's a clean, pre-parsed list of every capability the binary declares. A payload that imports WSASocketA, connect, send, and recv is broadcasting "I make network connections." Dynamic resolution removes that declaration entirely. The binary loads ws2_32.dll at runtime by name and walks its export directory to find function addresses. The IAT shows nothing.
The next step is hash-based resolution: instead of passing the string "WSASocketA" to GetProcAddress, you compute a hash of the name at build time, store just the hash constant, and walk the export table comparing hashes. Now the API name string doesn't exist in the binary at all — not in the IAT, not in .rdata, nowhere. The scanner has no string to match against.
The tell for defenders is the combination: LoadLibraryA + GetProcAddress in the IAT, or LoadLibraryA present with no corresponding DLL in the IAT. This pattern — present in a lot of legitimate software too — gets elevated to a high-confidence flag when combined with other indicators. In isolation it's noise; stacked with XOR strings and sandbox evasion it's a kill signal.
WSASocketA, connect, send, recv directly (which would appear in the PE's Import Address Table), the payload loads ws2_32.dll at runtime via a XOR-encoded library name, then resolves function pointers via GetProcAddress.WSASocketA or connect — these are in the PE's IAT and trivially visible. Dynamic resolution means the IAT is clean. The ws2_32.dll name itself is encoded so string search finds nothing.LoadLibraryA and GetProcAddress. What can it conclude?AV cloud sandboxes are virtualised execution environments built for throughput. To process thousands of samples per hour they cut corners: they run small VM images (minimal disk, 800x600 display), they have no real user activity, and critically, they accelerate or bypass time-based calls to push samples through faster. Sleep(500) on a sandbox might complete in 5ms. That gap is detectable with a simple GetTickCount comparison before and after.
Screen resolution is a blunt but effective check. A real workstation in 2025 has at minimum 1920x1080. Sandbox VMs default to 800x600 because they don't need a real display — the hypervisor renders nothing. The disk space check catches the other common VM signature: a freshly-provisioned 30GB virtual disk versus a real machine's 500GB+ drive. These are environmental facts that are hard to fake cheaply at scale.
The advanced checks compound the difficulty. Single-core VMs, fresh boot times under five minutes, and static cursor positions are all sandbox tells that require real investment to spoof. Each check adds cost to the sandbox operator. Stack enough checks and the sandbox either fails them all (legitimate samples also fail edge cases rarely) or the sandbox vendor has to burn engineering time keeping up with every new check. Asymmetric warfare — cheap for the attacker, expensive for the defender.
Sleep(500) and then compares GetTickCount(). What does it detect when the delta is less than 400ms?The PE header is a roadmap — it tells the Windows loader where to find sections, what permissions to set, and where to start execution. The loader reads this map when the binary is loaded into memory and sets everything up. Once that's done, the header is dead weight. Execution doesn't need it anymore. The CPU doesn't re-read the header to call functions — it just follows the instruction pointer through the code pages that the loader already mapped.
Memory scanners, on the other hand, need the header. When an EDR or AV product enumerates running processes and their loaded modules, it typically looks for the MZ signature (0x4D 0x5A) at the base address of each memory region to identify PE images. Without it, the region doesn't register as a loaded module — it looks like an anonymous heap or mapped file allocation. The scanner's PE validation fails, and it moves on.
The detection surface this creates is the VirtualProtect call itself. You have to make the header region writable before you can zero it. An EDR that hooks VirtualProtect and flags any call that targets a region containing the module's own base address will catch this. The HWBP bypass shown in Layer 8 eliminates that call entirely — no VirtualProtect, no detection event.
MZ), PE signature, and stub code. Memory scanners that look for loaded modules with valid PE headers find nothing.MZ\x4D\x5A. Execution continues normally because the OS already parsed the headers at load time.