Modern Endpoint Detection and Response (EDR) solutions rely heavily on monitoring processes from within user space by injecting DLLs and placing JMP hook instructions at the beginning of sensitive Windows API functions — functions like NtAllocateVirtualMemory, NtWriteVirtualMemory, and NtCreateThreadEx. This research demonstrates how indirect syscalls defeat both hook-based detection and modern call-stack validation.
The Hook Problem
When an EDR hooks NtAllocateVirtualMemory, the first bytes of the function inside ntdll.dll are overwritten with a JMP to the EDR's inspection routine. The EDR sees every call, logs suspicious context, and can terminate the process before the syscall executes.
Direct Syscalls — Why They Failed
The first evasion technique was Direct Syscalls: manually hardcoding the System Service Number (SSN) and executing the syscall instruction from within your own binary, bypassing ntdll.dll entirely. EDR vendors quickly adapted: they now monitor the call stack origin. If a syscall instruction originates from outside the legitimate ntdll.dll memory region, it's flagged immediately.
The Indirect Syscall Solution
Indirect Syscalls solve the call-stack problem. Instead of executing the syscall instruction from our binary, we load the SSN and arguments into registers, then jump to an unhooked syscall gadget that already exists inside ntdll.dll. From the EDR's call-stack perspective, the syscall originates from a legitimate Windows library.
Results on Live Engagements
Implemented in a custom Rust loader using this technique, we successfully blinded the telemetry of two major EDR platforms during our Q1 2026 engagements — loading reflective DLLs entirely undetected and maintaining persistent access for 14 days before simulated detection. The Rust implementation provides the added benefits of:
- No managed runtime — no CLR or JVM to detect
- Compiler-level optimizations that make static analysis harder
- Memory safety preventing accidental crashes that would alert defenders
- Easy cross-compilation to target ARM (cloud) and x86-64 (workstation)
Detection Recommendations
Defenders aren't helpless. These techniques can still be caught with the right controls:
- Enable kernel-mode call-stack validation (requires EDR with kernel sensor)
- Monitor for anomalous modules with low entropy loaded into memory
- Alert on processes calling sensitive APIs without a corresponding ntdll import table entry
- Deploy deception technology — canary tokens in LSASS that trigger on delegated reads