LOADER_PARAMETER_BLOCK

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.

Variability

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 version 5.2, however, each change of version brings a change of size:

Version Size (x86) Size (x64)
3.10 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

The growth from version 4.0 to 5.0 and again from 6.2 to 6.3 involves no additions, removals or rearrangements to the structure’s own members, just growth within a member, and in both cases within the member that was at the time the structure’s last. Version 6.0 brought a straightforward appending of one member. Such growth only at the end allows for backwards compatibility. A new loader can prepare the structure in its new layout but safely pass it to an old kernel. This was indeed depended on for multi-boot configurations in the days when the one NTLDR in the root directory of the bootable partition might load the kernel of any earlier Windows version (if not of a later one).

Version 6.0 separated the booting of Windows into a boot manager, e.g., BOOTMGR, and a boot loader, e.g., WINLOAD. The boot manager selects which Windows installation to proceed with. Each Windows version provides its own boot loader. This is what prepares the LOADER_PARAMETER_BLOCK and it only has to do so for the matching kernel. Backwards compatibility is no longer an issue for the structure. Most changes since are not just from growth at the end: members are inserted and removed without regard for continuity. Notably, version 6.1 inserted version numbers and a size at the structure’s very start, surely to provide some easy and reliable means for future kernels to validate that the structure received from the loader is plausibly what that kernel expects. This defence was apparently thought important enough to warrant its own bug-check, LOADER_BLOCK_MISMATCH (0x0100).

Layout

Until recently, Microsoft’s names for the LOADER_PARAMETER_BLOCK members and types were known only from type information in the 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. How the type information gets into symbol files for some versions but not others is not known. 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 was published by mistake.

Still, published it is. The following table presents the layout of the LOADER_PARAMETER_BLOCK for the original release of Windows 10 (build 10240), as inferred from Microsoft’s header. For earlier versions, the layout is directly from type information in symbol files, if available. Names, types and offsets for other versions are something of a guess from assuming continuity except where inspection of the loader or kernel shows that members have come and gone.

Offset (x86) Offset (x64) Definition Versions
0x00 0x00
ULONG OsMajorVersion;
6.1 and higher
0x04 0x04
ULONG OsMinorVersion;
6.1 and higher
0x08 0x08
ULONG Size;
6.1 and higher
0x0C 0x0C
ULONG Reserved;
6.1 to early 10.0
ULONG OsLoaderSecurityVersion;
10.0 and higher;
see note after table
0x00 (3.10 to 6.0);
0x10
0x00 (5.2 to 6.0);
0x10
LIST_ENTRY LoadOrderListHead;
all
0x08 (3.10 to 6.0);
0x18
0x10 (5.2 to 6.0);
0x20
LIST_ENTRY MemoryDescriptorListHead;
all
0x10 (3.10 to 6.0);
0x20
0x20 (5.2 to 6.0);
0x30
LIST_ENTRY BootDriverListHead;
all
0x28 0x40
LIST_ENTRY EarlyLaunchListHead;
6.2 and higher
0x30 0x50
LIST_ENTRY CoreDriverListHead;
6.2 and higher
0x38 0x60
LIST_ENTRY CoreExtensionsDriverListHead;
10.0 and higher
0x40 0x70
LIST_ENTRY TpmCoreDriverListHead;
10.0 and higher
0x18 (3.10 to 6.0);
0x28 (6.1);
0x38 (6.2 to 6.3);
0x48
0x30 (5.2 to 6.0);
0x40 (6.1);
0x60 (6.2 to 6.3);
0x80
ULONG_PTR KernelStack;
all
0x1C (3.10 to 6.0);
0x2C (6.1);
0x3C (6.2 to 6.3);
0x4C
0x38 (5.2 to 6.0);
0x48 (6.1);
0x68 (6.2 to 6.3);
0x88
ULONG_PTR Prcb;
all
0x20 (3.10 to 6.0);
0x30 (6.1);
0x40 (6.2 to 6.3);
0x50
0x40 (5.2 to 6.0);
0x50 (6.1);
0x70 (6.2 to 6.3);
0x90
ULONG_PTR Process;
all
0x24 (3.10 to 6.0);
0x34 (6.1);
0x44 (6.2 to 6.3);
0x54
0x48 (5.2 to 6.0);
0x58 (6.1);
0x78 (6.2 to 6.3);
0x98
ULONG_PTR Thread;
all
0x48 (6.2 to 6.3);
0x58
0x80 (6.2 to 6.3);
0xA0
ULONG KernelStackSize;
6.2 and higher
0x28 (3.10 to 6.0);
0x38 (6.1);
0x4C (6.2 to 6.3);
0x5C
0x50 (5.2 to 6.0);
0x60 (6.1);
0x84 (6.2 to 6.3);
0xA4
ULONG RegistryLength;
all
0x2C (3.10 to 6.0);
0x3C (6.1);
0x50 (6.2 to 6.3);
0x60
0x58 (5.2 to 6.0);
0x68 (6.1);
0x88 (6.2 to 6.3);
0xA8
PVOID RegistryBase;
all
0x30 (3.10 to 6.0);
0x40 (6.1);
0x54 (6.2 to 6.3);
0x64
0x60 (5.2 to 6.0);
0x70 (6.1);
0x90 (6.2 to 6.3);
0xB0
CONFIGURATION_COMPONENT_DATA *ConfigurationRoot;
all
0x34 (3.10 to 6.0);
0x44 (6.1);
0x58 (6.2 to 6.3);
0x68
0x68 (5.2 to 6.0);
0x78 (6.1);
0x98 (6.2 to 6.3);
0xB8
CHAR *ArcBootDeviceName;
all
0x38 (3.10 to 6.0);
0x48 (6.1);
0x5C (6.2 to 6.3);
0x6C
0x70 (5.2 to 6.0);
0x80 (6.1);
0xA0 (6.2 to 6.3);
0xC0
CHAR *ArcHalDeviceName;
all
0x3C (3.10 to 6.0);
0x4C (6.1);
0x60 (6.2 to 6.3);
0x70
0x78 (5.2 to 6.0);
0x88 (6.1);
0xA8 (6.2 to 6.3);
0xC8
CHAR *NtBootPathName;
all
0x40 (3.10 to 6.0);
0x50 (6.1);
0x64 (6.2 to 6.3);
0x74
0x80 (5.2 to 6.0);
0x90 (6.1);
0xB0 (6.2 to 6.3);
0xD0
CHAR *NtHalPathName;
all
0x44 (3.10 to 6.0);
0x54 (6.1);
0x68 (6.2 to 6.3);
0x78
0x88 (5.2 to 6.0);
0x98 (6.1);
0xB8 (6.2 to 6.3);
0xD8
CHAR *LoadOptions;
all
0x48 (3.10 to 6.0);
0x58 (6.1);
0x6C (6.2 to 6.3);
0x7C
0x90 (5.2 to 6.0);
0xA0 (6.1);
0xC0 (6.2 to 6.3);
0xE0
NLS_DATA_BLOCK *NlsData;
all
0x4C (3.10 to 6.0);
0x5C (6.1);
0x70 (6.2 to 6.3);
0x80
0x98 (5.2 to 6.0);
0xA8 (6.1);
0xC8 (6.2 to 6.3);
0xE8
ARC_DISK_INFORMATION *ArcDiskInformation;
all
0x50 (3.10 to 6.0);
0x60 (6.1)
0xA0 (5.2 to 6.0);
0xB0 (6.1)
PVOID OemFontFile;
3.10 to 6.1
0x54 (3.10 to 6.0) 0xA8 (5.2 to 6.0)
SETUP_LOADER_BLOCK *SetupLoaderBlock;
3.10 to 6.0
0x58 (3.10 to 6.0);
0x64 (6.1);
0x74 (6.2 to 6.3);
0x84
0xB0 (5.2 to 6.0);
0xB8 (6.1);
0xD0 (6.2 to 6.3);
0xF0
LOADER_PARAMETER_EXTENSION *Extension;
all
0x5C (3.10 to 6.0);
0x68 (6.1);
0x78 (6.2 to 6.3);
0x88
0xB8 (5.2 to 6.0);
0xC0 (6.1);
0xD8 (6.2 to 6.3);
0xF8
union {
    I386_LOADER_BLOCK I386;
    ALPHA_LOADER_BLOCK Alpha;
    IA64_LOADER_BLOCK Ia64;
} u;
5.0 and 6.0
union {
    I386_LOADER_BLOCK I386;
    IA64_LOADER_BLOCK Ia64;
} u;
6.1 only
union {
    I386_LOADER_BLOCK I386;
    ARM_LOADER_BLOCK Arm;
} u;
10.0 only
0x68 (6.0);
0x74 (6.1);
0x84 (6.2 to 6.3);
0x94
0xC8 (6.0);
0xD0 (6.1);
0xE8 (6.2 to 6.3);
0x0108
FIRMWARE_INFORMATION_LOADER_BLOCK FirmwareInformation;
6.0 and higher

The C-language definition in Microsoft’s ARC.H shows Reserved as being now put to use as OsLoaderSecurityVersion—which indeed it is, but for the build 10586 of version 10.0 from the 1511 release. Working out how to present developments now that Microsoft says all future Windows releases will be version 10.0 is a problem I leave for another time.

The u union is presented only for the versions that have type information in symbol files or for which Microsoft publishes a C-language definition. Members other than I386 presumably change with Microsoft’s choice of supported processor architectures and these notes are derived from inspection of the i386 and amd64 architectures only.

I386_LOADER_BLOCK

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;
all
0x04 0x08
ULONG MachineType;
all
0x08 0x0C
ULONG VirtualBias;
5.0 to 6.1

The kernel’s use of the CommonDataArea seems to have stopped for version 6.2. A comment in the C-language definition for Windows 10 would have it that the MachineType is “Temporary only”. In all known versions it is the source of what the 32-bit kernel exports as the KeI386MachineType variable.

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).

FIRMWARE_INFORMATION_LOADER_BLOCK

The FIRMWARE_INFORMATION_LOADER_BLOCK is also thought to have no independent existence.

Offset (x86) Offset (x64) Definition Versions
0x00 0x00
/*  bit flags, see below  */
6.0 and higher
0x04 0x08
union {
    EFI_FIRMWARE_INFORMATION EfiInformation;
    PCAT_FIRMWARE_INFORMATION PcatInformation;
} u;
6.0 and higher

The first of the bit flags selected from the union u according to the firmware type. Windows 10 added more to support Isolated User Mode (IUM) and the security of memory that is saved during hibernation.

Mask Definition Versions
0x00000001
ULONG FirmwareTypeEfi : 1;
6.0 and higher
0x00000002
ULONG EfiRuntimeUseIum : 1;
10.0 and higher
0x00000004
ULONG EfiRuntimePageProtectionEnabled : 1;
10.0 and higher
0x00000008
ULONG EfiRuntimePageProtectionSupported : 1;
10.0 and higher
 
ULONG Reserved : 31;
6.0 to 6.3
ULONG Reserved : 28;
10.0 and higher

The PCAT_FIRMWARE_INFORMATION structure has just the one member, a ULONG named PlaceHolder. The business of the  FIRMWARE_INFORMATION_LOADER_BLOCK has always been to provide EFI_FIRMWARE_INFORMATION:

Offset (x86) Offset (x64) Definition Versions
0x00 0x00
ULONG FirmwareVersion;
6.0 and higher
0x04 0x08
VIRTUAL_EFI_RUNTIME_SERVICES *VirtualEfiRuntimeServices;
6.0 and higher
0x08 0x10
NTSTATUS SetVirtualAddressMapStatus;
6.0 and higher
0x0C 0x14
ULONG MissedMappingsCount;
6.0 and higher
0x10 0x18
LIST_ENTRY FirmwareResourceList;
6.2 and higher
0x18 0x28
PVOID EfiMemoryMap;
6.3 and higher
0x1C 0x30
ULONG EfiMemoryMapSize;
6.3 and higher
0x20 0x34
ULONG EfiMemoryMapDescriptorSize;
6.3 and higher