System Routine Overview
NtQuerySystemInformation is a system routine that gathers system information specific to the class value provided. During this breakdown, we’ll look at it in much more depth because, like the previous system routine documented, it’s just a wrapper for an internal kernel routine that actually gathers the system information. However, the internal routine it invokes has over 130 switch cases… I will only be documenting and describing the results of four common information classes. They’ll be listed later in the article. To ensure you’re reading relevant information this information was obtained on Windows 10 x64 Professional.
takes 4 arguments. The first is the SystemInformationClass
which is any value part of the enumeration _SYSTEM_INFORMATION_CLASS
which is an opaque enumeration. You can grab the full documentation of the enumeration from uxtheme.dll
Before we begin documenting the checks performed by “NtQuerySystemInformation” it’s necessary to note that there are 179 values that are part of the _SYSTEM_INFORMATION_CLASS enumeration. A majority of these will result in an error status STATUS_INVALID_INFO_CLASS which is rather interesting. Some are marked as Obsolete, some are from previous versions of Windows, and a few of them I couldn’t find any information on in any previous versions of Windows or online documentation. This means that when using this routine developer’s need to be cautious and aware of which class value they’re using.
First, in the refined disassembly we see that there are 4 local variables created when the function is invoked. One of type NTSTATUS (named Status), two are of type ULONG_PTR (named Length and Placeholder, respectively), and the final is a PULONG_PTR (named ProcessorGroup). The declarations are pictured below.
The next thing that occurs, like all system routines, is a check to determine the PreviousMode of the calling thread. If the PreviousMode is UserMode (0x1) the SystemInformation parameter is probed to ensure that an access violation exception will not be raised when written to from “ExpQuerySystemInformation“. If everything checks out, we proceed to a conditional that is satisfied so long as SystemInformationClass is less than SystemWow64SharedInformationObsolete OR is greater than or equal to SystemProcessIdleCycleTimeInformation. If this condition is satisfied, we jump into the if-block and into a switch statement that contains 11 cases and a default case which is to call “ExpQuerySystemInformation“. All 11 of these cases are related processor information; ranging from performance information to cycle times to interrupt information. If any other case is specified, the default case is hit and “ExpQuerySystemInformation” is invoked.
There are two cases of interest primarily because all cases, except the default, SystemLogicalProcessorAndGroupInformation, and SystemNodeDistanceInformation; fall through to SystemProcessorPerformanceInformationEx.
The SystemProcessorPerformanceInformationEx case does a few interesting things, the first of which is to use an arbitrary variable named Length and set it’s value to 2. It then sets the ProcessorGroup variable to point to the CurrentPrcb’s member Group, which stores the currently active processor group in a multi-processor system. It then calls “ExpQuerySystemInformation” and gathers information from every processor related to the time spent in user-mode, kernel-mode, dpc times, interrupt timer information, and the interrupt count.
The other case, SystemLogicalProcessorInformation, performs the same operation on Length and then initializes the Placeholder variable to 0, sets the ProcessorGroup variable to point to Placeholder, and calls “ExpQuerySystemInformation“. After entering “ExpQuerySystemInformation” it then calls “KeQueryLogicalProcessInformation” which returns information about the physical processors and nodes on the system.
A final case of interest is SystemNodeDistanceInformation (0x79) which sets the Status to STATUS_INVALID_INFO_CLASS and breaks out of the switch statement to return.
The else block of this conditional is executed if the information class is any of the following:
It simply calls “ExpQuerySystemInformation” with two zeroed parameters – those being ProcessorGroup and Length. Before carrying on I wanted to note that the Exp-prefix for the routine name stands for Private Executive Routine. Microsoft follows a naming convention where the routine naming convention is written <Prefix><Object><Operation>, and p or i stand for private or internal, respectively.
Information Class Details
This is the extent to which “NtQuerySystemInformation” prepares data to be passed to “ExpQuerySystemInformation“, which means it’s essentially a wrapper for the aforementioned routine. That being said, I’ll describe what occurs when specific and common information classes are used when invoking “NtQuerySystemInformation“.
The first information class is SystemBasicInformation, one the vast majority are familiar with. When passed through it hits the default case which calls “ExpQuerySystemInformation” with standard parameters. Inside of “ExpQuerySystemInformation” we hit our case (0x0) and another call to “ExpGetSystemBasicInformation” is executed, which fills in the _SYSTEM_BASIC_INFORMATION structure with the proper values. These values are constants and global variables that reside in the kernel. The values, in order of assignment, are KeNumberProcessors, KeActiveProcessors, 0 (reserved value), KeMaximumIncrement, MmNumberOfPhysicalPages, MmLowestPhysicalPage, MmHighestPhysicalPage, PAGE_SIZE (0x1000), MM_ALLOCATION_GRANULARITY (0x10000), MM_HIGHEST_USER_ADDRESS (0x7FFFFFEFFFF). After appropriately assigning these values, it returns up the call chain.
The next widely used information class is SystemProcessInformation. Much like the last call it simply invokes another internal executive routine “ExpGetProcessInformation“. This routine is much more complicated than “ExpGetSystemBasicInformation“, so I’m just going to outline how it obtains the information that is part of _SYSTEM_PROCESS_INFORMATION. It first iterates over all process and checks for any idle processes by comparing the _EPROCESS object to the PsIdleProcess. If it is the system idle process, or an additional idle process information will be gathered and placed in the buffer accordingly. After obtaining details about the idle and system processes, the routine proceeds to get information on all other active processes. It’s important to note here that if the process is marked as exiting via PS_PROCESS_FLAGS_PROCESS_EXITING it is told to skip it.
To get information on each process “ExpGetProcessInformation” calls “ExpCopyProcessInfo” to copy information from the _EPROCESS object into the ProcessInformation buffer and then sets the SessionId depending on whether the process is marked as idle or not. If it is marked as idle the process SessionId is 0.
As an aside, all system and idle processes share an object table so the HandleCount member is set to 0. SessionId is also set to 0 for all system and idle processes.
Getting back on track, “ExpGetProcessInformation” then retrieves information for each thread by calling “ExpCopyThreadInfo” and copies information from the _KTRHEAD object into the ThreadInformation buffer.
To retrieve the image name it calls “SeLocateProcessImageName” and copies the data into the ImageName buffer. It then sets the ImageName buffer to point to user memory so that usermode operators can access it. Rinse and repeat until all processes have been queried.
The next information class to cover that’s not entirely common, but used enough to be considered standard, is SystemHandleInformation. Let’s dig right in…
It follows the same invocation method as all others. Once inside “ExpQuerySystemInformation” the proper case is found and a call to “ExpGetHandleInformation” is solicited. “ExpGetHandleInformation” is used to return information about open handles in the system and the objects that own those handles. Inside “ExpGetHandleInformation” we’ll see that there is a call to the object manager via “ObGetHandleInformation” taking in the HandleInformation buffer, SystemInformationLength, and a pointer to a placeholder variable.
“ObGetHandleInformation” is a routine that takes a snapshot of all handle tables and returns the information back to the caller. Once the handle information is received the operator can iterate through the handles to obtain certain information regarding what object the handle is retained by.
The last information class to cover is SystemModuleInformation, which is commonly used to iterate and display the drivers present on the system and their full paths. “ExpQuerySystemInformation” hits the specific case and immediately calls “ExpQueryModuleInformation” which begins by iterating kernel modules via the _KLDR_DATA_TABLE (very similar to how the PEB stores module information). It iterates each entry and stores the information in an _RTL_PROCESS_MODULE_INFORMATION structure by simply copying the data from the _KLDR_DATA_TABLE_ENTRY to the respective fields in _RTL_PROCESS_MODULE_INFORMATION. After everything has completed successfully it returns a status code to the caller and winds back up the call chain with the information passed through the SystemInformationBuffer.
There is plenty more information to be dug up about these routines and every case/class. I tried my best to cover important details of how these functions work together to provide the requested information. If I missed something or some bit of information is unclear leave a comment.
Any comments, ideas, suggestions, or otherwise are welcome.