Skip to content

Instantly share code, notes, and snippets.

@Wack0
Last active September 9, 2022 20:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Wack0/d657e5ca7296243c3af7576fe4f1a422 to your computer and use it in GitHub Desktop.
Save Wack0/d657e5ca7296243c3af7576fe4f1a422 to your computer and use it in GitHub Desktop.
Secure Boot Policy parser
using System;
using System.IO;
using LipingShare.LCLib.Asn1Processor;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
namespace SbpParse {
enum BcdElementTypes : uint
{
BcdLibraryDevice_ApplicationDevice = 0x11000001,
BcdLibraryString_ApplicationPath = 0x12000002,
BcdLibraryString_Description = 0x12000004,
BcdLibraryString_PreferredLocale = 0x12000005,
BcdLibraryObjectList_InheritedObjects = 0x14000006,
BcdLibraryInteger_TruncatePhysicalMemory = 0x15000007,
BcdLibraryObjectList_RecoverySequence = 0x14000008,
BcdLibraryBoolean_AutoRecoveryEnabled = 0x16000009,
BcdLibraryIntegerList_BadMemoryList = 0x1700000a,
BcdLibraryBoolean_AllowBadMemoryAccess = 0x1600000b,
BcdLibraryInteger_FirstMegabytePolicy = 0x1500000c,
BcdLibraryInteger_RelocatePhysicalMemory = 0x1500000D,
BcdLibraryInteger_AvoidLowPhysicalMemory = 0x1500000E,
BcdLibraryBoolean_DebuggerEnabled = 0x16000010,
BcdLibraryInteger_DebuggerType = 0x15000011,
BcdLibraryInteger_SerialDebuggerPortAddress = 0x15000012,
BcdLibraryInteger_SerialDebuggerPort = 0x15000013,
BcdLibraryInteger_SerialDebuggerBaudRate = 0x15000014,
BcdLibraryInteger_1394DebuggerChannel = 0x15000015,
BcdLibraryString_UsbDebuggerTargetName = 0x12000016,
BcdLibraryBoolean_DebuggerIgnoreUsermodeExceptions = 0x16000017,
BcdLibraryInteger_DebuggerStartPolicy = 0x15000018,
BcdLibraryString_DebuggerBusParameters = 0x12000019,
BcdLibraryInteger_DebuggerNetHostIP = 0x1500001A,
BcdLibraryInteger_DebuggerNetPort = 0x1500001B,
BcdLibraryBoolean_DebuggerNetDhcp = 0x1600001C,
BcdLibraryString_DebuggerNetKey = 0x1200001D,
BcdLibraryBoolean_EmsEnabled = 0x16000020,
BcdLibraryInteger_EmsPort = 0x15000022,
BcdLibraryInteger_EmsBaudRate = 0x15000023,
BcdLibraryString_LoadOptionsString = 0x12000030,
BcdLibraryBoolean_DisplayAdvancedOptions = 0x16000040,
BcdLibraryBoolean_DisplayOptionsEdit = 0x16000041,
BcdLibraryDevice_BsdLogDevice = 0x11000043,
BcdLibraryString_BsdLogPath = 0x12000044,
BcdLibraryBoolean_GraphicsModeDisabled = 0x16000046,
BcdLibraryInteger_ConfigAccessPolicy = 0x15000047,
BcdLibraryBoolean_DisableIntegrityChecks = 0x16000048,
BcdLibraryBoolean_AllowPrereleaseSignatures = 0x16000049,
BcdLibraryString_FontPath = 0x1200004A,
BcdLibraryInteger_SiPolicy = 0x1500004B,
BcdLibraryInteger_FveBandId = 0x1500004C,
BcdLibraryBoolean_ConsoleExtendedInput = 0x16000050,
BcdLibraryInteger_GraphicsResolution = 0x15000052,
BcdLibraryBoolean_RestartOnFailure = 0x16000053,
BcdLibraryBoolean_GraphicsForceHighestMode = 0x16000054,
BcdLibraryBoolean_IsolatedExecutionContext = 0x16000060,
BcdLibraryBoolean_BootUxDisable = 0x1600006C,
BcdLibraryBoolean_BootShutdownDisabled = 0x16000074,
BcdLibraryIntegerList_AllowedInMemorySettings = 0x17000077,
BcdLibraryBoolean_ForceFipsCrypto = 0x16000079,
BcdOSLoaderDevice_OSDevice = 0x21000001,
BcdOSLoaderString_SystemRoot = 0x22000002,
BcdOSLoaderObject_AssociatedResumeObject = 0x23000003,
BcdOSLoaderBoolean_DetectKernelAndHal = 0x26000010,
BcdOSLoaderString_KernelPath = 0x22000011,
BcdOSLoaderString_HalPath = 0x22000012,
BcdOSLoaderString_DbgTransportPath = 0x22000013,
BcdOSLoaderInteger_NxPolicy = 0x25000020,
BcdOSLoaderInteger_PAEPolicy = 0x25000021,
BcdOSLoaderBoolean_WinPEMode = 0x26000022,
BcdOSLoaderBoolean_DisableCrashAutoReboot = 0x26000024,
BcdOSLoaderBoolean_UseLastGoodSettings = 0x26000025,
BcdOSLoaderBoolean_AllowPrereleaseSignatures = 0x26000027,
BcdOSLoaderBoolean_NoLowMemory = 0x26000030,
BcdOSLoaderInteger_RemoveMemory = 0x25000031,
BcdOSLoaderInteger_IncreaseUserVa = 0x25000032,
BcdOSLoaderBoolean_UseVgaDriver = 0x26000040,
BcdOSLoaderBoolean_DisableBootDisplay = 0x26000041,
BcdOSLoaderBoolean_DisableVesaBios = 0x26000042,
BcdOSLoaderBoolean_DisableVgaMode = 0x26000043,
BcdOSLoaderInteger_ClusterModeAddressing = 0x25000050,
BcdOSLoaderBoolean_UsePhysicalDestination = 0x26000051,
BcdOSLoaderInteger_RestrictApicCluster = 0x25000052,
BcdOSLoaderBoolean_UseLegacyApicMode = 0x26000054,
BcdOSLoaderInteger_X2ApicPolicy = 0x25000055,
BcdOSLoaderBoolean_UseBootProcessorOnly = 0x26000060,
BcdOSLoaderInteger_NumberOfProcessors = 0x25000061,
BcdOSLoaderBoolean_ForceMaximumProcessors = 0x26000062,
BcdOSLoaderBoolean_ProcessorConfigurationFlags = 0x25000063,
BcdOSLoaderBoolean_MaximizeGroupsCreated = 0x26000064,
BcdOSLoaderBoolean_ForceGroupAwareness = 0x26000065,
BcdOSLoaderInteger_GroupSize = 0x25000066,
BcdOSLoaderInteger_UseFirmwarePciSettings = 0x26000070,
BcdOSLoaderInteger_MsiPolicy = 0x25000071,
BcdOSLoaderInteger_SafeBoot = 0x25000080,
BcdOSLoaderBoolean_SafeBootAlternateShell = 0x26000081,
BcdOSLoaderBoolean_BootLogInitialization = 0x26000090,
BcdOSLoaderBoolean_VerboseObjectLoadMode = 0x26000091,
BcdOSLoaderBoolean_KernelDebuggerEnabled = 0x260000a0,
BcdOSLoaderBoolean_DebuggerHalBreakpoint = 0x260000a1,
BcdOSLoaderBoolean_UsePlatformClock = 0x260000A2,
BcdOSLoaderBoolean_ForceLegacyPlatform = 0x260000A3,
BcdOSLoaderInteger_TscSyncPolicy = 0x250000A6,
BcdOSLoaderBoolean_EmsEnabled = 0x260000b0,
BcdOSLoaderInteger_DriverLoadFailurePolicy = 0x250000c1,
BcdOSLoaderInteger_BootMenuPolicy = 0x250000C2,
BcdOSLoaderBoolean_AdvancedOptionsOneTime = 0x260000C3,
BcdOSLoaderInteger_BootStatusPolicy = 0x250000E0,
BcdOSLoaderBoolean_DisableElamDrivers = 0x260000E1,
BcdOSLoaderInteger_HypervisorLaunchType = 0x250000F0,
BcdOSLoaderBoolean_HypervisorDebuggerEnabled = 0x260000F2,
BcdOSLoaderInteger_HypervisorDebuggerType = 0x250000F3,
BcdOSLoaderInteger_HypervisorDebuggerPortNumber = 0x250000F4,
BcdOSLoaderInteger_HypervisorDebuggerBaudrate = 0x250000F5,
BcdOSLoaderInteger_HypervisorDebugger1394Channel = 0x250000F6,
BcdOSLoaderInteger_BootUxPolicy = 0x250000F7,
BcdOSLoaderString_HypervisorDebuggerBusParams = 0x220000F9,
BcdOSLoaderInteger_HypervisorNumProc = 0x250000FA,
BcdOSLoaderInteger_HypervisorRootProcPerNode = 0x250000FB,
BcdOSLoaderBoolean_HypervisorUseLargeVTlb = 0x260000FC,
BcdOSLoaderInteger_HypervisorDebuggerNetHostIp = 0x250000FD,
BcdOSLoaderInteger_HypervisorDebuggerNetHostPort = 0x250000FE,
BcdOSLoaderInteger_TpmBootEntropyPolicy = 0x25000100,
BcdOSLoaderString_HypervisorDebuggerNetKey = 0x22000110,
BcdOSLoaderBoolean_HypervisorDebuggerNetDhcp = 0x26000114,
BcdOSLoaderInteger_HypervisorIommuPolicy = 0x25000115,
BcdOSLoaderInteger_XSaveDisable = 0x2500012b,
BcdBootMgrObjectList_DisplayOrder = 0x24000001,
BcdBootMgrObjectList_BootSequence = 0x24000002,
BcdBootMgrObject_DefaultObject = 0x23000003,
BcdBootMgrInteger_Timeout = 0x25000004,
BcdBootMgrBoolean_AttemptResume = 0x26000005,
BcdBootMgrObject_ResumeObject = 0x23000006,
BcdBootMgrObjectList_ToolsDisplayOrder = 0x24000010,
BcdBootMgrBoolean_DisplayBootMenu = 0x26000020,
BcdBootMgrBoolean_NoErrorDisplay = 0x26000021,
BcdBootMgrDevice_BcdDevice = 0x21000022,
BcdBootMgrString_BcdFilePath = 0x22000023,
BcdBootMgrBoolean_ProcessCustomActionsFirst = 0x26000028,
BcdBootMgrIntegerList_CustomActionsList = 0x27000030,
BcdBootMgrBoolean_PersistBootSequence = 0x26000031
}
struct ValueRange32 {
public uint Default;
public uint Start, End;
public ValueRange32(byte[] BinaryData) {
Default = BitConverter.ToUInt32(BinaryData, 2);
Start = BitConverter.ToUInt32(BinaryData, 6);
End = BitConverter.ToUInt32(BinaryData, 10);
}
}
struct ValueChoice32 {
public uint Default;
public uint[] Choices;
public ValueChoice32(byte[] BinaryData) {
Default = BitConverter.ToUInt32(BinaryData, 2);
var count = BitConverter.ToUInt16(BinaryData,6);
Choices = new uint[count];
for (int i = 0; i < count; i++) Choices[i] = BitConverter.ToUInt32(BinaryData,8 + (i * sizeof(uint)));
}
}
struct ValueRange64 {
public ulong Default;
public ulong Start, End;
public ValueRange64(byte[] BinaryData) {
Default = BitConverter.ToUInt64(BinaryData, 2);
Start = BitConverter.ToUInt64(BinaryData, 10);
End = BitConverter.ToUInt64(BinaryData, 18);
}
}
struct ValueChoice64 {
public ulong Default;
public ulong[] Choices;
public ValueChoice64(byte[] BinaryData) {
Default = BitConverter.ToUInt64(BinaryData, 2);
var count = BitConverter.ToUInt16(BinaryData,10);
Choices = new ulong[count];
for (int i = 0; i < count; i++) Choices[i] = BitConverter.ToUInt64(BinaryData,12 + (i * sizeof(ulong)));
}
}
class PolicyValue {
private int Type;
private bool BitLocker;
private bool VBS;
public object data;
public PolicyValue(byte[] BinaryData) {
Type = BinaryData[0] & 0x1f;
BitLocker = (BinaryData[0] & 0x20) != 0;
VBS = (BinaryData[0] & 0x40) != 0;
byte[] RawData;
switch (Type) {
case 0: // string
RawData = new byte[BitConverter.ToUInt16(BinaryData,2)];
Buffer.BlockCopy(BinaryData,4,RawData,0,RawData.Length);
data = Encoding.Unicode.GetString(RawData);
break;
case 1: // bool
ushort bval = BitConverter.ToUInt16(BinaryData,2);
bool val = (bval != 0);
data = val;
break;
case 2: // dword
data = BitConverter.ToUInt32(BinaryData,2);
break;
case 3: // dword range
data = new ValueRange32(BinaryData);
break;
case 4: // dword choice
data = new ValueChoice32(BinaryData);
break;
case 5: // qword
data = BitConverter.ToUInt64(BinaryData,2);
break;
case 6: // qword range
data = new ValueRange64(BinaryData);
break;
case 7: // qword choice
data = new ValueChoice64(BinaryData);
break;
case 8: // option
data = BitConverter.ToUInt16(BinaryData,2);
break;
case 10: // binary
RawData = new byte[BitConverter.ToUInt16(BinaryData,2)];
Buffer.BlockCopy(BinaryData,4,RawData,0,RawData.Length);
data = RawData;
break;
default:
throw new Exception("Unknown type "+Type);
}
}
public override string ToString() {
StringBuilder sb;
bool first = true;
string ret = "";
if (BitLocker) ret = "if BitLocker - ";
if (VBS) ret += "if VBS - ";
switch (Type) {
case 0:
return ret+"string - "+(string)data;
case 1:
return ret+"bool - "+( (bool)data ).ToString();
case 2:
return ret+"dword - 0x"+( (uint)data ).ToString("X");
case 3:
var r32 = (ValueRange32)data;
return ret+"range32: 0x"+r32.Start.ToString("X")+"-"+r32.End.ToString("X")+" ("+r32.Default.ToString("X")+")";
case 4:
var v32 = (ValueChoice32)data;
sb = new StringBuilder("choice32 - ");
foreach (var choice in v32.Choices) {
if (!first) sb.Append(';');
else first = false;
sb.Append("0x");
sb.Append(choice.ToString("X"));
}
sb.Append(" (0x");
sb.Append(v32.Default.ToString("X"));
sb.Append(')');
return ret+sb.ToString();
case 5:
return ret+"qword - 0x"+( (ulong)data ).ToString("X");
case 6:
var r64 = (ValueRange64)data;
return ret+"range64: 0x"+r64.Start.ToString("X")+"-"+r64.End.ToString("X")+" ("+r64.Default.ToString("X")+")";
case 7:
var v64 = (ValueChoice64)data;
sb = new StringBuilder("choice64 - ");
foreach (var choice in v64.Choices) {
if (!first) sb.Append(';');
else first = false;
sb.Append("0x");
sb.Append(choice.ToString("X"));
}
sb.Append(" (0x");
sb.Append(v64.Default.ToString("X"));
sb.Append(')');
return ret+sb.ToString();
case 8:
bool present = (ushort)data != 0;
return ret+"option - " + (present ? "no operation" : "always not found");
case 10:
return ret+"byte[] - "+BitConverter.ToString( (byte[])data ).Replace("-","");
default:
throw new Exception("Unknown type "+Type);
}
}
}
class SecureBootPolicy {
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct SBPHeader {
public ushort FormatVersion;
public uint PolicyVersion;
public Guid PolicyPublisher;
public ushort CanUpdateCount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct SBPFlags {
public uint OptionFlags;
public ushort BcdRulesCount;
public ushort RegistryRulesCount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BcdRuleStruct {
public uint ObjectType;
public uint Element;
public uint ValueOffset;
}
class BcdRule {
uint ObjectType;
uint Element;
PolicyValue Value;
public BcdRule(BcdRuleStruct s,byte[] ValueTable) {
ObjectType = s.ObjectType;
Element = s.Element;
byte[] RawData = new byte[ValueTable.Length - s.ValueOffset];
Buffer.BlockCopy(ValueTable,(int)s.ValueOffset,RawData,0,RawData.Length);
Value = new PolicyValue(RawData);
}
public override string ToString() {
var sb = new StringBuilder();
if (ObjectType == 0) sb.AppendLine(" ObjectType: any");
else sb.AppendLine(" ObjectType: 0x"+ObjectType.ToString("X"));
sb.AppendLine(string.Format(" Element: {0} (0x{1})", (BcdElementTypes)Element, Element.ToString("X")));
sb.AppendLine(" Value: "+Value.ToString());
return sb.ToString();
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct RegistryRuleStruct {
public uint RootKey;
public uint SubkeyNameOffset;
public uint ValueNameOffset;
public uint ValueOffset;
}
class RegistryRule {
uint RootKey;
string SubkeyName;
string ValueName;
PolicyValue Value;
public RegistryRule(RegistryRuleStruct s,byte[] ValueTable) {
RootKey = s.RootKey;
byte[] RawData = new byte[BitConverter.ToUInt16(ValueTable,(int)s.SubkeyNameOffset)];
Buffer.BlockCopy(ValueTable,(int)s.SubkeyNameOffset + 2,RawData,0,RawData.Length);
SubkeyName = Encoding.Unicode.GetString(RawData);
RawData = new byte[BitConverter.ToUInt16(ValueTable,(int)s.ValueNameOffset)];
Buffer.BlockCopy(ValueTable,(int)s.ValueNameOffset + 2,RawData,0,RawData.Length);
ValueName = Encoding.Unicode.GetString(RawData);
RawData = new byte[ValueTable.Length - s.ValueOffset];
Buffer.BlockCopy(ValueTable,(int)s.ValueOffset,RawData,0,RawData.Length);
Value = new PolicyValue(RawData);
}
public override string ToString() {
var sb = new StringBuilder();
sb.AppendLine(" Path: 0x"+RootKey.ToString("X")+" "+SubkeyName+"\\"+ValueName);
sb.AppendLine(" Value: "+Value.ToString());
return sb.ToString();
}
}
private SBPHeader Header;
ushort FormatVersion { get { return Header.FormatVersion; } set { Header.FormatVersion = value; } }
uint PolicyVersion { get { return Header.PolicyVersion; } set { Header.PolicyVersion = value; } }
Guid PolicyPublisher { get { return Header.PolicyPublisher; } set { Header.PolicyPublisher = value; } }
List<Guid> CanUpdate;
private SBPFlags Flags;
uint OptionFlags { get { return Flags.OptionFlags; } set { Flags.OptionFlags = value; } }
List<BcdRule> BcdRules;
List<RegistryRule> RegistryRules;
byte[] Sha256Hash;
private static Dictionary<int, string> s_OptionFlagsBits = new Dictionary<int, string>() {
{ 0, "Allowed:Prerelease Signers" },
{ 1, "Allowed:Kits Signers" },
{ 2, "Enabled:UMCI" },
{ 3, "Disabled:Winload Driver Signature Enforcement Menu" },
{ 4, "Enabled:UMCI Debug Options" },
{ 5, "Enabled:UMCI Cache Data Volumes" },
{ 6, "Policy intended for Windows Phone - Allowed:SeQuerySigningPolicy Extension" },
{ 7, "Required:WHQL" },
{ 8, "Enabled:Filter Edited Boot Options" },
{ 9, "Disabled:UMCI USN 0 Protection" },
{ 10, "Disabled:Winload Debugging Mode Menu" },
{ 14, "Enabled:UMCI Trust USN 0" },
{ 20, "Enabled:Flight Signing" },
{ 21, "Ignored:Flight Signing EKU" }
};
public SecureBootPolicy(string filename) {
byte[] RawPolicy;
try {
var Policy = new Asn1Parser();
Policy.LoadData(filename);
RawPolicy = Policy.GetNodeByPath("/1/0/2/1/0").Data;
} catch (Exception) {
RawPolicy = File.ReadAllBytes(filename);
}
Sha256Hash = new SHA256Managed().ComputeHash(RawPolicy);
int offset = 0;
Header = ByteArrayToStructure<SBPHeader>(RawPolicy,ref offset);
CanUpdate = new List<Guid>();
for (int i = 0; i < Header.CanUpdateCount; i++)
CanUpdate.Add(ByteArrayToStructure<Guid>(RawPolicy,ref offset));
Flags = ByteArrayToStructure<SBPFlags>(RawPolicy,ref offset);
var BcdRulesRaw = new List<BcdRuleStruct>();
for (int i = 0; i < Flags.BcdRulesCount; i++) {
BcdRulesRaw.Add(ByteArrayToStructure<BcdRuleStruct>(RawPolicy,ref offset));
}
var RegistryRulesRaw = new List<RegistryRuleStruct>();
for (int i = 0; i < Flags.RegistryRulesCount; i++)
RegistryRulesRaw.Add(ByteArrayToStructure<RegistryRuleStruct>(RawPolicy,ref offset));
byte[] ValueTable = new byte[(RawPolicy.Length - offset)];
Buffer.BlockCopy(RawPolicy,offset,ValueTable,0,ValueTable.Length);
BcdRules = new List<BcdRule>();
foreach (BcdRuleStruct BcdRaw in BcdRulesRaw)
BcdRules.Add(new BcdRule(BcdRaw,ValueTable));
RegistryRules = new List<RegistryRule>();
foreach (RegistryRuleStruct RegRaw in RegistryRulesRaw)
RegistryRules.Add(new RegistryRule(RegRaw,ValueTable));
}
public string OptionFlagsToString() {
var sb = new StringBuilder();
var bits = OptionFlags;
uint bit = 1 << 0;
for (int i = 0; i < 32; i++, bit <<= 1) {
string flagDesc;
if ((bits & bit) != 0) {
if (!s_OptionFlagsBits.TryGetValue(i, out flagDesc))
flagDesc = string.Format("(unknown bit {0})", i);
sb.AppendLine(" " + flagDesc);
}
}
return sb.ToString();
}
public override string ToString() {
var sb = new StringBuilder();
sb.AppendLine("SHA256 hash: "+BitConverter.ToString(Sha256Hash).Replace("-",""));
sb.AppendLine("Format version: "+FormatVersion);
sb.AppendLine("Policy version: "+PolicyVersion);
sb.AppendLine("Policy publisher: "+PolicyPublisher.ToString());
sb.AppendLine("Option flags: 0x"+OptionFlags.ToString("X"));
sb.Append(OptionFlagsToString());
sb.AppendLine("CanUpdate GUIDs: "+CanUpdate.Count);
foreach (Guid cu in CanUpdate)
sb.AppendLine(" "+cu.ToString());
sb.AppendLine("BCD rules: "+BcdRules.Count);
foreach (var Bcd in BcdRules)
sb.AppendLine(" "+Bcd.ToString());
sb.AppendLine("Registry rules: "+RegistryRules.Count);
foreach (var Reg in RegistryRules)
sb.AppendLine(" "+Reg.ToString());
return sb.ToString();
}
private T ByteArrayToStructure<T>(byte[] bytes,ref int position) where T: struct {
int length = Marshal.SizeOf(typeof(T));
IntPtr ptr = Marshal.AllocHGlobal(length);
Marshal.Copy(bytes, 0, ptr, length);
T stuff = (T)Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(bytes, position), typeof(T));
position += length;
Marshal.FreeHGlobal(ptr);
return stuff;
}
}
class Cli {
public static void Main(string[] args) {
foreach (string arg in args) {
Console.WriteLine(new SecureBootPolicy(arg).ToString());
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment