Geoff Chappell - Software Analyst
The SECTION structure (formally _SECTION) is the kernel’s representation of a section object. Both kernel-mode and user-mode software may obtain a handle to a section object. Kernel-mode software may reference the handle to obtain the address of the object. If the ObReferenceObjectByHandle function successfully resolves a handle though directed to do so only if the object type is MmSectionObjectType, then the pointer that the function produces for the object is a pointer to a SECTION.
The SECTION structure is not documented. Microsoft is not known to have disclosed even its name for—let alone any internal details of—whatever kernel-mode structure supports a handle to a section object. For the handful of exported functions, e.g., MmMapViewInSystemSpace and MmMapViewInSessionSpace, that take a pointer to a section object as an argument and for which Microsoft has published C-language declarations in headers from a Windows Driver Kit (WDK), the argument’s type is simply PVOID. Even the referencing of the object from a handle is obscure: though the MmSectionObjectType variable is a kernel export as far back as version 3.51, it never has been declared in any WDK header except an NTOSP.H that Microsoft disclosed in early editions of the WDK for Windows 10 (apparently only by oversight).
The structure of a section object is obscure even in the public symbol files. Before the original Windows 10, these show type information for a SECTION_OBJECT but not for the SECTION.
The SECTION is 0x28 and 0x40 bytes in 32-bit and 64-bit Windows, respectively, in all known versions. Sizes, types and Microsoft’s names of members are from public symbol files for the Windows 10 kernel. What is shown below for earlier versions is, of course, something of a guess.
Offset (x86) | Offset (x64) | Definition | Versions | Remarks |
---|---|---|---|---|
0x00 (3.10 to 5.1) |
PVOID StartingVa; |
3.10 to 5.1 | next at 0x0C | |
0x04 (3.10 to 5.1) |
PVOID EndingVa; |
3.10 to 5.1 | next at 0x10 | |
0x08 (3.10 to 5.1); 0x00 (5.2 to 6.1) |
0x00 (late 5.2 to 6.1) |
PVOID Parent; |
3.10 to 6.1 | next in SectionNode at 0x00 |
0x0C (3.10 to 5.1); 0x04 (5.2 to 6.1) |
0x08 (late 5.2 to 6.1) |
PVOID LeftChild; |
3.10 to 6.1 | next in SectionNode at 0x00 |
0x10 (3.10 to 5.1); 0x08 (5.2 to 6.1) |
0x10 (late 5.2 to 6.1) |
PVOID RightChild; |
3.10 to 6.1 | next in SectionNode at 0x00 |
0x00 | 0x00 |
MM_AVL_NODE SectionNode; |
6.2 only | |
RTL_BALANCED_NODE SectionNode; |
6.3 and higher | |||
0x0C | 0x18 |
PVOID StartingVa; |
5.2 to 6.1 | previously at 0x00 |
ULONG_PTR StartingVpn; |
6.2 and higher | |||
0x10 | 0x20 |
PVOID EndingVa; |
5.2 to 6.1 | previously at 0x04 |
ULONG_PTR EndingVpn; |
6.2 and higher | |||
0x14 | 0x28 |
SEGMENT *Segment; |
3.10 to 6.3 | |
union { CONTROL_AREA *ControlArea; FILE_OBJECT *FileObject; struct { RemoteImageFileObject : 1; RemoteDataFileObject : 1; }; } u1; |
10.0 and higher |
The original behaviour is consistent with the section object beginning as a SECTION_OBJECT, which is well known from symbol files, but continuing with members that symbol files for Windows 10 confirm are those of the SECTION (see below). The SECTION_OBJECT can usefully be seen as having its members in three sets: two for the virtual addresses of the section’s first and last bytes; three as links for the section object in a tree; a pointer to a SEGMENT. The kernel for Windows Server 2003 swaps the first two but the symbol files for the SECTION_OBJECT show no such reordering. This means that for whatever reason the SECTION_OBJECT survives in symbol files in version 5.2—indeed, all the way to the original Windows 10—it cannot be that the SECTION_OBJECT any longer had anything to do with section objects as actually implemented by the kernel.
In Windows 10, the section object ordinarily keeps a pointer to the control area, which has in its turn a FilePointer for access to the file. However, if the file’s device object has the FILE_REMOTE_DEVICE characteristic, then the section object instead keeps a pointer to the file object. In this case, one or other of the low two bits in the address is set according to whether the section is for an image or for data. The control area is then found through the file object’s SectionObjectPointer.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x18 | 0x30 |
ULONGLONG SizeOfSection; |
all |
0x20 | 0x38 |
union { ULONG LongFlags; MMSECTION_FLAGS Flags; } u; |
all |
0x24 | 0x3C |
ULONG InitialPageProtection; |
3.10 to 5.2 |
struct { /* changing bit fields, see below */ }; |
6.0 and higher |
The InitialPageProtection comes from the macros (defined in WDM.H and WINNT.H) that start with PAGE_NOACCESS. It will typically be just what was provided as the SectionPageProtection argument when creating the section, but PAGE_NOCACHE and PAGE_WRITECOMBINE bits can have been picked up by implication. The point to retaining it in this form is for validating that the Win32Protect argument that is presented when later mapping a view of the section is compatible with the page protection that was specified for the section.
Windows Vista recognised that with only 12 bits allowed for the InitialPageProtection, a truly new member could be added to the SECTION without changing the size:
Mask | Definition | Versions |
---|---|---|
0x00000FFF |
ULONG InitialPageProtection : 12; |
6.0 and higher |
0xFFFFF000 (6.0 to 6.1); 0x7FFFF000 |
ULONG SessionId : 20; |
6.0 to 6.1 |
ULONG SessionId : 19; |
6.2 and higher | |
0x80000000 |
ULONG NoValidationNeeded : 1; |
6.2 and higher |