Skip to main content

The anatomy of a syscall

Date: 11/10/25·Author: emryllfoundationwindowssyscalls

The anatomy of a syscall

On Windows, performing any meaningful action that interacts with system resources requires going through the Windows API (or invoking system calls directly). But what really happens under-the-hood when you do that?

Windows API under the hood

Let's start by briefly looking at what happens when you use the Windows API. Everything ultimately builds on top of ntdll; when you call a function from kernel32.dll or kernelbase.dll(among others), in the end it will call a corresponding function in ntdll.dll. All (legitimate) syscalls originate from ntdll (or win32u.dll for GUI-related syscalls, but let's forget this for now...).

Kernel32 and kernelbase are essentially just more user-friendly wrappers for ntdll functions, which in-turn are wrappers for syscalls. Ntdll also contains a lot of essential internals, like functions used by the OS to load applications. This is why every process loads ntdll.

This graph helps visualize the flow of a Windows API call, from user-mode to kernel-mode.

Syscalls

A syscall is the middle-man in the transition between user-mode and kernel-mode, as user-mode objects can't "touch" kernel-mode. Different syscalls (NT functions) have unique identification numbers called System Service Numbers (SSN). Windows does not expose SSNs, but you can dynamically resolve them with various methods, or see them by inspecting with a debugger. SSNs change all the time, and this is on purpose, to make it more difficult for malicious actors. Some more of that security by obscurity, which the lazy Windows devs seem to love...

The SSN is merely the index within the KiServiceTable (SSDT) which resides in the kernel. Within the ntdll.dll library, syscalls are generated sequentally, and interestingly they are in the same order as the function addresses of corresponding Nt/Zw functions.

Before a syscall instruction is invoked, there is a standard assembly stub which sets it up. It goes as follows:

mov r10, rcx
mov eax, {SSN}

The calling convention is very similar to the regular calling convention on x64 (fastcall), with some minor differences. Most importantly, eax/rax receives the SSN, and r10 is used instead of rcx, as rcx gets destroyed in the syscall transition.

According to the Intel reference, the syscall instruction jumps to the address stored in the IA32_LSTAR register, which is a model-specific register (MSR). On 64-bit Windows this normally points to the KiSystemCall64 function inside of ntoskrnl.exe.

After this step in the pipeline comes the System Service Descriptor Table(SSDT). The SSDT is like the kernel version of the Import Address Table, it translates the SSN into the address of the corresponding function to run.

Further reading

Syscalls on Windows 11hammertux.github.io

https://www.codeproject.com/articles/The-Quest-for-the-SSDTs#comments-sectionwww.codeproject.com

LogoWindows-Internals/System Architecture and Components/System Service Descriptor Table.md at main · Faran-17/Windows-InternalsGitHub

Logo04: Chapter 2 | Windows OS System CallsGitHub