Endpoint Detection and Response (EDR) agents predominantly rely on injecting a user-mode dynamic-link library (DLL) into newly spawned processes. This injected library patches sensitive Native API functions inside ntdll.dll (such as NtAllocateVirtualMemory, NtProtectVirtualMemory, and NtCreateThreadEx) with unconditional jumps (jmp) pointing into the EDR's inspection engine.

**Offensive Principle** When userland hooks are bypassed via raw direct syscalls alone, modern EDRs with kernel-mode telemetry (ETW-Ti) inspect the Call Stack. An unbacked thread executing a syscall outside of `ntdll.dll` boundaries is immediately flagged as anomalous.

1. The Evolution of Syscall Resolution

Traditional direct syscall techniques hardcoded System Service Numbers (SSNs). Because SSNs fluctuate across Windows kernel builds and service packs, modern offensive tooling dynamically calculates SSNs by parsing the Export Address Table (EAT) of ntdll.dll.

Using the HalosGate algorithm, an operator can locate the syscall stub even when neighboring functions are hooked by reading adjacent function opcodes up and down memory boundaries:

CCOMMAND / CODE
// Dynamic SSN extraction with hook tolerance
DWORD GetSyscallNumber(PVOID pFunctionAddress) {
    BYTE *pOpcode = (BYTE*)pFunctionAddress;
    
    // Check if clean stub: mov r10, rcx; mov eax, SSN
    if (pOpcode[0] == 0x4C && pOpcode[1] == 0x8B && pOpcode[2] == 0xD1 && pOpcode[3] == 0xB8) {
        return *(DWORD*)(pOpcode + 4);
    }
    
    // If hooked (jmp / 0xE9), inspect adjacent stubs
    for (WORD idx = 1; idx < 32; idx++) {
        BYTE *down = pOpcode + (idx * 0x20);
        if (down[0] == 0x4C && down[1] == 0x8B && down[2] == 0xD1 && down[3] == 0xB8) {
            return *(DWORD*)(down + 4) - idx;
        }
    }
    return 0;
}

2. Indirect Syscalls: Preserving the Return Pointer

Rather than executing the syscall instruction within private heap or image allocations, indirect syscalls jump directly to a legitimate syscall; ret gadget already resident inside ntdll.dll. This ensures that the kernel return address points into trusted Microsoft module memory space.

ASMCOMMAND / CODE
.code
IndirectSyscall PROC
    mov r10, rcx                ; Preserve 1st arg into r10
    mov eax, [rsp + 28h]        ; Load dynamically resolved SSN
    mov r11, [rsp + 30h]        ; Address of 'syscall; ret' inside ntdll.dll
    jmp r11                     ; Jump into legitimate ntdll gadget
IndirectSyscall ENDP
END

3. Synthetic Stack Spoofing

Even with indirect syscalls, static stack walk inspection (via ETW-Ti or asynchronous APC sampling) will reveal abnormal stack frame ancestry. By utilizing synthetic stack spoofing (desynchronizing the physical register stack pointer RSP from the unwind metadata registered in RUNTIME_FUNCTION), we can forge arbitrary stack frames such as:

  • ntdll.dll!NtAllocateVirtualMemory + 0x14
  • KERNELBASE.dll!VirtualAllocEx + 0x42
  • explorer.exe!ThreadStart + 0x110

4. Defensive Recommendations

To detect and mitigate advanced syscall bypasses, defensive teams must not rely solely on userland hooks:

  • Enable ETW-Ti (Threat Intelligence): Subscribe to kernel-level KERNEL_THREATINT_KEYWORD_INLINE_SYSCALL telemetry.
  • Implement Kernel-mode Call Stack Telemetry: Verify that unwind chains match continuous valid thread execution contexts.
  • Arbitrary Code Guard (ACG): Enforce W^X protections to prevent the allocation and execution of dynamically unbacked memory pages.