Syscall Methods & SSN Resolvers

← maldev README · docs/index

The win/syscall package composes three orthogonal concerns behind a single *Caller. Operators tune each axis independently; downstream packages (inject/*, evasion/*, c2/shell) accept a *Caller and inherit the chosen posture without recompiling.

Primer — vocabulary

Eight terms recur throughout the syscalls/* pages:

Syscall — direct kernel transition (syscall instruction on x64). The actual mechanism Windows uses to call into the kernel; everything else (Win32, NTAPI) is a wrapper that eventually issues a syscall.

NTAPIntdll.dll's Nt* functions (NtAllocateVirtualMemory, NtCreateThreadEx, …). Thin userland wrappers around the syscall instruction. Every Win32 API call (VirtualAlloc, CreateThread) eventually bottoms out in an NTAPI call.

SSN (System Service Number) — the integer index of a syscall in the kernel's service table. Hardcoded in each ntdll prologue. Rotates between Windows builds — NtAllocateVirtualMemory is SSN 0x18 on one build, 0x19 on the next.

Userland hook — inline patch (typically JMP rel32) an EDR installs at the start of an NTAPI prologue so it can inspect arguments before the syscall fires. "Bypassing hooks" means issuing the syscall without going through the patched bytes.

Direct syscall — issuing the syscall instruction from your own code with the SSN you obtained somehow. Skips the (possibly hooked) ntdll prologue entirely.

Indirect syscall — calling INTO ntdll's syscall instruction (which lives at a fixed offset past the prologue). Trades direct-syscall's "RIP outside ntdll" tell for "uses the canonical syscall site". EDRs scanning for syscall instructions in non-ntdll memory miss this.

API hashing — replacing string lookups ("NtAllocateVirtualMemory") with constant integer hashes (0xE0762FEA) so the implant's .rdata doesn't carry plaintext API names. ROR13 is the classical algorithm; Hellgate / HashGate use this internally to find Nt* exports.

Hellsgate / Halosgate / Tartarus / HashGate / Chain — SSN-resolution strategies. Each has different fallbacks for when the canonical source (a clean ntdll prologue) is compromised. See ssn-resolvers.md.

Three concerns (read this first)

flowchart LR
    subgraph callsite [Call site<br>e.g. inject.RemoteThread]
        C["*wsyscall.Caller"]
    end
    subgraph axis1 [1. Calling method<br>HOW the syscall fires]
        A1["MethodWinAPI / NativeAPI / Direct /<br>Indirect / IndirectAsm"]
    end
    subgraph axis2 [2. SSN resolver<br>WHERE the syscall number comes from]
        A2["HellsGate / HalosGate / Tartarus /<br>HashGate / Chain"]
    end
    subgraph axis3 [3. API hashing<br>HOW the symbol is found without a string]
        A3["ROR13 / FNV / Jenkins / custom HashFunc"]
    end
    C --> A1
    C --> A2
    A2 --> A3

The three axes answer different questions:

AxisQuestion it answersPages
1 — Calling methodHow does the implant issue the syscall once the SSN is known? Which userland boundary do we cross / skip?direct-indirect.md
2 — SSN resolverWhere does the SSN come from? What happens when the canonical source (the unhooked ntdll prologue) is unavailable?ssn-resolvers.md
3 — API hashingHow do we identify the right Nt* export without a plaintext name in the binary?api-hashing.md

Tuning one axis does NOT imply tuning the others. You can:

  • pick MethodIndirect (axis 1) with HellsGate (axis 2) — no api-hashing.
  • pick MethodWinAPI (axis 1) with HashGate (axis 2 — uses axis 3 internally).
  • swap the hash function on HashGate (axis 3) without touching axis 1 / 2.

Architecture Overview

graph TD
    subgraph "Consumer Packages"
        INJ[inject/]
        EVA[evasion/]
        C2[c2/shell]
    end

    subgraph "win/syscall"
        CALLER["*Caller"]
        CALLER -->|method| WINAPI[MethodWinAPI]
        CALLER -->|method| NATIVE[MethodNativeAPI]
        CALLER -->|method| DIRECT[MethodDirect]
        CALLER -->|method| INDIRECT[MethodIndirect]

        CALLER -->|resolver| HG[HellsGate]
        CALLER -->|resolver| HAG[HalosGate]
        CALLER -->|resolver| TG[TartarusGate]
        CALLER -->|resolver| HGR[HashGate]
        CALLER -->|resolver| CH[Chain]
    end

    subgraph "win/api"
        PEB["PEB Walk"]
        HASH["API Hashing"]
        RESOLVE["ResolveByHash"]
    end

    INJ -->|"*Caller (nil = WinAPI)"| CALLER
    EVA -->|"*Caller (nil = WinAPI)"| CALLER
    C2 -->|"*Caller (nil = WinAPI)"| CALLER
    HGR --> PEB
    HGR --> HASH
    RESOLVE --> PEB
    RESOLVE --> HASH

Quick Reference

MethodHook BypassStack CleanMemory CleanStealth
WinAPINoneN/AN/ALowest
NativeAPIkernel32N/AN/ALow
DirectAll userlandNoNoMedium
IndirectAll userlandYesYesHigh (heap stub, RW↔RX cycle)
IndirectAsmAll userlandYesYesHighest (Go-asm stub, no writable code)
ResolverUnhooked ntdllJMP-hooked ntdllFully hooked ntdllString-free
HellsGateYesNoNoNo
HalosGateYesYes (neighbor)NoNo
TartarusGateYesYes (trampoline)Yes (neighbor fallback)No
HashGateYesNoNoYes
ChainDepends on compositionDepends on compositionDepends on compositionDepends

Quick decision tree

You want to…Use
…call a Windows API with no plaintext name in the binaryapi-hashing.md (HashGate)
…skip kernel32-level hooks but stay in ntdlldirect-indirect.mdMethodNativeAPI
…skip every userland hook (kernel32 + ntdll)direct-indirect.mdMethodIndirect / MethodIndirectAsm
…make the syscall return inside ntdll's .text (call-stack stealth)direct-indirect.mdMethodIndirect family
…avoid any writable code page in the implantdirect-indirect.mdMethodIndirectAsm
…randomise the syscall return address per calldirect-indirect.md — gadget pool
…auto-fall-back when the target stub is hookedssn-resolvers.md — Halo's / Tartarus / Chain
…read the SSN even when the entire ntdll text section is hookedssn-resolvers.md — TartarusGate
…swap in your own hash function (defeat ROR13 fingerprints)NewHashGateWith(fn) + Caller.WithHashFunc(fn)

Documentation

DocumentDescription
Direct & Indirect SyscallsThe five invocation methods (incl. Go-asm IndirectAsm) and when to use each
API HashingPEB walk + ROR13 hashing to eliminate plaintext strings
SSN ResolversHell's Gate, Halo's Gate, Tartarus Gate, HashGate

MITRE ATT&CK

TechniqueIDDescription
Native APIT1106Directly interact with the native OS API

D3FEND Countermeasures

CountermeasureIDDescription
System Call AnalysisD3-SCAMonitor syscall origins and patterns
Function Call RestrictionD3-FCRRestrict dynamic function resolution

See also