← Documentation / Operator Guide

Evasion Guide

What each technique does, what it defeats, and known limitations

What each evasion technique does, what detection it defeats, and known limitations. Most flags compose freely, but some are mutually exclusive or have precedence rules — see Flag Compatibility & Precedence.

Always-On Techniques

These are compiled into every agent. No build flag needed.

Indirect Syscalls

What it does: Resolves syscall numbers at runtime using HalosGate (read SSN from ntdll, fall back to adjacent syscall if hooked) and executes via a jmp gadget pointing to the syscall; ret instruction inside ntdll. All sensitive operations — memory allocation, thread creation, context manipulation, section mapping — go through this path.

What it defeats:

  • Userland API hooking (EDR inline hooks on ntdll)
  • API call monitoring via IAT hooks

Limitations:

  • Kernel-level ETW telemetry still fires (e.g., EtwTi provider)
  • Some EDRs hook at the kernel level via callbacks, not userland hooks
  • HalosGate may fail if adjacent syscalls are also hooked (TartarusGate handles this)

Ekko Sleep Obfuscation

What it does: During sleep, the implant encrypts its own memory using a ROP chain:

  1. VirtualProtect → make .text RW
  2. SystemFunction032 → RC4 encrypt .text + .data sections
  3. WaitForSingleObject → sleep for the configured interval
  4. SystemFunction032 → RC4 decrypt
  5. VirtualProtect → restore .text to RX

Thread context is encrypted during the sleep window.

What it defeats:

  • Memory scanners that look for implant signatures during idle periods
  • Periodic memory integrity scans by EDR
  • Pattern matching on code sections

Limitations:

  • The ROP gadgets themselves are in memory (but they’re legitimate ntdll/kernel32 addresses)
  • Automatically disabled during keylogger operation (keylogger needs active polling)
  • Short sleep intervals reduce the window of protection

Sleep Mask

What it does: XOR-encrypts sensitive runtime state during Ekko sleep with a per-cycle random key:

  • HTTP/HTTPS config (server URL, paths, headers)
  • Child agent table
  • SOCKS channel state
  • Traffic profile strings

Combined with Ekko (.text RC4), stack encryption, and Schannel transport: zero cleartext IOC surfaces exist during sleep.

What it defeats:

  • Data section string extraction during sleep
  • Memory forensics recovery of C2 server URLs, User-Agent strings, API paths

Limitations:

  • State must be decrypted before use on wake — small window of exposure during active operation

Schannel TLS (No WinHTTP)

What it does: HTTPS transport uses raw Winsock sockets + Windows Schannel SSPI for TLS, with manual HTTP/1.1 request framing. No winhttp.dll dependency.

What it defeats:

  • WinHTTP internal heap cache IOC leakage (SSL sessions, URL buffers survive WinHttpCloseHandle)
  • winhttp.dll import table detection (legitimate processes rarely import it)
  • WinHTTP-specific behavioral monitoring

Limitations:

  • Schannel SSPI is more complex to maintain than WinHTTP
  • secur32.dll appears in imports (but this is expected for many legitimate processes)

Secure Free

What it does: Calls SecureZeroMemory before every HeapFree and VirtualFree across 45+ callsites. Freed memory is zeroed before being returned to the heap.

What it defeats:

  • Memory forensics recovery of freed allocations
  • Volatility-style heap analysis finding old strings, keys, or config data

Limitations:

  • Small performance cost (negligible in practice)
  • Doesn’t prevent the OS from paging memory to disk before it’s freed

PPID Spoofing

What it does: When spawning sacrificial processes (for migration or execute-assembly), uses PROC_THREAD_ATTRIBUTE_PARENT_PROCESS to set the parent to explorer.exe.

What it defeats:

  • Process tree analysis (child of cmd.exe or powershell.exe is suspicious)
  • Parent-child relationship heuristics

Evasion Flags

Hardware Breakpoint AMSI/ETW Bypass (--hwbp-bypass)

What it does: Installs a Vectored Exception Handler (VEH) that uses debug registers (DR0-DR3) to set hardware breakpoints on:

  • AmsiScanBuffer — returns S_OK + AMSI_RESULT_CLEAN (assembly declared clean)
  • NtTraceEvent — returns ERROR_SUCCESS (ETW event silently dropped)
  • GetStdHandle — redirects .NET console output to the capture pipe
  • ExitProcess — prevents assembly from killing the host process

What it defeats:

  • AMSI scanning of .NET assemblies and PowerShell
  • ETW telemetry for .NET runtime events, assembly load events
  • .text section integrity scans (no bytes are modified — breakpoints are in CPU registers)

Limitations:

  • Only 4 hardware breakpoints available (DR0-DR3) — all are used
  • A debugger or anti-cheat that uses debug registers will conflict
  • Doesn’t suppress kernel-mode callbacks (use --loadlib-proxy for that)

HWBP All Threads (--hwbp-all-threads)

What it does: Extends --hwbp-bypass to apply hardware breakpoints to all threads in the process, not just the main thread. Enumerates threads via NtQuerySystemInformation and sets DR0-DR3 on each via NtSetContextThread.

What it defeats:

  • EDR spawning worker threads to bypass single-thread HWBP hooks
  • CLR threadpool threads calling AmsiScanBuffer outside the hooked thread

Limitations:

  • Thread enumeration adds overhead (noticeable in processes with hundreds of threads)
  • New threads created after the hook is set are not automatically covered

Implies --hwbp-bypass.


Stack Spoofing (--stack-spoof)

What it does: During Ekko sleep, fabricates legitimate-looking thread stack frames using return addresses pointing to real ntdll.dll and kernel32.dll gadgets. The sleeping thread’s stack looks like a normal system thread.

What it defeats:

  • EDR thread stack-walk analysis
  • Stack-based attribution (tracing execution back to injected code)

Limitations:

  • Only active during sleep — during execution, the real stack is visible
  • Stack frame patterns may not match every EDR’s heuristic (but covers the major ones)

Stack Encryption (--stack-encrypt)

What it does: XOR-encrypts the poll thread’s entire used stack region during Ekko sleep. Prevents recovery of stack-resident strings, return addresses, and local variables.

What it defeats:

  • Memory scanners recovering URLs, paths, and config strings from the stack
  • Return address analysis during sleep

Limitations:

  • Only the poll thread stack is encrypted — other threads are not affected
  • Stack must be decrypted on wake before resuming

ETW NOP Call (--etw-nop-call)

What it does: Patches EtwpEventWriteFull in ntdll with xor eax, eax; ret (return success, do nothing). Suppresses all user-mode ETW event generation at the source — no events reach any ETW session.

What it defeats:

  • User-mode ETW providers (Microsoft-Windows-DotNETRuntime, Security-Auditing, Threat-Intelligence)
  • Process Monitor, ProcMon-style ETW consumers
  • EDR user-mode ETW-based detections

Limitations:

  • Byte-patches ntdll .text section — detectable by .text integrity scans
  • Kernel-mode ETW providers (EtwTi) are unaffected
  • Breaks legitimate ETW consumers in the same process

Module Stomping (--module-stomp)

What it does: Instead of allocating fresh memory with VirtualAlloc, the PE mapper stub loads a sacrificial system DLL (xpsservices.dll, ~1.4MB) via LoadLibraryExA with DONT_RESOLVE_DLL_REFERENCES (maps the DLL but skips DllMain and dependency loading). The stub then overwrites the DLL’s sections with the loader PE — relocations, imports, and protections applied in-place. The DLL’s PEB EntryPoint is nulled to prevent a crash at process exit.

What it defeats:

  • Unbacked memory detection — VAD tree shows xpsservices.dll (file-backed SEC_IMAGE) at the loader PE address, not a private RWX allocation
  • VirtualAlloc behavioral monitoring — no RWX memory allocation, no MEM_COMMIT/MEM_RESERVE calls for the loader PE
  • pe-sieve header scanning — PE headers are zeroed after mapping (before thread launch)

Limitations:

  • pe-sieve disk-to-memory comparison will detect modified code (accepted as residual risk; Ekko encrypts .text during sleep, making comparisons produce garbage)
  • Loader PE must fit within the DLL’s SizeOfImage (~1.4MB for xpsservices.dll)
  • Module-stomped migration requires DLL pre-loading on the stub thread to avoid a loader lock deadlock during Early Bird APC injection (schannel.dll, mswsock.dll, ncryptsslp.dll, bcryptprimitives.dll + Winsock init)
  • Falls back to VirtualAlloc if the target DLL is unavailable or too small

Module Overloading (--module-overload)

What it does: Maps a clean DLL as SEC_IMAGE (NtCreateSection + NtMapViewOfSection), then overwrites the section with payload — manual relocation and import resolution. No PEB loader-list entry (unlike LoadLibrary-based stomping).

What it defeats:

  • Unbacked memory detection (VAD shows legitimate DLL file backing)
  • PEB loader-list analysis (no entry for the overloaded module)

Limitations:

  • pe-sieve disk-to-memory comparison still detects modified code
  • Mutually exclusive with --module-stomp

LoadLibrary Proxy (--loadlib-proxy)

What it does: During execute-assembly, hooks LdrLoadDll in ntdll and selectively manual-maps CLR-specific DLLs (clr.dll, clrjit.dll) to suppress kernel-mode PsSetLoadImageNotifyRoutine callbacks. System DLLs load normally. Includes API set resolution (PEB->ApiSetMap), TLS directory processing, and fail-open design.

What it defeats:

  • Kernel-mode image-load notifications for CLR DLLs
  • EDR callbacks that trigger on clr.dll/clrjit.dll loading
  • User-mode ETW DLL-load events for filtered DLLs

Limitations:

  • Increases the CLR stub size (~8KB → ~20KB, must fit within mswsock.dll .text)
  • Only filters clr.dll and clrjit.dll (system DLLs use original loader)
  • Falls back to original LdrLoadDll on mapping failure (fail-open)
  • .NET Framework 4.x targeting only

Tampered Syscalls (--tampered-syscalls)

What it does: Instead of executing syscall from user-allocated memory, calls a decoy ntdll function (NtQuerySecurityObject). A VEH handler intercepts at the syscall instruction boundary via DR3 hardware breakpoint, swaps the SSN and register arguments to the real target. The syscall instruction executes from ntdll’s own stub — return address points into ntdll.

What it defeats:

  • SSN-based detection (SyscallDetect, InlineWhispers monitoring)
  • Return-address analysis (call stack shows ntdll, not injected code)
  • User-memory syscall instruction scanning

Limitations:

  • Uses DR3 (leaves DR0-DR1 for HWBP AMSI/ETW bypass)
  • DR3 conflict with any debugger or anti-cheat using that register
  • Slight overhead per syscall (VEH dispatch + register swap)

PE Fluctuation (--pe-fluctuation)

What it does: Timer-driven encrypt-at-rest for the mapped PE. VEH handler decrypts on ACCESS_VIOLATION (demand-paging pattern). Only the accessed page is live at any moment. Fresh RC4 key per resume cycle via QPC + PID + TID. Complement to Ekko — even between Ekko cycles, the PE image is encrypted.

What it defeats:

  • Continuous memory scanning (PE is encrypted even during active operation)
  • Memory dump analysis (only one page decrypted at a time)

Limitations:

  • VEH dispatch overhead on every code page access (mitigated by 500ms encrypt timer)
  • InterlockedCompareExchange required to prevent double-RC4 corruption

Heap Encryption (--heap-encrypt)

What it does: During Ekko sleep, calls HeapWalk() on all non-default process heaps and XOR-encrypts their contents. Catches arbitrary heap allocations from BOF output, temporary buffers, and leaked data that SleepMaskRegister() doesn’t know about.

What it defeats:

  • Heap forensics during sleep (volatility, WinDbg heap analysis)
  • Memory scanners finding BOF output strings in heap allocations

Limitations:

  • HeapWalk is relatively expensive — adds ~5-15ms to sleep/wake cycle
  • Default process heap is excluded (system allocations)

Foliage Sleep Obfuscation (--sleep-method foliage)

What it does: APC-based alternative to Ekko. Creates a suspended thread, builds a 7-step ROP chain via CONTEXT structs (NtWaitForSingleObject → VirtualProtect(RW) → SF032(encrypt) → WaitForSingleObjectEx(sleep) → SF032(decrypt) → VirtualProtect(RX) → ExitThread), queues each as an NtContinue APC.

What it defeats:

  • Same as Ekko (memory scanning during sleep) but with a different behavioral fingerprint
  • EDRs that specifically signature Ekko’s shellcode-stub pattern

Limitations:

  • No RWX shellcode page (that’s the advantage, not a limitation)
  • Stack-spoof and stack-encrypt are Ekko-specific (not compatible with Foliage)

Peer Reconnect (--peer-reconnect)

What it does: SMB/TCP child agents fall back to direct HTTPS beaconing if the parent agent dies. After a configurable timeout (default: 5 minutes), the child stops waiting for the parent and begins polling the C2 server directly.

What it defeats:

  • Loss of access when a parent agent is killed, migrated, or the parent host goes down

Limitations:

  • Child must have network egress to the C2 server (or redirector) for the fallback to work
  • Only applies to SMB and TCP child agents — HTTPS agents already connect directly

Migration Techniques

Multiple injection techniques are available for process migration. Each defeats different behavioral detections.

TechniqueFlagDefeats
EarlyBird APC(default)Classic, well-known
Thread context hijack--context-hijackEarlyBird APC detection
Section-based injection--section-mapNtWriteVirtualMemory detection
Delayed suspend--delayed-suspendCREATE_SUSPENDED callback detection
Phantom DLL hollowing--phantom-dllUnbacked memory detection
Remote thread execution--remote-threadNtSetContextThread detection (incompatible with --context-hijack — child thread lacks csrss registration, SSPI/TLS unavailable)
Process ghosting--ghostOn-disk AV scanning (file deleted before process runs)
Process herpaderping--herpaderpOn-disk AV scanning (file content changed after mapping)

Post-injection cleanup (SecureZeroMemory of injection blob + stub + config) is always performed.

Process Ghosting (--ghost)

What it does: Creates a file, writes the payload, marks it delete-pending via NtSetInformationFile(FileDispositionInformation), creates SEC_IMAGE section from the delete-pending file, creates process via NtCreateProcessEx, then closes the file handle (file disappears). The process runs from a file that no longer exists on disk.

What it defeats:

  • On-disk AV scanning (file is gone before process runs)
  • Forensic file recovery (delete-pending files are unlinked)

Limitations:

  • ETW kernel callbacks still fire for process creation
  • Some EDRs monitor NtSetInformationFile + NtCreateSection sequences

Process Herpaderping (--herpaderp)

What it does: Creates a file, writes the payload, creates SEC_IMAGE section from it, then overwrites the file content with a benign decoy (e.g., legitimate svchost.exe bytes) before creating the process. When AV/EDR reads the file for scanning, it sees the decoy — but the section mapped into memory still contains the original payload.

What it defeats:

  • On-disk AV scanning (file reads show benign content after section creation)
  • File-hash analysis (on-disk hash doesn’t match in-memory content)
  • Forensic file recovery (file on disk is the decoy, not the payload)

Limitations:

  • ETW kernel callbacks still fire for process creation
  • Mutually exclusive with --ghost
  • Requires write access to a temp directory

Flag Compatibility & Precedence

What “All Evasion” Means

--all-evasion (or the “All Evasion” toggle in the Web UI) enables the maximum compatible set of evasion flags. It does not blindly enable every flag — four are excluded to avoid conflicts:

Excluded FlagReason
--remote-threadMutually exclusive with --context-hijack. Creates a thread not registered with csrss.exe — SSPI/TLS is unavailable in the child.
--module-overloadRedundant with --module-stomp (stomp takes precedence at runtime).
--ghostProcess-level migration technique — replaces the entire migration flow. Use explicitly when needed.
--herpaderpSame category as ghost. Use explicitly.

The flags included by --all-evasion:

--hwbp-bypass --hwbp-all-threads --stack-spoof --stack-encrypt --module-stomp --foliage-sleep --delayed-suspend --phantom-dll --section-map --context-hijack --tampered-syscalls --peer-reconnect --loadlib-proxy --pe-fluctuation --heap-encrypt --etw-nop-call


Mutual Exclusions

These flag pairs cannot be used together. The build system will error if both are set.

Flag AFlag BWhy
--remote-thread--context-hijackRemote thread creates via NtCreateThreadEx (no csrss registration → SSPI/TLS broken in child). Context hijack redirects the main thread (properly registered → SSPI works).

These pairs are not hard errors, but one silently wins:

Flag A (wins)Flag B (ignored)Why
--module-stomp--module-overloadBoth set allocMode in the PE mapper stub. Stomp is checked first.
--ghost--herpaderpGhost returns early from MigrateToProcess(). Herpaderp never runs.
--ghost / --herpaderpAll other migration flagsGhost and herpaderp replace the entire injection pipeline. Section-map, phantom-dll, context-hijack, delayed-suspend are all bypassed.

Precedence Rules

When multiple flags are set, runtime if/else chains determine which technique runs. Higher in the list wins.

Execution trigger (how the injected code starts running):

PriorityFlagMethod
1--remote-threadNtCreateThreadEx — new thread in target
2--context-hijackNtSetContextThread — hijack main thread RIP
3(default)NtQueueApcThread — EarlyBird APC on main thread

Memory allocation (where the injection blob lives):

PriorityFlagMethod
1--phantom-dllSEC_IMAGE map of dbghelp.dll → overwrite .text section
2--section-mapAnonymous shared section (NtCreateSection + NtMapViewOfSection)
3(default)NtAllocateVirtualMemory (standard RWX allocation)

Process creation (how the sacrificial process is spawned):

PriorityFlagMethod
1--remote-threadCreateProcess non-suspended, wait 1s, inject via NtCreateThreadEx
2--delayed-suspendCreateProcess non-suspended, wait 1s, suspend main thread
3(default)CreateProcess suspended (CREATE_SUSPENDED)

Sleep obfuscation:

PriorityFlagMethod
1--sleep-method foliageAPC-based NtContinue chain (7-step ROP)
2(default)Ekko shellcode stub (timer-based ROP)

Both fall back to plain Sleep() on failure.


Dependencies

FlagRequiresNotes
--hwbp-all-threads--hwbp-bypassAll-threads extends the base HWBP hook to cover every thread. The build system auto-enables --hwbp-bypass when --hwbp-all-threads is set.
--stack-spoofEkko sleep (not Foliage)Stack spoofing is implemented inside the Ekko stub. When --sleep-method foliage is set, --stack-spoof is silently ignored.
--stack-encryptEkko sleep (not Foliage)Same as stack-spoof — Ekko-only feature.

Maximum evasion (recommended): --all-evasion

Uses context-hijack, module-stomp, phantom-dll, section-map, delayed-suspend, foliage, all HWBP hooks, tampered syscalls, PE fluctuation, heap/stack encryption, and ETW suppression. Covers memory, behavioral, and network-level detections.

Stealth with TLS integrity: --hwbp-bypass --hwbp-all-threads --context-hijack --section-map --phantom-dll --tampered-syscalls --pe-fluctuation --heap-encrypt --etw-nop-call

Skips foliage (uses Ekko) to retain stack-spoof and stack-encrypt. Good when you need maximum sleep-time obfuscation.

Minimal footprint: --hwbp-bypass --tampered-syscalls

Just AMSI/ETW bypass and indirect syscall hardening. Smallest behavioral change, lowest detection surface.

Process ghosting (standalone): --ghost

Replaces the standard migration pipeline entirely. Do not combine with other migration flags — they are ignored.


Runtime Interactions

These behaviors happen automatically at runtime regardless of build flags:

ConditionEffect
CLR loaded (execute-assembly)Ekko and Foliage disabled — background GC/JIT threads crash during .text encryption. Falls back to plain Sleep.
Keylogger activeEkko and Foliage disabled — keylogger polling thread needs active .text.
Migrated child processEkko always disabled (noEkko=TRUE) — manually-mapped PE has different .text boundaries, ROP gadget chain unreliable.
PE fluctuation + Ekko/FoliageCoordinated automatically — PE fluctuation suspends its timer before Ekko/Foliage encrypts .text, resumes after decryption. Both encrypting .text simultaneously would corrupt memory.

Traffic & Identity

Traffic Mimicry (--profile)

What it does: Shapes C2 HTTPS traffic to mimic legitimate SaaS API patterns. Each profile sets:

  • URI paths (e.g., /api/conversations.history for Slack)
  • User-Agent string matching the real application
  • Content-Type headers
  • Authorization headers (Bearer tokens)
  • JSON body wrappers (request payloads embedded as base64 inside realistic JSON)

What it defeats:

  • Network traffic analysis and IDS signatures
  • SOC alert triage (traffic looks like legitimate SaaS API calls)
  • Proxy categorization

Limitations:

  • Profile must match on both implant and redirector (see Deployment Guide)
  • Deep inspection of JSON payload structure may reveal anomalies
  • Certificate mismatch if not using domain fronting or proper TLS termination

Process Masquerade (--cover)

What it does: Spoofs PE version info resources (CompanyName, ProductName, InternalName, OriginalFilename, FileDescription) and PE timestamps to match a legitimate Windows process.

What it defeats:

  • Process metadata analysis (task manager, Process Explorer column inspection)
  • Automated process ancestry/legitimacy checks

Limitations:

  • Binary hash won’t match the real process
  • PEB ImagePathName still shows the actual path (unless combined with PPID spoofing + spawn from correct directory)

Auto-selected when --profile is used. --cover overrides.


BOF-Based Techniques

DLL Notification Unhooking (unhook-dllnotif)

What it does: Walks the LdrDllNotificationList in ntdll and removes EDR-registered DLL load notification callbacks.

What it defeats:

  • EDR DLL load monitoring (the EDR stops receiving notifications about new DLLs being loaded)

Limitations:

  • The EDR may detect that its callback was removed
  • Must be run after the EDR has registered its callback

Usage: unhook-dllnotif (BOF command, not a build flag)


Threadless Injection (threadless-inject)

What it does: Hooks a target process’s export function first 5 bytes with a CALL to a “memory hole” (unused region within ±1.75GB of the hook site). The memory hole contains 63 bytes of self-healing shellcode that restores the original 8 bytes before executing the payload.

What it defeats:

  • Thread creation monitoring (no new thread)
  • APC queue monitoring (no APC)
  • Context modification detection (no SetContextThread)

Limitations:

  • Payload executes only when target naturally calls the hooked export
  • Must find a suitable memory hole near the hook site

Usage: threadless-inject <pid> <dll> <export> <sc_file> (BOF command)


ETW Hijack (etw-hijack)

What it does: Restarts active ETW tracing sessions with a fake log file path, effectively blinding any ETW consumer (EDR, Sysmon, ProcMon) that relies on the hijacked session.

What it defeats:

  • ETW-based EDR telemetry (the session is redirected to a dead-end log)
  • Sysmon event collection (if the Sysmon ETW session is targeted)

Limitations:

  • The original ETW consumer may detect the session was restarted
  • Must have sufficient privileges to restart ETW sessions

Usage: etw-hijack (BOF command)


Hook Detection (detect-hooks)

What it does: Compares loaded ntdll/kernel32/kernelbase against clean copies from \KnownDlls to identify inline hooks placed by EDR.

Usage: detect-hooks / detect-hooks -v (BOF command — informational, not evasive)