Geoff Chappell - Software Analyst
CURRENT WORK ITEM - PREVIEW ONLY
This function gets a wide range of system properties.
Specially noteworthy is that this function in its user-mode form, NtQuerySystemInformation, is occasionally the target of malware, not just to use it but to hook it, the idea being to mislead other software about the malware’s presence. See particularly the cases where this function returns information about the running processes, loaded modules and opened handles.
NTSTATUS ZwQuerySystemInformation ( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, ULONG *ReturnLength);
The SystemInformationClass argument tells what sort of information is sought. There are very many supported values.
The SystemInformation and SystemInformationLength arguments are respectively the address and size (in bytes) of a buffer that receives the information. What the function puts into this buffer depends on the information class. For some information classes, the function expects input from the buffer (too or instead). For some information classes, the SystemInformationLength argument can be 0 and the SystemInformation argument is then ignored.
The ReturnLength argument is the address of a variable whose value on output tells how much information, in bytes, the successful function has put into the buffer or may tell how much the failed function might have put into the buffer (had the buffer been large enough). This argument can be NULL if the caller does not want to know how much information is produced or is available.
The function returns STATUS_SUCCESS if successful, else a negative error code.
Of particular importance are STATUS_INFO_LENGTH_MISMATCH and STATUS_BUFFER_TOO_SMALL, which are the function’s indications that the information buffer was given with the wrong size. A size that would have sufficed for a full return of available information will have been set into the variable, if any, that was specified through the ReturnLength argument.
A size may be returned via ReturnLength for other error codes, but this outcome is here treated as unreliable since it looks not to be uniformly intended. Moreover, pending expansion of this article to account in detail for variations between Windows versions, it is as well to note that before version 6.0 the return of an expected size is unreliable even when the error code is STATUS_INFO_LENGTH_MISMATCH. (Roughly, the expected size tends to be returned for information classes for which the output can vary in size, but tends not to be for information classes whose output is a fixed-size structure.)
Note that when the function succeeds, what it sets via ReturnLength tells nothing of what more information the function might have put in a bigger buffer.
The function appears to be free to use the information buffer however it wants while it works towards its result. The understanding throughout these notes is that calling this function leaves the information buffer with undefined contents except for however many bytes the function declares as its output on success.
Beware that the preceding paragraphs attempt only to describe what seems intended in general for the parameters and return value. Cases exist which do not exactly conform. This may mean that the inferred description is wrong or at least needs to be refined, but some cases seem so special that it seems reasonable to suspect that the defect is not with my interpretation but with Microsoft’s implementation. For instance, after the function’s success for the information class SystemSessionProcessInformation (0x35), the size in the variable at ReturnLength can be vastly greater than the size that was given for SystemInformationLength.
The ZwQuerySystemInformation and NtQuerySystemInformation functions were originally for the kernel’s user-mode interface only. For this purpose, they are ancient, being exported by name from NTDLL in version 3.10 and higher. In user mode, the functions are aliases for a stub that transfers execution to the NtQuerySystemInformation implementation in kernel mode such that the execution is recognised as originating in user mode.
Availability in kernel mode wasn’t long in coming: ZwQuerySystemInformation is exported by name from the kernel in version 4.0 and higher; NtQuerySystemInformation in version 5.0 and higher. In kernel mode, ZwQuerySystemInformation is also a stub that transfers execution to the NtQuerySystemInformation implementation but such that the execution is recognised as originating in kernel mode.
As NtQuerySystemInformation only, and only then as exported from NTDLL, this function got documented at roughly the time of Microsoft’s settlement compliance in 2002. It is declared in WINTERNL.H from the Software Development Kit (SDK). The definition given there for the SYSTEM_INFORMATION_CLASS enumeration names a handful of the very many values that are acceptable to the function.
After a few years, certainly by 2007, this documentation of NtQuerySystemInformation was joined by slightly different documentation of ZwQuerySystemInformation, still only as exported from NTDLL. This documentation nowadays states plainly that the function “is no longer available for use as of Windows 8.”
Though NtQuerySystemInformation is mentioned in a comment in NTDDK.H from as long ago as the Device Driver Kit (DDK) for Windows NT 3.51, no header from a DDK or Windows Driver Kit (WDK) declares the function under either name until ZwQuerySystemInformation is declared in the ZWAPI.H that was published, possibly by oversight, in the Enterprise WDK for Windows 10 Version 1511.
The following implementation notes are primarily from inspection of the kernel from the original release of Windows 10. They may some day be treated to systematic revision to account for earlier versions. Meanwhile, where anything is added about earlier versions, take it not as an attempt at comprehensiveness but as a bonus from my being unable to resist at least a quick look into the history. Really, for a function such as this with so many cases, these notes can’t ever be anything but a work in progress.
Information classes SystemLogicalProcessorAndGroupInformation (0x6B) and SystemNodeDistanceInformation (0x79) are completely invalid for this function and cause its immediate failure, returning STATUS_INVALID_INFO_CLASS. Arguments other than SystemInformationClass are irrelevant. These information classes need ZwQuerySystemInformationEx instead.
If executing for a user-mode request, the function has some general defensiveness about addresses passed as arguments. Failure at any of these defences is failure for the function, which typically returns STATUS_DATATYPE_MISALIGNMENT or STATUS_ACCESS_VIOLATION (showing in kernel mode as a raised but handled exception).
If an information buffer is given, meaning here that SystemInformationLength is non-zero, then its address SystemInformation must have 4-byte alignment (in general) and the whole buffer must be in user-mode address space and be writable (at its first byte and also for one byte at each page boundary that is inside the buffer). For some information classes, the alignment requirement is effectively waived by requiring only 1-byte alignment:
The x64 builds make an extra case of SystemLocksInformation (0x0C), which requires 8-byte alignment.
If a variable is given by address in the ReturnLength argument for learning how much information is or could be produced, then the address must be in user-mode address space and the variable must be writable.
The large table below lists the information classes that ZwQuerySystemInformation does not dismiss as invalid (after the preceding defences). For all others, the function fails, returning STATUS_INVALID_INFO_CLASS.
Names of the information classes and of the related structures and of their members are from type information in public symbol files for various high-level user-mode modules such as URLMON.DLL starting with Windows 8 and in statically linked libraries from early editions of the Device Driver Kit (DDK). No symbol files for the kernel or NTDLL have relevant type information in any known Windows release.
Note that the versions that are shown below for each information class are those for which the function does not fail trivially as invalid. More than a few are failed as trivially, but to return some other error code such as STATUS_NOT_IMPLEMENTED or STATUS_NOT_SUPPORTED. The following shorthands are used for changes within versions:
Numeric Value | Symbolic Name | Versions | Remarks |
---|---|---|---|
0x00 | SystemBasicInformation | 3.10 and higher | |
0x01 | SystemProcessorInformation | 3.10 and higher | |
0x02 | SystemPerformanceInformation | 3.10 and higher | |
0x03 | SystemTimeOfDayInformation | 3.10 and higher | |
0x04 | SystemPathInformation | 3.10 and higher | |
0x05 | SystemProcessInformation | 3.10 and higher | |
0x06 | SystemCallCountInformation | 3.10 and higher | |
0x07 | SystemDeviceInformation | 3.10 and higher | |
0x08 | SystemProcessorPerformanceInformation | 3.10 and higher | |
0x09 | SystemFlagsInformation | 3.10 and higher | |
0x0A | SystemCallTimeInformation | 3.50 and higher | |
0x0B | SystemModuleInformation | 3.10 and higher | |
0x0C | SystemLocksInformation | 3.10 and higher | |
0x0D | SystemStackTraceInformation | 3.10 and higher | |
0x0E | SystemPagedPoolInformation | 3.10 and higher | |
0x0F | SystemNonPagedPoolInformation | 3.10 and higher | |
0x10 | SystemHandleInformation | 3.10 and higher | |
0x11 | SystemObjectInformation | 3.10 and higher | |
0x12 | SystemPageFileInformation | 3.10 and higher | |
0x13 | SystemVdmInstemulInformation | 3.10 and higher | |
0x15 | SystemFileCacheInformation | 3.10 and higher | |
0x16 | SystemPoolTagInformation | 3.50 and higher | |
0x17 | SystemInterruptInformation | 3.51 and higher | |
0x18 | SystemDpcBehaviorInformation | 3.51 and higher | |
0x19 | SystemFullMemoryInformation | 4.0 and higher | |
0x1C | SystemTimeAdjustmentInformation | 3.50 and higher | |
0x1D | SystemSummaryMemoryInformation | 4.0 and higher | |
0x1E | SystemNextEventIdInformation | 3.50 to 4.0 | |
0x1F | SystemEventIdsInformation | 3.50 to 4.0 | |
SystemPerformanceTraceInformation | 6.0 and higher | ||
0x20 | SystemCrashDumpInformation | 3.50 to 5.0 | |
0x21 | SystemExceptionInformation | 3.50 and higher | |
0x22 | SystemCrashDumpStateInformation | 3.50 to 5.0 | |
0x23 | SystemKernelDebuggerInformation | 3.50 and higher | |
0x24 | SystemContextSwitchInformation | 3.50 and higher | |
0x25 | SystemRegistryQuotaInformation | 3.51 and higher | |
0x28 | SystemPlugPlayBusInformation | 3.51 to 4.0 | |
0x29 | SystemDockInformation | 3.51 to 4.0 | |
0x2A | SystemProcessorIdleInformation | 5.1 and higher | |
0x2B | SystemLegacyDriverInformation | 5.0 and higher | |
0x2C | SystemCurrentTimeZoneInformation | 4.0 and higher | |
0x2D | SystemLookasideInformation | 4.0 and higher | |
0x32 | SystemRangeStartInformation | 5.0 and higher | |
0x33 | SystemVerifierInformation | 5.0 and higher | |
0x35 | SystemSessionProcessInformation | 5.0 and higher | |
0x36 | SystemObjectSecurityMode | late 5.0 only | next as 0x46 |
0x37 | unknown | late 5.0 only | next as 0x47 |
SystemNumaProcessorMap | 5.1 and higher | ||
0x38 | SystemPrefetcherInformation | 5.1 and higher | |
0x39 | SystemExtendedProcessInformation | 5.1 and higher | |
0x3A | SystemRecommendedSharedDataAlignment | 5.1 and higher | |
0x3B | SystemComPlusPackage | 5.1 and higher | |
0x3C | SystemNumaAvailableMemory | 5.1 and higher | |
0x3D | SystemProcessorPowerInformation | 5.1 and higher | |
0x3E | SystemEmulationBasicInformation | 5.1 and higher | |
0x3F | SystemEmulationProcessorInformation | 5.1 and higher | |
0x40 | SystemExtendedHandleInformation | 5.1 and higher | |
0x41 | SystemLostDelayedWriteInformation | 5.1 and higher | |
0x42 | unknown | late 5.1 only | also as 0x46 |
SystemBigPoolInformation | 5.2 and higher | ||
0x43 | SystemSessionPoolTagInformation | 5.2 and higher | |
0x44 | SystemSessionMappedViewInformation | 5.2 and higher | |
0x45 | SystemHotpatchInformation | 5.2 and higher | |
0x46 | SystemObjectSecurityMode | late 5.1 and higher | previously 0x36 |
0x47 | unknown | late 5.1 only | previously 0x37 |
0x48 | SystemWatchdogTimerInformation | 5.2 and higher | |
0x49 | SystemLogicalProcessorInformation | very late 5.1 and higher | |
0x4C | SystemFirmwareTableInformation | late 5.2 and higher | |
0x4D | SystemModuleInformationEx | 6.0 and higher | |
0x4F | SystemSuperfetchInformation | 6.0 and higher | |
0x50 | SystemMemoryListInformation | 6.0 and higher | |
0x51 | SystemFileCacheInformationEx | late 5.2 and higher | |
0x53 | SystemProcessorIdleCycleTimeInformation | 6.0 and higher | |
0x54 | SystemVerifierCancellationInformation | 6.0 only | |
0x56 | SystemRefTraceInformation | 6.0 and higher | |
0x57 | SystemSpecialPoolInformation | 6.0 and higher | |
0x58 | SystemProcessIdInformation | 6.0 and higher | |
0x5A | SystemBootEnvironmentInformation | 6.0 and higher | |
0x5B | SystemHypervisorInformation | 6.0 and higher | |
0x5C | SystemVerifierInformationEx | 6.0 and higher | |
0x5F | SystemCoverageInformation | 6.0 and higher | |
0x60 | SystemPrefetchPatchInformation | 6.0 and higher | |
0x62 | SystemSystemPartitionInformation | 6.0 and higher | |
0x63 | SystemSystemDiskInformation | 6.0 and higher | |
0x64 | SystemProcessorPerformanceDistribution | 6.0 and higher | |
0x65 | SystemNumaProximityNodeInformation | 6.0 and higher | |
0x66 | SystemDynamicTimeZoneInformation | 6.0 and higher | |
0x67 | SystemCodeIntegrityInformation | 6.0 and higher | |
0x69 | SystemProcessorBrandString | late 6.0 and higher | |
0x6A | SystemVirtualAddressInformation | late 6.0 and higher | |
0x6C | SystemProcessorCycleTimeInformation | 6.1 and higher | |
0x6D | SystemStoreInformation | 6.1 and higher | |
0x70 | SystemVhdBootInformation | 6.1 and higher | |
0x71 | SystemCpuQuotaInformation | 6.1 and higher | |
0x72 | SystemNativeBasicInformation | 6.2 and higher | |
0x73 | SystemErrorPortTimeouts | 6.2 and higher | |
0x74 | SystemLowPriorityIoInformation | 6.1 and higher | |
0x75 | SystemBootEntropyInformation | 6.1 and higher | |
0x76 | SystemVerifierCountersInformation | 6.1 and higher | |
0x77 | SystemPagedPoolInformationEx | 6.1 and higher | |
0x78 | SystemSystemPtesInformationEx | 6.1 and higher | |
0x7A | SystemAcpiAuditInformation | 6.1 and higher | |
0x7B | SystemBasicPerformanceInformation | 6.1 and higher | |
0x7C | SystemQueryPerformanceCounterInformation | late 6.1 and higher | |
0x7D | SystemSessionBigPoolInformation | 6.2 and higher | |
0x7E | SystemBootGraphicsInformation | 6.2 and higher | |
0x80 | SystemBadPageInformation | 6.2 and higher | |
0x85 | SystemPlatformBinaryInformation | 6.2 and higher | |
0x86 | SystemPolicyInformation | 6.3 and higher | |
0x87 | SystemHypervisorProcessorCountInformation | 6.2 and higher | |
0x88 | SystemDeviceDataInformation | 6.2 and higher | |
0x89 | SystemDeviceDataEnumerationInformation | 6.2 and higher | |
0x8A | SystemMemoryTopologyInformation | 6.2 and higher | |
0x8B | SystemMemoryChannelInformation | 6.2 and higher | |
0x8C | SystemBootLogoInformation | 6.2 and higher | |
0x8D | SystemProcessorPerformanceInformationEx | 6.2 and higher | |
0x8F | SystemSecureBootPolicyInformation | 6.2 and higher | |
0x90 | SystemPageFileInformationEx | 6.2 and higher | |
0x91 | SystemSecureBootInformation | 6.2 and higher | |
0x93 | SystemPortableWorkspaceEfiLauncherInformation | 6.2 and higher | |
0x94 | SystemFullProcessInformation | 6.2 and higher | |
0x95 | SystemKernelDebuggerInformationEx | 6.3 and higher | |
0x96 | SystemBootMetadataInformation | 6.3 and higher | |
0x97 | SystemSoftRebootInformation | 6.3 and higher | |
0x99 | SystemOfflineDumpConfigInformation | 6.3 and higher | |
0x9A | SystemProcessorFeaturesInformation | 6.3 and higher | |
0x9C | SystemEdidInformation | 6.3 and higher | |
0x9D | SystemManufacturingInformation | 10.0 and higher | |
0x9E | SystemEnergyEstimationConfigInformation | 10.0 and higher | |
0x9F | SystemHypervisorDetailInformation | 10.0 and higher | |
0xA0 | SystemProcessorCycleStatsInformation | 10.0 and higher | |
0xA2 | SystemTrustedPlatformModuleInformation | 10.0 and higher | |
0xA3 | SystemKernelDebuggerFlags | 10.0 and higher | |
0xA4 | SystemCodeIntegrityPolicyInformation | 10.0 and higher | |
0xA5 | SystemIsolatedUserModeInformation | 10.0 and higher | |
0xA6 | SystemHardwareSecurityTestInterfaceResultsInformation | 10.0 and higher | |
0xA7 | SystemSingleModuleInformation | 10.0 and higher | |
0xA9 | SystemDmaProtectionInformation | 10.0 and higher | |
0xAB | SystemSecureBootPolicyFullInformation | 10.0 and higher | |
0xAC | SystemCodeIntegrityPolicyFullInformation | 10.0 and higher | |
0xAD | SystemAffinitizedInterruptProcessorInformation | 10.0 and higher | |
0xAE | SystemRootSiloInformation | 10.0 and higher | |
0xAF | SystemCpuSetInformation | 10.0 and higher | |
0xB2 | SystemSecureKernelProfileInformation | 1511 and higher | |
0xB3 | SystemCodeIntegrityPlatformManifestInformation | 1607 and higher | |
0xB4 | SystemInterruptSteeringInformation | 1607 and higher | |
0xB5 | SystemSupportedProcessorArchitectures | 1607 and higher | |
0xB6 | SystemMemoryUsageInformation | 1607 and higher | |
0xB7 | SystemCodeIntegrityCertificateInformation | 1607 and higher | |
0xB8 | SystemPhysicalMemoryInformation | 1703 and higher | |
0xB9 | SystemControlFlowTransition | 1703 and higher | |
0xBA | SystemKernelDebuggingAllowed | 1703 and higher | |
0xBC | SystemActivityModerationUserSettings | 1703 and higher | |
0xBD | SystemCodeIntegrityPoliciesFullInformation | 1703 and higher | |
0xBE | SystemCodeIntegrityUnlockInformation | 1703 and higher | |
0xC0 | SystemFlushInformation | 1703 and higher | |
0xC1 | SystemProcessorIdleMaskInformation | 1709 and higher | |
0xC3 | SystemWriteConstraintInformation | 1709 and higher | |
0xC4 | SystemKernelVaShadowInformation | 1803 and higher | |
0xC5 | SystemHypervisorSharedPageInformation | 1803 and higher | |
0xC6 | SystemFirmwareBootPerformanceInformation | 1803 and higher | |
0xC7 | SystemCodeIntegrityVerificationInformation | 1803 and higher | |
0xC8 | SystemFirmwarePartitionInformation | 1803 and higher | |
0xC9 | SystemSpeculationControlInformation | 1803 and higher | |
0xCA | SystemDmaGuardPolicyInformation | 1803 and higher |
For these valid information classes, all remaining behaviour varies with the information class.
Almost all information classes are associated with a structure that is at least the start of what the function produces as its output or expects as input. Mostly, the structure has no other purpose. Rather than have a separate page for each information class and then another for the corresponding structure, the remainder of this page gives for each information class a brief description of the general behaviour, and then the meaning of whatever the function puts in the structure or inteprets in it is taken up, if at all, in separate documentation of the structure.
Before proceeding to the information classes one by one, it is as well to note some general points.
For a few of the valid information classes, the function does little or nothing other than return an error such as STATUS_NOT_IMPLEMENTED or STATUS_NOT_SUPPORTED. For brevity, these are typically said to be not implemented or not supported, respectively. Signs exist that some of these may be implemented non-trivially in debug builds, which are outside the scope of these notes.
Except if noted explicitly below, the function never accesses the SystemInformation or writes to the variable at ReturnLength without preparing for exceptions. If executing for a user-mode request, the occurrence of an exception during such access is fatal for the function, which returns the exception code as its own result. If executing for a kernel-mode request, exceptions are handled only to continue as if unhandled, which will typically be fatal to Windows.
Some information classes are sensitive in that the corresponding information may contain kernel-mode addresses. Plainly, this is not an accidental leakage: it’s intended for user-mode processes that exist for diagnostics and instrumentation. But relatively few user-mode processes have that purpose, none should be trusted more than can’t be avoided, and some are deliberately up to no good. Revealing a kernel-mode address to a malicious user-mode caller is arguably not itself a security vulnerability, but it may help the latter’s success at attacking some other vulnerability. For version 6.3, the Windows kernel introduced the notion of a restricted caller that is not to be given kernel-mode addresses. When this function executes for a user-mode request from a restricted caller, it fails for some information classes rather than produce any information, and for some others it edits kernel-mode addresses out of whatever information is produced.
What counts as a restricted caller depends on the Windows version. Originally, a restricted caller is specifically a low-integrity process. In detail, a process has low integrity if an integrity level cannot be obtained from the process’s primary token or if the integrity level is less than SECURITY_MANDATORY_MEDIUM_RID (0x2000). Windows 10 has a wider restriction that is essentially the same as implemented for the RtlIsSandboxedToken function: a user-mode caller is restricted unless it can pass an access check for READ_CONTROL rights to securable objects that have medium integrity.
The information to be produced for some information classes comes from enumerating items whose existence, state and relationships during ordinary execution are subject to instant change. The different types of item provide differently for synchronisation. Describing these in each case is too much for these notes at present, but some general caution seems in order.
Even without complications from synchronisation, the enumeration is sometimes of tens of thousands of items and can therefore take enough time in total to affect how frequently the information can usefully be polled. Because of synchronisation, however, the enumeration can have measureable effects not just on the program that calls the function but on multi-tasking performance in general and thus on other people’s software.
In the simplest but most comprehensive synchronisation, the function locks all other access to all instances of whatever is being enumerated. For the whole of the enumeration, while the function retrieves information about each item and follows links between items, no other access is possible to any of the items. In some (few) cases, this means the function holds a spin lock, with one processor at DISPATCH_LEVEL, for an unusually long time. Measurements in a debugger easily produce times of the order of 10ms, to compare with long-standing advice in the WDK that “no routine should hold a spin lock for longer than 25 microseconds.”
Where synchronisation is more sophisticated, access to the items being enumerated is subject to multiple levels of synchronisation such that the function’s retrieval of information about one item does not prevent others’ access to other items that have been enumerated already or may soon be. This has an implication that seems not much remarked upon. The information that this function produces from enumerating a given type of item is not a snapshot of the state of all such items at any one time or during any one interval. It ordinarily is a self-consistent snapshot of each item, but at different times for different items. For instance, that two processes appear in the results from SystemProcessInformation (0x05) does not mean that these two processes ever coexisted.
Somewhat related to synchronisation—indeed, sometimes demanded by the particular means of synchronisation—is that the function may, for the whole duration of the enumeration, both lock the supplied information buffer into physical memory and map it into system address space. In all such cases, the function itself has no notion of whether SystemInformationLength is reasonable. The whole information buffer gets locked and mapped, even if this is vastly more than turns out to be needed. When executing for a user-mode request, the function locks and maps even if the information buffer is vastly more than the user-mode caller would be able to lock without having increased its working set, and even if the user-mode caller lacks the necessary privilege to have been permitted to increase its working set.
Many information classes have similar elements to their treatment. This allows some shorthands. Notably, where the descriptions below say simply that the function sets the return length, it’s left as understood that what gets set is the variable at the address given by ReturnLength if the latter is not NULL.
Please understand that devising shorthands so that the behaviour can be described both usefully and accurately without tedious repetition is a work in progress, surely requiring multiple passes, each susceptible to error. Take more care than would be usual even for draft material.
In the simplest cases that are well described by a shorthand, the information buffer is to receive some fixed-size structure. In the very simplest, the information buffer must provide exactly this structure.
Shorthand: | The information buffer must provide exactly an X for the function to fill. |
Meaning (Failure): | If the SystemInformationLength is not
an exact fit for an X, the function sets the return length to the size of an X and returns STATUS_INFO_LENGTH_MISMATCH. |
Meaning (Success); | The function prepares an X at SystemInformation,
sets the return length to the size of an X and returns STATUS_SUCCESS. |
Example: | SystemBasicInformation (0x00) |
Only a little different is that the function tolerates a caller who provides more space than necessary.
Shorthand: | The information buffer must provide at least an X for the function to fill. |
Meaning (Failure): | If the SystemInformationLength is too
small for an X, the function sets the return length to the size of an X and returns STATUS_INFO_LENGTH_MISMATCH. |
Meaning (Success); | The function prepares an X at SystemInformation,
sets the return length to the size of an X and returns STATUS_SUCCESS. |
Example: | SystemProcessorInformation (0x01) |
These shorthands are in many cases the whole of the handling: nothing is checked other than the SystemInformationLength; and nothing can go wrong with filling in the structure. Where this is not true, variations are described under the heading Preliminaries, for applying in advance of checking the SystemInformationLength, else Retrieval.
Inevitably, the information classes for which the information can vary in size from one call to another each have very different requirements of the SystemInformationLength and interpretations of what length they should indicate via ReturnLength if the information buffer is too small. Two broad categories may lend themselves to shorthands: the information is an array of fixed-size structures; or the information is a fixed-size header whose last member begins an array of fixed-size structures.
Descriptions of information classes that produce variable-size information are mostly in this faded colour to signify that they are even more just a draft than is everything else on this page. Only after the descriptions are all at least drafted is it easy to see what repeats and what thus might be condensed.
Where a description below states that the information buffer is to receive some fixed-size structure A whose B array has some fixed-size structure C for each D, the following is implied as the whole behaviour. The function enumerates the D, writing to the information buffer as it goes. If an error occurs while enumerating, the function fails. For this purpose, it is not an error to find that the information buffer is too small. Without writing more to the information buffer, the function continues the enumeration to determine how much information might be produced were the buffer large enough. When the enumeration completes, the function sets the return length to this total, and fails, returning STATUS_INFO_LENGTH_MISMATCH. If enumeration completes such that the information buffer has all the information, the function sets the return length to this same total, and returns STATUS_SUCCESS. The oldest example is SystemModuleInformation (0x0B).
The information buffer for SystemBasicInformation must provide exactly a SYSTEM_BASIC_INFORMATION structure for the function to fill.
In version 3.51 and higher, the information buffer for SystemProcessorInformation must provide at least a SYSTEM_PROCESSOR_INFORMATION structure for the function to fill.
Exactly what’s intended before version 3.51 is unclear. The information buffer must provide at least a SYSTEM_PROCESSOR_INFORMATION for each processor. Perhaps as a coding error, the function fills in only the first such structure.
The information buffer for SystemPerformanceInformation must provide at least enough for the function to fill a SYSTEM_PERFORMANCE_INFORMATION structure up to and including its SystemCalls member (this having been the last member in the structure’s original layout). If the SystemInformationLength is too small for the partial structure, the function sets the return length to the size of the full structure and returns STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills in as much of the whole structure as fits, sets the return length to however many bytes are written to the buffer, and returns STATUS_SUCCESS.
This information class is unusual in requiring that the information buffer provides no more than the fixed-size SYSTEM_TIMEOFDAY_INFORMATION structure. If given a bigger buffer, the function sets the return length to the size of the expected structure, and returns STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills in as much of the whole structure as fits, sets the return length to however many bytes are written to the buffer, and returns STATUS_SUCCESS.
In case it’s not clear: when given this information class but no information buffer, the function succeeds!
Failure for being given too large an information buffer dates from the structure’s extension for version 5.0. In earlier versions, the information buffer must provide exactly a SYSTEM_TIMEOFDAY_INFORMATION structure for the function to fill.
This information class is only trivially valid. The function returns STATUS_NOT_IMPLEMENTED.
In versions before 3.51 the information buffer is to receive a UNICODE_STRING structure and the path to the Windows directory. Whether Microsoft ever wrapped this UNICODE_STRING into another structure, perhaps named SYSTEM_PATH_INFORMATION, is not known.
Version 3.51 moved the path to the KUSER_SHARED_DATA, as the NtSystemRoot member, presumably so that the path is readily available in user mode without the expense of calling the kernel. This change was sufficiently important that versions 3.51 to 5.0 break to the debugger to show a message before failing the function as not implemented:
EX: SystemPathInformation now available via SharedUserData
The information buffer is to receive a collection of irregularly spaced SYSTEM_PROCESS_INFORMATION structures, one per process. The spacing is irregular because each such structure can be followed by varying numbers of other fixed-size structures and by variable-size data too: an array of SYSTEM_THREAD_INFORMATION structures, one for each of the process’s threads; a SYSTEM_PROCESS_INFORMATION_EXTENSION structure; and the process’s name.
The least requirement for describing even one process, with no threads and no name, is a SYSTEM_PROCESS_INFORMATION and SYSTEM_PROCESS_INFORMATION_EXTENSION together. If given less for the information buffer but without having been asked for a return length, the function fails immediately, returning STATUS_INFO_LENGTH_MISMATCH.
Ordinarily, however, the function proceeds to enumerate processes until some error occurs that must be returned. The function writes to the information buffer while enumerating, for as long as space remains for each whole item. If the function has an item to add that will not fit, then if no return length was asked for, the function fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function continues the enumeration but without writing more to the information buffer. The intention is still to return STATUS_INFO_LENGTH_MISMATCH if no other error occurs, but with the return length showing how much data the enumeration would have produced had the buffer been big enough. Note that the size thus reported need not suffice when the function is next called. This is not just the usual theoretical point, but a practical one: the number of processes and threads not only can change between calls but is highly likely to.
If the enumeration completes without error and without exhausting the buffer, the function sets the return length to however many bytes it put in the buffer, and succeeds.
This information class is only trivially valid. The function returns STATUS_NOT_SUPPORTED.
This is recent, however. Although the function is not even valid in version 3.10, all other versions before 10.0 fill the information buffer with a SYSTEM_CALL_COUNT_INFORMATION structure followed by variable-size data.
The information buffer must provide exactly a SYSTEM_DEVICE_INFORMATION structure for the function to fill.
The information buffer is to receive an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION structures, one for each active processor in the current processor group. If the information buffer is not an exact fit for one or more such structures, the function sets the return length to the size of the array it could produce, and fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function copies to the buffer as many whole structures from the full array as fit, sets the return length to the size of what has been put in the buffer, and succeeds.
The information buffer must provide exactly a SYSTEM_FLAGS_INFORMATION structure for the function to fill.
This information class is only trivially valid. The function returns STATUS_NOT_IMPLEMENTED.
Rejection of this information class as unimplemented dates from version 3.50. Before then, this information class is instead rejected as invalid. Might it be implemented non-trivially in debug builds of some sort, even if only for Microsoft’s own testing during development? Microsoft has published symbol files that define a SYSTEM_CALL_TIME_INFORMATION structure, which would be curiously named if not to support this information class.
If executing for a user-mode request from a restricted caller, the function fails, returning STATUS_ACCESS_DENIED.
The information buffer is to receive an RTL_PROCESS_MODULES structure whose Modules array has an RTL_PROCESS_MODULE_INFORMATION for each module.
The information buffer is to receive an RTL_PROCESS_LOCKS structure whose Locks array has an RTL_PROCESS_LOCK_INFORMATION for each lock. Before the function sets about producing this information, There are two variations from this shorthand:
If executing for a user-mode request from a restricted caller, the function fails, returning STATUS_ACCESS_DENIED.
With these preliminaries out of the way, the information class is handled as implied by the shorthand.
The locks in question are what kernel-mode programmers know as system resources, each implemented as an ERESOURCE structure (declared in WDM.H). Beware that there can be very many locks. Expect tens of thousands even on a computer that has only just started. Beware also that their number not only can change between calls to the function but is highly likely to—indeed, almost always will.
The information buffer is to receive an RTL_PROCESS_BACKTRACES structure whose BackTraces array has an RTL_PROCESS_BACKTRACE_INFORMATION for each stack. There are several variations from this shorthand.
If executing for a user-mode request from a restricted caller, the function fails, returning STATUS_ACCESS_DENIED.
With these preliminaries out of the way, the information class is handled as implied by the shorthand.
This information class is only trivially valid. The function sets the return length to zero and returns STATUS_NOT_IMPLEMENTED.
Versions before 5.1 first check for a minimum expectation and then have a subroutine complain of being not implemented. In versions before 3.51, the subroutine does non-trivial work, notably to lock the information buffer in anticipation of producing a snapshot of the pool, before concluding that there is no implementation. The expectation in all these old versions appears to be that the information buffer is to receive a SYSTEM_POOL_INFORMATION structure whose Entries member has a SYSTEM_POOL_ENTRY for each pool entry, allocated or not. Perhaps this information class was implemented non-trivially in debug builds.
This information class is only trivially valid. The function sets the return length to zero and returns STATUS_NOT_IMPLEMENTED.
Versions before 5.1 first check for a minimum expectation and then have a subroutine complain of being not implemented. In versions before 3.51, the subroutine does non-trivial work, notably to lock the information buffer in anticipation of producing a snapshot of the pool, before concluding that there is no implementation. The expectation in all these old versions appears to be that the information buffer is to receive a SYSTEM_POOL_INFORMATION structure whose Entries member has a SYSTEM_POOL_ENTRY for each pool entry, allocated or not. Perhaps this information class was implemented non-trivially in debug builds.
The information buffer is to receive a SYSTEM_HANDLE_INFORMATION structure whose Handles array has a SYSTEM_HANDLE_TABLE_ENTRY_INFO for each handle. If the buffer is too small even for the formally defined structure, with its capacity for describing one handle, the function sets the return length to the size of the formal structure, and fails, returning STATUS_INFO_LENGTH_MISMATCH. The function also returns STATUS_INFO_LENGTH_MISMATCH if the buffer is large enough for describing one handle but not large enough to receive the whole array. In this case, the function sets the return length to the size that would be needed for the full description. Beware that there can be very many handles and that their number not only can change between calls to the function but is highly likely to.
The information buffer must be pointer-aligned. If it is not, the function fails, returning STATUS_DATATYPE_MISALIGNMENT.
If executing for a user-mode request from a restricted caller, the function fails, returning STATUS_ACCESS_DENIED.
The information buffer is to receive a collection of irregularly spaced SYSTEM_OBJECTTYPE_INFORMATION structures, one for each type of object. Each of these can be followed by some number of SYSTEM_OBJECT_INFORMATION structures, one for each object of the corresponding type. These too are irregularly spaced because each can be followed by the object’s name. If the buffer is too small even for one SYSTEM_OBJECTTYPE_INFORMATION, the function sets the return length to show this minimal expectation, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
If executing for a user-mode request from a restricted caller, the function fails, returning STATUS_ACCESS_DENIED.
The information buffer is to receive a collection of irregularly spaced SYSTEM_PAGEFILE_INFORMATION structures, one for each paging file (not counting swap files and virtual stores). If the buffer is too small even for the first of these structures, the function sets the return length to show this minimal expectation, and fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills the buffer with as many structures and other data as fits, and sets the return length to the size of what has been put in the buffer.
In 64-bit Windows, which has no Virtual DOS Machine (VDM) support, this information class is only trivially valid. The function sets the return length to zero and returns STATUS_NOT_IMPLEMENTED.
In 32-bit Windows, the information buffer must provide at least a SYSTEM_VDM_INSTEMUL_INFORMATION structure for the function to fill.
In 64-bit Windows, the information buffer must provide at least a SYSTEM_FILECACHE_INFORMATION structure for the function to fill.
The 32-bit Windows implementation allows a smaller information buffer, to account for an original definition that reached only to the PageFaultCount member. If the information buffer is not large enough for the original structure, the function sets the return length to this smaller requirement, and returns STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills in either the partial structure or the whole structure, if the latter fits, sets the return length to however many bytes were written to the buffer, and returns STATUS_SUCCESS.
The information buffer is to receive a SYSTEM_POOLTAG_INFORMATION structure whose TagInfo array has a SYSTEM_POOLTAG for each tag. If the buffer is too small even for the formally defined structure, with its capacity for describing one tag, the function sets the return length to the size of the formal structure, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
The information buffer is to receive an array of SYSTEM_INTERRUPT_INFORMATION structures, one for each active processor in the current processor group. If the information buffer is not large enough for them all, the function sets the return length to the size of the array it could produce, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
The information buffer must provide exactly a SYSTEM_DPC_BEHAVIOR_INFORMATION structure for the function to fill.
This information class is only trivially valid. The function returns STATUS_NOT_IMPLEMENTED.
In versions 4.0 to 5.1, the information buffer must provide at least a SYSTEM_MEMORY_INFORMATION. Assuming it does, the information class is handled by an internal routine which symbol files name as MmMemoryUsage. In builds that have been inspected, but perhaps not in debug builds, this routine also is trivial: the function still returns as if not implemented.
The information buffer must provide exactly a SYSTEM_QUERY_TIME_ADJUST_INFORMATION structure for the function to fill.
This information class is only trivially valid. The function returns STATUS_NOT_IMPLEMENTED.
In versions 4.0 to 5.1, the information buffer must provide at least a SYSTEM_MEMORY_INFORMATION. Assuming it does, the information class is handled by an internal routine which symbol files name as MmMemoryUsage. In builds that have been inspected, but perhaps not in debug builds, this routine also is trivial: the function still returns as if not implemented.
This information class is valid in versions 3.50 to 4.0 but only trivially. The function returns STATUS_NOT_IMPLEMENTED.
This information class is valid in versions 3.50 to 4.0 but only trivially. The function returns STATUS_NOT_IMPLEMENTED.
The information buffer not only receives output but provides input. Moreover, the input begins with a secondary information class that subdivides the behaviour. If the information buffer is too small to provide this EVENT_TRACE_INFORMATION_CLASS as input, the function returns STATUS_INVALID_PARAMETER. Remaining behaviour varies with the second information class.
This information class is nowadays invalid. In versions 3.50 to 5.0, however, the information buffer must provide at least a SYSTEM_CRASH_DUMP_INFORMATION structure for the function to fill.
The information buffer must provide at least a SYSTEM_EXCEPTION_INFORMATION structure for the function to fill.
This information class is nowadays invalid for queries. In versions 3.50 to 5.0, however, the information buffer must provide at least a SYSTEM_CRASH_STATE_INFORMATION structure for the function to fill.
The information buffer must provide at least a SYSTEM_KERNEL_DEBUGGER_INFORMATION structure for the function to fill.
The information buffer must provide at least a SYSTEM_CONTEXT_SWITCH_INFORMATION structure for the function to fill.
The information buffer must provide at least a SYSTEM_REGISTRY_QUOTA_INFORMATION structure for the function to fill.
This information class is valid in versions 3.51 to 4.0 but only trivially. The function returns STATUS_NOT_IMPLEMENTED.
Microsoft is known to have defined a SYSTEM_PLUGPLAY_BUS_INFORMATION structure, which would be curiously named if it was never at least intended for supporting this information class.
This information class is valid in versions 3.51 to 4.0 but only trivially. The function returns STATUS_NOT_IMPLEMENTED.
Microsoft is known to have defined a SYSTEM_DOCK_INFORMATION structure, which would be curiously named if it was never at least intended for supporting this information class.
The information buffer is to receive an array of SYSTEM_PROCESSOR_IDLE_INFORMATION structures, one for each active processor in the current processor group. If the information buffer is not large enough for them all, the function sets the return length to the size of the array it could produce, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
This information class is invalid before version 5.1 but Microsoft is known to have defined it differently, as SystemPowerInformation, in versions 3.51 and 4.0. Though the information class with this name is not known ever to have been valid, Microsoft is known to have defined a SYSTEM_POWER_INFORMATION structure at the time, well ahead of and very differently from the documented structure that later has this name. It seems safe to speculate that this SYSTEM_POWER_INFORMATION was at least intended for supporting this information class while it was named SystemPowerInformation.
The information buffer is to receive a fixed-size SYSTEM_LEGACY_DRIVER_INFORMATION structure and a variable-size string. If the information buffer is too small even for the fixed-size structure, the function sets the return length to show the minimal expectation, and fails, returning STATUS_INFO_LENGTH_MISMATCH. If it’s too small for all that the function would copy, the function sets the return length to show what it could produce, and fails, returning STATUS_BUFFER_OVERFLOW.
This information class is invalid before version 5.0 but Microsoft is known to have defined it differently, as SystemProcessorSpeedInformation, in versions 3.51 and 4.0. Though the information class with this name is not known ever to have been valid, Microsoft is known to have defined a SYSTEM_PROCESSOR_SPEED_INFORMATION structure, which would be curiously named if it was never at least intended for supporting this information class while it was named SystemProcessorSpeedInformation.
The information buffer must provide at least an RTL_TIME_ZONE_INFORMATION structure for the function to fill.
The information buffer is to receive an array of SYSTEM_LOOKASIDE_INFORMATION structures, one for each lookaside list in four sets. Broadly, these sets are:
Finer resolution of the lookaside lists that can be enumerated would necessarily be a separate article (or articles). For present purposes it must suffice to know that there typically exist very many lookaside lists: easily in the thousands. The function fills the buffer with as many whole structures as there are lists to describe and which fit. It then sets the return length to show how much has been put there, and succeeds.
In case it’s not clear: when given this information class but no information buffer, the function succeeds!
In case it’s not clear: when given this information class, the function can succeed without having produced all the possible information and without indicating how much it could have produced.
Except when succeeding trivially because the information buffer is too small for even one structure, the function expects to write at least something into the information buffer. So that enumerating the lookaside lists does not itself cause allocations from and frees to lookaside lists because of paging I/O or exception handling, the function locks the information buffer into physical memory and maps it into system address space. Failure at this is failure for the function.
The function does not synchronise its enumeration of either the pool or system lookaside lists with code that would initialise more such lists for a newly added processor. These lookaside lists are themselves in two double-linked lists, one for each set. Is it possible that addition of a processor could disturb a link just as it gets followed by this function for the enumeration?
The other sets of lookaside lists each have a corresponding spin lock which is held while any lookaside list of the same type is initialised or deleted. The function holds each lock in turn, thus running the current processor at DISPATCH_LEVEL while enumerating these sets.
The information buffer must provide exactly a pointer for the function to set. If Microsoft has a structure for this, Microsoft’s name for it is not known.
The buffer receives a copy of the kernel’s MmSystemRangeStart variable. The kernel exports this variable in version 4.0 from Windows NT 4.0 SP3, and higher. It is declared in NTDDK.H as far back as the DDK for Windows 2000. Since this variable is exported from all known kernel versions that support SystemRangeStartInformation, this information class has little or no reason to exist except to help user-mode callers.
Exposure of MmSystemRangeStart in user mode anyway predates this information class. Again starting with Windows NT 4.0 SP3, the kernel copies the MmSystemRangeStart to the KUSER_SHARED_DATA, albeit to a member whose only known name is Reserved3.
The information buffer is to receive a collection of irregularly spaced SYSTEM_VERIFIER_INFORMATION structures. If the buffer is too small even for the first of these structures, the function sets the return length to show this minimal expectation, and fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills the buffer with as many structures and other data as it can generate and fit within the buffer, to a maximum of 10MB, and sets the return length to the size of what has been put in the buffer.
In case it’s not clear, when given this information class but no information buffer, the function may fail, telling of a minimum size that is required for the buffer, and then when given that size of buffer, the function may succeed but say it put nothing in the buffer.
The information buffer must provide at least a SYSTEM_SESSION_PROCESS_INFORMATION structure as input. Given less, the function sets the return length to the size of the structure, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
As input, the structure provides a session ID and the address and size of a secondary information buffer for the function to use as if for SystemProcessInformation but refined to query just for processes in the given session. This secondary information buffer must have four-byte alignment, lie wholly in user-mode address space and be writable.
DETAILS IN PREPARATION
For this information class, the size returned via ReturnLength need not be the amount of information that has been or might have been put in the information buffer as described by SystemInformation and SystemInformationLength. The output value of the variable at ReturnLength may instead describe the secondary information buffer. Whether this is by design or oversight is unclear. Oversight seems more plausible, however. See especially that it is inconsistent with the annotations on Microsoft’s one published declaration of the function (in ZWAPI.H from the WDK for Windows 10).
The information buffer is to receive as much of a SYSTEM_NUMA_INFORMATION structure as there are nodes to describe. If the buffer is too small for the function to fill in at least the HighestNodeNumber at the structure’s start, then the function sets the return length to that member’s size, and fails, returning STATUS_INFO_LENGTH_MISMATCH. If the buffer is too small for even one GROUP_AFFINITY in the ActiveProcessorsGroupAffinity array, the function sets the HighestNodeNumber in the buffer, sets the return length to that member’s size and declares success. Otherwise, it fills as many GROUP_AFFINITY structures as there are nodes to describe and which fit in the buffer, and sets the return length to the size of what has been put in the buffer.
If executing for a user-mode request, the caller must have SeProfileSingleProcessPrivilege. Without it, the function fails, returning STATUS_ACCESS_DENIED.
The information buffer must provide exactly a SUPERFETCH_INFORMATION structure as input. This is not certainly Microsoft’s name for the structure as expected for this information class. It is, however, Microsoft’s name for a structure that has the same layout and which is known to be correct for the information class SystemSuperfetchInformation.
The first dword must be 0x01 and the second 0x6B756843, else the function fails, returning STATUS_INVALID_PARAMETER. If the third dword is not a valid Prefetcher information class, the function fails, returning STATUS_INVALID_INFO_CLASS. The valid cases are beyond the present scope of this review.
The information buffer is to receive a collection of irregularly spaced SYSTEM_PROCESS_INFORMATION structures, one per process. The spacing is irregular because each such structure can be followed by varying numbers of other fixed-size structures and by variable-size data too: an array of SYSTEM_EXTENDED_THREAD_INFORMATION structures, one for each of the process’s threads; a SYSTEM_PROCESS_INFORMATION_EXTENSION structure; and the process’s name.
The least requirement for describing even one process, with no threads and no name, is a SYSTEM_PROCESS_INFORMATION and SYSTEM_PROCESS_INFORMATION_EXTENSION together. If given less for the information buffer but without having been asked for a return length, the function fails immediately, returning STATUS_INFO_LENGTH_MISMATCH.
Ordinarily, however, the function proceeds to enumerate processes until some error occurs that must be returned. The function writes to the information buffer while enumerating, for as long as space remains for each whole item. If the function has an item to add that will not fit, then if no return length was asked for, the function fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function continues the enumeration but without writing more to the information buffer. The intention is still to return STATUS_INFO_LENGTH_MISMATCH if no other error occurs, but with the return length showing how much data the enumeration would have produced had the buffer been big enough. Note that the size thus reported need not suffice when the function is next called. This is not just the usual theoretical point, but a practical one: the number of processes and threads not only can change between calls but is highly likely to.
If the enumeration completes without error and without exhausting the buffer, the function sets the return length to however many bytes it put in the buffer, and succeeds.
The information buffer must provide at least a ULONG for the function to set. If this is dressed as a structure, Microsoft’s name for it is not known.
What the buffer receives is simply the result of the documented kernel function KeGetRecommendedSharedDataAlignment. This is the size of the largest cache line of any processor, which the kernel will have determined from initialising its Second-Level Cache Support for successive processors.
The information buffer must provide exactly a ULONG for the function to set. If this is dressed as a structure, Microsoft’s name for it is not known.
The buffer receives a copy of the ComPlusPackage from the KUSER_SHARED_DATA except that if what’s there is (still) 0xFFFFFFFF, the kernel first clears it to 0 and then tries to load it from the registry:
Key: | HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework |
Value: | Enable64Bit |
Failure to read the data for any reason other than absence of the key or value is failure for the function, which returns the error from the registry operation. If the key or value is not found or if the data is not a dword of REG_DWORD data, the result of this and future queries is zero.
Microsoft documents one meaningful value: COMPLUS_ENABLE_64BIT (1). The documented KERNEL32 function GetComPlusPackageInstallStatus ordinarily reads from the KUSER_SHARED_DATA, presumably for efficiency, but queries via SystemComPlusPackage if the cached value is 0xFFFFFFFF. Though Microsoft documents the KERNEL32 function only for Windows Vista and higher, it is exported as early as version 5.1. What it can mean to have the 64-bit Common Language Runtime installed in version 5.1 is unclear but immaterial: for what looks to be the lack of a break statement, all builds of version 5.1 fill the information buffer for SystemComPlusPackage as if for SystemLostDelayedWriteInformation.
The information buffer is to receive as much of a SYSTEM_NUMA_INFORMATION structure as there are nodes to describe. If the buffer is too small for the function to fill in at least the HighestNodeNumber at the structure’s start, then the function sets the return length to that member’s size, and fails, returning STATUS_INFO_LENGTH_MISMATCH. If the buffer is too small for even one ULONGLONG in the AvailableMemory array, the function sets the HighestNodeNumber in the buffer, sets the return length to that member’s size and declares success. Otherwise, it fills as many array members as there are nodes to describe and which fit in the buffer and sets the return length to the size of what has been put in the buffer.
The information buffer is to receive an array of SYSTEM_PROCESSOR_POWER_INFORMATION structures, one for each active processor in the current processor group. If the information buffer is not large enough for them all, the function sets the return length to the size of the array it could produce, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
The information buffer must provide exactly a SYSTEM_BASIC_INFORMATION structure for the function to fill.
The information buffer must provide at least a SYSTEM_PROCESSOR_INFORMATION structure for the function to fill.
The information buffer is to receive a SYSTEM_HANDLE_INFORMATION_EX structure whose Handles array has a SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX for each handle. If the buffer is too small even for the formally defined structure, with its capacity for describing one handle, the function sets the return length to the size of the formal structure, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
The information buffer must be pointer-aligned. If it is not, the function fails, returning STATUS_DATATYPE_MISALIGNMENT.
If executing for a user-mode request from a restricted caller, the function fails, returning STATUS_ACCESS_DENIED.
The information buffer must provide at least a ULONG for the function to set. If this is dressed as a structure, Microsoft’s name for it is not known.
What the buffer receives is the total of the per-processor CcLostDelayedWrites counters, one per KPRCB.
The information buffer is to receive a SYSTEM_BIGPOOL_INFORMATION structure whose AllocatedInfo array has a SYSTEM_BIGPOOL_ENTRY for each allocation. If the buffer is too small even for the formally defined structure, with its capacity for describing one allocation, the function sets the return length to the size of the formal structure, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
TO BE DONE
The information buffer must provide at least a SYSTEM_SESSION_MAPPED_VIEW_INFORMATION structure for both input and output. Given less, the function sets the return length to the size of the structure, and fails, returning STATUS_INFO_LENGTH_MISMATCH.
As input, the structure provides a session ID. This can be 0xFFFFFFFF to mean all sessions.
For output, the information buffer is to receive an array of these structures. If the information buffer does not have 8-byte alignment, the function fails, returning STATUS_DATATYPE_MISALIGNMENT.
This information class is only trivially valid. The function sets the return length to zero and returns STATUS_NOT_SUPPORTED.
The information buffer must provide exactly a 32-bit integer for the function to set. If Microsoft has a structure for this, Microsoft’s name for it is not known.
What the buffer receives is a copy of an internal variable that the kernel initialises from the registry but does not otherwise use:
Key: | HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager |
Value: | ObjectSecurityMode |
Type: | REG_DWORD |
Default: | 1 |
The kernel merely holds the value for callers of this function, presumably so that their interpretation of it at different times cannot be made inconsistent by changes to the registry value. (Changes take effect only after Windows restarts.) A known interpretation is by BASESRV.DLL for setting the access that Everyone (S-1-1-0) is allowed to the per-session \Session\id\BaseNamedObjects and \Session\id\BaseNamedObjects\Restricted directories in the object namespace. Protection is stronger when the mode is non-zero, for Everyone is permitted only DIRECTORY_QUERY and DIRECTORY_TRAVERSE instead of also getting DIRECTORY_CREATE_OBJECT, DIRECTORY_CREATE_SUBDIRECTORY and READ_CONTROL.
Introducing SystemObjectSecurityMode was evidently some trouble. It has three different numerical values: 0x36 in the version 5.0 from Windows 2000 SP4; 0x42 in the version 5.1 from Windows XP SP2; and 0x46 in that and all later versions.
This information class is only trivially valid. The function returns STATUS_NOT_SUPPORTED.
The information buffer is to receive an array of SYSTEM_LOGICAL_PROCESSOR_INFORMATION structures, one for each active processor in processor group 0. (The structure is declared in WDM.H and WINNT.H, for kernel-mode and user-mode programming, respectively.)
TO BE DONE
TO BE DONE
TO BE DONE
TO BE DONE
In 64-bit Windows, the information buffer must provide at least a SYSTEM_FILECACHE_INFORMATION structure for the function to fill.
The 32-bit Windows implementation allows a smaller information buffer, to account for an original definition that reached only to the PageFaultCount member. If the information buffer is too small for the original structure, the function sets the return length to this smaller requirement, and returns STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills in either the partial structure or the whole structure, if the latter fits, sets the return length to however many bytes were written to the buffer, and returns STATUS_SUCCESS.
The information buffer is to receive an array of SYSTEM_PROCESSOR_IDLE_CYCLE_TIME structures, one for each active processor in the current processor group. If the information buffer is too small for even one such structure, the function sets the return length to the size of the array it could produce, and fails, returning STATUS_BUFFER_TOO_SMALL. Otherwise, the function fills the buffer with as many structures as fit, up to the number of processors, and sets the return length to the size of the whole array. If the buffer is too small for the whole array, the function fails, returning STATUS_INFO_LENGTH_MISMATCH (but with meaningful data in the buffer).
TO BE DONE
The information buffer must provide exactly a SYSTEM_SPECIAL_POOL_INFORMATION structure for the function to fill.
The information buffer must provide exactly a SYSTEM_PROCESS_ID_INFORMATION structure for both input and output.
The essence of the function is to get the full image name of a process, given the process ID. The structure must provide as input both the ProcessId and the address and size of a buffer that is to receive the name. A UNICODE_STRING named ImageName describes the buffer with the usual interpretations. The address and size are in Buffer and MaximumLength, respectively, and Length tells how many bytes hold meaningful content. If the buffer is already in use, i.e., Length is not zero, or if its capacity is not a whole number of Unicode characters, i.e., MaximumLength is not a multiple of two, then the function returns STATUS_INVALID_PARAMETER. If executing for a user-mode request and MaximumLength is non-zero, the buffer must be word-aligned and must lie wholly in user-mode address space. Failure at these defences causes the function to return STATUS_DATATYPE_MISALIGNMENT or STATUS_ACCESS_VIOLATION (as raised but handled exceptions).
If no process can be found for the given ProcessId or if whatever is found is not in the current silo, the function returns STATUS_INVALID_CID.
If the process’s image name (meaning what the EPROCESS has in its SeAuditProcessCreationInfo) is too large for the given buffer, the function returns STATUS_INFO_LENGTH_MISMATCH, having set the MaximumLength to the size required and the return length to the size of the input structure.
Ordinarily, the function copies the image name to the given Buffer, updates Length and MaximumLength to fit the new content, sets the return length to the size of the input structure, and succeeds. One variation is that the process may have no image name, as with the system process. In such a case, there is nothing to copy and the function instead clears MaximumLength to 0 and Buffer to NULL.
Note the absence of any check for privilege or access rights. The information class SystemProcessIdInformation lets user-mode callers of NtQuerySystemInformation get the names of processes that they cannot open.
The information buffer must provide at least a SYSTEM_BOOT_ENVIRONMENT_INFORMATION structure up to and including its FirmwareType member. If the information buffer is too small for the partial structure, the function sets the return length to the size of the full structure and returns STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills in either the partial structure or the whole structure, if the latter fits, sets the return length to however many bytes were written to the buffer, and returns STATUS_SUCCESS.
This information class is for kernel-mode use only. If executing for a user-mode request, the function fails, returning STATUS_ACCESS_DENIED.
The information buffer must provide exactly a SYSTEM_HYPERVISOR_QUERY_INFORMATION structure for the function to fill.
The information buffer must provide exactly a SYSTEM_VERIFIER_INFORMATION_EX structure for the function to fill.
TO BE DONE
This information class is only trivially valid. The function returns STATUS_NOT_IMPLEMENTED.
In older versions whose x64 builds support this information class, the information buffer must supply at least a SYSTEM_PREFETCH_PATCH_INFORMATION structure for the function to fill.
TO BE DONE
TO BE DONE
TO BE DONE
The information buffer must provide at least a SYSTEM_NUMA_PROXIMITY_MAP structure for both input and output.
The information buffer must provide at least an RTL_DYNAMIC_TIME_ZONE_INFORMATION structure for the function to fill.
This information class is handled outside the kernel, by a callback that the kernel learns from CI.DLL via the CiInitialize function. If the callback is not yet known, this function fails, setting the return length to zero and returning STATUS_UNSUCCESSFUL.
The information buffer must provide exactly a SYSTEM_CODEINTEGRITY_INFORMATION structure for both input to and output from the CI callback function. (This structure is nowadays documented by Microsoft, apparently in full.)
As input, the structure provides only its Length member, which must hold the structure’s size, presumably to allow for expansion in future without having to define a new information class.
TO BE DONE
The information buffer must provide at least an array of six SYSTEM_VA_LIST_INFORMATION structures for the function to fill.
The information buffer is to receive an array of SYSTEM_PROCESSOR_CYCLE_TIME structures, one for each active processor in the current processor group. If the information buffer is too small for even one such structure, the function sets the return length to the size of the array it could produce, and fails, returning STATUS_BUFFER_TOO_SMALL. Otherwise, the function fills the buffer with as many structures as fit, up to the number of processors, and sets the return length to the size of the whole array. If the buffer is too small for the whole array, the function fails, returning STATUS_INFO_LENGTH_MISMATCH (but with meaningful data in the buffer).
The information buffer must provide exactly some structure for both input and output. Microsoft’s name for this structure is not known. The structure is 0x10 or 0x18 bytes in 32-bit and 64-bit Windows, respectively.
On input, the first dword must be 1, else the function fails, returning STATUS_INVALID_PARAMETER. A second dword provides a store information class. For values other than 2, 5, 8, 13 and 16, a user-mode caller must have SeProfileSingleProcessPrivilege, else the function fails, returning STATUS_ACCESS_DENIED. The valid store information classes are 2, 5, 8, 13, 15 and 16. Given anything else, the function fails, returning STATUS_INVALID_INFO_CLASS.
The third and fourth members of the input structure are the address and size of a secondary buffer which is to receive the function’s output. This output varies with the store information class. The meaning of each store information class is presently beyond the scope of these notes.
The information buffer is to receive a SYSTEM_VHD_BOOT_INFORMATION structure whose OsVhdParentVolume array is a null-terminated Unicode string to be followed by another null-terminated Unicode string. If the information buffer is too small for the total, the function sets the return length to the total and returns STATUS_BUFFER_TOO_SMALL.
The information buffer is to receive a PS_CPU_QUOTA_QUERY_INFORMATION structure whose SessionInformation array has a PS_CPU_QUOTA_QUERY_ENTRY for each session.
This information class depends on DFSS to be enabled by a registry configuration (details of which are presently beyond the scope of this note). If it is not, the function fails, returning STATUS_QUOTA_NOT_ENABLED.
If executing for a user-mode request, the caller must have SeIncreaseQuotaPrivilege. Without it, the function fails, returning STATUS_PRIVILEGE_NOT_HELD.
The function allows that SystemInformationLength can be zero. Otherwise, the information buffer must provide at least for fixed-size part of the structure, i.e., up to but not including the SessionInformation array, and the excess must provide exactly a whole number of PS_CPU_QUOTA_QUERY_ENTRY structures that the function might fill. If either expectation is not met, the function returns STATUS_INFO_LENGTH_MISMATCH but without having set the return length.
The function enumerates sessions. Details are beyond the scope of this note. For each that has a scheduling group, the function fills the next PS_CPU_QUOTA_QUERY_ENTRY in the array, if space remains. When enumeration is complete, the function sets the SessionCount in the PS_CPU_QUOTA_QUERY_INFORMATION, again if space is sufficient. If space does not suffice for all the available information, the function sets the return length to match what it might have put in the buffer, and returns STATUS_BUFFER_TOO_SMALL. If space did suffice, the function sets the return length to match what it did put in the buffer, and returns STATUS_SUCCESS.
The information buffer must provide exactly a SYSTEM_BASIC_INFORMATION structure for the function to fill.
It is not known why this information class exists separately from SystemBasicInformation. Perhaps the reason is nothing more than to make an explicit contrast with SystemEmulationBasicInformation.
TO BE DONE
The information buffer must provide at least a SYSTEM_LOW_PRIORITY_IO_INFORMATION structure for the function to fill.
If the information buffer is too small, the function sets the return length to the expected size and returns STATUS_BUFFER_TOO_SMALL.
The information buffer must provide exactly a BOOT_ENTROPY_NT_RESULT structure for the function to fill.
This information class is for kernel-mode use only. If executing for a user-mode request, the function fails, returning STATUS_ACCESS_DENIED. Even in kernel mode, the boot entropy information can be successfully queried at most once, and no later than phase 1 of the kernel’s initialisation. The intended error code for a call that is too late is STATUS_UNSUCCESSFUL.
The information buffer is to receive a collection of irregularly spaced SYSTEM_VERIFIER_COUNTERS_INFORMATION structures. If the buffer is too small even for the first of these structures, the function sets the return length to show this minimal expectation, and fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills the buffer with as many structures and other data as fits, to a maximum of 10MB, and sets the return length to the size of what has been put in the buffer.
In 64-bit Windows, the information buffer must provide at least a SYSTEM_FILECACHE_INFORMATION structure for the function to fill.
The 32-bit Windows implementation allows a smaller information buffer, to account for an original definition that reached only to the PageFaultCount member. If the information buffer is too small for the original structure, the function sets the return length to this smaller requirement, and returns STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills in either the partial structure or the whole structure, if the latter fits, sets the return length to however many bytes were written to the buffer, and returns STATUS_SUCCESS.
In 64-bit Windows, the information buffer must provide at least a SYSTEM_FILECACHE_INFORMATION structure for the function to fill.
The 32-bit Windows implementation allows a smaller information buffer, to account for an original definition that reached only to the PageFaultCount member. If the information buffer is too small for the original structure, the function sets the return length to this smaller requirement, and returns STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills in either the partial structure or the whole structure, if the latter fits, sets the return length to however many bytes were written to the buffer, and returns STATUS_SUCCESS.
The information buffer must provide exactly a SYSTEM_ACPI_AUDIT_INFORMATION structure for the function to fill.
The information buffer must provide exactly a SYSTEM_BASIC_PERFORMANCE_INFORMATION structure for the function to fill.
The information buffer must provide at least a SYSTEM_QUERY_PERFORMANCE_COUNTER_INFORMATION structure for both input and output.
The Version member is expected as input. If the information buffer is not at least large enough to provide this member, the function sets the return length to the size of the expected structure and fails, returning STATUS_INFO_LENGTH_MISMATCH. If the Version member is not 1 on input, the function fails, returning STATUS_NOT_SUPPORTED. Apparently, the SYSTEM_QUERY_PERFORMANCE_COUNTER_INFORMATION as presently defined is only for version 1. The information buffer must provide at least this structure for the function to fill.
TO BE DONE
The information buffer must provide exactly a SYSTEM_BOOT_GRAPHICS_INFORMATION structure for the function to fill.
TO BE DONE
TO BE DONE
The information buffer must provide exactly a SYSTEM_POLICY_INFORMATION structure for both input and output. Details are presently beyond the scope of this article.
The information buffer must provide at least a SYSTEM_HYPERVISOR_PROCESSOR_COUNT_INFORMATION structure for the function to fill.
The information buffer must provide exactly a SYSTEM_DEVICE_DATA_INFORMATION structure for both input and output.
If SystemInformationLength is correct but SystemInformation is NULL, the function returns STATUS_INFO_LENGTH_MISMATCH but without setting the return length. The information buffer must have 4-byte alignment and lie wholly in user-mode address space (even if the request originated in kernel mode).
The information buffer must provide exactly a SYSTEM_DEVICE_DATA_INFORMATION structure for both input and output.
If SystemInformationLength is correct but SystemInformation is NULL, the function returns STATUS_INFO_LENGTH_MISMATCH but without setting the return length. The information buffer must have 4-byte alignment and lie wholly in user-mode address space (even if the request originated in kernel mode).
TO BE DONE
TO BE DONE
The information buffer is to receive a SYSTEM_BOOT_LOGO_INFORMATION structure and a variable-size bitmap for the boot logo.
If the information buffer is too small even for the formally defined structure but the function is not asked for a return length, the function fails, returning STATUS_INVALID_PARAMETER. If the information buffer is too small for the structure and bitmap, the function sets the return length to the total and returns STATUS_BUFFER_TOO_SMALL. Along the way, the function may fail for other reasons and return other errors, such as STATUS_UNSUCCESSFUL.
If the information buffer is large enough for the structure and bitmap, the function copies both to the buffer, sets the return length to the total, and succeeds.
The information buffer is to receive an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_EX structures, one for each active processor in the current processor group. If the information buffer is not an exact fit for one or more such structures, the function sets the return length to the size of the array it could produce, and fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills the buffer with as many structures as fit, up to the number of processors, and sets the return length to the size of what has been put in the buffer. Even if this is not the whole array that the function could have put in the buffer, the function declares success.
If the kernel has not correctly received a Secure Boot policy from the loader, the function can do nothing with this information class: it fails, returning STATUS_SECUREBOOT_NOT_ENABLED.
The information buffer must provide at least a SYSTEM_SECUREBOOT_POLICY_INFORMATION structure for the function to fill.
The information buffer is to receive a collection of irregularly spaced SYSTEM_PAGEFILE_INFORMATION_EX structures, one for each paging file (not counting swap files and virtual stores). If the buffer is too small even for the first of these structures, the function sets the return length to show this minimal expectation, and fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function fills the buffer with as many structures and other data as fits, and sets the return length to the size of what has been put in the buffer.
The information buffer must provide at least a SYSTEM_SECUREBOOT_INFORMATION structure for the function to fill.
Beware that the expected structure is naturally byte-aligned but the function’s user-mode defences for this information class require four-byte alignment.
TO BE DONE
The information buffer is to receive a collection of irregularly spaced SYSTEM_PROCESS_INFORMATION structures, one per process. The spacing is irregular because each such structure can be followed by varying numbers of other fixed-size structures and by variable-size data too: an array of SYSTEM_EXTENDED_THREAD_INFORMATION structures, one for each of the process’s threads; a SYSTEM_PROCESS_INFORMATION_EXTENSION structure; and as many as four variable-size items including the user SID, the package name and application ID, and the process’s name.
The least requirement for describing even one process, with no threads and no name, is a SYSTEM_PROCESS_INFORMATION and SYSTEM_PROCESS_INFORMATION_EXTENSION together. If given less for the information buffer but without having been asked for a return length, the function fails immediately, returning STATUS_INFO_LENGTH_MISMATCH.
Unlike its earlier forms, SystemProcessInformation and SystemExtendedProcessInformation, this information class has access restrictions. First, it is available only for user-mode requests. If the function is instead executing for a kernel-mode request, it fails, returning STATUS_ACCESS_DENIED. A permitted user-mode caller (or group) can be configured in the registry:
Key: | HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows |
Value: | FullProcessInformationSID |
The expected data is a SID in binary form. (The type is not checked. Any data is accepted provided there is not more of it than is possible for a SID, i.e., 0x44 bytes.) The SID designates the one user or group that is, along with all members of the Administrators group, permitted to use this information class. For all other user-mode callers, the function fails, returning STATUS_ACCESS_DENIED.
Ordinarily, however, the function proceeds to enumerate processes until some error occurs that must be returned. The function writes to the information buffer while enumerating, for as long as space remains for each whole item. If the function has an item to add that will not fit, then if no return length was asked for, the function fails, returning STATUS_INFO_LENGTH_MISMATCH. Otherwise, the function continues the enumeration but without writing more to the information buffer. The intention is still to return STATUS_INFO_LENGTH_MISMATCH if no other error occurs, but with the return length showing how much data the enumeration would have produced had the buffer been big enough. Note that the size thus reported need not suffice when the function is next called. This is not just the usual theoretical point, but a practical one: the number of processes and threads not only can change between calls but is highly likely to.
If the enumeration completes without error and without exhausting the buffer, the function sets the return length to however many bytes it put in the buffer, and succeeds.
The information buffer must provide at least a SYSTEM_KERNEL_DEBUGGER_INFORMATION_EX structure for the function to fill.
Beware that the expected structure is naturally byte-aligned but the function’s user-mode defences for this information class require four-byte alignment.
TO BE DONE
TO BE DONE
TO BE DONE
The information buffer must provide at least a SYSTEM_PROCESSOR_FEATURES_INFORMATION structure for the function to fill.
The information buffer must provide exactly 0x80 bytes for the function to fill. If Microsoft has a structure for this, Microsoft’s name for it is not known.
What the buffer receives is a 128-byte Extended Display Identification Data (EDID), as defined by the Video Electronics Standards Association (VESA). Details of how the kernel gets the EDID from the loader (via the BgContext member of the LOADER_PARAMETER_EXTENSION) are beyond the scope of this article.
TO BE DONE
TO BE DONE
The information buffer must provide exactly a SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure for the function to fill, but with the variation that if the information buffer is not the expected size, the function sets the return length to zero and returns STATUS_INVALID_PARAMETER_1.
TO BE DONE
TO BE DONE
The information buffer must provide at least a BOOLEAN for the function to set. If this is dressed as a structure, Microsoft’s name for it is not known. What the buffer receives is simply the kernel’s record of whether to ignore user-mode exceptions.
This information class is handled outside the kernel, by a callback that the kernel learns from CI.DLL via the CiInitialize function. If the callback is not yet known, this function fails, setting the return length to zero and returning STATUS_UNSUCCESSFUL.
The information buffer must provide exactly a SYSTEM_CODEINTEGRITYPOLICY_INFORMATION structure for the CI callback function to fill.
The information buffer must provide exactly a SYSTEM_ISOLATED_USER_MODE_INFORMATION structure for the function to fill.
TO BE DONE
TO BE DONE
TO BE DONE
If the kernel has not correctly received a Secure Boot policy from the loader, the function can do nothing with this information class: it fails, returning STATUS_SECUREBOOT_NOT_ENABLED.
The information buffer must provide at least enough for the function to fill a SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION structure with a variable-size Policy member at its end.
This information class is handled outside the kernel, by a callback that the kernel learns from CI.DLL via the CiInitialize function. If the callback is not yet known, this function fails, setting the return length to zero and returning STATUS_UNSUCCESSFUL.
If executing for a user-mode request, the function fails unless the caller has SeIncreaseBasePriorityPrivilege or is running as Local System or as a particular service that has not yet been identified.
The information buffer must provide exactly a KAFFINITY_EX structure for the function to fill. Each set bit in that structure’s Bitmap signifies that the corresponding processor has at least some interrupt steered to it for handling.
The information buffer is to receive a SYSTEM_ROOT_SILO_INFORMATION structure whose SiloIdList array has an identifier for each root silo, i.e., each silo that is not itself in a silo. If the buffer is too small even for the NumberOfSilos member, the function sets the return length to zero, and fails, returning STATUS_BUFFER_TOO_SMALL. The function also returns STATUS_BUFFER_TOO_SMALL if the buffer is not large enough for the whole array, however big that turns out to be. In this case too, here suspected of being coded defectively, the function sets the return length to zero. The only way the function sets the return length to anything but zero is if it succeeds.
The information buffer is to receive an array of SYSTEM_CPU_SET_INFORMATION structures, one for each CPU set. (The structure is defined in WDM.H and WINNT.H, for kernel-mode and user-mode programming, respectively.) If the information buffer is not large enough for them all, the function sets the return length to the size of the array it could produce, and fails, returning STATUS_BUFFER_TOO_SMALL.