Skip to content

15. Advanced Evasion Techniques

15.1 Sleep Obfuscation (In-Memory Encryption During Sleep)

Core idea: when the Beacon is idle it encrypts its own memory (RW state) → EDR memory scanning only sees encrypted data → a timer/APC triggers decryption (RWX) → the task executes → re-encrypts. EDR always sees encrypted memory during scans.

Comparison of the three mainstream implementations:

Technique Trigger mechanism Core API Principle
Ekko Timer Queue CreateTimerQueueTimer + NtContinue Chained Timer callbacks: Timer1 → change memory to RW + encrypt → Sleep → Timer2 → decrypt + change to RX + resume execution. All callbacks pass a forged thread context via NtContinue
Foliage APC NtQueueApcThread + NtContinue Queues a series of APCs on a worker thread: APC1 → encrypt memory + RW → APC2 → Sleep → APC3 → decrypt + RX + resume execution. Each APC passes a different thread context
Zilean Wait Object RegisterWait + NtContinue An Ekko variant that uses a RegisterWait callback instead of a Timer Queue
Hypnus Multi-mode TpSetTimer/TpSetWait/NtQueueApcThread Implemented in Rust, three modes + call-stack spoofing + heap encryption. Dynamically registers CFG (Control Flow Guard) targets

Actual attack flow (Ekko example):

1. CreateTimerQueueTimer registers the callback → points to NtContinue (with forged CONTEXT)
2. Callback fires: VirtualProtect(payload, RW) → SystemFunction032 (RC4 encryption)
3. Sleep (wait interval)
4. Second timer fires: SystemFunction032 (RC4 decryption) → VirtualProtect(payload, RX)
5. NtContinue restores the original thread context → Beacon resumes execution
6. Loop

Detection and countermeasures: - Hunt-Sleeping-Beacons (HSB): enumerates timers and analyzes whether callback addresses point to NtContinue → detects Sleep Obfuscation - EkkoMod bypasses HSB: the Timer callback pointer points to the 8 bytes before NtContinue (nop instruction 0F 1F 84 00 00 00 00 00) → at execution nop → NtContinue → HSB does not recognize it as an NtContinue callback - Stack Duplication: duplicate the thread's registers + stack (including return addresses) → during sleep the call stack looks like a normal callback thread → avoids the stack containing the NtSignalAndWaitForSingleObject IOC - Module Stomping combined: load the payload into a legitimate DLL's memory region → memory appears to belong to a legitimate module → avoids unbacked-memory detection - Memory state transitions: the safe approach is RW↔RX (never going through RWX) → Havoc's Sleep Mask uses RWX and is easily flagged by EDR

Linux Sleep Obfuscation (SilentPulse): - A Linux version appeared in 2025: uses POSIX timer_create + SIGEV_THREAD → encrypt/decrypt in the callback - Faces similar challenges: detectable by stack analysis → requires evasion similar to Stack Duplication - Tools: Ekko (archived) / Hypnus (Rust, recommended)

15.2 Evolution of Indirect Syscalls

The evolution from direct to indirect syscalls: - Direct Syscall: hardcode the syscall instruction in your own code → EDR detects via call-stack analysis that the syscall is not inside ntdll → flagged - Indirect Syscall: find the address of a syscall; ret instruction in ntdll → jmp to that address and execute → the call stack shows the return address inside ntdll → looks legitimate - Tartarus' Gate: randomize the syscall stub address at runtime → a different stub each execution → avoids hardcoded signatures - HalosGate/TartarusGate: when ntdll is hooked (the first few instructions replaced with jmp) → search for adjacent syscall stubs → skip the hook and find a clean stub - Mockingjay: doesn't need ntdll! Searches loaded legitimate DLLs for a syscall; ret instruction sequence → no involvement with ntdll at all → no ntdll hook to detect - RecycledGate: searches loaded DLLs for syscall instruction sequences → a more generalized Mockingjay

API Hashing (tool level): - Avoid GetModuleHandle+GetProcAddress (monitored) → manually implement module traversal + function-name hash matching → fully evade API monitoring - In practice: toxoglosser uses GetModuleHandle+GetProcAddress via hashing + Tartarus' Gate + no LazyDLL

15.3 Evolution of ETW/AMSI Bypasses

ETW Patching (event tracing): - Method 1: modify the EtwEventWrite entry to ret 0 (return immediately) → all ETW events are silently dropped - Method 2: lower-level patch → modify the first few bytes of ntdll!EtwEventWrite → does not trigger memory protection - Detection: EDR checks ntdll memory integrity → must restore after patching or use hardware breakpoints instead

AMSI Bypass (Anti-Malware Scan Interface): - Method 1: patch the AmsiScanBuffer/AmsiScanString entry to return immediately → all content scans return "clean" - Method 2: hardware breakpoint hooking → set a breakpoint on AmsiScanBuffer via DR registers → no memory modification → stealthier - Method 3: CLM (Constrained Language Mode) bypass → bypass the execution policy in PowerShell restricted mode - Evolution: from highly signatured scripts like [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils') → to C# inline compilation or direct syscall-level patches

15.4 Evolution of Injection Techniques

Module Stomping: - Principle: load a legitimate DLL → overwrite its memory contents with the malicious payload → memory appears to belong to a legitimate DLL - Advantage: avoids unbacked memory (memory with no backing file) → common EDR memory-scan IOCs become ineffective - In practice: choose an uncommon system DLL to stomp → reduces the chance of inspection

Map Injection: - Principle: manually parse the PE → map it section-by-section into the target process → no LoadLibrary → no module-load events - Advantage: does not trigger DllMain notifications → does not appear in the module list

Early Bird APC Injection: - Principle: create a suspended process → inject via QueueUserAPC → ResumeThread → the APC executes before the main thread initializes - Advantage: completes injection before EDR installs its hooks → bypasses process-creation monitoring

Hardware Breakpoint Injection: - Principle: set hardware breakpoints using the DR0-DR3 registers → no code memory modified → execute malicious logic when the breakpoint fires - Advantage: no memory modification → EDR integrity checks cannot detect it