Skip to content

Instantly share code, notes, and snippets.

@mattifestation
Last active June 21, 2021 13:37
Show Gist options
  • Save mattifestation/f1e160bc970c8a7b82355d7e5946901b to your computer and use it in GitHub Desktop.
Save mattifestation/f1e160bc970c8a7b82355d7e5946901b to your computer and use it in GitHub Desktop.
Partially-completed Secure Boot policy parser. I need help with parsing our the BCD element values.
function Get-SecureBootPolicy {
<#
.SYNOPSIS
Parses a Secure Boot policy.
.DESCRIPTION
Get-SecureBootPolicy parses either the default, system Secure Boot policy or a policy passed as a byte array. The byte array must be a raw, unsigned policy.
To my knowledge, the retrieval of the system Secure Boot policy is only possible in Win 10.
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
.EXAMPLE
Get-SecureBootPolicy
Description
-----------
Parses the system default Secure Boot policy.
.EXAMPLE
$UnsignedPolicyBytes = [IO.File]::ReadAllBytes('C:\Foo\Microsoft-Windows-Kits-Secure-Boot-Policy_Unsigned.p7b')
Get-SecureBootPolicy -PolicyBytes $UnsignedPolicyBytes
.OUTPUTS
SecureBoot.PolicyInformation
Outputs an object representing a parsed Secure Boot policy.
.NOTES
The binary format parsing (excluding the BCD object/element parsing) was derived from https://gist.github.com/Wack0/d657e5ca7296243c3af7576fe4f1a422
Also, this parser would not be complete to the extent it is without the invaluable Secure Boot policy information from Geoff Chappell: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/secureboot_policy_full.htm
This parser is not designed to incorporate robust bounds checking. If I had more than two unique secure boot policies to test against, I might consider adding more robust parsing logic.
Registry rule parsing is also not incorporated as I have yet to encounter a Secure Boot policy that has this information included.
PLEASE SEND ME YOUR SECURE BOOT POLICIES! I've only encountered three unique policies thus far. They are hard to come by, it seems.
#>
[CmdletBinding()]
param (
[Parameter()]
[Byte[]]
$PolicyBytes
)
# Values obtained from https://gist.github.com/Wack0/d657e5ca7296243c3af7576fe4f1a422
Add-Type -ErrorAction Stop -TypeDefinition @'
namespace CodeIntegrity {
[System.FlagsAttribute()]
public enum SIPolicyRules {
AllowedPrereleaseSigners = 0x00000001,
AllowedKitsSigners = 0x00000002,
EnabledUMCI = 0x00000004,
EnabledBootMenuProtection = 0x00000008,
AllowedUMCIDebugOptions = 0x00000010,
EnabledUMCICacheDataVolumes = 0x00000020,
AllowedSeQuerySigningPolicyExtension = 0x00000040,
RequiredWHQL = 0x00000080,
EnabledFilterEditedBootOptions = 0x00000100,
DisabledUMCIUSN0Protection = 0x00000200,
DisabledWinloadDebuggingModeMenu = 0x00000400,
EnabledStrongCryptoForCodeIntegrity = 0x00000800,
AllowedNonMicrosoftUEFIApplicationsForBitLocker = 0x00001000,
EnabledAlwaysUsePolicy = 0x00002000,
EnabledUMCITrustUSN0 = 0x00004000,
DisabledUMCIDebugOptionsTCBLowering = 0x00008000,
EnabledAuditMode = 0x00010000,
DisabledFlightSigning = 0x00020000,
EnabledInheritDefaultPolicy = 0x00040000,
EnabledUnsignedSystemIntegrityPolicy = 0x00080000,
AllowedDebugPolicyAugmented = 0x00100000,
RequiredEVSigners = 0x00200000,
EnabledBootAuditOnFailure = 0x00400000,
EnabledAdvancedBootOptionsMenu = 0x00800000,
DisabledScriptEnforcement = 0x01000000,
RequiredEnforceStoreApplications = 0x02000000,
EnabledSecureSettingPolicy = 0x04000000
}
}
'@
$ObjectTypes = @{
1 = 'Application'
2 = 'Inherit'
3 = 'Device'
}
$ImageTypes = @{
1 = 'Firmware'
2 = 'WindowsBootApp'
3 = 'LegacyLoader'
4 = 'RealMode'
}
$InheritableTypes = @{
1 = 'InheritableByAnyObject'
2 = 'InheritableByApplicationObject'
3 = 'InheritableByDeviceObject'
}
$ElementClassTypes = @{
1 = 'Library'
2 = 'Application'
3 = 'Device'
4 = 'Template'
5 = 'OEM'
}
$FormatTypes = @{
1 = 'Device'
2 = 'String'
3 = 'Object'
4 = 'ObjectList'
5 = 'Integer'
6 = 'Boolean'
7 = 'IntegerList'
}
# reactos/boot/environ/include/bcd.h
$ApplicationTypes = @{
1 = 'FWBootMgr'
2 = 'BootMgr'
3 = 'OSLoader'
4 = 'Resume'
5 = 'MemDiag'
6 = 'NTLdr'
7 = 'SetupLdr'
8 = 'Bootsector'
9 = 'StartupCom'
}
$LibraryElementTypes = @{
0x11000001 = 'Device'
0x12000002 = 'Path'
0x12000004 = 'Description'
0x12000005 = 'Locale'
0x14000006 = 'Inherit'
0x15000007 = 'TruncateMemory'
0x14000008 = 'RecoverySequence'
0x16000009 = 'RecoveryEnabled'
0x1700000A = 'BadMemoryList'
0x1600000B = 'BadMemoryAccess'
0x1500000C = 'FirstMegabytePolicy'
0x1500000D = 'RelocatePhysical'
0x1500000E = 'AvoidLowMemory'
0x1600000F = 'TraditionalKseg'
0x16000010 = 'BootDebug'
0x15000011 = 'DebugType'
0x15000012 = 'DebugAddress'
0x15000013 = 'DebugPort'
0x15000014 = 'BaudRate'
0x15000015 = 'Channel'
0x12000016 = 'TargetName'
0x16000017 = 'NoUMEx'
0x15000018 = 'DebugStart'
0x12000019 = 'BusParams'
0x1500001A = 'HostIP'
0x1500001B = 'Port'
0x1600001C = 'Dhcp'
0x1200001D = 'Key'
0x1600001E = 'VM'
0x16000020 = 'BootEms'
0x15000022 = 'EmsPort'
0x15000023 = 'EmsBaudRate'
0x12000030 = 'LoadOptions'
0x16000040 = 'AdvancedOptions'
0x16000041 = 'OptionsEdit'
0x11000043 = 'BsdLogDevice'
0x12000044 = 'BsdLogPath'
0x16000046 = 'GraphicsModeDisabled'
0x15000047 = 'ConfigAccessPolicy'
0x16000048 = 'NoIntegrityChecks'
0x16000049 = 'TestSigning'
0x1200004A = 'FontPath'
0x1500004B = 'IntegrityServices'
0x1500004C = 'VolumeBandId'
0x16000050 = 'ExtendedInput'
0x15000051 = 'InitialConsoleInput'
0x15000052 = 'GraphicsResolution'
0x16000053 = 'RestartOnFailure'
0x16000054 = 'HighestMode'
0x16000060 = 'IsolatedContext'
0x15000065 = 'DisplayMessage'
0x15000066 = 'DisplayMessageOverride'
0x16000067 = 'BootUxLogoDisable'
0x16000068 = 'NoBootUxText'
0x16000069 = 'NoBootUxProgress'
0x1600006A = 'NoBootUxFade'
0x1600006B = 'BootUxReservePoolDebug'
0x1600006C = 'BootUxDisabled'
0x1500006D = 'BootUxFadeFrames'
0x1600006E = 'BootUxDumpStats'
0x1600006F = 'BootUxShowStats'
0x16000071 = 'MultiBootSystem'
0x16000072 = 'NoKeyboard'
0x15000073 = 'AliasWindowsKey'
0x16000074 = 'BootShutdownDisabled'
0x15000075 = 'PerformanceFrequency'
0x15000076 = 'SecurebootRawPolicy'
0x17000077 = 'AllowedInMemorySettings'
0x15000079 = 'BootUxTransitionTime'
0x1600007A = 'MobileGraphics'
0x1600007B = 'ForceFipsCrypto'
0x1500007D = 'BootErrorUx'
0x1600007E = 'FlightSigning'
0x1500007F = 'MeasuredBootLogFormat'
0x15000080 = 'DisplayRotation'
0x15000081 = 'LogControl'
0x16000082 = 'NoFirmwareSync'
}
$OSLoaderElementTypes = @{
0x21000001 = 'OSDevice'
0x22000002 = 'SystemRoot'
0x23000003 = 'ResumeObject'
0x26000004 = 'StampDisks'
0x26000010 = 'DetectHal'
0x22000011 = 'Kernel'
0x22000012 = 'Hal'
0x22000013 = 'DbgTransport'
0x25000020 = 'NX'
0x25000021 = 'PAE'
0x26000022 = 'WinPEMode'
0x26000024 = 'NoCrashAutoReboot'
0x26000025 = 'LastKnownGood'
0x26000026 = 'OSLNoIntegrityChecks'
0x26000027 = 'OSLTestSigning'
0x26000030 = 'NoLowMem'
0x25000031 = 'RemoveMemory'
0x25000032 = 'IncreaseUserVa'
0x25000033 = 'PerfMem'
0x26000040 = 'Vga'
0x26000041 = 'QuietBoot'
0x26000042 = 'NoVesa'
0x26000043 = 'NoVga'
0x25000050 = 'ClusterModeAddressing'
0x26000051 = 'UsePhysicalDestination'
0x25000052 = 'RestrictApicCluster'
0x22000053 = 'EVStore'
0x26000054 = 'UseLegacyApicMode'
0x25000055 = 'X2ApicPolicy'
0x26000060 = 'OneCPU'
0x25000061 = 'NumProc'
0x26000062 = 'MaxProc'
0x25000063 = 'ConfigFlags'
0x26000064 = 'MaxGroup'
0x26000065 = 'GroupAware'
0x25000066 = 'GroupSize'
0x26000070 = 'UseFirmwarePciSettings'
0x25000071 = 'Msi'
0x25000072 = 'PCIExpress'
0x25000080 = 'SafeBoot'
0x26000081 = 'SafeBootAlternateShell'
0x26000090 = 'BootLog'
0x26000091 = 'SOS'
0x260000a0 = 'Debug'
0x260000a1 = 'HalBreakpoint'
0x260000A2 = 'UsePlatformClock'
0x260000A3 = 'ForceLegacyPlatform'
0x260000A4 = 'UsePlatformTick'
0x260000A5 = 'DisableDynamicTick'
0x250000A6 = 'TscSyncPolicy'
0x260000b0 = 'Ems'
0x250000C0 = 'ForceFailure'
0x250000c1 = 'DriverLoadFailurePolicy'
0x250000C2 = 'BootMenuPolicy'
0x260000C3 = 'OneTimeAdvancedOptions'
0x260000C4 = 'OneTimeOptionsEdit'
0x250000E0 = 'BootStatusPolicy'
0x260000E1 = 'DisableElamDrivers'
0x250000F0 = 'HypervisorLaunchType'
0x220000F1 = 'HypervisorPath'
0x260000F2 = 'HypervisorDebug'
0x250000F3 = 'HypervisorDebugType'
0x250000F4 = 'HypervisorDebugPortNumber'
0x250000F5 = 'HypervisorBaudrate'
0x250000F6 = 'HypervisorChannel'
0x250000F7 = 'BootUx'
0x260000F8 = 'HypervisorDisableSLAT'
0x220000F9 = 'HypervisorBusParams'
0x250000FA = 'HypervisorNumProc'
0x250000FB = 'HypervisorRootProcPerNode'
0x260000FC = 'HypervisorUseLargeVTlb'
0x250000FD = 'HypervisorHostIp'
0x250000FE = 'HypervisorHostPort'
0x250000FF = 'HypervisorDebuggerPages'
0x25000100 = 'TpmBootEntropy'
0x22000110 = 'HypervisorUseKey'
0x22000112 = 'HypervisorProductSkuType'
0x25000113 = 'HypervisorRootProc'
0x26000114 = 'HypervisorDhcp'
0x25000115 = 'HypervisorIommuPolicy'
0x26000116 = 'HypervisoruseVAPic'
0x22000117 = 'HypervisorLoadOptions'
0x25000118 = 'HypervisorMSRFilterPolicy'
0x25000119 = 'HypervisorMMIONXPolicy'
0x25000120 = 'XSavePolicy'
0x25000121 = 'XSaveAddFeature0'
0x25000122 = 'XSaveAddFeature1'
0x25000123 = 'XSaveAddFeature2'
0x25000124 = 'XSaveAddFeature3'
0x25000125 = 'XSaveAddFeature4'
0x25000126 = 'XSaveAddFeature5'
0x25000127 = 'XSaveAddFeature6'
0x25000128 = 'XSaveAddFeature7'
0x25000129 = 'XSaveRemoveFeature'
0x2500012A = 'XSaveProcessorsMask'
0x2500012b = 'XSaveDisable'
0x2500012C = 'KernelDebugType'
0x2200012D = 'KernelBusParams'
0x2500012E = 'KernelDebugAddress'
0x2500012F = 'KernelDebugPort'
0x25000130 = 'ClaimedTPMCounter'
0x25000131 = 'KernelChannel'
0x22000132 = 'KernelTargetName'
0x25000133 = 'KernelHostIP'
0x25000134 = 'KernelPort'
0x26000135 = 'KernelDHCP'
0x22000136 = 'KernelKey'
0x22000137 = 'IMCHiveName'
0x21000138 = 'IMCDevice'
0x25000139 = 'KernelBaudRate'
0x22000140 = 'MfgMode'
0x26000141 = 'Event'
0x25000142 = 'VsmLaunchType'
0x25000144 = 'HypervisorEnforcedCodeIntegrity'
0x21000150 = 'SystemDataDevice'
}
$ResumeElementTypes = @{
0x21000001 = 'FileDevice'
0x22000002 = 'FilePath'
0x26000003 = 'CustomSettings'
0x26000004 = 'PAE'
0x21000005 = 'AssociatedOsDevice'
0x26000006 = 'DebugOptionEnabled'
0x25000007 = 'BootUx'
0x25000008 = 'BootMenuPolicy'
0x26000024 = 'HormEnabled'
}
$LegacyLoaderElementTypes = @{
0x22000001 = 'BPBString'
}
function Get-SystemSecureBootPolicyInformation {
<#
.SYNOPSIS
Obtains the system Secure Boot policy blob.
.DESCRIPTION
Get-SystemSecureBootPolicyInformation obtains Secure Boot policy information including the raw bytes of the policy.
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
.EXAMPLE
Get-SystemSecureBootPolicyInformation
.OUTPUTS
SecureBoot.PolicyInformation
An object representing a parsed SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION and the Secure Boot policy bytes.
#>
[CmdletBinding()]
param ()
# Helper function to interpret NTSTATUS codes as human-readable exceptions
function Get-NTStatusException
{
param (
[Parameter(Position = 0, Mandatory = $True)]
[Int32]
$ErrorCode
)
$Win32Native = [Int].Assembly.GetType('Microsoft.Win32.Win32Native')
$LsaNtStatusToWinError = $Win32Native.GetMethod('LsaNtStatusToWinError', [Reflection.BindingFlags] 'NonPublic, Static')
$GetMessage = $Win32Native.GetMethod('GetMessage', [Reflection.BindingFlags] 'NonPublic, Static')
$WinErrorCode = $LsaNtStatusToWinError.Invoke($null, @($ErrorCode))
$GetMessage.Invoke($null, @($WinErrorCode)).TrimEnd("`r`n")
}
Add-Type -ErrorAction Stop -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
namespace SecureBoot {
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION
{
public Guid PolicyPublisher;
public uint PolicyVersion;
public uint PolicyOptions;
public uint PolicySize;
}
public class NativeMethods {
[DllImport("ntdll.dll")]
public static extern int NtQuerySystemInformation(uint SystemInformationClass, IntPtr SystemInformation, uint SystemInformationLength, out uint ReturnLength);
}
}
'@
$PolicyInfo = New-Object -TypeName SecureBoot.SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION
[UInt32] $PolicyInfoSize = [Runtime.InteropServices.Marshal]::SizeOf([Type][SecureBoot.SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION])
$ReturnedSize = [UInt32]::MinValue
# dt uxtheme!_SYSTEM_INFORMATION_CLASS
[UInt32] $SystemSecureBootPolicyFullInformation = 0xAB
$Result = [SecureBoot.NativeMethods]::NtQuerySystemInformation(
$SystemSecureBootPolicyFullInformation,
[IntPtr]::Zero,
[UInt32]::MinValue,
[Ref] $ReturnedSize)
$InsufficientBuffer = 0xC0000004
# A 0xC0000004 NTSTATUS is expected where the size of the buffer to be allocated will be returned.
if ($Result -ne $InsufficientBuffer) {
throw (Get-NTStatusException -ErrorCode $Result)
return
}
# Allocate the required size for the SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION structure.
$PtrPolicy = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ReturnedSize)
$BufferSize = $ReturnedSize
$ReturnedSize = [UInt32]::MinValue
$Result = [SecureBoot.NativeMethods]::NtQuerySystemInformation(
$SystemSecureBootPolicyFullInformation,
$PtrPolicy,
[UInt32] $BufferSize,
[Ref] $ReturnedSize)
# An unexpected error occurred.
if ($Result) {
throw (Get-NTStatusException -ErrorCode $Result)
# Free the unmanaged memory
[Runtime.InteropServices.Marshal]::FreeHGlobal($PtrPolicy)
return
}
$PolicyInformation = [Runtime.InteropServices.Marshal]::PtrToStructure($PtrPolicy, [Type][SecureBoot.SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION])
$PolicyInfoStructSize = [Runtime.InteropServices.Marshal]::SizeOf([Type][SecureBoot.SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION])
$PolicyBytes = New-Object -TypeName Byte[]($PolicyInformation.PolicySize)
[Runtime.InteropServices.Marshal]::Copy([IntPtr]::Add($PtrPolicy, $PolicyInfoStructSize), $PolicyBytes, 0, $PolicyInformation.PolicySize)
[Runtime.InteropServices.Marshal]::FreeHGlobal($PtrPolicy)
[PSCustomObject] @{
PSTypeName = 'SecureBoot.PolicyInformation'
PolicyPublisher = $PolicyInformation.PolicyPublisher
PolicyVersion = $PolicyInformation.PolicyVersion
PolicyOptions = $PolicyInformation.PolicyOptions
PolicySize = $PolicyInformation.PolicySize
PolicyBytes = $PolicyBytes
}
}
$SecureBootPolicyBytes = $null
if ($PolicyBytes) {
$SecureBootPolicyBytes = $PolicyBytes
} else {
# If -PolicyBytes was not specified, obtain the system default Secure Boot policy.
$PolicyInfo = Get-SystemSecureBootPolicyInformation -ErrorAction Stop
$SecureBootPolicyBytes = $PolicyInfo.PolicyBytes
}
try {
$MemoryStream = New-Object -TypeName IO.MemoryStream -ArgumentList @(,$SecureBootPolicyBytes)
$BinaryReader = New-Object -TypeName IO.BinaryReader -ArgumentList $MemoryStream, ([Text.Encoding]::Unicode)
} catch {
throw $_
return
}
$FormatVersion = $BinaryReader.ReadUInt16()
$PolicyVersion = $BinaryReader.ReadUInt32()
$PolicyPublisher = [Guid] $BinaryReader.ReadBytes(16)
$CanUpdateCount = $BinaryReader.ReadUInt16()
$CanUpdateGuids = $null
# I assume this has to do with the signers who are permitted
# to update secure boot policy.
if ($CanUpdateCount) {
$CanUpdateGuids = New-Object -TypeName Guid[]($CanUpdateCount)
}
for ($i = 0; $i -lt $CanUpdateCount; $i++) {
$CanUpdateGuids[$i] = [Guid] $BinaryReader.ReadBytes(16)
}
$SIPolicyValue = $BinaryReader.ReadUInt32()
$SIPolicyOptions = $null
# Obtain the system integrity (SI) options. These are equivalent to Device Guard
# code integrity rule options.
if ($SIPolicyValue) { $SIPolicyOptions = [CodeIntegrity.SIPolicyRules] $SIPolicyValue }
$BcdRulesCount = $BinaryReader.ReadUInt16()
# To do: Registry rules will not be parsed now as I don't have
# an example secure boot policy that contains registry rules.
$RegistryRulesCount = $BinaryReader.ReadUInt16()
if ($RegistryRulesCount) {
Write-Warning "If you're seeing this warning, your Secure Boot policy has registry rules present. Registry rules are not currently parsed as I have yet to encounter a policy that had them. Please let @mattifestation know and ideally supply your secure boot policy."
}
$SecureBootPolicy = [PSCustomObject] @{
PSTypeName = 'SecureBoot.Policy'
FormatVersion = $FormatVersion
PolicyVersion = $PolicyVersion
PolicyPublisher = $PolicyPublisher
CanUpdate = $CanUpdateGuids
SIPolicyOptions = $SIPolicyOptions
BCDRules = $null
PolicyBytes = $SecureBootPolicyBytes
}
$RawBCDRules = New-Object -TypeName PSObject[]($BcdRulesCount)
# Parse the BCD objects aside from the BCD element values.
# Those will need to be captured in the second pass.
for ($i = 0; $i -lt ([Int] $BcdRulesCount); $i++) {
$ObjectTypeVal = $BinaryReader.ReadInt32()
$ElementTypeVal = $BinaryReader.ReadInt32()
$ValueOffset = $BinaryReader.ReadUInt32()
$ObjectType = $ObjectTypes[(($ObjectTypeVal -band 0xF0000000) -shr 28)]
$ImageCodeVal = ($ObjectTypeVal -band 0x00F00000) -shr 20
$ObjectSubType = $null
switch ($ObjectType) {
'Application' { $ObjectSubType = $ImageTypes[$ImageCodeVal] }
'Inherit' { $ObjectSubType = $InheritableTypes[$ImageCodeVal] }
}
$ElementType = $ElementClassTypes[(($ElementTypeVal -band 0xF0000000) -shr 28)]
# Get the datatype of the element value
$ElementFormat = $FormatTypes[(($ElementTypeVal -band 0x0F000000) -shr 24)]
$ApplicationType = $null
if ($ObjectType -eq 'Application') {
$ApplicationType = $ApplicationTypes[($ObjectTypeVal -band 0x000FFFFF)]
}
$ElementName = $null
# To add: BOOTMGR, DEVICE, MEMTEST. These are unlikely though.
if ($ElementType -eq 'Library') {
$ElementName = $LibraryElementTypes[$ElementTypeVal]
} elseif ($ApplicationType -eq 'OSLoader') {
$ElementName = $OSLoaderElementTypes[$ElementTypeVal]
} elseif ($ApplicationType -eq 'Resume') {
$ElementName = $ResumeElementTypes[$ElementTypeVal]
} elseif ($ApplicationType -eq 'NTLdr') {
$ElementName = $LegacyLoaderElementTypes[$ElementTypeVal]
} elseif ($ElementName -eq $null) {
# Fallback in case there isn't element name coverage
# This mirrors how bcdedit.exe handles this case.
$ElementName = "Custom:$($ElementTypeVal.ToString('X8'))"
}
$RawBCDRules[$i] = [PSCustomObject] @{
PSTypeName = 'SecureBoot.BCDRule'
ObjectType = $ObjectSubType
ObjectName = $ApplicationType
ObjectTypeRawValue = "0x$($ObjectTypeVal.ToString('X8'))"
ElementName = $ElementName
ElementTypeRawValue = "0x$($ElementTypeVal.ToString('X8'))"
ElementPolicyType = $null
ElementPolicyValue = $null
ElementPolicyDefaultValue = $null
ElementSubjectToBitlocker = $False
ElementSubjectToVBS = $False
}
}
$CurrentPosition = $BinaryReader.BaseStream.Position
Write-Verbose "Element policy value table offset: 0x$($CurrentPosition.ToString('X8'))"
# Second pass to retrieve BCD element policies from their respective offsets.
# This portion would not have been possible without the help of Geoff Chappell's article:
# www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/secureboot_policy_full.htm
for ($i = 0; $i -lt $BcdRulesCount; $i++) {
$ValueTypeVal = $BinaryReader.ReadUInt16()
$ValType = ([Byte] ($ValueTypeVal -band 0x1F)).ToString()
$ValApplicability = [Byte] ($ValueTypeVal -band 0xE0)
$SubjectToBitlocker = $False
$SubjectToVBS = $False
if ($ValApplicability -band 0x20) { $SubjectToBitlocker = $True }
if ($ValApplicability -band 0x40) { $SubjectToVBS = $True }
$ValueType = $null
$DefaultValue = $null
$Value = $null
# Consider resolving values for well-known elements -
# e.g. NX values would map to human-readable values.
# Until then, refer to Geoff Chappell's "BCD Elements" reference.
switch ($ValType) {
'0' {
# FYI, I have not personally encountered this type yet (i.e. I make no guarantees that I am parsing this correctly). I need more secure boot policies to parse!
$ValueType = 'String'
$StringSizeInBytes = $BinaryReader.ReadUInt16()
$ValueBytes = $BinaryReader.ReadBytes($StringSizeInBytes)
$Value = [Text.Encoding]::Unicode.GetString($ValueBytes)
}
'1' {
$ValueType = 'Boolean'
# $DefaultValue is not applicable here
$Value = [Bool] $BinaryReader.ReadUInt16()
}
'2' {
# FYI, I have not personally encountered this type yet (i.e. I make no guarantees that I am parsing this correctly). I need more secure boot policies to parse!
$ValueType = 'UInt32'
$DefaultValue = $BinaryReader.ReadUInt32()
}
'3' {
# FYI, I have not personally encountered this type yet (i.e. I make no guarantees that I am parsing this correctly). I need more secure boot policies to parse!
$ValueType = 'UInt32AcceptableRange'
$DefaultValue = $BinaryReader.ReadUInt32()
$Value = New-Object -TypeName UInt32[](2)
$Value[0] = $BinaryReader.ReadUInt32() # Lowest acceptable value
$Value[1] = $BinaryReader.ReadUInt32() # Highest acceptable value
}
'4' {
# FYI, I have not personally encountered this type yet (i.e. I make no guarantees that I am parsing this correctly). I need more secure boot policies to parse!
$ValueType = 'UInt32Array'
$DefaultValue = $BinaryReader.ReadUInt32()
$ULongArraySize = $BinaryReader.ReadUInt16()
$Value = New-Object -TypeName UInt32[]($ULongArraySize)
for ($j = 0; $j -lt $ULongArraySize; $j++) {
$Value[$j] = $BinaryReader.ReadUInt32()
}
}
'5' {
$ValueType = 'UInt64Array'
$DefaultValue = $BinaryReader.ReadUInt64()
}
'6' {
# FYI, I have not personally encountered this type yet (i.e. I make no guarantees that I am parsing this correctly). I need more secure boot policies to parse!
$ValueType = 'UInt64AcceptableRange'
$DefaultValue = $BinaryReader.ReadUInt64()
$Value = New-Object -TypeName UInt64[](2)
$Value[0] = $BinaryReader.ReadUInt64() # Lowest acceptable value
$Value[1] = $BinaryReader.ReadUInt64() # Highest acceptable value
}
'7' {
# FYI, I have not personally encountered this type yet (i.e. I make no guarantees that I am parsing this correctly). I need more secure boot policies to parse!
$ValueType = 'UInt64Array'
$DefaultValue = $BinaryReader.ReadUInt64()
$ULongArraySize = $BinaryReader.ReadUInt16()
$Value = New-Object -TypeName UInt64[]($ULongArraySize)
for ($j = 0; $j -lt $ULongArraySize; $j++) {
$Value[$j] = $BinaryReader.ReadUInt64()
}
}
'8' {
$ValueType = 'ValuePermission'
# $DefaultValue is not applicable here
$RawValue = $BinaryReader.ReadUInt16()
$Value = $null
if ($RawValue -eq 0) {
$Value = 'CreationNotPermitted'
} else {
$Value = 'SetAndDeleteNotPermitted'
}
}
'9' {
# It's not clear on how this data should be properly interpreted based on Geoff's description:
# https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/secureboot_policy_full.htm
Write-Warning "Element table entry (type 9) encountered! It is currently unclear as to how this data should be parsed. If you're seeing this warning, please let @mattifestation know and ideally supply your secure boot policy!"
}
'10' {
# FYI, I have not personally encountered this type yet (i.e. I make no guarantees that I am parsing this correctly). I need more secure boot policies to parse!
$ValueType = 'ByteArray'
$StringSizeInBytes = $BinaryReader.ReadUInt16()
$Value = $BinaryReader.ReadBytes($StringSizeInBytes)
}
}
$RawBCDRules[$i].ElementPolicyType = $ValueType
$RawBCDRules[$i].ElementPolicyValue = $Value
$RawBCDRules[$i].ElementPolicyDefaultValue = $DefaultValue
$RawBCDRules[$i].ElementSubjectToBitlocker = $SubjectToBitlocker
$RawBCDRules[$i].ElementSubjectToVBS = $SubjectToVBS
}
$BinaryReader.Close()
$SecureBootPolicy.BCDRules = $RawBCDRules
$SecureBootPolicy
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment