EDR Evasion for Research: Direct Syscalls Explained Without the Hype
How direct syscalls actually work in controlled defensive study, why they remain detectable, and what blue teams should look for before buying the next black box.
Direct syscalls became a meme across YouTube channels and Twitter threads, yet most published research barely scratches what a modern EDR (CrowdStrike Falcon 7.x, SentinelOne Singularity, Microsoft Defender for Endpoint with kernel ETW-TI) really sees. At Basilisk OffSec we study the technique in an isolated lab - Windows 11 23H2 with no internet, versioned VMware snapshots, and zero intention of packaging any of this as a service. The goal is clear: understand the NTDLL -> SSN -> syscall -> kernel path so we can write better detections, not sell bypass kits. If your plan is to stack IOCs and pretend that qualifies as professional red teaming, you can close the tab.
Recap without condescension: every documented function in ntdll.dll (NtAllocateVirtualMemory, NtCreateThreadEx, NtProtectVirtualMemory) carries a System Service Number (SSN) that changes with each Windows build. EDRs typically install an inline JMP hook on the first bytes of those user-mode stubs, so when your loader calls VirtualAlloc the control flow goes through the injected DLL before reaching the kernel. A direct syscall means replicating the stub inside your binary - mov r10, rcx; mov eax, SSN; syscall; ret - bypassing the hook entirely. It works, but as you will see it is a cheap win against user-mode telemetry and nothing against kernel telemetry. Worth revisiting AMSI and ETW Bypass for Defensive Research: What Blue Teams Should Know for related logic.
To reproduce at home I use SysWhispers3 (the fork maintained by klezVirus) generating MASM stubs for a C++ project compiled with MSVC 19.38. The SSN is resolved at runtime via Hell's Gate or Halo's Gate by walking the EAT of the ntdll mapped from disk - not the in-memory version, which may be tampered with. In three hours at the bench I had a POC that allocates RW, writes benign shellcode (vanilla calc.exe, no payload attached), flips NtProtectVirtualMemory to RX and fires NtCreateThreadEx. Sysmon 15 with SwiftOnSecurity's config logs the thread creation, but ProcessAccess stays clean. Looks like a win until you flip Defender into EDR block mode and the alert comes from the kernel via the Threat Intelligence ETW provider, not from user-mode at all.
Here is the part nobody wants to say on stage: Microsoft-Windows-Threat-Intelligence emits events for NtAllocateVirtualMemory, NtProtectVirtualMemory with RWX, NtMapViewOfSection and NtCreateThreadEx regardless of any user-mode hook. You skipped NTDLL, fine, but the kernel still saw the syscall arrive with a return stack pointing at your binary's .text instead of ntdll.dll - exactly what call stack anomaly detection looks for. Elastic Security has had a public rule since 2023, and the Threat Hunting with Sigma and Elastic: From Indicator to Detection Rule crowd already consumes it by default. Indirect syscalls (jumping back into ntdll and issuing the syscall there) help with the stack but introduce other artifacts that YARA-X catches.
On authorized engagements we discuss this trade-off with the client before any execution, which connects directly to Red Team 101: How Pentests Differ from Real Adversarial Operations: adversary emulation has scope, rules of engagement and a defined window. The defensive exercise is actually richer - run the POC, capture ETW-TI with SilkETW or krabsetw, and write a Sigma rule that covers the heuristic without triggering on the Teams installer. I usually pair this with hardening per Windows 11 Hardening for High-Risk Offensive Security Workstations and check lateral movement as described in Lateral Movement in the Lab: SMB, WMI and WinRM with a Detection Focus, because an isolated direct syscall is rarely the full story of a real incident.
Common questions answered fast: no, we do not publish a ready loader; no, unhooking via a fresh NTDLL copy from disk is not magic - Defender has mapped that since 2022 via memory image integrity; and no, ChatGPT writing your shellcode in Nim does not make you invisible, it just gives you weird entropy in .text that the EDR's ML classifier loves. If you are starting offensive work seriously, build your foundation with Active Directory Pentest: Step-by-Step Kerberoasting in a GOAD Lab and Building C2 Infra with Sliver in an Isolated Lab for Defensive Research before diving into syscalls, because without understanding what comes after execution you are just admiring your own reflection in Process Hacker.
Practical takeaway: if you are a defender, enable the Microsoft-Windows-Threat-Intelligence provider (requires PPL on the EDR service), ingest it into your SIEM and build three rules - call stack not originating in ntdll, NtProtectVirtualMemory flipping RW to RX in a private region, and cross-process NtCreateThreadEx with a start address in heap. That catches 80% of public POCs with no extra licensing cost. If you are a researcher, document, isolate, do not distribute compiled binaries, and remember that the line between study and crime is the asset owner's written consent - something OPSEC for Security Researchers: Building a Personal Threat Model covers properly.