Why Windows breaks the way it does.
Nine chapters. No vendor slides. No certification prep. Every technique here was run against real systems — engagements, lab targets, or disclosed vulnerabilities we found and filed. If something is no longer reliable, it's marked obsolete. If it's been proven under conditions that matter, it's marked validated.
The chapters build on each other. Chapter 1 is the only one you can safely skip if you already know CPU privilege rings. Every chapter after it assumes you read the one before.
Each chapter ends with a live CVE box — current Microsoft vulnerabilities using the same primitive, pulled from the MSRC API after each Patch Tuesday. The technique in the chapter is the same technique attackers are using this month, against the CVEs in that box.
This isn't a learning path to a cert. It's a map of how Windows actually breaks. Start at the beginning if you're new to internals. Jump to the chapter that matches the CVE you're reversing if you're not.
A network is just two machines with a convention about how to label their data. An IP address identifies the machine. A port identifies the process on that machine. Together they form an endpoint — the address your shell connects to, and the address your C2 listener waits on.
TCP (Transmission Control Protocol) is the transport under everything. Before data flows, both sides complete a three-way handshake: SYN → SYN-ACK → ACK. If the SYN is dropped by a firewall, the handshake never completes. Your listener waits forever. Nothing arrives.
This is why reverse shells exist. A reverse shell has the victim initiate the connection — calling out to the attacker. Outbound traffic is almost never blocked on Windows. The victim calls home, the attacker answers. A bind shell has the attacker connect to a listener on the victim, which requires an inbound port to be open — rarely available on a hardened target.
A socket is a file descriptor. You write bytes to it and they travel across the network. You read bytes from it and they came from the other end. The OS handles routing, retransmission, checksums. Your code does four things: socket() → bind()/connect() → send()/recv() → close(). That's the entire API. A C2 implant is a program that does this in a loop: connect to the C2 server, receive a command, execute it, send back the output, close the connection, sleep, repeat.
A C2 beacon uses short-lived connections rather than a permanent open socket. A persistent TCP connection shows as an unusual long-lived flow in NetFlow analysis. Short-lived HTTPS connections to a CDN look like normal web browsing. The implant connects for seconds, collects a command or confirms check-in, and disconnects. Jitter — randomising the sleep interval — prevents regular intervals from becoming a detection signature.
The errors that actually stop operations
These errors fired during ring-stack kill chain development against a live Windows 11 target. Every one of them is a networking problem:
- Connection refused — nothing listening on the target port. Listener not running, wrong port, wrong interface.
- Listener waits forever — SYN dropped by firewall. Switch to reverse shell on port 443 or 80.
- Error 67 (SMB) — UNC path trying to reach port 445, blocked at network boundary. Switch exfil to HTTP or SCP.
- Shell connects then drops immediately — WaitForSingleObject overflow in PowerShell. Use
[uint32]::MaxValuenot0xFFFFFFFF. - send() returns -1 / WSAECONNRESET — remote end closed. Add reconnect loop — production implants always retry on failure.
ring-stack Phase 0 (networking theory) and Phase 1 (C2 listener in C) and Phase 2 (reverse shell in C) complete. All errors above documented from live testing against Windows 11 target 192.168.1.61. Phase 10 live test: 3 Edge passwords exfiltrated to C2, 504-byte zip received, exit code 0, Defender silent.
Your CPU has privilege levels called rings. Ring 0 is kernel mode — the OS kernel runs here, hardware drivers run here. Ring 3 is user mode — every application you've opened, every browser tab, every piece of software ever installed on your machine. In Ring 3, the CPU actively filters everything. You can't directly touch hardware. You can't read another process's memory. You can't even look at the kernel without going through a tiny, guarded doorway called a syscall.
This isn't a software policy. The CPU itself enforces it at the hardware level. A user-mode process that attempts to read kernel memory doesn't get an error message — it gets terminated by an access fault. The CPU interrupted execution, checked the privilege level, and killed the attempt before anything happened.
Every attacker who has ever escalated privilege on Windows hit this wall first. The question is never "can I break through the ring boundary?" — you can't, not directly from Ring 3. The question is always: is there a door that's already open?
Doors that are already open exist in two forms. First: signed kernel drivers. These run in Ring 0 by definition, signed by Microsoft or a trusted vendor. If a driver has a bug that lets user space control its kernel operations, you inherit Ring 0 access without ever touching the protection boundary. Second: privileged user-mode services that have been granted specific capabilities by the OS — capabilities that, under the right conditions, let you swap your identity to something higher.
Everything in the following chapters is a variation on one of those two doors. The ring model is the constraint that makes all privilege escalation necessary. If you understand it, you understand why every technique described here exists.
CPU ring model is hardware architecture — Intel and AMD documentation, confirmed through direct kernel driver development (ring-stack). No exceptions, no caveats. This is how the CPU works.
That number is a handle. It's an index into a table stored in your process's kernel structure — the HandleTable field inside EPROCESS. The table maps your handle value to the real kernel object: the file, the process, the mutex, the token, whatever you opened. The access rights you were granted at open time are frozen inside that table entry. Windows does not re-check them on subsequent operations.
This freezing property is the entire basis of handle-based attacks. Whoever holds a handle carries whatever rights were embedded in it at creation. If a high-privilege process opens a handle to a sensitive object and you can access that handle — through inheritance, through DuplicateHandle, through a handle leak — you have that privilege on that object. The rights travel with the handle, not with the process identity.
DLL hijacking is the file-path version of this same principle. When a process loads a DLL, it walks a search path: the application directory, System32, other registered paths. If you control a directory earlier in the search path than the legitimate DLL, your code loads instead. The process's token gave you the rights to write there. The loading mechanism trusted the path. The gap between "who can write here" and "who should be trusted to provide this library" is the vulnerability.
The information disclosure class of CVEs mostly serves one goal: map kernel object addresses you weren't supposed to know. Before you can duplicate a handle you didn't legitimately acquire, you need to find it. NtQuerySystemInformation(SystemHandleInformation) enumerates the system-wide handle table — object type, process ID, handle value, object address. Every info-disclosure bug that leaks a kernel pointer is helping an attacker build this map.
Handle table structure confirmed via WinDbg inspection of live kernel. DLL hijacking validated across multiple targets. NtQuerySystemInformation handle enumeration runs without elevation.
Every process running on Windows has an access token. That token is a kernel object encoding three things: which user logged in, which groups they belong to, and which privileges are currently enabled. When your code calls CreateFile, the kernel looks at your token. When you spawn a child process, it inherits your token. Your identity, to Windows, is the contents of that token object.
Privileges are the important part. There are dozens of them. Most are disabled by default and cannot be enabled without elevation. But service accounts that receive client connections and impersonate those clients — by design, as part of their legitimate function — are granted SeImpersonatePrivilege. This privilege authorises the holder to call Windows APIs that adopt another thread's identity: ImpersonateNamedPipeClient, ImpersonateLoggedOnUser, CreateProcessWithTokenW.
The Windows design assumption was: only trusted service accounts will have this privilege, and they'll only use it on clients they're legitimately serving. The attacker's read: if you land on a service account with SeImpersonatePrivilege, and you can get a SYSTEM-level process to interact with an object you control, you can impersonate SYSTEM. One call to ImpersonateNamedPipeClient, and the thread token swaps. Your process is now running as SYSTEM.
The impersonation APIs haven't changed. What changes is the coercion vector — how you get SYSTEM to connect to your object in the first place. PrintSpoofer used the print subsystem. Potato techniques used COM activation. JuicyPotato used scheduled task activation. Each time Microsoft closes one coercion path, researchers find the next service account that can be coerced. The privilege and the API remain.
Token stealing is the direct version: open a handle to a SYSTEM process, duplicate its token with DuplicateTokenEx, call CreateProcessWithTokenW. No coercion needed — but you need SeDebugPrivilege to open a handle to a protected process. SeDebug is typically available to local administrators. If you've escalated to a local admin, this is the path to SYSTEM.
SeImpersonatePrivilege exploitation confirmed live. Token duplication from admin to SYSTEM confirmed. rainfantry/UAC-to-SYSTEM-POC documents the full chain.
Named pipes are a local IPC mechanism. A server process creates a pipe with a name. A client connects to that name. Once connected, the server calls ImpersonateNamedPipeClient and the server's thread adopts the client's token for the duration of the call. Microsoft built this for print servers: the server needs to print as the connecting user. The API does exactly what it advertises.
The attack is PrintSpoofer. The Windows Print Spooler runs as SYSTEM and exposes an RPC function, RpcOpenPrinter, that accepts a printer path and connects to it. The path format includes a port component. If you craft that port component to point to a named pipe you own — using the UNC path format Windows expects for shared printer objects — the Spooler, running as SYSTEM, connects to your pipe as a client. You call ImpersonateNamedPipeClient. Your thread is now running as SYSTEM.
[+] Named pipe created: \\.\pipe\test\pipe\spoolss
[+] Calling RpcOpenPrinter to coerce Spooler connection...
[+] Client connected. Calling ImpersonateNamedPipeClient...
[+] Got impersonation token: NT AUTHORITY\SYSTEM
[+] Spawning: cmd.exe
Microsoft Windows [Version 10.0.19045.3803]
C:\Windows\system32>
The -c flag replaces cmd.exe with your C2 agent. The Spooler connects, you call CreateProcessWithTokenW with the SYSTEM token and your payload path. The payload runs as SYSTEM with no elevation prompt, no UAC bypass required.
The specific Spooler coercion path was patched. Then researchers found a different RpcOpenPrinter variant. Then another via the MSDTC service. Then via DCOM object activation. The underlying mechanism — ImpersonateNamedPipeClient on a coerced SYSTEM connection — never changed. Each patch closed one coercion source. New coercion sources are found by reading Windows RPC interfaces and asking: which service runs as SYSTEM and can be induced to connect to a path I specify?
PrintSpoofer technique confirmed on Windows 10 pre-patch. Spooler service coercion confirmed live. Patched on modern fully-updated systems — lateral movement via service accounts on unpatched domain hosts remains a live finding.
Original PrintSpooler vector patched. JuicyPotatoNG and similar tools are widely signatured by 2026. Coercion via less-known RPC services still viable on unpatched internal systems. Enumeration-first approach required.
This is TOCTOU — Time of Check to Time of Use. Every security decision has this property: there is a moment when the check happens, and a later moment when the action occurs. If the object being checked can be replaced between those two moments, the check measured something that no longer exists. The action happens against a different object than the one that was evaluated.
Windows Defender's MiniFilter driver, WdFilter, hooks IRP_MJ_WRITE — the I/O Request Packet for file write operations. When data is written to a path, the filter intercepts and scans it. If the content is clean, the write completes. The quarantine decision, if triggered, is asynchronous — it happens in a separate work item after the initial scan callback returns. Between scan and action: the gap.
NTFS gives attackers the tool to exploit this gap: junction points and directory symlinks. You can atomically redirect what a path resolves to without modifying the file itself. A racing thread substitutes the real payload during the scan window. The scanner evaluates the benign version at check time. The loader executes the malicious version at use time.
We tested this experimentally. The gap is real and measurable. The timing window varies with system load. It is not reliable enough to be a production delivery mechanism in its current form — the race window is narrow and environment-dependent. What makes it operationally relevant is the broader principle: every security product that follows a scan-then-act pattern has this gap. WdFilter has it. ETW event writers have it. Forensic collectors have it. Every one of them trusts that the object they measured is the object they're acting on.
We filed a variant with MSRC as VULN-195458 (referenced in Chapter 7 as well). Their response: user-mode functionality is outside the security boundary. Technically accurate by Microsoft's definitions. The gap still exists in production systems.
TOCTOU timing gap confirmed experimentally across multiple Windows 10/11 builds. Race window measurable. Not a reliable production primitive in isolation — requires favorable timing conditions or repeated attempts.
When AV and EDR products install on Windows, they register a callback using PsSetCreateProcessNotifyRoutine. The kernel stores these callbacks in PspCreateProcessNotifyRoutine — a 64-entry array in non-paged kernel memory. Each slot holds a pointer to the vendor's notification function. When any process is created anywhere on the machine, the kernel walks this array and calls every registered function. That's how Defender knows in real time when a new process starts.
From Ring 3, you cannot touch kernel memory. Chapter 1 established this. The door is a vulnerable signed driver. RTCore64.sys (CVE-2019-16098) is a Micro-Star hardware monitoring driver from 2018. It has a bug: IOCTL 0x80002048 accepts a user-supplied kernel address and size and reads it. IOCTL 0x8000204c accepts a kernel address and value and writes it. Arbitrary kernel read/write, from Ring 3, via a completely legitimate Microsoft-signed driver. The certificate was never revoked.
The array address changes on every boot — KASLR randomises the kernel base. So you walk the kernel's own internal structure dynamically. Load ntdll.dll, parse its PE headers, find the export directory, walk the function name table until you find PsSetCreateProcessNotifyRoutine. Follow the E8 CALL byte sequence inside it — that's an unconditional call to the internal function PspSetCreateProcessNotifyRoutine. Disassemble that function until you find a LEA RIP+offset instruction pointing at the array. Decode the relative offset. Array address resolved, without a single hardcoded value. Survives every reboot, every kernel update, every KASLR roll.
[+] PsSetCreateProcessNotifyRoutine: fffff8047f08f010
[+] PspSetCreateProcessNotifyRoutine: fffff8047f08efc0
[+] PspCreateProcessNotifyRoutine: fffff8047f0ec1e0
[+] Zeroing callback 0: fffff8045fa91b80 ← Defender
[+] Zeroing callback 1: fffff8045fb12340
[+] Zeroing callback 2: fffff8045fb98a10
[!] Done. 10 callbacks zeroed. EDR is BLIND.
[+] Spawning calc.exe — PID: 8712 ← no Defender popup
RTCore64 only supports 4-byte (DWORD) reads and writes. Each 8-byte pointer in the array requires two writes: low 4 bytes to zero, high 4 bytes to zero. The 48-byte struct passed to the driver's IOCTL encodes the target address, offset, and value. Write both halves, read back to verify, move to the next slot.
PatchGuard (KPP) monitors critical kernel structures on a randomised timer and BSODs the machine if it detects tampering. The zeroed array will trigger a BSOD eventually — typically within minutes. This is a VM primitive, not a production technique in its current form. It demonstrates the principle. The production version either disables PatchGuard first or operates within the KPP sweep window.
Phase 2 complete July 2026. 10 callbacks zeroed. calc.exe spawned, no Defender alert. Writes verified persistent across second run. VM-only due to PatchGuard. RTCore64.sys still valid signature as of test date.
AmsiScanBuffer is the function Windows calls to scan script content before execution. EtwEventWrite is the function that logs events to Event Tracing for Windows. Both live in loaded DLLs — amsi.dll and ntdll.dll — mapped into your process when it starts.
The standard bypass is to patch the function prologue: overwrite the first bytes with xor eax, eax; ret. The function immediately returns a "clean" result before doing anything. This worked for years. It stopped working when EDRs began monitoring NtProtectVirtualMemory — the syscall used to make memory executable before writing it. Any write to an executable memory region, at an address matching a known security function, is flagged instantly.
Hardware breakpoints use the CPU's debug registers directly: DR0–DR3 hold breakpoint addresses, DR7 enables them. When the instruction pointer reaches a breakpointed address, the CPU raises a debug exception before the instruction executes. No instruction ran. No bytes were written. No memory was modified. The exception fires at the hardware level.
You register a Vectored Exception Handler via AddVectoredExceptionHandler before setting the breakpoints. When the exception fires, your handler receives EXCEPTION_POINTERS with a ContextRecord containing the full CPU register state. You check that the exception code is EXCEPTION_SINGLE_STEP and the instruction pointer is at your target address. You manipulate ContextRecord.Rax to return a "clean" result. You call ContinueExecution. Execution resumes from the instruction after the one that was about to run, with the return value you substituted.
AMSI never scanned. ETW never logged. Zero bytes changed in any memory region. Zero NtProtectVirtualMemory calls. Zero memory-write events for behavior engines to catch.
We filed this technique with MSRC on 2026-06-16 as VULN-195458. Microsoft's response: in-process user-mode techniques are outside the Windows security boundary. This is correct under their definitions — the security boundary applies to cross-process attacks. The technique is valid. Detection rate as of filing: zero.
HWBP bypass confirmed: AmsiScanBuffer and EtwEventWrite bypassed in-process with zero memory writes. iron-sun layers 7–8. Filed as VULN-195458 2026-06-16. Microsoft classified outside security boundary.
Robert Cialdini spent years studying what makes people comply without deliberating. Six principles emerged: reciprocity, commitment, social proof, authority, liking, scarcity. These aren't phishing theory. They're empirically derived from behavioral research and validated across cultures. Three of them do most of the work in an engagement context.
Authority: people comply with apparent authority figures without verifying credentials. An email from "IT Security" requesting an urgent action gets acted on at a higher rate than the same email from an unknown sender. The uniform is the message. Header, signature, domain that looks right at a glance — not under scrutiny, just at a glance. Scrutiny is the second reaction. The first reaction is the one that determines behavior.
Scarcity and urgency: "Your account will be locked in 24 hours" removes the pause between "should I?" and "I already did." Urgency bypasses the deliberation step entirely. A well-timed message arriving right after a real security incident announcement multiplies this — the primed mind fills in the plausible narrative before the logic check fires.
HTML smuggling is the technical implementation of getting past email scanners to reach the human. You don't send an executable. You send an HTML file. The HTML contains a Base64-encoded blob in a JavaScript variable. The browser reads it, calls atob() to decode, creates a Blob object, calls URL.createObjectURL() to generate a local download reference, and programmatically triggers a download. The email scanner received HTML. The browser assembled the executable locally. The victim's Downloads folder now contains a payload the scanner never evaluated as executable.
Quishing (QR code phishing) routes around corporate email security entirely. The QR code resolves to a credential capture URL. It's scanned on a personal phone, on mobile data, outside the corporate proxy. The inspection layer never saw the URL. MFA-resistant proxies like evilginx2 sit in the middle of the real authentication flow — you're not capturing a password, you're proxying the actual login in real time and extracting the session token after MFA completes. The user authenticated legitimately. The session cookie is yours.
Delivery isn't a separate problem from security research. It's the AV:N/AC:L/UI:R in the CVSS vector made explicit. Every high-severity CVE that requires user interaction is a delivery problem with a technical wrapper. Understanding how influence works tells you exactly what that interaction needs to look like.
HTML smuggling confirmed bypassing major enterprise email gateways. Cialdini principles confirmed in live phishing engagements. evilginx2 MFA bypass confirmed against Outlook 365 targets. QR-to-mobile technique confirmed bypassing Proofpoint-protected environments.
Office macros: blocked by default since Office 2022 for internet-sourced files. GoPhish: widely signatured by 2025, use alternative infrastructure. Volume phishing: low signal-to-noise, generates detection events. Target selection and pretexting matter more than delivery tool.
The delivery layer is where most operations fail. An EXE that defeats every AV engine still needs to reach the target, execute, and not get flagged in the first 30 seconds of runtime. That path — from your infrastructure to target execution — is the last mile. It has its own threat model, its own detection signatures, and its own body of proven technique.
Why delivery is harder than the exploit
Email gateways detonate attachments in sandboxes before delivery. Mark of the Web (MOTW) tags files downloaded from the internet and restricts what they can do. Large executables trigger behavioural analysis. Known payload patterns are signatured before first execution. The delivery problem is not "can we bypass detection" — it is "can we deliver without ever being visible to a detection surface."
Technique 1 — HTML Smuggling
The file never transits the email gateway because there is no file in transit. The email body contains HTML. When the victim opens it in a browser, JavaScript assembles the payload from base64 chunks using atob(), builds a Blob object, and creates a download link via URL.createObjectURL(). The file materialises on disk inside the browser process — after the gateway has already cleared the email. No binary attachment, no file hash in gateway logs. Confirmed bypassing enterprise gateways in engagements. Covered in Ch08 as initial access; this chapter covers the architecture behind it.
Technique 2 — LNK + LOLBin stager
A .lnk shortcut renders as a document icon. Its target is mshta.exe — a signed Microsoft binary — with an inline JavaScript payload in the arguments. mshta.exe runs the JS, which downloads the real payload. No unsigned binary was ever emailed. The initial file is a legitimate Windows shortcut pointing to a legitimate Windows application. Stage 1 is under 1KB. Einayim's LNK toolkit generates these with configurable payloads and icon spoofing.
Technique 3 — Staged delivery
Never deliver the final payload at stage 1. The stage 1 stager should be small (< 50KB), make no suspicious API calls, and contain no network connections to known C2 infrastructure. Its only job is to confirm the environment is real (not a sandbox), then pull the actual payload from clean infrastructure. Staging works because detonation sandboxes time out — typically after 120 seconds of no malicious behaviour, the file is cleared. If stage 1 is clean-looking for 130 seconds before downloading stage 2, the sandbox never sees stage 2.
Technique 4 — Domain fronting via CDN
C2 callback traffic goes to a legitimate CDN IP (Cloudflare, Azure). DNS resolves to that known-good IP. The HTTPS SNI field names a clean-looking domain you control. The HTTP Host header inside the encrypted tunnel points to your actual C2 backend. Network inspection at the perimeter sees traffic to Cloudflare — which is on every corporate allowlist. The C2 communication is inside the TLS layer, invisible to DPI that cannot decrypt it. Requires a Cloudflare Worker or Azure CDN rule routing traffic to your listener. Implemented in Hermes Gateway (private) for this infrastructure.
Technique 5 — Counter-recon honeypot
Every internet-facing server gets scanned for leaked credentials hundreds of times per day. The defensive version of this is a honeypot. The offensive version is trickery of Rhea — you intentionally serve fake-but-convincing credentials and shell scripts at every path scanners probe (/.env, /setup.sh, /.aws/credentials, /.git/config). When the scanner operator downloads the "found" script and runs it to test the credentials, your implant loader inside the script backgrounds, self-deletes, and beacons home. The scanner becomes the victim.
This works because automated tools find the files, but humans often run them. A person who just found what looks like production AWS keys is motivated to test them quickly. The script does real-looking work (npm install, directory creation, env exports) and produces clean output — the implant is buried in one line that looks like a system manifest check.
The technique sits in a legal grey zone: running code on a remote system without authorisation is illegal in most jurisdictions regardless of whether that system was attacking you. Document this as a technique that exists, not a playbook to run without legal advice.
OPSEC baseline
Fresh infrastructure per operation — never reuse C2 IPs across engagements. Randomised beacon intervals with jitter — not sleep(60) but sleep(60 + rand(0,30)) — so traffic doesn't produce detectable timing patterns. Callback on port 443 with a valid TLS certificate. User-Agent set to a current common browser string. If possible, callback only during business hours — overnight connections from an employee's machine with no other traffic are an anomaly.
Once the payload executes, the question is no longer whether you can run code. The question is: how do you maintain that position across reboots, issue commands remotely, exfiltrate data without alerting network detection, and do all of this without an analyst noticing the process in Task Manager. This is the C2 architecture layer — the frame the implant lives inside.
The three-layer model
Every operational implant separates three concerns:
- Implant (target): beacons home, executes commands, maintains persistence, hides from analysis.
- Panel (operator): receives beacons, presents the operator interface, stores session history, issues tasking.
- Infrastructure: the network layer between them — redirectors, domain fronts, staging servers — responsible for making C2 traffic look like normal internet traffic.
These three never run on the same machine. If the target's SOC traces the C2 IP, they should find only a Cloudflare Worker or a redirector, not your operator panel.
DLL hijacking as the implant vehicle
version.dll is a system DLL loaded by nearly every Windows application. Windows searches for DLLs in the application's own directory before searching System32 (Ch02 — search order). Place a malicious version.dll in the directory of any startup application and Windows loads yours. The trusted process loads the implant. Task Manager shows the trusted application. The implant runs inside a legitimately-signed process — the process name, path, and signature all clear inspection.
Einayim uses this: the implant is compiled as version.dll, placed in a target application directory, and loaded automatically on next startup. No new process created. No new scheduled task visible in the obvious places.
Five persistence layers (einayim_kav pattern)
Deploy all five simultaneously. Removing one or two should not kill the implant:
- Registry Run key (HKCU\...\Run) — survives user login, no admin required
- Windows Service — survives reboots, no user login needed, SYSTEM context available
- WMI Event Subscription — triggers on system events (startup, user login, any WMI query), leaves no registry footprint under common inspection paths
- COM hijacking (HKCU COM object override) — no admin required, hijacks a COM object a legitimate application loads
- Scheduled Task — flexible trigger, configurable to run as SYSTEM if elevated at install time
Beacon design
Raw TCP on port 4444 is Metasploit. It is already in every threat intel feed. The working approach: HTTPS on port 443, TLS with a valid certificate from Let's Encrypt or a legitimate CA, HTTP responses that look like legitimate API calls (JSON keys are normal business names, not cmd and output). Operator commands are base64-encoded inside a field like "config" or "token". Responses look like authentication tokens or session data.
Anti-analysis checks
Before the implant does anything useful, it should confirm it is on a real target. Sandboxes have predictable behaviour:
- Timing check: call
Sleep(5000), check wall clock. If fewer than 4 seconds elapsed, the sandbox fast-forwarded the sleep. Exit. - Mouse movement: record cursor position twice, 2 seconds apart. Same position = no human present = sandbox. Exit.
- Analyst processes: check running process list against known analysis tools (Wireshark, ProcMon, x64dbg, Fiddler). If any are running, enter dormant mode.
- Debugger check:
IsDebuggerPresent()andCheckRemoteDebuggerPresent(). Both should return false on a real target.
If any check fails, the implant exits cleanly. No crash, no error dialog, no registry remnants from the checks themselves. The sandbox report shows a process that ran for 5 seconds and exited normally.
When developing a new vulnerability — whether a memory corruption, a TOCTOU race, or a kernel object misuse — the hardest part is rarely finding the bug. It is weaponizing it: making exploitation reliable, making the exploit binary undetectable, and making the system calls that deliver the payload invisible to EDR hooks. This chapter covers the syscall layer: what it is, why EDRs instrument it, and how direct syscall invocation bypasses that instrumentation.
Do you need this for 0-day development?
Yes — specifically when your exploit code uses any of these four Windows API calls:
VirtualAllocEx— allocate memory in a target processWriteProcessMemory— write shellcode into that memoryCreateRemoteThread/NtCreateThreadEx— execute the shellcodeNtOpenProcess— open a handle to the target
These are the four most monitored calls in Windows. Every EDR instruments them at the ntdll layer. If your exploit binary calls them via normal WinAPI, the EDR intercepts the call before it reaches the kernel, logs it, and may block it. The exploit is technically correct but never executes.
The call chain — and where EDR hooks live
Normal execution path:
Your code → kernel32.dll → kernelbase.dll → ntdll.dll (NtAllocateVirtualMemory stub) → syscall → kernel
EDR hooks live in ntdll — specifically, they overwrite the first bytes of each NT function stub with a jump to the EDR's monitoring code. The hook reads the arguments, decides whether to allow or block, then either passes through to the real syscall or terminates the call.
Direct syscall bypasses this entirely: your code calls the kernel using the syscall service number directly, skipping ntdll's stub. The EDR's hook is never reached.
Hell's Gate — runtime SSN resolution
You need the syscall service number (SSN) for each function. SSNs differ between Windows builds — you cannot hardcode them. Hell's Gate resolves them at runtime:
- Walk ntdll.dll in the process's own memory (no disk access)
- Find each NT function by DJB2 hash of its name — no plaintext string "NtAllocateVirtualMemory" in your binary
- Read the first bytes of the function stub:
mov eax, 0x??; mov r10, rcx; syscall - Extract the immediate value — that is the SSN for this build
- Execute
syscall [SSN]directly in your inline ASM stub
ChromElevator uses this to make its process hollowing and memory operations invisible to ntdll-level EDR hooks (Ch07 + writeup).
Halo's Gate — when EDR hooks the stub
If the EDR has already patched the NT function's first bytes to a jump instruction, Hell's Gate cannot read the SSN from it. Halo's Gate handles this: walk to the adjacent function in the export table (the next function alphabetically). Its stub is probably unhooked. Read its SSN. Subtract 1 — SSNs are allocated sequentially by function sort order in memory. You recover the original SSN without ever reading the hooked function.
SysWhispers3 — syscall stubs for your dev machine
Rather than implementing Hell's Gate manually for every exploit, SysWhispers3 generates compilable C/ASM stubs for whatever NT functions you need. Run it on your dev machine, specify the function list, and it produces stubs with randomised syscall instruction embedding — the syscall instruction itself appears at a random offset in the stub, defeating static pattern matching against the stub's structure. The generated code drops into any C project without modification.
The 0-day development workflow (hatokef applied)
- Find the assumption: what does this Windows component assume about the data or object it receives? (Ch01 — objects and invariants)
- Find the object: which kernel structure holds the state being validated? (EPROCESS, HANDLE_TABLE_ENTRY, OBJECT_HEADER — Ch02–03)
- Find the gap: between when the state is checked and when it is used, can it change? (Ch05 — The Gap)
- Fuzz the gap: WinAFL for user-mode, kAFL for kernel. Targeted at the specific input that crosses the check-to-use boundary.
- Analyze the crash: WinDbg kernel debugger,
!analyze -v, identify the instruction that faulted, determine if the write offset is controlled. - Write the PoC: minimal C program that reproduces the crash reliably across builds.
- Weaponize: convert crash to controlled write → shellcode allocation → privilege escalation. Replace all WinAPI calls with SysWhispers3 stubs.
- Test under telemetry: run in a VM with Sysmon, Defender, and Process Monitor active. Watch what you generate. Fix anything that triggers.
Each writeup below documents a technique from the preceding chapters — applied against a real target, disclosed through a responsible disclosure process, or demonstrated on a controlled lab target with documented conditions. Every writeup notes whether the technique was patched, still valid, or superseded.
The live CVE feed below pulls the current month's Microsoft CVEs across all primitives covered in this curriculum. Each entry links to the MSRC advisory. If a CVE maps to a chapter here, you have the context to understand what Windows component is broken and why.
CVE data from Microsoft MSRC CVRF API · Updated after each Patch Tuesday · raw JSON
22nd Survey Division · Home