Background
Background
This page explains the concepts that all Sysplant iterators and methods build on. If you are already familiar with Windows syscall internals and EDR hooking, you can skip to the Iterators section.
Windows syscalls
A syscall is the mechanism by which a user-mode process asks the OS kernel to perform a privileged operation — allocating memory, opening a handle, creating a thread, and so on.
On x86-64 Windows, the transition works as follows:
- The user-mode code sets
EAXto the Syscall Service Number (SSN) — an integer that identifies which kernel function to invoke. R10is set to the first argument (the calling convention for syscalls requiresR10 = RCX).- The
syscallinstruction is executed, switching the CPU into kernel mode at a fixed entry point. - The kernel reads
EAX, dispatches to the right handler, and returns.
The SSN is the critical piece: it is a small integer (e.g. 0, 1, 2 …) that is Windows-version-specific and changes between releases.
ntdll.dll — the system call bridge
ntdll.dll is the lowest-level DLL in every Windows process. It exposes the Nt* and Zw* API families, which are thin wrappers around the syscall instruction.
A typical unhooked x64 stub looks like:
NtOpenProcess:
mov r10, rcx ; save first arg (syscall ABI requirement)
mov eax, 0x26 ; load SSN for NtOpenProcess
syscall ; kernel transition
ret
The opcodes for mov r10, rcx; mov eax, SSN produce the byte sequence:
4C 8B D1 B8 [SSN_lo] [SSN_hi] 00 00
This fixed pattern is what the opcode-scanning iterators look for.
EDR hooking
Endpoint Detection & Response (EDR) products need to observe what processes do at the kernel level. The most common technique is to patch the first bytes of sensitive Nt* functions in the in-memory copy of ntdll.dll with a JMP instruction that redirects execution to the EDR's own callback:
NtOpenProcess: E9 xx xx xx xx ← JMP to EDR DLL
...
(original bytes now unreachable or displaced)
The EDR callback inspects arguments, logs telemetry, then optionally continues into the real syscall.
ntdll.dll — the file on disk is untouched. The clean bytes are therefore still accessible in the DLL file or in the memory of another process.What is hooked?
The E9 (relative JMP near) opcode is a 5-byte instruction. It typically replaces the first 5 bytes of the stub, overwriting MOV R10, RCX and part of MOV EAX, SSN. After the hook, reading opcode bytes at the function's entry point no longer reveals the SSN.
Some hooks target the third byte instead, leaving MOV R10, RCX intact but overwriting MOV EAX, SSN itself.
Why this breaks naive code
If your code simply calls a function like NtOpenProcess(&handle, ...), the execution path is:
your code → NtOpenProcess() → JMP → EDR callback → (optionally) kernel
The EDR always sees the call.
If instead you skip the hook and call the kernel directly with the right SSN, the EDR callback is never entered. The problem then becomes: how do you learn the correct SSN at runtime, without reading the hooked bytes?
That is exactly what each of Sysplant's iterators solves, each in a different way.
The Process Environment Block (PEB)
All iterators locate ntdll.dll at runtime by walking the PEB (Process Environment Block) — a Windows data structure accessible without any API call:
- x64:
GS:[0x60]→ pointer to the PEB - x86:
FS:[0x30]→ pointer to the PEB
The PEB contains a Ldr pointer to the loader module list, which enumerates every loaded DLL with its name and in-memory base address. Iterators walk this list to find the base of ntdll.dll, then parse its PE headers directly.
Using the PEB avoids calling GetModuleHandle("ntdll.dll"), which itself is a Win32 call that could be monitored.
The PE Export Directory
Once the ntdll base address is known, all iterators parse its Export Directory — the PE table that maps function names to their Relative Virtual Addresses (RVAs). From this, each function's in-memory address is derived.
Some iterators (Canterlot) additionally parse the Exception Directory (IMAGE_DIRECTORY_ENTRY_EXCEPTION), which contains IMAGE_RUNTIME_FUNCTION_ENTRY records used by the x64 exception-handling mechanism.
Summary
| Concept | Role in Sysplant |
|---|---|
| SSN | The integer passed in EAX to identify the kernel function — what iterators must recover |
| ntdll.dll stub | The user-mode wrapper containing MOV EAX, SSN; syscall — what iterators read from |
| EDR hook | A JMP patch replacing the stub's first bytes — what iterators work around |
| PEB | How ntdll's base address is found without API calls |
| Export Directory | How function addresses are enumerated |
| Exception Directory | Used by Canterlot to enumerate stubs in SSN order |