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:

and variable-size data indicated by members of the preceding structures.

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.

This page was created on 9th July 2016 but was not published
until 26th October 2016. It was
last modified on 27th June 2019.