NtQuerySection is a system routine for the Windows operating system that queries information related to a section object and provides the information regarding that object by filling a buffer passed to the function. As an important note, all disassembly and reversing took place on Windows 10 x64 Professional.
It requires 4 arguments, 5 if you include the optional ReturnLength argument which is a pointer to a variable that receives the correct buffer size.
There are some interesting details worth noting about NtQuerySection, the first of which is the _SECTION_INFORMATION_CLASS which is an undocumented enumeration. You’ll notice that there are 3 enumeration members, all of which appear to be usable and valid to pass to NtQuerySection. We’ll see shortly that the usage of one of those enumeration values will result in return status STATUS_INVALID_INFO_CLASS.
To help with references to local variables later this routine creates 3 local variables. One of type _KPROCESSOR_MODE (referred to as ProcessorMode herein), one of type NTSTATUS (referred to as Status herein), and the final as _SECTION* (referred to as SectionObject herein).
The first thing that “NtQuerySection” does is get the PreviousMode of the calling thread to determine whether or not the data source should be implicitly trusted. It does so by calling “KeGetPreviousMode” and assigning the return value to ProcessorMode. The only thing dependent on the value returned by “KeGetPreviousMode” is an opening conditional that is satisfied only when PreviousMode is set to UserMode which ensures that the user-mode buffer can be written to without raising an access violation exception. The conditional also verifies whether the 5th – optional – parameter is present or not. If it is present, it verifies that the pointer can be dereferenced and written to without raising an access violation exception.
To provide a visual for how this looks, here’s an idea:
This next part is where I noticed something odd. After checking the PreviousMode and verifying parameters the routine reaches a conditional that checks for specific _SECTION_INFORMATION_CLASS values. Those being SectionBasicInformation, which supplies information such as the section object base address, attributes, and maximum size; the other is SectionImageInformation, which provides information like the maximum stack size, committed stack size, subsystem type, dll characteristics, etc. A third member SectionRelocationInformation is used to return the new base address of the section object. If the SectionInformationClass parameter is not one of these values the caller receives an error status STATUS_INVALID_INFO_CLASS.
Now, if you glance at the first image picturing the enumeration of the section classes you’ll quickly realize there is a fourth that isn’t mentioned here or in the disassembly. It simply a placeholder (I’m assuming) to inform developers that there isn’t a valid value to be used at or after this member. I don’t understand fully why this would be a part of the enumeration since it could cause confusion – I’m sure they have their reasons.
Moving on the routine checks that the InformationBufferSize
(parameter 4) matches the sizeof
the associated structure. If the sizes do not match, the caller receives an error status STATUS_INFO_LENGTH_MISMATCH
, an error code we’re all too familiar with. It’s worth noting that only two structures are associated with this Nt routine – SECTION_BASIC_INFORMATION
. We’ll explore the usage of each of these classes later in this article.
After all checks and verification conditions have been satisfied we reach a call to “ObReferenceObjectByHandle” that is used to get a reference to the object, which is stored in our _ESECTION *SectionObject, and prevent other system operations from deleting the section object by closing the handle. After receiving a pointer to the section object, “NtQuerySection” then calls “MmGetSectionInformation” with the SectionObject pointer, SectionInformationClass, and SectionInformationBuffer. “MmGetSectionInformation” is where the actual retrieval and loading of content occurs.
Finally, after the section information is stored and passed back through the buffer, a call to “ObfDereferenceObject” is made which decrements the reference count of the object and performs retention checks.
Essentially with this information it becomes obvious that “NtQuerySection” is a wrapper for “MmGetSectionInformation” that simply verifies parameters and retains the section object so that “MmGetSectionInformation” can succeed.
As mentioned earlier, I will briefly describe the uses for each of the enumerations except for SectionRelocationInformation since there isn’t much to detail on it. It returns the address of the relocated section object.
SectionBasicInformation is primarily used to obtain the base address, section attributes (SEC_IMAGE, SEC_RESERVE, SEC_FILE), and the size of the section in bytes. As an example, this information could be used to dump the section to disk (if it’s not already resident on disk) and perform analysis on it. You’d need the BaseAddress and SizeOfSection to get a proper dump.
SectionImageInformation is much more interesting since you can query information about a file object (SEC_FILE) without needing to map it into a process’ address space. It is primarily used to verify PE Header fields and subsequently fix any errors or modifications prior to having the object mapped. As a previous post points out, the SizeOfStackReserve field can be exploited to prevent “CreateThread” from successfully running and if SizeOfStackReserve is modified while the image is residing on disk “CreateProcess” will fail. Querying this information and comparing with valid entry values can help prevent these sorts of exploits from wreaking havoc on your applications.
Any comments, ideas, or feedback are welcome.