SYSTEM_PROCESS_INFORMATION

The SYSTEM_PROCESS_INFORMATION structure is what a successful call to ZwQuerySystemInformation or NtQuerySystemInformation produces at the start of its output buffer and irregularly throughout the buffer when given the information class SystemProcessInformation (0x05), SystemExtendedProcessInformation (0x39) or SystemFullProcessInformation (0x94).

These information classes produce descriptions not just of the running processes but also of those processes’ threads. A quick summary is that the information for each process is:

The totality of the output for all processes is a sequence of these variable-size sets, one per process. In each set, the NextEntryOffset member at the beginning of the SYSTEM_PROCESS_INFORMATION tells how many bytes to advance from that SYSTEM_PROCESS_INFORMATION to the next, or is zero in the last.

Documentation Status

The SYSTEM_PROCESS_INFORMATION structure is defined in WINTERNL.H from the Software Development Kit (SDK). The definition there is greatly reduced, defining just the NextEntryOffset, UniqueProcessId, HandleCount, PeakPagefileUsage and PrivatePageCount members. Documentation of NtQuerySystemInformation describes the SystemProcessInformation case as returning “an array of SYSTEM_PROCESS_INFORMATION structures, one for each process running in the system”, with no mention of what information lies in between. Separate documentation of ZwQuerySystemInformation presents a SYSTEM_PROCESS_INFORMATION that differs slightly from the WINTERNL.H definition in showing the NumberOfThreads member too, again without noting the connection with information between the “documented” structures.

Microsoft does publish the practical equivalent of a C-language definition as type information in public symbol files, though not for the kernel, where the structure is prepared, nor even for low-level user-mode DLLs that interpret the structure, but for various higher-level user-mode DLLs such as URLMON.DLL and only then starting with version 6.2.

Two earlier disclosures of type information are known, though not in symbol files but in statically linked libraries: GDISRVL.LIB from the Device Driver Kit (DDK) for Windows NT 3.51; and SHELL32.LIB from the DDK for Windows NT 4.0.

Layout

Since version 5.0, the SYSTEM_PROCESS_INFORMATION is 0xB8 or 0x0100 bytes in 32-bit and 64-bit Windows, respectively. The original structure is 0x88 bytes. The change can ultimately be traced to the I/O counters. Though version 3.10 provided for 64-bit counts of bytes transferred, it allowed only 32 bits for counting operations. The need to widen the latter to 64 bits each seems to have caused them all to be dropped for several versions. It is not just that they are not retrievable in this structure or through the ProcessIoCounters case of NtQueryInformationProcess, but that the kernel stops maintaining them. When they were not brought back for version 5.0, all now 64-bit, they were appended. Not until version 6.1 is the space they had occupied fully reassigned.

Offset (x86) Offset (x64) Definition Versions
0x00 0x00
ULONG NextEntryOffset;
all
0x04 0x04
ULONG NumberOfThreads;
all
0x08  
LARGE_INTEGER ReadTransferCount;
3.10 only
0x08
LARGE_INTEGER SpareLi1;
3.50 to 5.2
LARGE_INTEGER WorkingSetPrivateSize;
6.0 and higher
0x10  
LARGE_INTEGER WriteTransferCount;
3.10 only
0x10
LARGE_INTEGER SpareLi2;
3.50 to 6.0
ULONG HardFaultCount;
6.1 and higher
0x14 0x14
ULONG NumberOfThreadsHighWatermark;
6.1 and higher
0x18  
LARGE_INTEGER OtherTransferCount;
3.10 only
0x18
LARGE_INTEGER SpareLi3;
3.50 to 6.0
ULONGLONG CycleTime;
6.1 and higher
0x20 0x20
LARGE_INTEGER CreateTime;
all
0x28 0x28
LARGE_INTEGER UserTime;
all
0x30 0x30
LARGE_INTEGER KernelTime;
all
0x38 0x38
UNICODE_STRING ImageName;
all
0x40 0x48
LONG BasePriority;
all
0x44 0x50
PVOID UniqueProcessId;
all
0x48 0x58
PVOID InheritedFromUniqueProcessId;
all
0x4C  
ULONG ReadOperationCount;
3.10 only
ULONG SpareUl1;
3.50 only
0x60
ULONG HandleCount;
3.51 and higher
0x50  
ULONG WriteOperationCount;
3.10 only
ULONG SpareUl2;
3.50 to 4.0
0x64
ULONG SessionId;
5.0 and higher
0x54  
ULONG OtherOperationCount;
3.10 only
ULONG SpareUl3;
3.50 to 5.0
0x68
ULONG_PTR UniqueProcessKey;
5.1 and higher
0x58 0x70
ULONG_PTR PeakVirtualSize;
all
0x5C 0x78
ULONG_PTR VirtualSize;
all
0x60 0x80
ULONG PageFaultCount;
all
0x64 0x88
ULONG_PTR PeakWorkingSetSize;
all
0x68 0x90
ULONG_PTR WorkingSetSize;
all
0x6C 0x98
ULONG_PTR QuotaPeakPagedPoolUsage;
all
0x70 0xA0
ULONG_PTR QuotaPagedPoolUsage;
all
0x74 0xA8
ULONG_PTR QuotaPeakNonPagedPoolUsage;
all
0x78 0xB0
ULONG_PTR QuotaNonPagedPoolUsage;
all
0x7C 0xB8
ULONG_PTR PagefileUsage;
all
0x80 0xC0
ULONG_PTR PeakPagefileUsage;
all
0x84 0xC8
ULONG_PTR PrivatePageCount;
all
0x88 0xD0
LARGE_INTEGER ReadOperationCount;
5.0 and higher
0x90 0xD8
LARGE_INTEGER WriteOperationCount;
5.0 and higher
0x98 0xE0
LARGE_INTEGER OtherOperationCount;
5.0 and higher
0xA0 0xE8
LARGE_INTEGER ReadTransferCount;
5.0 and higher
0xA8 0xF0
LARGE_INTEGER WriteTransferCount;
5.0 and higher
0xB0 0xF8
LARGE_INTEGER OtherTransferCount;
5.0 and higher

Strictly speaking, the name SpareUl1 is an invention. Microsoft’s names SpareUl2 and SpareUl3 for the other 32-bit counts that were discontinued for version 3.50 survive in the GDISRVL.LIB for version 3.51 but the first in the sequence had already been ressigned as the HandleCount.

Whatever may be suggested to the contrary by its name, the PrivatePageCount is of bytes, just like the other counters whose names talk of size or usage.

Variable-Size Data

Some of the members are specially important to the process’s description beyond the SYSTEM_PROCESS_INFORMATION.

Immediately following the SYSTEM_PROCESS_INFORMATION is an array of zero or more SYSTEM_THREAD_INFORMATION structures if the information class is SystemProcessInformation, else SYSTEM_EXTENDED_THREAD_INFORMATION structures. Either way, the NumberOfThreads member tells how many.

The process’s name may be present as a null-terminated Unicode string. Its address and size are in the ImageName member. This is the full name if the information class is SystemFullProcessInformation. For the older information classes, this name is an extract, beginning after the last backslash.

Information Class Variations

The UniqueProcessKey is undefined for SystemProcessInformation. For the newer information classes it originally revealed the page number of the process’s page directory base. Version 6.0 instead reveals the address of the EPROCESS structure that represents the process as a kernel object. Whether either or both were thought to disclose too much is not known, but since version 6.1 the UniqueProcessKey is set identically to the UniqueProcessId.