Methods
Methods
A method controls how the syscall instruction is actually invoked at runtime once the SSN has been resolved by the iterator. Different methods present different artefacts to EDR stack-inspection heuristics.
Comparison table
| Name | Description | Return address | Complexity |
|---|---|---|---|
direct | Inline syscall instruction in the stub | Inside your module | Lowest |
indirect | Jump to ntdll function start; ntdll handles syscall | Inside ntdll | Low |
random | Jump to a random syscall;ret gadget in ntdll | Inside ntdll (random) | Low |
egg_hunter | 8-byte marker patched before main() runs | Inside your module | Highest |
Method details
direct
The generated stub contains a literal syscall instruction. The CPU executes the kernel transition directly from within your module's memory.
Pros: Simple; no runtime patching; compatible with all iterators.
Cons: Return address (and call stack) is inside your code — not inside ntdll. Advanced EDR products that inspect the kernel call stack can detect this.
call stub → [your module] syscall → kernel
indirect
Rather than executing syscall inline, the stub jumps to the syscall;ret sequence at the real ntdll stub (past any hook). The return address visible to the kernel therefore appears to be inside ntdll.
Pros: Call stack looks natural — consistent with a legitimate ntdll call.
Cons: The target syscall;ret location must be located; slight overhead.
call stub → jmp &ntdll_syscall_ret → [ntdll] syscall → kernel
random
Like indirect, but instead of jumping to the corresponding ntdll stub, the code jumps to a randomly chosen syscall;ret gadget in ntdll. This further breaks call-stack correlation because the return address will differ on every invocation.
Pros: Defeats EDR heuristics that look for consistent call-origin addresses.
Cons: Slightly slower due to random selection at runtime.
egg_hunter
The most sophisticated method. The generated stub contains an 8-byte egg (marker sequence) instead of a syscall. Before any Nt* call is made, the egg must be replaced with real syscall; nop; nop; ret; nop; int3; int3 bytes by scanning the binary's .text section.
Automatic initialisation: The sanitizer function SPT_SanitizeSyscalls() is registered in the .CRT$XCU CRT init table and runs automatically before main() is called when compiled with MinGW/GCC. No explicit call in application code is required. A manual call to SPT_SanitizeSyscalls() is also available for toolchains that do not honour .CRT$XCU.
Choosing a method
The generate command accepts any iterator/method combination via the custom gate:
sysplant generate -c -p common -o stubs custom -i canterlot -m egg_hunter
When using a named gate (hell, halo, syswhispers, etc.), the default method for that gate is applied automatically. See the Iterators page for the default method of each gate.