Geoff Chappell, Software Analyst
DRAFT: Take more than your usual care.
The SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION structure is what a successful call to ZwQuerySystemInformation or NtQuerySystemInformation produces in its output buffer when given the information class SystemSecureBootPolicyFullInformation (0xAB).
The SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION structure is not documented.
Formally, the SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION is 0x20 bytes in both 32-bit and 64-bit Windows. In practice, it is a fixed-size header of 0x1C bytes plus a variable-size Policy whose size in bytes is given by PolicySize in the header.
Offset | Definition |
---|---|
0x00 |
SYSTEM_SECUREBOOT_POLICY_INFORMATION PolicyInformation; |
0x18 |
ULONG PolicySize; |
0x1C |
UCHAR Policy [ANYSIZE_ARRAY]; |
As far as concerns the ZwQuerySystemInformation function and the SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION structure, the Policy is an opaque blob. The kernel will have received the blob from the loader, else the function has nothing to return. The kernel interprets some of the contents, but relies on parsing that is also received from the loader. In the possibly temporary absence of somewhere better, the blob’s interpretation is as well given here. No formal structure is known. The loader parses it as a stream. In the original release of version 10.0, this parsing picks out the following sequence of items:
The terms BCD rule, registry rule and value table are surmised from the names of relevant routines as known from public symbol files for the loader and kernel.
The least size for a valid Secure Boot Policy blob is 0x20 bytes, being 0x18 bytes for the first four items as a fixed-size header, plus the PolicyOptions and two counts, with all variable-size items empty.
Each BCD rule describes the policy expectations of one boot option:
Offset | Size | Description |
---|---|---|
0x00 | dword | BCD object type in which option is subject to rule; else zero if option is subject to rule in any BCD object |
0x04 | dword | BCD element type |
0x08 | dword | offset into value table of value table entry |
Remember that each boot option is a BCD element in a BCD object. The same numerical value for a BCD element can be defined for multiple BCD objects yet denote completely unrelated boot options. For instance, the element type 0x23000003 is default in a Windows Boot Manager object (with object type 0x10200001) but resumeobject in a Windows Boot Loader object (with object type 0x10200003). Were a rule to have zero for the object type and 0x23000003 as the element type, then it would apply to both default and resumeobject in all BCD objects.
If a rule specifies a BCD object, then it applies only to that precise combination of BCD object and BCD element. Suppose, for instance, that a BCD rule would prevent the setting of the debug option (element type 0x260000A0) in BCD objects of type 0x10200003. Then the kernel’s enforcement of the policy does indeed mean that while Secure Boot is enabled the debug option can not be set in any Windows Boot Loader object such as created by the BCDEDIT command-line switches /create /application osloader and that the option will be deleted from all such objects if Secure Boot is later enabled. However, the rule does not prevent the option from being set in a {bootloadersettings} or {kerneldbgsettings} object. This is useful in practice because the latter types of object are inheritable by the former. Set the debug option in an inheritable object, and its effect is nullified by policy for the inheriting object while Secure Boot is enabled but because the option in the inherited object is not subject to policy, it is not deleted and it regains its effect if Secure Boot is later disabled.
Each registry rule describes a key and value in a Secure Boot registry hive.
Offset | Size | Description |
---|---|---|
0x00 | dword | must be 0x81000000 |
0x04 | dword | offset into value table of registry key as sized string |
0x08 | dword | offset into value table of registry value as sized string |
0x0C | dword | offset into value table of value table entry |
The registry key and value that the rule applies to are held in the value table as a one-word size in bytes of Unicode characters that follow. The characters end with a null that is not in the size.
The value table holds variable-size details for the rules. Some such details are counted strings, as described above for registry rules. Others are value table entries that each begin with a word of bit fields. The type, as masked by 0x1F, determines the value table entry’s layout beyond its first word.
Mask | Description |
---|---|
0x001F | type |
0x0020 | rule subject to BitLocker |
0x0040 | rule subject to Virtualization Based Security (VBS) |
The precise conditions are meant above by BitLocker and VBS are beyond the present scope of this note.
A value table entry of type 0 defines a string value.
Offset | Type | Description |
---|---|---|
0x00 | word | flags; 0x00 as type |
0x02 | word | size in bytes of string that follows, not including terminating null |
0x04 | word array | default value as case-insensitive null-terminated Unicode string |
Type 1 defines a boolean value.
Offset | Type | Description |
---|---|---|
0x00 | word | flags; 0x01 as type |
0x02 | word | zero if default value is FALSE; non-zero if default value is TRUE |
Types 2 to 4 are for ULONG values. All these value types allow that a policy can force one value as the default, but the different types allow that the enforcement can be conditional. Type 2 is for a policy that insists on the one value unconditionally:
Offset | Type | Description |
---|---|---|
0x00 | word | flags; 0x02 as type |
0x02 | dword | default value |
Type 3 elaborates with a range of acceptable ULONG values:
Offset | Type | Description |
---|---|---|
0x00 | word | flags; 0x03 as type |
0x02 | dword | default value |
0x06 | dword | lowest acceptable value |
0x0A | dword | highest acceptable value |
For type 4 the elaboration of acceptable ULONG values is instead an enumeration:
Offset | Type | Description |
---|---|---|
0x00 | word | flags; 0x04 as type |
0x02 | dword | default value |
0x06 | word | count of acceptable values |
0x08 | dword array | array of acceptable values |
Types 5 to 7 are the same but for ULONGLONG values. Type 5 is the basic case of a single ULONGLONG value to enforce by policy:
Offset | Size | Description |
---|---|---|
0x00 | word | flags; 0x05 as type |
0x02 | qword | default value |
Type 6 defines the acceptable ULONGLONG values as a range:
Offset | Size | Description |
---|---|---|
0x00 | word | flags; 0x06 as type |
0x02 | qword | default value |
0x0A | qword | lowest acceptable value |
0x12 | qword | highest acceptable value |
Type 7 lists the acceptable ULONGLONG values as an array:
Offset | Size | Description |
---|---|---|
0x00 | word | flags; 0x07 as type |
0x02 | qword | default value |
0x0A | word | count of acceptable values |
0x0C | qword array | array of acceptable values |
For type 8, there is no default value to enforce. Instead, the policy is whether the BCD option or registry value is permitted at all. According to public symbols for the Boot Manager, this type of value table entry is an option.
Offset | Type | Description |
---|---|---|
0x00 | word | flags; 0x08 as type |
0x02 | word | zero if not permitted; else non-zero to preserve |
If the second word is zero in this value table entry for a BCD rule, then the corresponding BCD option’s existence is a policy violation. The option cannot be set to any value while Secure Boot is enabled. It can be deleted, however. Indeed, any updating of the BCD store that is done when Secure Boot gets enabled is meant to delete the option. If the second word is non-zero, then not only does the rule allow the corresponding BCD option, it makes a policy violation of deleting it.
Type 9 is valid for a value table entry, but the only code that is yet known for interpreting it does no more than compute the size:
Offset | Type | Description |
---|---|---|
0x00 | word | flags; 0x09 as type |
0x02 | unknown two bytes | |
0x04 | word | size in bytes of data at offset 0x0A |
0x06 | unknown four bytes | |
0x0A | unknown data |
Type 10 defines a binary value:
Offset | Type | Description |
---|---|---|
0x00 | word | flags; 0x0A as type |
0x02 | word | size in bytes of data that follows |
0x04 | byte array | default value |