Geoff Chappell, Software Analyst
The LOADER_PARAMETER_BLOCK is the structure through which the kernel and HAL learn the initialisation data that was gathered by the loader.
Historically, and for most practical purposes even in recent Windows versions, there is only ever the one instance of this structure. It is prepared by the loader as its means of handing over to the kernel. When the loader calls the kernel’s initialisation routine, the address of the loader block is the one argument. The kernel saves the address for a while in the exported variable KeLoaderBlock. At the end of the kernel’s initialisation, the structure gets freed and the variable gets cleared. Meanwhile, especially while debugging device driver initialisation, knowledge of this structure can be very helpful.
Perhaps because the LOADER_PARAMETER_BLOCK is accessible through an exported variable and is vital as shared data between the loader, kernel and HAL, it was highly stable for many Windows versions, certainly in comparison with other undocumented structures. Since Windows 7, however, it has become highly variable. Importantly, the changes after version 6.0 are not just from growth at the end: members are inserted and removed without regard for continuity. Notably, version 6.1 inserted four members at the structure’s very start. If only for now, I do not propose to track the variations much beyond the more or less mechanical work of reading symbol files. However, some measure of variability is readily available from the changing size of the structure. The following are known:
Version | Size (x86) | Size (x64) |
---|---|---|
3.51 to 4.0 | 0x64 | |
5.0 to 5.2 | 0x68 | 0xC8 |
6.0 | 0x7C | 0xE8 |
6.1 | 0x88 | 0xF0 |
6.2 | 0xA0 | 0x0118 |
6.3 | 0xAC | 0x0128 |
10.0 | 0xBC | 0x0148 |
Until recently, Microsoft’s names for the LOADER_PARAMETER_BLOCK members and types were known only from the published symbol files for occasional Windows versions: first for Windows 2000 SP3 and SP4, then for all releases of Windows Vista and Windows 7, but for none since. Windows 10, however, brings something new…
A header file, ARC.H, in the Windows Driver Kit (WDK) for Windows 10 supplies a C-language definition of the LOADER_PARAMETER_BLOCK. This appears to be Microsoft’s first formal disclosure of the structure’s layout. It comes with no conditional compilation blocks for accommodating earlier versions. As supplied, it is immediately useful only for programming that targets Windows 10 specifically, yet doesn’t say so. Add that the header is beneath a subdirectory named “um”, presumably to mean user-mode, but that the LOADER_PARAMETER_BLOCK is long gone by the time any user-mode software gets to execute, and one might wonder if this structure’s definition is included by mistake.
Still, included it is. The following table presents the layout of the LOADER_PARAMETER_BLOCK for the original release of Windows 10 (build 10240), using names, types and offsets from Microsoft’s header:
Offset (x86) | Offset (x64) | Definition |
---|---|---|
0x00 | 0x00 |
ULONG OsMajorVersion; |
0x04 | 0x04 |
ULONG OsMinorVersion; |
0x08 | 0x08 |
ULONG Size; |
0x0C | 0x0C |
ULONG OsLoaderSecurityVersion; |
0x10 | 0x10 |
LIST_ENTRY LoadOrderListHead; |
0x18 | 0x20 |
LIST_ENTRY MemoryDescriptorListHead; |
0x20 | 0x30 |
LIST_ENTRY BootDriverListHead; |
0x28 | 0x40 |
LIST_ENTRY EarlyLaunchListHead; |
0x30 | 0x50 |
LIST_ENTRY CoreDriverListHead; |
0x38 | 0x60 |
LIST_ENTRY CoreExtensionsDriverListHead; |
0x40 | 0x70 |
LIST_ENTRY TpmCoreDriverListHead; |
0x48 | 0x80 |
ULONG_PTR KernelStack; |
0x4C | 0x88 |
ULONG_PTR Prcb; |
0x50 | 0x90 |
ULONG_PTR Process; |
0x54 | 0x98 |
ULONG_PTR Thread; |
0x58 | 0xA0 |
ULONG KernelStackSize; |
0x5C | 0xA4 |
ULONG RegistryLength; |
0x60 | 0xA8 |
PVOID RegistryBase; |
0x64 | 0xB0 |
CONFIGURATION_COMPONENT_DATA *ConfigurationRoot; |
0x68 | 0xB8 |
CHAR *ArcBootDeviceName; |
0x6C | 0xC0 |
CHAR *ArcHalDeviceName; |
0x70 | 0xC8 |
CHAR *NtBootPathName; |
0x74 | 0xD0 |
CHAR *NtHalPathName; |
0x78 | 0xD8 |
CHAR *LoadOptions; |
0x7C | 0xE0 |
NLS_DATA_BLOCK *NlsData; |
0x80 | 0xE8 |
ARC_DISK_INFORMATION *ArcDiskInformation; |
0x84 | 0xF0 |
LOADER_PARAMETER_EXTENSION *Extension; |
0x88 | 0xF8 |
union { I386_LOADER_BLOCK I386; ARM_LOADER_BLOCK Arm; } u; |
0x94 | 0x0108 |
FIRMWARE_INFORMATION_LOADER_BLOCK FirmwareInformation; |
Until the WDK for Windows 10, Microsoft’s names for the LOADER_PARAMETER_BLOCK members and types were known only from type information in symbol files for Windows Vista and Windows 7, and before then for Windows 2000 Service Packs 3 and 4. How the type information gets into symbol files for some versions but not others is not known. Names, types and offsets given below are from those symbol files in the versions for which they have type information. Names and types for other versions are inferred by assuming continuity except in the few cases where inspection of the loader or kernel shows that members have come and gone. Correlation of the more substantial changes since Windows 7 is left for another day.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x00 | 0x00 |
ULONG OsMajorVersion; |
6.0 to 6.1 |
0x04 | 0x04 |
ULONG OsMinorVersion; |
6.0 to 6.1 |
0x08 | 0x08 |
ULONG Size; |
6.0 to 6.1 |
0x0C | 0x0C |
ULONG Reserved; |
6.0 to 6.1 |
0x00 (3.51 to 6.0); 0x10 |
0x00 (5.2 to 6.0); 0x10 |
LIST_ENTRY LoadOrderListHead; |
3.51 to 6.1 |
0x08 (3.51 to 6.0); 0x18 |
0x10 (5.2 to 6.0); 0x20 |
LIST_ENTRY MemoryDescriptorListHead; |
3.51 to 6.1 |
0x10 (3.51 to 6.0); 0x20 |
0x20 (5.2 to 6.0); 0x30 |
LIST_ENTRY BootDriverListHead; |
3.51 to 6.1 |
0x18 (3.51 to 6.0); 0x28 |
0x30 (5.2 to 6.0); 0x40 |
ULONG_PTR KernelStack; |
3.51 to 6.1 |
0x1C (3.51 to 6.0); 0x2C |
0x38 (5.2 to 6.0); 0x48 |
ULONG_PTR Prcb; |
3.51 to 6.1 |
0x20 (3.51 to 6.0); 0x30 |
0x40 (5.2 to 6.0); 0x50 |
ULONG_PTR Process; |
3.51 to 6.1 |
0x24 (3.51 to 6.0); 0x34 |
0x48 (5.2 to 6.0); 0x58 |
ULONG_PTR Thread; |
3.51 to 6.1 |
0x28 (3.51 to 6.0); 0x38 |
0x50 (5.2 to 6.0); 0x60 |
ULONG RegistryLength; |
3.51 to 6.1 |
0x2C (3.51 to 6.0); 0x3C |
0x58 (5.2 to 6.0); 0x68 |
PVOID RegistryBase; |
3.51 to 6.1 |
0x30 (3.51 to 6.0); 0x40 |
0x60 (5.2 to 6.0); 0x70 |
CONFIGURATION_COMPONENT_DATA *ConfigurationRoot; |
3.51 to 6.1 |
0x34 (3.51 to 6.0); 0x44 |
0x68 (5.2 to 6.0); 0x78 |
PSTR ArcBootDeviceName; |
3.51 to 6.1 |
0x38 (3.51 to 6.0); 0x48 |
0x70 (5.2 to 6.0); 0x80 |
PSTR ArcHalDeviceName; |
3.51 to 6.1 |
0x3C (3.51 to 6.0); 0x4C |
0x78 (5.2 to 6.0); 0x88 |
PSTR NtBootPathName; |
3.51 to 6.1 |
0x40 (3.51 to 6.0); 0x50 |
0x80 (5.2 to 6.0); 0x90 |
PSTR NtHalPathName; |
3.51 to 6.1 |
0x44 (3.51 to 6.0); 0x54 |
0x88 (5.2 to 6.0); 0x98 |
PSTR LoadOptions; |
3.51 to 6.1 |
0x48 (3.51 to 6.0); 0x58 |
0x90 (5.2 to 6.0); 0xA0 |
NLS_DATA_BLOCK *NlsData; |
3.51 to 6.1 |
0x4C (3.51 to 6.0); 0x5C |
0x98 (5.2 to 6.0); 0xA8 |
ARC_DISK_INFORMATION *ArcDiskInformation; |
3.51 to 6.1 |
0x50 (3.51 to 6.0); 0x60 |
0xA0 (5.2 to 6.0); 0xB0 |
PVOID OemFontFile; |
3.51 to 6.1 |
0x54 (3.51 to 6.0) | 0xA8 (5.2 to 6.0) |
SETUP_LOADER_BLOCK *SetupLoaderBlock; |
3.51 to 6.0 |
0x58 (3.51 to 6.0); 0x64 |
0xB0 (5.2 to 6.0); 0xB8 |
LOADER_PARAMETER_EXTENSION *Extension; |
3.51 to 6.1 |
0x5C (3.51 to 6.0); 0x68 |
0xB8 (5.2 to 6.0); 0xC0 |
union { I386_LOADER_BLOCK I386; ALPHA_LOADER_BLOCK Alpha; IA64_LOADER_BLOCK Ia64; } u; |
3.51 to 6.0 |
union { I386_LOADER_BLOCK I386; IA64_LOADER_BLOCK Ia64; } u; |
6.1 only | ||
0x68 (6.0); 0x74 |
0xC8 (6.0); 0xD0 |
FIRMWARE_INFORMATION_LOADER_BLOCK FirmwareInformation; |
6.0 to 6.1 |
Since the I386_LOADER_BLOCK seems to have no purpose outside the LOADER_PARAMETER_BLOCK, it may as well be presented here:
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x00 | 0x00 |
PVOID CommonDataArea; |
3.51 to 6.1 |
0x04 | 0x08 |
ULONG MachineType; |
3.51 to 6.1 |
0x08 | 0x0C |
ULONG VirtualBias; |
5.0 to 6.1 |
The VirtualBias member was added for version 5.0, which is the first to allow for increasing user-mode address space at the expense of kernel-mode (via the /3GB switch in the BOOT.INI configuration file).