#!/usr/bin/env python3
import hivex
import argparse
import struct

from hivex import Hivex
from hivex.hive_types import *


# Docs:
#
# https://www.geoffchappell.com/notes/windows/boot/bcd/objects.htm
# https://learn.microsoft.com/en-us/previous-versions/windows/desktop/bcd/bcdbootmgrelementtypes

#print(f"Root: {root}")


BCD_Enumerations = {
    "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,


    "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,

    "BcdDeviceInteger_RamdiskImageOffset"            : 0x35000001,
    "BcdDeviceInteger_TftpClientPort"                : 0x35000002,
    "BcdDeviceInteger_SdiDevice"                     : 0x31000003,
    "BcdDeviceInteger_SdiPath"                       : 0x32000004,
    "BcdDeviceInteger_RamdiskImageLength"            : 0x35000005,
    "BcdDeviceBoolean_RamdiskExportAsCd"             : 0x36000006,
    "BcdDeviceInteger_RamdiskTftpBlockSize"          : 0x36000007,
    "BcdDeviceInteger_RamdiskTftpWindowSize"         : 0x36000008,
    "BcdDeviceBoolean_RamdiskMulticastEnabled"       : 0x36000009,
    "BcdDeviceBoolean_RamdiskMulticastTftpFallback"  : 0x3600000A,
    "BcdDeviceBoolean_RamdiskTftpVarWindow"          : 0x3600000B,

    "BcdMemDiagInteger_PassCount"                    : 0x25000001,
    "BcdMemDiagInteger_FailureCount"                 : 0x25000003,

    "Reserved1"                                      : 0x21000001,
    "Reserved2"                                      : 0x22000002,
    "BcdResumeBoolean_UseCustomSettings"             : 0x26000003,
    "BcdResumeDevice_AssociatedOsDevice"             : 0x21000005,
    "BcdResumeBoolean_DebugOptionEnabled"            : 0x26000006,
    "BcdResumeInteger_BootMenuPolicy"                : 0x25000008,

    "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
}


def format_value(bcd, bcd_value):

    name  = bcd.value_key(bcd_value)
    (type, length) = bcd.value_type(bcd_value)

    typename = ""
    str_value = ""
    if type == REG_SZ:
        typename = "SZ"
        str_value = bcd.value_string(bcd_value)
    elif type == REG_DWORD:
        typename = "DWORD"
        dval = bcd.value_dword(bcd_value)

        str_value = hex(dval) + " (" + str(bcd.value_dword(bcd_value)) + ")"
    elif type == REG_BINARY:
        typename = "BIN"
        (length, value) = bcd.value_value(bcd_value)
        str_value = value.hex()
    elif type == REG_DWORD_BIG_ENDIAN:
        typename = "DWORD_BE"
    elif type == REG_EXPAND_SZ:
        typename = "EXPAND SZ"
    elif type == REG_FULL_RESOURCE_DESCRIPTOR:
        typename = "RES DESC"
    elif type == REG_LINK:
        typename = "LINK"
    elif type == REG_MULTI_SZ:
        typename = "MULTISZ"
        (length, str_value) = bcd.value_value(bcd_value)
        str_value = str_value.decode('utf-16le')
        str_value = str_value.replace("\0", ";")
        #value = ";".join("\0".split(value))
    elif type == REG_NONE:
        typename = "NONE"
    elif type == REG_QWORD:
        typename = "QWORD"
    elif type == REG_RESOURCE_LIST:
        typename = "RES LIST"
    elif type == REG_RESOURCE_REQUIREMENTS_LIST:
        typename = "REQ LIST"
    else:
        typename = str(type)
        str_value = "???"


    return (typename, length, str_value)

def dump_all(root, depth = 0):

    padding = "\t" * depth

    children = bcd.node_children(root)

    if len(children) > 0:

        for child in children:
            name = bcd.node_name(child)
            print(f"{padding}{name}")

            dump_all(child, depth + 1)
        #   print(f"Child: {child}")

            #print(f"Values: {num_vals}")
        return





    values = bcd.node_values(root)
    #print(f"Value list: {values}")

    for v in values:
        (type_name, length, str_value) = format_value(bcd, v)
        name  = bcd.value_key(v)

        print(f"{padding}{name: <16}: [{type_name: <10}]; ({length: < 4}) {str_value}")


class WindowsBCD:
    def __init__(self, filename):
        self.filename = filename
        self.bcd = Hivex(filename)

    def dump(self, root=None, depth = 0):
        padding = "\t" * depth

        if root is None:
            root = self.bcd.root()

        children = self.bcd.node_children(root)

        if len(children) > 0:
            for child in children:
                name = self.bcd.node_name(child)
                print(f"{padding}{name}")

                self.dump(child, depth + 1)
            return

        values = self.bcd.node_values(root)

        for v in values:
            (type_name, length, str_value) = format_value(self.bcd, v)
            name  = self.bcd.value_key(v)

            print(f"{padding}{name: <16}: [{type_name: <10}]; ({length: < 4}) {str_value}")

    def list(self):
        root = self.bcd.root()
        objects = self.bcd.node_get_child(root, "Objects")

        for child in self.bcd.node_children(objects):
            entry_id = self.bcd.node_name(child)

            elements = self.bcd.node_get_child(child, "Elements")
            description_entry = self.bcd.node_get_child(elements, "12000004")

            if description_entry:
                values = self.bcd.node_values(description_entry)
                if values:
                    (type_name, length, str_value) = format_value(self.bcd, values[0])
                    print(f"{entry_id}: {str_value}")
                else:
                    print(f"{entry_id}: [no description value!?]")


            appdevice_entry = self.bcd.node_get_child(elements, "11000001")

            if appdevice_entry:
                values = self.bcd.node_values(appdevice_entry)
                (length, data) = self.bcd.value_value(values[0])
                hex = data.hex()
                print(f"LEN: {length}, HEX: {hex}, RAW: {data}")
                if len(data) > 10:
                    etype = struct.unpack_from('<I', data, offset = 16)
                    print(f"Type: {etype}")



            else:
                print(f"{entry_id}: [no description entry 12000004]")


parser = argparse.ArgumentParser(
    prog="Windows BCD parser",
    description="Parses the BCD",
)

parser.add_argument("--db", type=str, metavar='BCD file', help="Database to use")
parser.add_argument("--dump", action='store_true', help="Dumps the specified database")
parser.add_argument("--list", action='store_true', help="Lists boot entries in the specified database")

args = parser.parse_args()

bcdobj = WindowsBCD(args.db)

if args.dump:
    # "/home/vadim/opengnsys/winboot/boot-copy/EFI/Microsoft/Boot/BCD"
    #bcd = Hivex(args.dump)

    #root = bcd.root()
    #dump_all(root)
    bcdobj.dump()
elif args.list:
    bcdobj.list()
