Skip to content

Hell’s Gate (Direct Syscalls)

[!WARNING] This is written by AI temporarily. The content should be re-write later.

TL;DR

See the code example

Hell’s Gate is a technique that invokes Windows system calls directly to bypass user‑mode API hooks. Instead of calling high‑level Win32 APIs (which EDRs commonly instrument), it discovers the syscall numbers (SSNs) for selected ntdll.dll exports at runtime and triggers them using the CPU’s syscall instruction. In this folder, the implementation resolves four syscalls commonly used by classic injection flows and demonstrates local or remote execution paths on Windows 10.

  • Focus: education and defense. No new code or step‑by‑step instructions are added here.
  • Requirements: Windows 10 (version check enforced in code).
  • Core syscalls used: NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory, NtCreateThreadEx.

Windows System Calls (High Level)

  • System calls are the kernel entry points that user‑mode code reaches through a well‑defined boundary. Typical apps call Win32 APIs, which then call into ntdll.dll, which finally performs the syscall transition.
  • Security tools often hook Win32 APIs or ntdll.dll functions to observe behavior. Calling syscall directly after discovering the right SSN can reduce the visibility of that behavior to user‑mode hooks.
  • SSNs and function prologues can vary across Windows builds; robust implementations resolve them dynamically from the module already loaded in the process.

Technique Overview

  • Resolve ntdll.dll loaded in the current process without calling loader APIs by walking the PEB/TEB loader lists.
  • Parse the export directory of ntdll.dll and identify target routines by hashing export names (djb2 with a custom seed in this code).
  • For each target routine, scan the function prologue to extract the SSN embedded in the mov eax, <imm> that precedes the syscall instruction on x64.
  • Store the SSNs in a small lookup table and invoke them via two tiny assembly stubs:
    • hells_gate(number): records the SSN.
    • hell_descent(...): moves rcx to r10, loads eax with the recorded SSN, then executes syscall.
  • Use those direct syscalls to perform a classic four‑step memory execution flow.

Repository Structure

  • src/hell.zig: Core Hell’s Gate implementation.
  • src/main.zig: Demo driver that validates environment, initializes resolution, and executes the injection flow locally or into a target PID.
  • build.zig, build.zig.zon: Zig build configuration.

Implementation Walkthrough (High Level)

  • isAvailable(): Checks Windows major version (requires 0xA → Windows 10).
  • PEB/TEB access: Reads the current thread’s TEB and process PEB directly (GS‑based on x64) to locate loader lists and the ntdll.dll module entry.
  • Export parsing: Reads the PE headers and IMAGE_EXPORT_DIRECTORY of ntdll.dll to enumerate exported names and RVAs.
  • Name hashing: Uses a djb2 variant with a custom seed to match exports by hash instead of plaintext strings.
  • Prologue scan → SSN: For each matched export, scans the first bytes for the standard x64 mov r10, rcx and mov eax, imm32 pattern in front of syscall, capturing the immediate SSN.
  • VX table: Stores, per target syscall, the resolved address and SSN for later invocation.
  • Assembly stubs: Global assembly sets a writable SSN slot and performs the minimal register setup for syscall.
  • Injection sequence (conceptual):
    • Allocate RW memory in the target process using the resolved NtAllocateVirtualMemory.
    • Write the payload using the resolved NtWriteVirtualMemory.
    • Change protection to executable using the resolved NtProtectVirtualMemory.
    • Create a thread at the allocated address using the resolved NtCreateThreadEx.
  • Demo driver:
    • Can run “local” (current process) or “remote” (opens a handle to a specified PID) based on compile‑time constants.
    • Parses a PID from the command line (if remote mode), initializes the VX table, and performs the conceptual sequence above.
    • Adds small prompts to pause between steps for demonstration.

Notes for Defenders

  • API hook bypass: Direct syscall avoids user‑mode API hooks; rely on kernel telemetry, ETW providers, or call stack/context‑based analytics where possible.
  • Behavioral signals: Look for sequences such as RW→RX memory transitions, cross‑process memory writes, and thread creation targeting unusual entry points.
  • Module scraping: Access to PEB/TEB loader lists and export parsing from ntdll.dll are strong signals of direct‑syscall discovery logic.
  • Version fragility: SSNs and prologues change across Windows builds; mismatches can crash processes. Monitoring for repeated failed transitions or abnormal STATUS_INVALID_PARAMETER patterns can be informative.
  • Memory content heuristics: Newly created RX regions not backed by images, or that closely follow an RW→RX change and a thread start, are suspicious.

Limitations and Portability

  • Windows‑version specific: The implementation explicitly checks for Windows 10 and depends on ntdll.dll prologue conventions for SSN extraction.
  • Evasion trade‑offs: While it may evade some user‑mode hooks, kernel‑level monitoring, CFG mitigations, and modern EDR heuristics can still detect the behavior.
  • Pattern brittleness: Relying on a fixed prologue pattern can break on patched builds or alternative instruction sequences.

Ethical Use

This documentation is provided for legitimate, defensive, and educational purposes in a controlled lab setting. Do not use these concepts to target systems without explicit authorization. When analyzing such techniques, prefer to operate in isolated environments and focus on improving detection and mitigation.