Skip to main content
Syscall emulation is the core mechanism that allows Sogen to run Windows binaries without a Windows kernel. This page explains how syscalls are intercepted, dispatched, and handled.

Overview

Windows applications interact with the kernel through NT syscalls. In Sogen:
  1. The CPU backend hooks the syscall instruction
  2. When executed, control transfers to the syscall dispatcher
  3. The dispatcher looks up and invokes the appropriate handler
  4. The handler emulates kernel behavior
  5. Control returns to the application with results

Syscall Discovery

Syscalls in Windows are not statically numbered—each Windows version may have different syscall IDs. Sogen discovers syscall numbers dynamically by analyzing the actual ntdll.dll and win32u.dll modules.

Extraction Process

From syscall_dispatcher.cpp:28:
The find_syscalls function searches for the syscall stub pattern:
By pattern matching against exported functions starting with Nt or Zw, Sogen builds a complete syscall table for the target Windows version.

Syscall Dispatch

Dispatch Entry Point

When the CPU backend encounters a syscall instruction, it calls syscall_dispatcher::dispatch() (from syscall_dispatcher.cpp:64):

Syscall Context

The syscall_context provides handlers with access to:
Handlers extract arguments from registers following the Windows x64 calling convention:
  • RCX: First argument
  • RDX: Second argument
  • R8: Third argument
  • R9: Fourth argument
  • Stack: Additional arguments

Syscall Handlers

Sogen implements hundreds of syscall handlers across different categories:

File Operations

From syscalls/file.cpp:

Memory Operations

From syscalls/memory.cpp:

Thread Operations

From syscalls/thread.cpp:

Handler Categories

Syscall handlers are organized by functionality:

Return Values

Syscall handlers return NTSTATUS codes:
The dispatcher writes the return value to the RAX register, matching Windows syscall convention.

User Callbacks

Some syscalls require callbacks into user mode (e.g., window procedures). Sogen handles this through a callback stack:
From emulator_thread.hpp:271:

Callback Dispatch Flow

  1. Syscall handler needs user callback (e.g., window creation)
  2. Create callback_frame with current register state
  3. Set RIP to callback function in user code
  4. Execute user callback code
  5. Callback returns via NtCallbackReturn
  6. Restore registers from callback_frame
  7. Resume syscall handler with callback result
This enables complex operations like window creation that require multiple round-trips between kernel and user mode.

Unimplemented Syscalls

When a syscall is not implemented:
The emulator logs the call and returns STATUS_NOT_SUPPORTED. Applications typically have fallback behavior for unsupported features.

Instrumentation Hooks

The on_syscall callback allows instrumentation before handler execution:
This enables:
  • Logging: Record all syscalls with arguments
  • Modification: Change arguments before handler
  • Replacement: Implement custom syscall behavior
  • Blocking: Prevent certain syscalls from executing

WOW64 Syscalls

32-bit processes use a different syscall mechanism. Sogen handles this through:
  1. Syscall ID masking: syscall_id & 0xFFFF extracts the actual ID
  2. Argument conversion: Translate 32-bit pointers/structures to 64-bit
  3. Heaven’s Gate: Transitions between 32-bit and 64-bit mode
  4. Dual dispatch: Some syscalls have separate 32-bit handlers
The WOW64 layer is transparent to most syscall handlers, with conversion happening at the boundary.

Next Steps