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 a problem that early versions had with 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 brought back for version 5.0, all now 64-bit, they were appended. Not until version 6.1 is the space that had been occupied by the 32-bit counts fully reassigned.

Offset (x86) Offset (x64) Definition Versions Remarks
0x00 0x00
ULONG NextEntryOffset;
all  
0x04 0x04
ULONG NumberOfThreads;
all  
0x08  
LARGE_INTEGER ReadTransferCount;
3.10 only later at 0xA0
0x08
LARGE_INTEGER SpareLi1;
3.50 to 5.2  
LARGE_INTEGER WorkingSetPrivateSize;
6.0 and higher  
0x10  
LARGE_INTEGER WriteTransferCount;
3.10 only later at 0xA8
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 later at 0xB0
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 later as LARGE_INTEGER at 0x88
ULONG SpareUl1;
3.50 only  
0x60
ULONG HandleCount;
3.51 and higher  
0x50  
ULONG WriteOperationCount;
3.10 only later as LARGE_INTEGER at 0x90
ULONG SpareUl2;
3.50 to 4.0  
0x64
ULONG SessionId;
5.0 and higher  
0x54  
ULONG OtherOperationCount;
3.10 only later as LARGE_INTEGER at 0x98
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 last member in 3.10 to 4.0
0x88 0xD0
LARGE_INTEGER ReadOperationCount;
5.0 and higher earlier as ULONG at 0x4C
0x90 0xD8
LARGE_INTEGER WriteOperationCount;
5.0 and higher earlier as ULONG at 0x50
0x98 0xE0
LARGE_INTEGER OtherOperationCount;
5.0 and higher earlier as ULONG at 0x54
0xA0 0xE8
LARGE_INTEGER ReadTransferCount;
5.0 and higher earlier at 0x08
0xA8 0xF0
LARGE_INTEGER WriteTransferCount;
5.0 and higher earlier at 0x10
0xB0 0xF8
LARGE_INTEGER OtherTransferCount;
5.0 and higher earlier at 0x18

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 SYSTEM_PROCESS_INFORMATION members are specially important for describing the variable-size data that continues the process’s description.

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 the member was named UniqueProcessKey in these versions is not known. Whatever it was named, what it contained may have been thought to disclose too much: since version 6.1 the UniqueProcessKey is set identically to the UniqueProcessId.