BlackLotus_Ioc_scan_Powershell/TCGLogTools.psm1

1793 lines
93 KiB
PowerShell

# APIs required to interface with the TPM service
Add-Type -ErrorAction Stop -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
namespace TPMBaseServices {
public enum TPM_VERSION {
TPM_VERSION_UNKNOWN = 0,
TPM_VERSION_12,
TPM_VERSION_20
}
public enum TPM_IFTYPE : uint {
TPM_IFTYPE_UNKNOWN = 0,
TPM_IFTYPE_1,
TPM_IFTYPE_TRUSTZONE,
TPM_IFTYPE_HW,
TPM_IFTYPE_EMULATOR,
TPM_IFTYPE_SPB
}
public enum TBS_TCGLOG : uint {
TBS_TCGLOG_SRTM_CURRENT = 0,
TBS_TCGLOG_DRTM_CURRENT,
TBS_TCGLOG_SRTM_BOOT,
TBS_TCGLOG_SRTM_RESUME
}
public struct TPM_DEVICE_INFO {
public uint structVersion;
public TPM_VERSION tpmVersion;
public TPM_IFTYPE tpmInterfaceType;
public uint tpmImpRevision;
}
public class UnsafeNativeMethods {
[DllImport("tbs.dll")]
public static extern uint Tbsi_GetDeviceInfo(ulong Size, out TPM_DEVICE_INFO Info);
// This API is much more flexible than Tbsi_Get_TCG_Log because you don't need to get a TBS context.
[DllImport("tbs.dll")]
public static extern uint Tbsi_Get_TCG_Log_Ex(TBS_TCGLOG logType, IntPtr pOutputBuf, ref uint OutputBufLen);
}
}
'@
#region: enums required for multiple functions
# Used to display friendly error messages if a TBS function fails.
$Script:TBSReturnCodes = @{
([UInt32] 2150121473) = 'An internal software error occurred.'
([UInt32] 2150121474) = 'One or more parameter values are not valid.'
([UInt32] 2150121475) = 'A specified output pointer is bad.'
([UInt32] 2150121476) = 'The specified context handle does not refer to a valid context.'
([UInt32] 2150121477) = 'The specified output buffer is too small.'
([UInt32] 2150121478) = 'An error occurred while communicating with the TPM.'
([UInt32] 2150121479) = 'A context parameter that is not valid was passed when attempting to create a TBS context.'
([UInt32] 2150121480) = 'The TBS service is not running and could not be started.'
([UInt32] 2150121481) = 'A new context could not be created because there are too many open contexts.'
([UInt32] 2150121482) = 'A new virtual resource could not be created because there are too many open virtual resources.'
([UInt32] 2150121483) = 'The TBS service has been started but is not yet running.'
([UInt32] 2150121484) = 'The physical presence interface is not supported.'
([UInt32] 2150121485) = 'The command was canceled.'
([UInt32] 2150121486) = 'The input or output buffer is too large.'
([UInt32] 2150121487) = 'A compatible Trusted Platform Module (TPM) Security Device cannot be found on this computer.'
([UInt32] 2150121488) = 'The TBS service has been disabled.'
([UInt32] 2150121489) = 'The TBS event log is not available.'
([UInt32] 2150121490) = 'The caller does not have the appropriate rights to perform the requested operation.'
([UInt32] 2150121491) = 'The TPM provisioning action is not allowed by the specified flags.'
([UInt32] 2150121492) = 'The Physical Presence Interface of this firmware does not support the requested method.'
([UInt32] 2150121493) = 'The requested TPM OwnerAuth value was not found.'
# 2150121493 may be a typo in the docs for the below return code. Need to reverse tbs.dll to confirm
([UInt32] 2150121494) = "The TPM provisioning did not complete. For more information on completing the provisioning, call the Win32_Tpm WMI method for provisioning the TPM ('Provision') and check the returned information."
}
# Obtained from wbcl.h in the WDK
# These refer to Windows-specific data types for PCR 12-14 and -1 (TrustPoint)
$Script:SIPAEventMapping = @{
# SIPAEVENTTYPE_CONTAINER
# All of these types will contain embedded event data
0x40010001 = 'TrustBoundary' # SIPAEVENT_TRUSTBOUNDARY
0x40010002 = 'ELAMAggregation' # SIPAEVENT_ELAM_AGGREGATION
0x40010003 = 'LoadedModuleAggregation' # SIPAEVENT_LOADEDMODULE_AGGREGATION
0xC0010004 = 'TrustpointAggregation' # SIPAEVENT_TRUSTPOINT_AGGREGATION
0x40010005 = 'KSRAggregation' # SIPAEVENT_KSR_AGGREGATION
0x40010006 = 'KSRSignedMeasurementAggregation' # SIPAEVENT_KSR_SIGNED_MEASUREMENT_AGGREGATION
# SIPAEVENTTYPE_INFORMATION
0x00020001 = 'Information' # SIPAEVENT_INFORMATION
0x00020002 = 'BootCounter' # SIPAEVENT_BOOTCOUNTER
0x00020003 = 'TransferControl' # SIPAEVENT_TRANSFER_CONTROL
0x00020004 = 'ApplicationReturn' # SIPAEVENT_APPLICATION_RETURN
0x00020005 = 'BitlockerUnlock' # SIPAEVENT_BITLOCKER_UNLOCK
0x00020006 = 'EventCounter' # SIPAEVENT_EVENTCOUNTER
0x00020007 = 'CounterID' # SIPAEVENT_COUNTERID
0x00020008 = 'MORBitNotCancelable' # SIPAEVENT_MORBIT_NOT_CANCELABLE
0x00020009 = 'ApplicationSVN' # SIPAEVENT_APPLICATION_SVN
0x0002000A = 'SVNChainStatus' # SIPAEVENT_SVN_CHAIN_STATUS
0x0002000B = 'MORBitAPIStatus' # SIPAEVENT_MORBIT_API_STATUS
# SIPAEVENTTYPE_PREOSPARAMETER
0x00040001 = 'BootDebugging' # SIPAEVENT_BOOTDEBUGGING
0x00040002 = 'BootRevocationList' # SIPAEVENT_BOOT_REVOCATION_LIST
# SIPAEVENTTYPE_OSPARAMETER
0x00050001 = 'OSKernelDebug' # SIPAEVENT_OSKERNELDEBUG
0x00050002 = 'CodeIntegrity' # SIPAEVENT_CODEINTEGRITY
0x00050003 = 'TestSigning' # SIPAEVENT_TESTSIGNING
0x00050004 = 'DataExecutionPrevention' # SIPAEVENT_DATAEXECUTIONPREVENTION
0x00050005 = 'SafeMode' # SIPAEVENT_SAFEMODE
0x00050006 = 'WinPE' # SIPAEVENT_WINPE
0x00050007 = 'PhysicalAddressExtension' # SIPAEVENT_PHYSICALADDRESSEXTENSION
0x00050008 = 'OSDevice' # SIPAEVENT_OSDEVICE
0x00050009 = 'SystemRoot' # SIPAEVENT_SYSTEMROOT
0x0005000A = 'HypervisorLaunchType' # SIPAEVENT_HYPERVISOR_LAUNCH_TYPE
0x0005000B = 'HypervisorPath' # SIPAEVENT_HYPERVISOR_PATH
0x0005000C = 'HypervisorIOMMUPolicy' # SIPAEVENT_HYPERVISOR_IOMMU_POLICY
0x0005000D = 'HypervisorDebug' # SIPAEVENT_HYPERVISOR_DEBUG
0x0005000E = 'DriverLoadPolicy' # SIPAEVENT_DRIVER_LOAD_POLICY
0x0005000F = 'SIPolicy' # SIPAEVENT_SI_POLICY
0x00050010 = 'HypervisorMMIONXPolicy' # SIPAEVENT_HYPERVISOR_MMIO_NX_POLICY
0x00050011 = 'HypervisorMSRFilterPolicy' # SIPAEVENT_HYPERVISOR_MSR_FILTER_POLICY
0x00050012 = 'VSMLaunchType' # SIPAEVENT_VSM_LAUNCH_TYPE
0x00050013 = 'OSRevocationList' # SIPAEVENT_OS_REVOCATION_LIST
0x00050020 = 'VSMIDKInfo' # SIPAEVENT_VSM_IDK_INFO
0x00050021 = 'FlightSigning' # SIPAEVENT_FLIGHTSIGNING
0x00050022 = 'PagefileEncryptionEnabled' # SIPAEVENT_PAGEFILE_ENCRYPTION_ENABLED
0x00050023 = 'VSMIDKSInfo' # SIPAEVENT_VSM_IDKS_INFO
0x00050024 = 'HibernationDisabled' # SIPAEVENT_HIBERNATION_DISABLED
0x00050025 = 'DumpsDisabled' # SIPAEVENT_DUMPS_DISABLED
0x00050026 = 'DumpEncryptionEnabled' # SIPAEVENT_DUMP_ENCRYPTION_ENABLED
0x00050027 = 'DumpEncryptionKeyDigest' # SIPAEVENT_DUMP_ENCRYPTION_KEY_DIGEST
0x00050028 = 'LSAISOConfig' # SIPAEVENT_LSAISO_CONFIG
# SIPAEVENTTYPE_AUTHORITY
0x00060001 = 'NoAuthority' # SIPAEVENT_NOAUTHORITY
0x00060002 = 'AuthorityPubKey' # SIPAEVENT_AUTHORITYPUBKEY
# SIPAEVENTTYPE_LOADEDIMAGE
0x00070001 = 'FilePath' # SIPAEVENT_FILEPATH
0x00070002 = 'ImageSize' # SIPAEVENT_IMAGESIZE
0x00070003 = 'HashAlgorithmID' # SIPAEVENT_HASHALGORITHMID
0x00070004 = 'AuthenticodeHash' # SIPAEVENT_AUTHENTICODEHASH
0x00070005 = 'AuthorityIssuer' # SIPAEVENT_AUTHORITYISSUER
0x00070006 = 'AuthoritySerial' # SIPAEVENT_AUTHORITYSERIAL
0x00070007 = 'ImageBase' # SIPAEVENT_IMAGEBASE
0x00070008 = 'AuthorityPublisher' # SIPAEVENT_AUTHORITYPUBLISHER
0x00070009 = 'AuthoritySHA1Thumbprint' # SIPAEVENT_AUTHORITYSHA1THUMBPRINT
0x0007000A = 'ImageValidated' # SIPAEVENT_IMAGEVALIDATED
0x0007000B = 'ModuleSVN' # SIPAEVENT_MODULE_SVN
# SIPAEVENTTYPE_TRUSTPOINT
0x80080001 = 'Quote' # SIPAEVENT_QUOTE
0x80080002 = 'QuoteSignature' # SIPAEVENT_QUOTESIGNATURE
0x80080003 = 'AIKID' # SIPAEVENT_AIKID
0x80080004 = 'AIKPubDigest' # SIPAEVENT_AIKPUBDIGEST
# SIPAEVENTTYPE_ELAM
0x00090001 = 'ELAMKeyname' # SIPAEVENT_ELAM_KEYNAME
0x00090002 = 'ELAMConfiguration' # SIPAEVENT_ELAM_CONFIGURATION
0x00090003 = 'ELAMPolicy' # SIPAEVENT_ELAM_POLICY
0x00090004 = 'ELAMMeasured' # SIPAEVENT_ELAM_MEASURED
# SIPAEVENTTYPE_VBS
0x000A0001 = 'VBSVSMRequired' # SIPAEVENT_VBS_VSM_REQUIRED
0x000A0002 = 'VBSSecurebootRequired' # SIPAEVENT_VBS_SECUREBOOT_REQUIRED
0x000A0003 = 'VBSIOMMURequired' # SIPAEVENT_VBS_IOMMU_REQUIRED
0x000A0004 = 'VBSNXRequired' # SIPAEVENT_VBS_MMIO_NX_REQUIRED
0x000A0005 = 'VBSMSRFilteringRequired' # SIPAEVENT_VBS_MSR_FILTERING_REQUIRED
0x000A0006 = 'VBSMandatoryEnforcement' # SIPAEVENT_VBS_MANDATORY_ENFORCEMENT
0x000A0007 = 'VBSHVCIPolicy' # SIPAEVENT_VBS_HVCI_POLICY
0x000A0008 = 'VBSMicrosoftBootChainRequired' # SIPAEVENT_VBS_MICROSOFT_BOOT_CHAIN_REQUIRED
# SIPAEVENTTYPE_KSR
0x000B0001 = 'KSRSignature' # SIPAEVENT_KSR_SIGNATURE
}
$Script:DigestAlgorithmMapping = @{
[UInt16] 0 = 'TPM_ALG_ERROR'
[UInt16] 1 = 'TPM_ALG_RSA'
[UInt16] 4 = 'TPM_ALG_SHA1'
[UInt16] 5 = 'TPM_ALG_HMAC'
[UInt16] 6 = 'TPM_ALG_AES'
[UInt16] 7 = 'TPM_ALG_MGF1'
[UInt16] 8 = 'TPM_ALG_KEYEDHASH'
[UInt16] 10 = 'TPM_ALG_XOR'
[UInt16] 11 = 'TPM_ALG_SHA256'
[UInt16] 12 = 'TPM_ALG_SHA384'
[UInt16] 13 = 'TPM_ALG_SHA512'
[UInt16] 16 = 'TPM_ALG_NULL'
[UInt16] 18 = 'TPM_ALG_SM3_256'
}
$Script:HashAlgorithmMapping = @{
0x00008001 = 'CALG_MD2'
0x00008002 = 'CALG_MD4'
0x00008003 = 'CALG_MD5'
0x00008004 = 'CALG_SHA1'
0x0000800C = 'CALG_SHA_256'
0x0000800D = 'CALG_SHA_384'
0x0000800E = 'CALG_SHA_512'
}
$Script:OSDeviceMapping = @{
0x00000000 = 'UNKNOWN'
0x00010001 = 'BLOCKIO_HARDDISK'
0x00010002 = 'BLOCKIO_REMOVABLEDISK'
0x00010003 = 'BLOCKIO_CDROM'
0x00010004 = 'BLOCKIO_PARTITION'
0x00010005 = 'BLOCKIO_FILE'
0x00010006 = 'BLOCKIO_RAMDISK'
0x00010007 = 'BLOCKIO_VIRTUALHARDDISK'
0x00020000 = 'SERIAL'
0x00030000 = 'UDP'
}
$Script:EventTypeMapping = @{
[UInt32] 0 = 'EV_PREBOOT_CERT' # The event field contains certificates such as the Validation Certificates.
[UInt32] 1 = 'EV_POST_CODE' # The digest field contains the SHA-1 hash of the POST portion of the BIOS. The event field SHOULD NOT contain the actual POST code but MAY contain informative information about the POST code.
[UInt32] 2 = 'EV_UNUSED' # The event type was never used and is considered reserved.
[UInt32] 3 = 'EV_NO_ACTION' # The event field contains informative data that was not extended into any PCR. The fields: pcrIndex and digest MUST contain the value 0.
[UInt32] 4 = 'EV_SEPARATOR' # Delimits actions taken during the Pre-Operating System State and the Operating System Present State
# This will often be "WBCL" - Windows Boot Configuration Log (Microsoft's name for the TCG log)
[UInt32] 5 = 'EV_ACTION' # A specific action measured as a string defined in Section 10.4.3.
[UInt32] 6 = 'EV_EVENT_TAG' # The event field contains the structure defined in Section 10.4.2.1.
[UInt32] 7 = 'EV_S_CRTM_CONTENTS' # The digest field contains is the SHA-1 hash of the SCRTM. The event field SHOULD NOT contain the actual S-CRTM code but MAY contain informative information about the S-CRTM code.
[UInt32] 8 = 'EV_S_CRTM_VERSION' # The event field contains the version string of the SCRTM.
[UInt32] 9 = 'EV_CPU_MICROCODE' # The event field contains a descriptor of the microcode but the digest field contains the actual hash of the microcode patch that was applied.
[UInt32] 10 = 'EV_PLATFORM_CONFIG_FLAGS' # The format and contents to be defined by the platform manufacturer. Examples of information contained in this event type are the capabilities of the platform?s measurements, whether the Owner has disabled measurements, etc.
[UInt32] 11 = 'EV_TABLE_OF_DEVICES' # The event field contains the Platform manufacturerprovided Table of Devices or other Platform manufacturer-defined information. The Platform manufacturer defines the content and format of the Table of Devices. The Host Platform Certificate may provide a reference to the meaning of these structures and data. This structure is measured into PCR[1] using the following.
[UInt32] 12 = 'EV_COMPACT_HASH' # This event is entered using the TCG_CompactHashLogExtendEvent. While it can be used by any function, it is typically used by IPL Code to measure events. The contents of the event field is specified by the caller but is not part of the measurement; rather, it is just informative.
[UInt32] 13 = 'EV_IPL' # The digest field contains the SHA-1 hash of the IPL Code. The event field SHOULD NOT contain the actual IPL Code but MAY contain informative information about the IPL Code. Note: The digest may not cover the entire area hosting the IPL Image, but only the portion that contains the IPL Code. For example, if the IPL Image is a disk drive MBR, this MUST NOT include the portion of the MBR that contains the disk geometry.
[UInt32] 14 = 'EV_IPL_PARTITION_DATA' # The data and partition portion of the IPL Image.
[UInt32] 15 = 'EV_NONHOST_CODE' # The executable component of any Non-host Platform. The contents of the event field are defined by the manufacturer of the Non-host Platform.
[UInt32] 16 = 'EV-NONHOST_CONFIG' # The parameters associated with a Non-host Platform. The contents of the event field are defined by the manufacturer of the Non-host Platform.
[UInt32] 17 = 'EV_NONHOST_INFO' # The event is information about the presence of a Non-host Platform. This information could be, but is not required to be, information such as the Non-host Platform manufacturer, model, type, version, etc. The information and formatting is to be determined by the BIOS.
[UInt32] (2147483648 + 1) = 'EV_EFI_VARIABLE_DRIVER_CONFIG' # EFI variables, either defined in the EFI spec or private, that typically do not change from boot-to-boot and contain system configuration information.
[UInt32] (2147483648 + 2) = 'EV_EFI_VARIABLE_BOOT' # This event is used to measure boot variables. The event field MUST contain a UEFI_VARIABLE_DATA structure
[UInt32] (2147483648 + 3) = 'EV_EFI_BOOT_SERVICES_APPLICATION' # EFI application (e.g. EFI OSLoader)
[UInt32] (2147483648 + 4) = 'EV_EFI_BOOT_SERVICES_DRIVER' # EFI Boot Services Drivers from adapter or loaded by driver in adapter.
[UInt32] (2147483648 + 5) = 'EV_EFI_RUNTIME_SERVICES_DRIVER' # EFI Runtime drivers from adapter or loaded by driver in adapter.
[UInt32] (2147483648 + 6) = 'EV_EFI_GPT_EVENT' # GPT Table
[UInt32] (2147483648 + 7) = 'EV_EFI_ACTION' # Measurement of a specific string value that indicates a specific event occurred during the platform or OS boot process.
[UInt32] (2147483648 + 8) = 'EV_EFI_PLATFORM_FIRMWARE_BLOB' # The event MUST contain a UEFI_PLATFORM_FIRMWARE_BLOB structure
[UInt32] (2147483648 + 9) = 'EV_EFI_HANDOFF_TABLES' # Describes the measurement of industry-standard tables and data structure regions.
[UInt32] (2147483648 + 0x0A) = 'EV_EFI_HCRTM_EVENT' # This event is used to record an event for the digest extended to PCR[0] as part of an H-CRTM event.
[UInt32] (2147483648 + 0xE0) = 'EV_EFI_VARIABLE_AUTHORITY' # Documented here: https://docs.microsoft.com/en-us/windows-hardware/test/hlk/testref/trusted-execution-environment-efi-protocol
}
$Script:DigestSizeMapping = @{
'TPM_ALG_SHA1' = 20
'TPM_ALG_SHA256' = 32
'TPM_ALG_SHA384' = 48
'TPM_ALG_SHA512' = 64
'TPM_ALG_SM3_256' = 32
}
# To-do: expand out the device subtype parsers
$Script:DevicePathTypeMapping = @{
[Byte] 1 = 'HARDWARE_DEVICE_PATH' # Hardware Device Path
[Byte] 2 = 'ACPI_DEVICE_PATH' # ACPI Device Path
[Byte] 3 = 'MESSAGING_DEVICE_PATH'# Messaging Device Path
[Byte] 4 = 'MEDIA_DEVICE_PATH' # Media Device Path
[Byte] 5 = 'BBS_DEVICE_PATH' # BIOS Boot Specification Device Path
[Byte] 0x7F = 'END_DEVICE_PATH_TYPE'
}
$Script:MediaDeviceSubTypeMapping = @{
[Byte] 1 = 'MEDIA_HARDDRIVE_DP' # Corresponding struct: HARDDRIVE_DEVICE_PATH
[Byte] 2 = 'MEDIA_CDROM_DP' # Corresponding struct: CDROM_DEVICE_PATH
[Byte] 3 = 'MEDIA_VENDOR_DP' # Corresponding struct: ?
[Byte] 4 = 'MEDIA_FILEPATH_DP' # Corresponding struct: FILEPATH_DEVICE_PATH
[Byte] 5 = 'MEDIA_PROTOCOL_DP' # Corresponding struct: MEDIA_PROTOCOL_DEVICE_PATH
[Byte] 6 = 'MEDIA_PIWG_FW_FILE_DP' # Corresponding struct: MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
[Byte] 7 = 'MEDIA_PIWG_FW_VOL_DP' # Corresponding struct: MEDIA_FW_VOL_DEVICE_PATH
[Byte] 8 = 'MEDIA_RELATIVE_OFFSET_RANGE_DP' # Corresponding struct: MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH
[Byte] 9 = 'MEDIA_RAM_DISK_DP' # Corresponding struct: MEDIA_RAM_DISK_DEVICE_PATH
}
$Script:ACPIDeviceSubTypeMapping = @{
[Byte] 1 = 'ACPI_DP' # Corresponding struct: ACPI_HID_DEVICE_PATH
[Byte] 2 = 'ACPI_EXTENDED_DP' # Corresponding struct: ACPI_EXTENDED_HID_DEVICE_PATH
[Byte] 3 = 'ACPI_ADR_DP' # Corresponding struct: ACPI_ADR_DEVICE_PATH
}
$Script:PartitionGUIDMapping = @{
'00000000-0000-0000-0000-000000000000' = 'PARTITION_ENTRY_UNUSED_GUID'
'ebd0a0a2-b9e5-4433-87c0-68b6b72699c7' = 'PARTITION_BASIC_DATA_GUID'
'c12a7328-f81f-11d2-ba4b-00a0c93ec93b' = 'PARTITION_SYSTEM_GUID'
'e3c9e316-0b5c-4db8-817d-f92df00215ae' = 'PARTITION_MSFT_RESERVED_GUID'
'5808c8aa-7e8f-42e0-85d2-e1e90434cfb3' = 'PARTITION_LDM_METADATA_GUID'
'af9b60a0-1431-4f62-bc68-3311714a69ad' = 'PARTITION_LDM_DATA_GUID'
'de94bba4-06d1-4d40-a16a-bfd50179d6ac' = 'PARTITION_MSFT_RECOVERY_GUID'
}
#endregion
# Helper function to retrieve SIPA events - i.e. Windows-specific PCR measurements
# I still have no clue what SIPA refers to. I use it because it's referenced all over wbcl.h.
# This function should not be exported.
function Get-SIPAEventData {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[Byte[]]
$SIPAEventBytes
)
# We need to identify container structures and recurse accordingly.
$ContainerType = 0x00010000
$EventMemoryStream = New-Object -TypeName IO.MemoryStream -ArgumentList @(,$SIPAEventBytes)
$EventBinaryReader = New-Object -TypeName IO.BinaryReader -ArgumentList $EventMemoryStream, ([Text.Encoding]::Unicode)
while (($EventBinaryReader.BaseStream.Position) -lt $SIPAEventBytes.Count) {
$SIPAEventTypeVal = $EventBinaryReader.ReadInt32()
$SIPAEventType = $SIPAEventMapping[$SIPAEventTypeVal]
$SIPAEventSize = $EventBinaryReader.ReadUInt32()
$EventBytes = $EventBinaryReader.ReadBytes($SIPAEventSize)
# All SIPA event types _should_ be defined but just in case one isn't, print it out in hex.
if (-not $SIPAEventType) { $SIPAEventType = "0x$($SIPAEventTypeVal.ToString('X8'))" }
if ((($SIPAEventTypeVal -band 0x000F0000) -eq $ContainerType) -or (($SIPAEventType -eq 'NoAuthority') -or ($SIPAEventType -eq 'AuthorityPubKey'))) {
switch ($SIPAEventType) {
'TrustBoundary' {
$PropertyTemplate = [Ordered] @{
Information = $null
PreOSParameters = $null
OSParameters = $null
LoadedModules = $null # This appears to be the only one that will have multiple entries
ELAM = $null
VBS = $null
}
$InformationTemplate = @{
Information = $null
BootCounter = $null
TransferControl = $null
ApplicationReturn = $null
BitlockerUnlock = $null
EventCounter = $null
CounterID = $null
MORBitNotCancelable = $null
ApplicationSVN = $null
SVNChainStatus = $null
MORBitAPIStatus = $null
}
$PreOSTemplate = @{
BootDebugging = $null
BootRevocationList = $null
}
$OSTemplate = @{
OSKernelDebug = $null
CodeIntegrity = $null
TestSigning = $null
DataExecutionPrevention = $null
SafeMode = $null
WinPE = $null
PhysicalAddressExtension = $null
OSDevice = $null
SystemRoot = $null
HypervisorLaunchType = $null
HypervisorPath = $null
HypervisorIOMMUPolicy = $null
HypervisorDebug = $null
DriverLoadPolicy = $null
SIPolicy = $null
HypervisorMMIONXPolicy = $null
HypervisorMSRFilterPolicy = $null
VSMLaunchType = $null
OSRevocationList = $null
VSMIDKInfo = $null
FlightSigning = $null
PagefileEncryptionEnabled = $null
VSMIDKSInfo = $null
HibernationDisabled = $null
DumpsDisabled = $null
DumpEncryptionEnabled = $null
DumpEncryptionKeyDigest = $null
LSAISOConfig = $null
}
$VBSTemplate = @{
VBSVSMRequired = $null
VBSSecurebootRequired = $null
VBSIOMMURequired = $null
VBSNXRequired = $null
VBSMSRFilteringRequired = $null
VBSMandatoryEnforcement = $null
VBSHVCIPolicy = $null
VBSMicrosoftBootChainRequired = $null
}
$ContainerEvents = Get-SIPAEventData -SIPAEventBytes $EventBytes
$LoadedModuleList = New-Object 'System.Collections.Generic.List[PSObject]'
$ELAMList = New-Object 'System.Collections.Generic.List[PSObject]'
$InformationTemplateSet = $False
$PreOSTemplateSet = $False
$OSTemplateSet = $False
$VBSTemplateSet = $False
foreach ($Container in $ContainerEvents) {
if ($Container.SIPAEventType -eq 'LoadedModuleAggregation') {
$LoadedModuleList.Add($Container.SIPAEventData)
} elseif ($Container.SIPAEventType -eq 'ELAMAggregation') {
$ELAMList.Add($Container.SIPAEventData)
} else {
switch ($Container.Category) {
'Information' {
$InformationTemplateSet = $True
$InformationTemplate[$Container.SIPAEventType] = $Container.SIPAEventData
}
'PreOSParameter' {
$PreOSTemplateSet = $True
$PreOSTemplate[$Container.SIPAEventType] = $Container.SIPAEventData
}
'OSParameter' {
$OSTemplateSet = $True
$OSTemplate[$Container.SIPAEventType] = $Container.SIPAEventData
}
'VBS' {
$VBSTemplateSet = $True
$VBSTemplate[$Container.SIPAEventType] = $Container.SIPAEventData
}
}
}
}
$InformationObject = $null
$PreOSParameterObject = $null
$OSParameterObject = $null
$VBSObject = $null
if ($InformationTemplateSet) { $InformationObject = [PSCustomObject] $InformationTemplate }
if ($PreOSTemplateSet) { $PreOSParameterObject = [PSCustomObject] $PreOSTemplate }
if ($OSTemplateSet) { $OSParameterObject = [PSCustomObject] $OSTemplate }
if ($VBSTemplateSet) { $VBSObject = [PSCustomObject] $VBSTemplate }
$PropertyTemplate['Information'] = $InformationObject
$PropertyTemplate['PreOSParameters'] = $PreOSParameterObject
$PropertyTemplate['OSParameters'] = $OSParameterObject
$PropertyTemplate['VBS'] = $VBSObject
if ($LoadedModuleList.Count) { $PropertyTemplate['LoadedModules'] = $LoadedModuleList }
if ($ELAMList) { $PropertyTemplate['ELAM'] = $ELAMList }
[PSCustomObject] $PropertyTemplate
}
'LoadedModuleAggregation' {
$PropertyTemplate = [Ordered] @{
FilePath = $null
ImageBase = $null
ImageSize = $null
HashAlgorithmID = $null
AuthenticodeHash = $null
ImageValidated = $null
AuthorityIssuer = $null
AuthorityPublisher = $null
AuthoritySerial = $null
AuthoritySHA1Thumbprint = $null
ModuleSVN = $null
}
$ContainerEvents = Get-SIPAEventData -SIPAEventBytes $EventBytes
foreach ($Container in $ContainerEvents) {
$PropertyTemplate[$Container.SIPAEventType] = $Container.SIPAEventData
}
$ContainerObject = [PSCustomObject] $PropertyTemplate
[PSCustomObject] @{
IsContainer = $False
SIPAEventType = $SIPAEventType
SIPAEventData = $ContainerObject
}
}
'ELAMAggregation' {
$PropertyTemplate = [Ordered] @{
ELAMKeyname = $null
ELAMConfiguration = $null
ELAMPolicy = $null
ELAMMeasured = $null
}
$ContainerEvents = Get-SIPAEventData -SIPAEventBytes $EventBytes
foreach ($Container in $ContainerEvents) {
$PropertyTemplate[$Container.SIPAEventType] = $Container.SIPAEventData
}
$ContainerObject = [PSCustomObject] $PropertyTemplate
[PSCustomObject] @{
IsContainer = $False
SIPAEventType = $SIPAEventType
SIPAEventData = $ContainerObject
}
}
'TrustpointAggregation' {
$PropertyTemplate = [Ordered] @{
AIKID = $null
AIKPubDigest = $null
Quote = $null
QuoteSignature = $null
}
$ContainerEvents = Get-SIPAEventData -SIPAEventBytes $EventBytes
foreach ($Container in $ContainerEvents) {
$PropertyTemplate[$Container.SIPAEventType] = $Container.SIPAEventData
}
$ContainerObject = [PSCustomObject] $PropertyTemplate
$ContainerObject
}
'NoAuthority' {
[PSCustomObject] @{
NoAuthority = $EventBytes
}
}
'AuthorityPubKey' {
[PSCustomObject] @{
AuthorityPubKey = ($EventBytes | ForEach-Object { $_.ToString('X2') }) -join ':'
}
}
default {
# Return raw event data for KSR containers until I have data to actually parse
# KSRAggregation
# KSRSignedMeasurementAggregation
Write-Warning "Uncategorized SIPA Event Category"
[PSCustomObject] @{
IsContainer = $True
SIPAEventType = $SIPAEventType
SIPAEventData = Get-SIPAEventData -SIPAEventBytes $EventBytes
}
}
}
} else {
# Each SIPA event data structure will differ depending on the type.
# Many of these data types are not formally defined but can be easily inferred.
# If the strucutre is not explicitly stated, it is inferred from multiple events.
switch ($SIPAEventType) {
'Information' { $EventData = $EventBytes; $Category = 'Information' }
'BootCounter' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'Information' }
'TransferControl' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'Information' }
'ApplicationReturn' { $EventData = $EventBytes; $Category = 'Information' }
'BitlockerUnlock' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'Information' }
'EventCounter' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'Information' }
'CounterID' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'Information' }
'MORBitNotCancelable' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'Information' }
'ApplicationSVN' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'Information' }
'SVNChainStatus' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'Information' }
# MemoryOverwriteRequest - Introduced in the TCG Platform Reset Attack Mitigation Specification
'MORBitAPIStatus' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'Information' }
'BootDebugging' { $EventData = [Bool] $EventBytes[0]; $Category = 'PreOSParameter' }
'BootRevocationList' {
# SIPAEVENT_REVOCATION_LIST_PAYLOAD structure
# I haven't spent time to determine how to translate the creation time yet.
$CreationTime = [BitConverter]::ToUInt64($EventBytes, 0)
$DigestLength = [BitConverter]::ToUInt32($EventBytes, 8)
$HashAlgorithm = $DigestAlgorithmMapping[[BitConverter]::ToUInt16($EventBytes, 0x0C)]
$Digest = [BitConverter]::ToString($EventBytes[0x0E..(0x0E + $DigestLength - 1)]).Replace('-', '')
$Category = 'PreOSParameter'
$EventData = [PSCustomObject] @{
CreationTime = $CreationTime
HashAlgorithm = $HashAlgorithm
Digest = $Digest
}
}
'OSKernelDebug' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'CodeIntegrity' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'TestSigning' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'DataExecutionPrevention' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'OSParameter' }
'SafeMode' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'WinPE' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'PhysicalAddressExtension' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'OSParameter' }
'OSDevice' { $EventData = $OSDeviceMapping[[BitConverter]::ToInt32($EventBytes, 0)]; $Category = 'OSParameter' }
'SystemRoot' { $EventData = [Text.Encoding]::Unicode.GetString($EventBytes).TrimEnd(@(0)); $Category = 'OSParameter' }
'HypervisorLaunchType' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'OSParameter' }
'HypervisorPath' { $EventData = [Text.Encoding]::Unicode.GetString($EventBytes).TrimEnd(@(0)); $Category = 'OSParameter' }
'HypervisorIOMMUPolicy' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'OSParameter' }
'HypervisorDebug' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'DriverLoadPolicy' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'OSParameter' }
'SIPolicy' {
# SIPAEVENT_SI_POLICY_PAYLOAD structure
$Revision = [Int32][BitConverter]::ToInt16($EventBytes, 0)
$Build = [Int32][BitConverter]::ToInt16($EventBytes, 2)
$Minor = [Int32][BitConverter]::ToInt16($EventBytes, 4)
$Major = [Int32][BitConverter]::ToInt16($EventBytes, 6)
$PolicyVersion = New-Object -TypeName Version -ArgumentList @($Major, $Minor, $Build, $Revision)
$PolicyNameLength = [BitConverter]::ToInt16($EventBytes, 8)
$HashAlgorithm = $DigestAlgorithmMapping[[BitConverter]::ToUInt16($EventBytes, 0x0A)]
$DigestLength = [BitConverter]::ToUInt16($EventBytes, 0x0C)
$DigestIndex = 0x10 + $PolicyNameLength
$PolicyName = [Text.Encoding]::Unicode.GetString($EventBytes[0x10..($DigestIndex - 1)]).TrimEnd(@(0))
$Digest = [BitConverter]::ToString($EventBytes[($DigestIndex)..($DigestIndex + $DigestLength - 1)]).Replace('-', '')
$Category = 'OSParameter'
$EventData = [PSCustomObject] @{
PolicyVersion = $PolicyVersion
PolicyName = $PolicyName
HashAlgorithm = $HashAlgorithm
Digest = $Digest
}
}
'HypervisorMMIONXPolicy' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'OSParameter' }
'HypervisorMSRFilterPolicy' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'OSParameter' }
'VSMLaunchType' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'OSParameter' }
'OSRevocationList' {
# SIPAEVENT_REVOCATION_LIST_PAYLOAD structure
# I haven't spent time to determine how to translate the creation time yet.
$CreationTime = [BitConverter]::ToUInt64($EventBytes, 0)
$DigestLength = [BitConverter]::ToUInt32($EventBytes, 8)
$HashAlgorithm = $DigestAlgorithmMapping[[BitConverter]::ToUInt16($EventBytes, 0x0C)]
$Digest = [BitConverter]::ToString($EventBytes[0x0E..(0x0E + $DigestLength - 1)]).Replace('-', '')
$Category = 'OSParameter'
$EventData = [PSCustomObject] @{
CreationTime = $CreationTime
HashAlgorithm = $HashAlgorithm
Digest = $Digest
}
}
'VSMIDKInfo' {
# SIPAEVENT_VSM_IDK_INFO_PAYLOAD structure
# Type: VSM_IDK_ALG_ID (I can't find this defined anywhere. I'm personally not worried about it. IDK what "IDK" is)
# This should only be 1.
$KeyAlgID = [BitConverter]::ToUInt32($EventBytes, 0)
$null = [BitConverter]::ToUInt32($EventBytes, 4) # KeyBitLength
$PublicExpLengthBytes = [BitConverter]::ToUInt32($EventBytes, 8)
$ModulusSizeBytes = [BitConverter]::ToUInt32($EventBytes, 0x0C)
$ModulusIndex = 0x10 + $PublicExpLengthBytes
[Byte[]] $PublicExponent = $EventBytes[0x10..($ModulusIndex - 1)]
[Byte[]] $Modulus = $EventBytes[($ModulusIndex)..($ModulusIndex + $ModulusSizeBytes - 1)]
$Category = 'OSParameter'
$EventData = [PSCustomObject] @{
KeyAlgID = $KeyAlgID
PublicExponent = ($PublicExponent | ForEach-Object {$_.ToString('X2')}) -join ':'
Modulus = ($Modulus | ForEach-Object {$_.ToString('X2')}) -join ':'
}
}
'FlightSigning' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'PagefileEncryptionEnabled' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'VSMIDKSInfo' {
# SIPAEVENT_VSM_IDK_INFO_PAYLOAD structure
# Type: VSM_IDK_ALG_ID (I can't find this defined anywhere. I'm personally not worried about it. IDK what "IDK" is)
# This should only be 1.
$KeyAlgID = [BitConverter]::ToUInt32($EventBytes, 0)
$null = [BitConverter]::ToUInt32($EventBytes, 4) # KeyBitLength
$PublicExpLengthBytes = [BitConverter]::ToUInt32($EventBytes, 8)
$ModulusSizeBytes = [BitConverter]::ToUInt32($EventBytes, 0x0C)
$ModulusIndex = 0x10 + $PublicExpLengthBytes
[Byte[]] $PublicExponent = $EventBytes[0x10..($ModulusIndex - 1)]
[Byte[]] $Modulus = $EventBytes[($ModulusIndex)..($ModulusIndex + $ModulusSizeBytes - 1)]
$Category = 'OSParameter'
$EventData = [PSCustomObject] @{
KeyAlgID = $KeyAlgID
PublicExponent = ($PublicExponent | ForEach-Object {$_.ToString('X2')}) -join ':'
Modulus = ($Modulus | ForEach-Object {$_.ToString('X2')}) -join ':'
}
}
'HibernationDisabled' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'DumpsDisabled' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
'DumpEncryptionEnabled' { $EventData = [Bool] $EventBytes[0]; $Category = 'OSParameter' }
# SHA-256 digest of thefollowing regkey value:
# CurrentControlSet\Control\CrashControl\EncryptionCertificates\Certificate.1::PublicKey
'DumpEncryptionKeyDigest' { $EventData = $EventBytes; $Category = 'OSParameter' }
'LSAISOConfig' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'OSParameter' }
'FilePath' { $EventData = [Text.Encoding]::Unicode.GetString($EventBytes).TrimEnd(@(0)); $Category = 'LoadedImage' }
'SIPAEventData' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'LoadedImage' }
'HashAlgorithmID' { $EventData = $HashAlgorithmMapping[[BitConverter]::ToInt32($EventBytes, 0)]; $Category = 'LoadedImage' }
'AuthenticodeHash' { $EventData = [BitConverter]::ToString($EventBytes).Replace('-', ''); $Category = 'LoadedImage' }
'AuthorityIssuer' { $EventData = [Text.Encoding]::Unicode.GetString($EventBytes).TrimEnd(@(0)); $Category = 'LoadedImage' }
'AuthoritySerial' { $EventData = [BitConverter]::ToString($EventBytes).Replace('-', ''); $Category = 'LoadedImage' }
'ImageBase' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'LoadedImage' }
'ImageSize' { $EventData = [BitConverter]::ToUInt64($EventBytes, 0); $Category = 'LoadedImage' }
'AuthorityPublisher' { $EventData = [Text.Encoding]::Unicode.GetString($EventBytes).TrimEnd(@(0)); $Category = 'LoadedImage' }
'AuthoritySHA1Thumbprint' { $EventData = [BitConverter]::ToString($EventBytes).Replace('-', ''); $Category = 'LoadedImage' }
'ImageValidated' { $EventData = [Bool] $EventBytes[0]; $Category = 'LoadedImage' }
'ModuleSVN' { $EventData = [BitConverter]::ToUInt32($EventBytes, 0); $Category = 'LoadedImage' }
'AIKID' { $EventData = [Text.Encoding]::Unicode.GetString($EventBytes).TrimEnd(@(0)); $Category = 'Trustpoint' }
'AIKPubDigest' { $EventData = [BitConverter]::ToString($EventBytes).Replace('-', ''); $Category = 'Trustpoint' }
'Quote' { $EventData = ($EventBytes | ForEach-Object { $_.ToString('X2') }) -join ':'; $Category = 'Trustpoint' }
'QuoteSignature' { $EventData = ($EventBytes | ForEach-Object { $_.ToString('X2') }) -join ':'; $Category = 'Trustpoint' }
'VBSVSMRequired' { $EventData = [Bool] $EventBytes[0]; $Category = 'VBS' }
'VBSSecurebootRequired' { $EventData = [Bool] $EventBytes[0]; $Category = 'VBS' }
'VBSIOMMURequired' { $EventData = [Bool] $EventBytes[0]; $Category = 'VBS' }
'VBSNXRequired' { $EventData = [Bool] $EventBytes[0]; $Category = 'VBS' }
'VBSMSRFilteringRequired' { $EventData = [Bool] $EventBytes[0]; $Category = 'VBS' }
'VBSMandatoryEnforcement' { $EventData = $EventBytes; $Category = 'VBS' }
'VBSHVCIPolicy' { $EventData = $EventBytes; $Category = 'VBS' }
'VBSMicrosoftBootChainRequired' { $EventData = [Bool] $EventBytes[0]; $Category = 'VBS' }
'ELAMKeyname' { $EventData = [Text.Encoding]::Unicode.GetString($EventBytes).TrimEnd(@(0)); $Category = 'ELAM' }
'ELAMMeasured' { $EventData = [BitConverter]::ToString($EventBytes).Replace('-', ''); $Category = 'ELAM' }
'ELAMConfiguration' { $EventData = $EventBytes; $Category = 'ELAM' }
'ELAMPolicy' { $EventData = $EventBytes; $Category = 'ELAM' }
default {
$Category = 'Uncategorized'
$EventData = $EventBytes
}
}
[PSCustomObject] @{
Category = $Category
SIPAEventType = $SIPAEventType
SIPAEventData = $EventData
}
}
}
$EventBinaryReader.Close()
}
function Get-TPMDeviceInfo {
<#
.SYNOPSIS
Retrieves TPM information.
.DESCRIPTION
Get-TPMDeviceInfo retrieves limited TPM information.
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
.EXAMPLE
Get-TPMDeviceInfo
#>
$TPM_DEVICE_INFO_Size = 16
$DeviceInfo = New-Object -TypeName TPMBaseServices.TPM_DEVICE_INFO
$Result = [TPMBaseServices.UnsafeNativeMethods]::Tbsi_GetDeviceInfo($TPM_DEVICE_INFO_Size, [Ref] $DeviceInfo)
if ($Result -eq 0) {
$DeviceInfo
} else {
Write-Error "Tbsi_GetDeviceInfo: $($TBSReturnCodes[$Result])"
}
}
function Get-TCGLogContent {
<#
.SYNOPSIS
Retrieves the contents of the Trusted Computing Group (TCG) log.
.DESCRIPTION
Get-TCGLogContent retrieves the contents of the TCG log (referred to as the "Windows Boot Configuration Log" (WBCL) by Microsoft). This log captures the various boot and runtime measurements used for device health attestation.
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
.PARAMETER LogType
Specifies the type of TCG log to retrieve. The following arguments are supported:
* SRTMCurrent (default): The log associated with PCRs 0-15 for the current session (boot or resume).
* This option retrieves the contents of HKLM\SYSTEM\CurrentControlSet\Control\IntegrityServices\WBCL
* DRTMCurrent: The log associated with PCRs 17-22 for the current session (boot or resume).
* This option retrieves the contents of HKLM\SYSTEM\CurrentControlSet\Control\IntegrityServices\WBCLDrtm
* The presence of DRTM is validated with NtQuerySystemInformation - SYSTEM_BOOT_ENVIRONMENT_INFORMATION.DbgMeasuredLaunch
* SRTMBoot: The log associated with PCRs 0-15 for the most recent clean boot session.
* This log is retrieved from the most current MeasuredBoot log from a clean boot state. For example, if the most recent log is C:\Windows\Logs\MeasuredBoot\0000000029-0000000003.log, This option will retrieve C:\Windows\Logs\MeasuredBoot\0000000029-0000000000.log (indicating the first MeasuredBoot log taken from a clean boot state).
* SRTMResume: The log associated with PCRs 0-15 for the most recent resume from hibernation.
* This log is retrieved from the most current MeasuredBoot log taken immediately after the clean state boot log. For example, if the clean boot log is C:\Windows\Logs\MeasuredBoot\0000000029-0000000000.log, this options will retrieve C:\Windows\Logs\MeasuredBoot\0000000029-0000000001.log.
.EXAMPLE
Get-TCGLogContent
Retrieves the TCG log bytes associated with PCRs 0-15 for the current session (boot or resume).
.EXAMPLE
Get-TCGLogContent -LogType SRTMBoot
Retrieves the TCG log bytes associated with PCRs 0-15 for the most recent clean boot session.
.OUTPUTS
System.Byte[]
Outputs a byte array consisting of a raw TCG log. Supply the byte array to ConvertTo-TCGEventLog to parse the contents of the log.
#>
[OutputType([Byte[]])]
[CmdletBinding()]
param (
[Parameter(Position = 0)]
[String]
[ValidateSet('SRTMCurrent', 'DRTMCurrent', 'SRTMBoot', 'SRTMResume')]
$LogType = 'SRTMCurrent'
)
switch ($LogType) {
'SRTMCurrent' { $LogTypeEnumVal = [TPMBaseServices.TBS_TCGLOG]::TBS_TCGLOG_SRTM_CURRENT }
'DRTMCurrent' { $LogTypeEnumVal = [TPMBaseServices.TBS_TCGLOG]::TBS_TCGLOG_DRTM_CURRENT }
'SRTMBoot' { $LogTypeEnumVal = [TPMBaseServices.TBS_TCGLOG]::TBS_TCGLOG_SRTM_BOOT }
'SRTMResume' { $LogTypeEnumVal = [TPMBaseServices.TBS_TCGLOG]::TBS_TCGLOG_SRTM_RESUME }
}
$TCGLogSize = 0
# Supply an empty buffer so that the size of the buffer will be returned.
$Result = [TPMBaseServices.UnsafeNativeMethods]::Tbsi_Get_TCG_Log_Ex($LogTypeEnumVal, [IntPtr]::Zero, [Ref] $TCGLogSize)
if ($Result -ne 0) {
Write-Error "Tbsi_Get_TCG_Log_Ex: $($TBSReturnCodes[$Result])"
return
}
if ($TCGLogSize) {
Write-Verbose "TCG log size: 0x$($TCGLogSize.ToString('X8'))"
$TCGLogBuffer = [Runtime.InteropServices.Marshal]::AllocHGlobal($TCGLogSize)
# Initialize the buffer to zero. AllocHGlobal won't initialize memory nor will Tbsi_Get_TCG_Log_Ex.
for ($i = 0; $i -lt $TCGLogSize; $i++) {
[Runtime.InteropServices.Marshal]::WriteByte($TCGLogBuffer, $i, 0)
}
$TCGLogBytes = New-Object -TypeName Byte[]($TCGLogSize)
# Read the TCG log buffer.
$Result = [TPMBaseServices.UnsafeNativeMethods]::Tbsi_Get_TCG_Log_Ex($LogTypeEnumVal, $TCGLogBuffer, [Ref] $TCGLogSize)
if ($Result -ne 0) {
Write-Error "Tbsi_Get_TCG_Log_Ex: $($TBSReturnCodes[$Result])"
# Free the unmanaged memory
[Runtime.InteropServices.Marshal]::FreeHGlobal($TCGLogBuffer)
return
}
# Copy the buffer to the byte array
[Runtime.InteropServices.Marshal]::Copy($TCGLogBuffer, $TCGLogBytes, 0, $TCGLogSize)
# Free the unmanaged memory
[Runtime.InteropServices.Marshal]::FreeHGlobal($TCGLogBuffer)
}
$TCGLogBytes
}
filter ConvertTo-TCGEventLog {
<#
.SYNOPSIS
Parses a Trusted Computing Group (TCG) log.
.DESCRIPTION
ConvertTo-TCGEventLog parses one or more TCG logs (referred to as the "Windows Boot Configuration Log" (WBCL) by Microsoft). This log captures the various boot and runtime measurements used for device health attestation. ConvertTo-TCGEventLog will parse the log as a byte array or from one or more log files on disk.
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
.PARAMETER LogBytes
Specifies an array of bytes consisting of a raw TCG log.
.PARAMETER LogPath
Specifies the path to one or more TCG log files. On Windows 10 with TPM enabled, these logs are located at %windir%\Logs\MeasuredBoot by default. Optionally, you can specify an alternate TCG log path with HKLM\System\CurrentControlSet\services\TPM\WBCLPath (REG_EXPAND_SZ).
.PARAMETER
Specifies that any object that return a signature object should return an X509Certificate object. If this switch is not specified, X509Certificate2 objects will be returned. This switch is present in order to reduce the amount of data in JSON output.
.EXAMPLE
$TCGLogBytes = Get-TCGLogContent -LogType SRTMCurrent
$TCGLog = ConvertTo-TCGEventLog -LogBytes $TCGLogBytes
.EXAMPLE
ls C:\Windows\Logs\MeasuredBoot\*.log | ConvertTo-TCGEventLog
.EXAMPLE
ConvertTo-TCGEventLog -LogPath C:\Windows\Logs\MeasuredBoot\0000000001-0000000000.log
.EXAMPLE
ConvertTo-TCGEventLog -LogBytes (Get-TCGLogContent -LogType SRTMBoot) -MinimizedX509CertInfo | ConvertTo-Json -Depth 8 | Out-File TCGlog.json
Using the -MinimizedX509CertInfo so that JSON output is not as verbose.
.INPUTS
System.String
Accepts one or more TCG log file paths.
.OUTPUTS
PSCustomObject
Outputs a parsed TCG log.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ParameterSetName = 'Bytes')]
[Byte[]]
$LogBytes,
[Parameter(Mandatory, ParameterSetName = 'LogFile', ValueFromPipelineByPropertyName)]
[String]
[Alias('FullName')]
[ValidateNotNullOrEmpty()]
$LogPath,
[Switch]
$MinimizedX509CertInfo
)
$LogFullPath = $null
# The header should be at least this long in order to proceed with parsing.
$MinimumHeaderLength = 65
if ($LogBytes) {
$TCGLogBytes = $LogBytes
if ($TCGLogBytes.Count -lt $MinimumHeaderLength) {
Write-Error "The supplied byte array is not of sufficient size to be a TCG log. It must be at least $MinimumHeaderLength bytes in length. It is likely that the data supplied to ConvertTo-TCGEventLog is not a TCG log."
return
}
} else {
# -LogPath was specified
$LogFullPath = (Resolve-Path $LogPath).Path
$TCGLogBytes = [IO.File]::ReadAllBytes($LogFullPath)
if ($TCGLogBytes.Count -lt $MinimumHeaderLength) {
Write-Error "$LogFullPath is not of sufficient size to be a TCG log. It must be at least $MinimumHeaderLength bytes in length. It is likely that the data supplied to ConvertTo-TCGEventLog is not a TCG log."
return
}
}
if ($MinimizedX509CertInfo) {
$SignatureObjectType = 'Security.Cryptography.X509Certificates.X509Certificate'
} else {
$SignatureObjectType = 'Security.Cryptography.X509Certificates.X509Certificate2'
}
try {
$MemoryStream = New-Object -TypeName IO.MemoryStream -ArgumentList @(,$TCGLogBytes)
$BinaryReader = New-Object -TypeName IO.BinaryReader -ArgumentList $MemoryStream, ([Text.Encoding]::Unicode)
} catch {
throw $_
return
}
$PCRIndex = $BinaryReader.ReadUInt32()
if ($PCRIndex -ne 0) {
Write-Error "TCG_PCR_EVENT.PCRIndex expected value: 0. Actual value: $PCRIndex. It is likely that the data supplied to ConvertTo-TCGEventLog is not a TCG log."
$BinaryReader.Close()
return
}
$EventType = $EventTypeMapping[$BinaryReader.ReadUInt32()]
if ($EventType -ne 'EV_NO_ACTION') {
Write-Error "TCG_PCR_EVENT.EventType expected value: EV_NO_ACTION. Actual value: $EventType. It is likely that the data supplied to ConvertTo-TCGEventLog is not a TCG log."
$BinaryReader.Close()
return
}
$Digest = $BinaryReader.ReadBytes(20)
$DigestString = [BitConverter]::ToString($Digest).Replace('-', '')
if ($DigestString -ne '0000000000000000000000000000000000000000') {
Write-Error "TCG_PCR_EVENT.Digest expected value: 0000000000000000000000000000000000000000. Actual value: $DigestString. It is likely that the data supplied to ConvertTo-TCGEventLog is not a TCG log."
$BinaryReader.Close()
return
}
$EventSize = $BinaryReader.ReadUInt32()
# Read the TCG_EfiSpecIdEventStruct instance contents
$Signature = [Text.Encoding]::ASCII.GetString($BinaryReader.ReadBytes(16)).TrimEnd(@(0, 0))
if ($Signature -ne 'Spec ID Event03') {
Write-Error "TCG_PCR_EVENT.Event.Signature expected value: Spec ID Event03. Actual value: $Signature. It is likely that the data supplied to ConvertTo-TCGEventLog is not a TCG log."
$BinaryReader.Close()
return
}
# At this point, there is a very reasonable confidence that this is a well-formed TCG log.
$PlatformClass = $BinaryReader.ReadUInt32()
$SpecVersionMinor = $BinaryReader.ReadByte()
$SpecVersionMajor = $BinaryReader.ReadByte()
$SpecErrata = $BinaryReader.ReadByte()
$UintNSize = $BinaryReader.ReadByte()
$NumberOfAlgorithms = $BinaryReader.ReadUInt32()
$DigestSizes = New-Object -TypeName PSObject[]($NumberOfAlgorithms)
for ($i = 0; $i -lt $NumberOfAlgorithms; $i++) {
$DigestSizes[$i] = New-Object -TypeName PSObject -Property @{
HashAlg = $DigestAlgorithmMapping[$BinaryReader.ReadUInt16()]
DigestSize = $BinaryReader.ReadUInt16()
}
}
$VendorInfoSize = $BinaryReader.ReadByte()
$VendorInfo = $BinaryReader.ReadBytes($vendorInfoSize)
# Described here: https://msdn.microsoft.com/en-us/library/windows/desktop/bb530712(v=vs.85).aspx
$TCG_EfiSpecIdEventStruct = [PSCustomObject] @{
PSTypeName = 'TCGEfiSpecIdEvent'
Signature = $Signature
PlatformClass = $PlatformClass
SpecVersionMinor = $SpecVersionMinor
SpecVersionMajor = $SpecVersionMajor
SpecErrata = $SpecErrata
UintNSize = $UintNSize
NumberOfAlgorithms = $NumberOfAlgorithms
DigestSizes = $DigestSizes
VendorInfoSize = $VendorInfoSize
VendorInfo = $VendorInfo
}
$TCGHeader = [PSCustomObject] @{
PSTypeName = 'TCGPCREvent'
PCR = $PCRIndex
EventType = $EventType
Digest = $DigestString
Event = $TCG_EfiSpecIdEventStruct
}
# Loop through all the remaining measurements, parsing each TCG_PCR_EVENT2 struct along the way
$Events = while ($BinaryReader.PeekChar() -ne -1) {
$PCRIndex = $BinaryReader.ReadInt32()
$EventTypeVal = $BinaryReader.ReadUInt32()
$EventType = $EventTypeMapping[$EventTypeVal]
if (-not $EventType) { $EventType = $EventTypeVal.ToString('X8') }
# Multiple digests can be calculated/stored but in plractice, you will likely only over see one digest.
$DigestValuesCount = $BinaryReader.ReadUInt32()
if ($DigestValuesCount -eq 1) {
$HashAlg = $DigestAlgorithmMapping[$BinaryReader.ReadUInt16()]
$DigestSize = $DigestSizeMapping[$HashAlg]
$Digests = [BitConverter]::ToString($BinaryReader.ReadBytes($DigestSize)).Replace('-', '')
} else {
$Digests = New-Object -TypeName PSObject[]($DigestValuesCount)
for ($i = 0; $i -lt $DigestValuesCount; $i++) {
$HashAlg = $DigestAlgorithmMapping[$BinaryReader.ReadUInt16()]
$DigestSize = $DigestSizeMapping[$HashAlg]
$Digests[$i] = [BitConverter]::ToString($BinaryReader.ReadBytes($DigestSize)).Replace('-', '')
}
}
$EventSize = $BinaryReader.ReadUInt32()
$Event = $null
# Parse specific event types. Event types that are not explicitly parsed will return a byte array of the contents.
switch ($EventType) {
'EV_S_CRTM_CONTENTS' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
$Event = [Text.Encoding]::ASCII.GetString($EventBytes).TrimEnd(@(0))
}
'EV_POST_CODE' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
$Event = [Text.Encoding]::ASCII.GetString($EventBytes).TrimEnd(@(0))
}
'EV_EFI_PLATFORM_FIRMWARE_BLOB' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
$BlobBase = [BitConverter]::ToUInt64($EventBytes, 0)
$BlobLength = [BitConverter]::ToUInt64($EventBytes, 8)
# Chipsec can dump this for validation:
# chipsec_util.py mem read [BlobBase] [BlobLength] firmwareblob.bin
# What's dumped will likely be a firmware volume. I use UEFITool.exe to extract contents.
$Event = [PSCustomObject] @{
PSTypeName = 'TCGUEFIPlatformFirmwareBlob'
BlobBase = $BlobBase
BlobLength = $BlobLength
}
}
'EV_EVENT_TAG' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
# These will be Windows-specific data structures
$Event = Get-SIPAEventData -SIPAEventBytes $EventBytes
}
'EV_NO_ACTION' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
if ($PCRIndex -eq -1) {
# Extact TrustPoint information - used for log attestation
$Event = Get-SIPAEventData -SIPAEventBytes $EventBytes
} else {
$Event = $EventBytes
}
}
'EV_EFI_GPT_EVENT' {
# This will consist of a UEFI_GPT_DATA structure.
# EFI_TABLE_HEADER: Start
$EventBytes = $BinaryReader.ReadBytes($EventSize)
$GPTMemoryStream = New-Object -TypeName IO.MemoryStream -ArgumentList @(,$EventBytes)
$GPTBinaryReader = New-Object -TypeName IO.BinaryReader -ArgumentList $GPTMemoryStream, ([Text.Encoding]::Unicode)
$Signature = [Text.Encoding]::ASCII.GetString($GPTBinaryReader.ReadBytes(8)).TrimEnd(@(0))
$SpecMinor = [Int32] $GPTBinaryReader.ReadInt16()
$SpecMajor = [Int32] $GPTBinaryReader.ReadInt16()
$Revision = New-Object -TypeName Version -ArgumentList @($SpecMajor, $SpecMinor, 0, 0)
$null = $GPTBinaryReader.ReadUInt32() # HeaderSize
$CRC32 = $GPTBinaryReader.ReadUInt32()
$null = $GPTBinaryReader.ReadUInt32() # Reserved
$TableHeader = [PSCustomObject] @{
Signature = $Signature
Revision = $Revision
CRC32 = $CRC32
}
# EFI_TABLE_HEADER: End
# EFI_PARTITION_TABLE_HEADER: Start
$MyLBA = $GPTBinaryReader.ReadUInt64()
$AlternateLBA = $GPTBinaryReader.ReadUInt64()
$FirstUsableLBA = $GPTBinaryReader.ReadUInt64()
$LastUsableLBA = $GPTBinaryReader.ReadUInt64()
$DiskGUID = [Guid][Byte[]] $GPTBinaryReader.ReadBytes(16)
$PartitionEntryLBA = $GPTBinaryReader.ReadUInt64()
$NumberOfPartitionEntries = $GPTBinaryReader.ReadUInt32()
$SizeOfPartitionEntry = $GPTBinaryReader.ReadUInt32()
$PartitionEntryArrayCRC32 = $GPTBinaryReader.ReadUInt32()
# EFI_PARTITION_TABLE_HEADER: End
$EFIPartitionHeader = [PSCustomObject] @{
Header = $TableHeader
MyLBA = $MyLBA
AlternateLBA = $AlternateLBA
FirstUsableLBA = $FirstUsableLBA
LastUsableLBA = $LastUsableLBA
DiskGUID = $DiskGUID
PartitionEntryLBA = $PartitionEntryLBA
NumberOfPartitionEntries = $NumberOfPartitionEntries
SizeOfPartitionEntry = $SizeOfPartitionEntry
PartitionEntryArrayCRC32 = $PartitionEntryArrayCRC32
}
$NumberOfPartitions = $GPTBinaryReader.ReadUInt64()
$Partitions = New-Object PSObject[]($NumberOfPartitions)
for ($i = 0; $i -lt $NumberOfPartitions; $i++) {
$PartitionTypeGUID = [Guid][Byte[]] $GPTBinaryReader.ReadBytes(16)
$PartitionTypeName = $PartitionGUIDMapping[$PartitionTypeGUID.Guid]
$UniquePartitionGUID = [Guid][Byte[]] $GPTBinaryReader.ReadBytes(16)
$StartingLBA = $GPTBinaryReader.ReadUInt64()
$EndingLBA = $GPTBinaryReader.ReadUInt64()
$Attributes = $GPTBinaryReader.ReadUInt64()
$PartitionName = [Text.Encoding]::Unicode.GetString($GPTBinaryReader.ReadBytes(72)).TrimEnd(@(0))
$Partitions[$i] = [PSCustomObject] @{
PartitionTypeGUID = $PartitionTypeGUID
PartitionTypeName = $PartitionTypeName
UniquePartitionGUID = $UniquePartitionGUID
StartingLBA = $StartingLBA
EndingLBA = $EndingLBA
Attributes = $Attributes
PartitionName = $PartitionName
}
}
$Event = [PSCustomObject] @{
EfiPartitionHeader = $EfiPartitionHeader
NumberOfPartitions = $NumberOfPartitions
Partitions = $Partitions
}
$GPTBinaryReader.Close()
}
'EV_SEPARATOR' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
if ($PCRIndex -gt 11) {
$Event = [Text.Encoding]::ASCII.GetString($EventBytes)
} else {
$Event = ($EventBytes | ForEach-Object {$_.ToString('X2')}) -join ':'
}
}
'EV_EFI_VARIABLE_AUTHORITY' {
$VariableName = [Guid] $BinaryReader.ReadBytes(16)
$UnicodeNameLength = $BinaryReader.ReadUInt64()
$VariableDataLength = $BinaryReader.ReadUInt64()
$UnicodeName = [Text.Encoding]::Unicode.GetString($BinaryReader.ReadBytes($UnicodeNameLength * 2)).TrimEnd(@(0))
[Byte[]] $SignatureDataBytes = $BinaryReader.ReadBytes($VariableDataLength)
if (@('PK', 'KEK', 'db', 'dbx') -contains $UnicodeName) {
# A EFI_SIGNATURE_DATA instance
# "The EFI_VARIABLE_DATA.VariableData value shall be the EFI_SIGNATURE_DATA value from
# the EFI_SIGNATURE_LIST that contained the authority that was used to validate the image
# and the EFI_VARIABLE_DATA.VariableName shall be set to EFI_IMAGE_SECURITY_DATABASE_GUID.
# The EFI_VARIABLE_DATA.UnicodeName shall be set to the value of EFI_IMAGE_SECURITY_DATABASE."
$SignatureOwner = [Guid][Byte[]] $SignatureDataBytes[0..15]
$SignatureBytes = $SignatureDataBytes[16..($SignatureDataBytes.Count - 1)]
$SignatureData = New-Object -TypeName $SignatureObjectType -ArgumentList (@(,$SignatureBytes))
$VariableData = [PSCustomObject] @{
SignatureOwner = $SignatureOwner
SignatureData = $SignatureData
}
} else {
# Just return a byte array for unknown/new UEFI variables
$VariableData = $SignatureDataBytes
}
$Event = [PSCustomObject] @{
PSTypeName = 'TCGUEFIVariable'
VariableGUID = $VariableName
VariableName = $UnicodeName
VariableData = $VariableData
}
}
'EV_EFI_VARIABLE_DRIVER_CONFIG' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
$VarMemoryStream = New-Object -TypeName IO.MemoryStream -ArgumentList @(,$EventBytes)
$VarBinaryReader = New-Object -TypeName IO.BinaryReader -ArgumentList $VarMemoryStream, ([Text.Encoding]::Unicode)
$VariableName = [Guid] $VarBinaryReader.ReadBytes(16)
# To-do: These lengths are dependant upon the platform architecture. Currently, I'm only considering 64-bit platforms
$UnicodeNameLength = $VarBinaryReader.ReadUInt64()
$VariableDataLength = $VarBinaryReader.ReadUInt64()
$UnicodeName = [Text.Encoding]::Unicode.GetString($VarBinaryReader.ReadBytes($UnicodeNameLength * 2)).TrimEnd(@(0))
if (@('PK', 'KEK', 'db', 'dbx') -contains $UnicodeName) {
# Parse out the EFI_SIGNATURE_LIST structs
$SignatureTypeMapping = @{
'C1C41626-504C-4092-ACA9-41F936934328' = 'EFI_CERT_SHA256_GUID' # Most often used for dbx
'A5C059A1-94E4-4AA7-87B5-AB155C2BF072' = 'EFI_CERT_X509_GUID' # Most often used for db
}
while ($VarBinaryReader.PeekChar() -ne -1) {
$SignatureType = $SignatureTypeMapping[([Guid][Byte[]] $VarBinaryReader.ReadBytes(16)).Guid]
$SignatureListSize = $VarBinaryReader.ReadUInt32()
$SignatureHeaderSize = $VarBinaryReader.ReadUInt32()
$SignatureSize = $VarBinaryReader.ReadUInt32()
$null = $VarBinaryReader.ReadBytes($SignatureHeaderSize) # SignatureHeader
# 0x1C is the size of the EFI_SIGNATURE_LIST header
$SignatureCount = ($SignatureListSize - 0x1C) / $SignatureSize
$Signature = 1..$SignatureCount | ForEach-Object {
$SignatureDataBytes = $VarBinaryReader.ReadBytes($SignatureSize)
$SignatureOwner = [Guid][Byte[]] $SignatureDataBytes[0..15]
switch ($SignatureType) {
'EFI_CERT_SHA256_GUID' {
$SignatureData = ([Byte[]] $SignatureDataBytes[0x10..0x2F] | ForEach-Object { $_.ToString('X2') }) -join ''
}
'EFI_CERT_X509_GUID' {
$SignatureData = New-Object $SignatureObjectType -ArgumentList @(,([Byte[]] $SignatureDataBytes[16..($SignatureDataBytes.Count - 1)]))
}
}
[PSCustomObject] @{
PSTypeName = 'EFI.SignatureData'
SignatureOwner = $SignatureOwner
SignatureData = $SignatureData
}
}
$VariableData = [PSCustomObject] @{
SignatureType = $SignatureType
Signature = $Signature
}
}
} else {
$VariableData = $VarBinaryReader.ReadBytes($VariableDataLength)
}
$VarBinaryReader.Close()
$Event = [PSCustomObject] @{
PSTypeName = 'TCGUEFIVariable'
VariableGUID = $VariableName
VariableName = $UnicodeName
VariableData = $VariableData
}
}
'EV_EFI_BOOT_SERVICES_APPLICATION' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
$ImageLocationInMemory = [BitConverter]::ToUInt64($EventBytes, 0)
$ImageLengthInMemory = [BitConverter]::ToUInt64($EventBytes, 8)
$ImageLinkTimeAddress = [BitConverter]::ToUInt64($EventBytes, 16)
$LengthOfDevicePath = [BitConverter]::ToUInt64($EventBytes, 24)
$DevicePathBytes = $EventBytes[32..(32 + $LengthOfDevicePath - 1)]
$FilePathList = $null
# Parse all the file list entries
if ($DevicePathBytes.Count) {
$MoreToParse = $True
$FilePathEntryIndex = 0
$FilePathList = while ($MoreToParse) {
# Parse the EFI_DEVICE_PATH_PROTOCOL struct.
$DevicePathType = $DevicePathTypeMapping[$DevicePathBytes[$FilePathEntryIndex]]
$Length = [BitConverter]::ToUInt16($DevicePathBytes, $FilePathEntryIndex + 2)
[Byte[]] $DataBytes = $DevicePathBytes[($FilePathEntryIndex + 4)..($FilePathEntryIndex + $Length - 1)]
switch ($DevicePathType) {
'ACPI_DEVICE_PATH' {
$DeviceSubType = $ACPIDeviceSubTypeMapping[$DevicePathBytes[$FilePathEntryIndex + 1]]
switch ($DeviceSubType) {
'ACPI_DP' {
$HID = [BitConverter]::ToUInt32($DevicePathBytes, $FilePathEntryIndex + 4 + 0)
$UID = [BitConverter]::ToUInt32($DevicePathBytes, $FilePathEntryIndex + 4 + 4)
$DeviceInfo = [PSCustomObject] @{
HID = $HID # Device's PnP hardware ID stored in a numeric 32-bit
# compressed EISA-type ID. This value must match the
# corresponding _HID in the ACPI name space.
UID = $UID # Unique ID that is required by ACPI if two devices have the
# same _HID. This value must also match the corresponding
# _UID/_HID pair in the ACPI name space.
}
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
'ACPI_EXTENDED_DP' {
$HID = [BitConverter]::ToUInt32($DevicePathBytes, $FilePathEntryIndex + 4 + 0)
$UID = [BitConverter]::ToUInt32($DevicePathBytes, $FilePathEntryIndex + 4 + 4)
$CID = [BitConverter]::ToUInt32($DevicePathBytes, $FilePathEntryIndex + 4 + 8)
$DeviceInfo = [PSCustomObject] @{
HID = $HID
UID = $UID
CID = $CID # Device's compatible PnP hardware ID stored in a numeric
# 32-bit compressed EISA-type ID.
}
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
'ACPI_ADR_DP' {
$ADR = [BitConverter]::ToUInt32($DevicePathBytes, $FilePathEntryIndex + 4 + 0)
$DeviceInfo = [PSCustomObject] @{
ADR = $ADR # For video output devices the value of this
# field comes from Table B-2 of the ACPI 3.0 specification.
}
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
}
}
'MEDIA_DEVICE_PATH' {
$DeviceSubType = $MediaDeviceSubTypeMapping[$DevicePathBytes[$FilePathEntryIndex + 1]]
switch ($DeviceSubType) {
'MEDIA_HARDDRIVE_DP' {
$PartitionNumber = [BitConverter]::ToUInt32($DevicePathBytes, $FilePathEntryIndex + 4 + 0)
$PartitionStart = [BitConverter]::ToUInt64($DevicePathBytes, $FilePathEntryIndex + 4 + 4)
$PartitionSize = [BitConverter]::ToUInt64($DevicePathBytes, $FilePathEntryIndex + 4 + 4 + 8)
$SignatureIndex = $FilePathEntryIndex + 4 + 4 + 8 + 8
[Byte[]] $SignatureBytes = $DevicePathBytes[$SignatureIndex..($SignatureIndex + 16 - 1)]
$MBRType = @{ [Byte] 1 = 'MBR_TYPE_PCAT'; [Byte] 2 = 'MBR_TYPE_EFI_PARTITION_TABLE_HEADER' }[$DevicePathBytes[$SignatureIndex + 16]]
$SignatureType = @{ [Byte] 0 = 'NO_DISK_SIGNATURE'; [Byte] 1 = 'SIGNATURE_TYPE_MBR'; [Byte] 2 = 'SIGNATURE_TYPE_GUID' }[$DevicePathBytes[$SignatureIndex + 16 + 1]]
$DeviceInfo = [PSCustomObject] @{
PartitionNumber = $PartitionNumber
PartitionStart = $PartitionStart
PartitionSize = $PartitionSize
Signature = ($SignatureBytes | ForEach-Object {$_.ToString('X2')}) -join ':'
MBRType = $MBRType
SignatureType = $SignatureType
}
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
'MEDIA_FILEPATH_DP' {
$PathName = [Text.Encoding]::Unicode.GetString($DataBytes).TrimEnd(@(0))
$DeviceInfo = [PSCustomObject] @{ PathName = $PathName }
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
'MEDIA_PIWG_FW_VOL_DP' {
$DeviceInfo = [PSCustomObject] @{ FvName = [Guid] $DataBytes }
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
'MEDIA_PIWG_FW_FILE_DP' {
$DeviceInfo = [PSCustomObject] @{ FvFileName = [Guid] $DataBytes }
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
default {
$DeviceSubType = $DevicePathBytes[$FilePathEntryIndex + 1].ToString('X2')
$DeviceInfo = [PSCustomObject] @{ RawDeviceBytes = $DataBytes }
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
}
}
'END_DEVICE_PATH_TYPE' { }
default {
# Until other subtypes are added, just supply the bytes.
$DeviceSubType = $DevicePathBytes[$FilePathEntryIndex + 1].ToString('X2')
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
Length = $Length
Data = ($DataBytes | ForEach-Object {$_.ToString('X2')}) -join ':'
}
}
}
$FilePathEntryIndex = $FilePathEntryIndex + $Length
$MoreToParse = $null -ne $DevicePathBytes[$FilePathEntryIndex]
}
}
$Event = [PSCustomObject] @{
ImageLocationInMemory = $ImageLocationInMemory
ImageLengthInMemory = $ImageLengthInMemory
ImageLinkTimeAddress = $ImageLinkTimeAddress
DevicePath = $FilePathList
}
}
'EV_EFI_ACTION' {
$EventBytes = $BinaryReader.ReadBytes($EventSize)
$Event = [Text.Encoding]::ASCII.GetString($EventBytes).TrimEnd(@(0))
}
'EV_EFI_VARIABLE_BOOT' {
$VariableName = [Guid] $BinaryReader.ReadBytes(16)
$UnicodeNameLength = $BinaryReader.ReadUInt64()
$VariableDataLength = $BinaryReader.ReadUInt64()
$UnicodeName = [Text.Encoding]::Unicode.GetString($BinaryReader.ReadBytes($UnicodeNameLength * 2)).TrimEnd(@(0))
if ($UnicodeName -eq 'BootOrder') {
$VariableData = 1..($VariableDataLength / 2) | ForEach-Object { $BinaryReader.ReadUInt16().ToString('X4') }
} elseif ($UnicodeName -match '^Boot[0-9A-F]{4}$') {
$VariableDataBytes = $BinaryReader.ReadBytes($VariableDataLength)
$Attributes = [BitConverter]::ToUInt32($VariableDataBytes, 0)
$FilePathListLength = [BitConverter]::ToUInt16($VariableDataBytes, 4)
$Index = 6
$DescriptionChars = do {
$CharVal = [BitConverter]::ToUInt16($VariableDataBytes, $index)
[Char] $CharVal
$Index += 2
} while ($CharVal -ne 0)
[String] $Description = $DescriptionChars -join ''
$FilePathListEndIndex = $Index + $FilePathListLength - 1
# This will be of type: EFI_DEVICE_PATH_PROTOCOL
[Byte[]] $FilePathListBytes = $VariableDataBytes[$Index..$FilePathListEndIndex]
$FilePathList = $null
# Parse all the file list entries
if ($FilePathListBytes.Count) {
$MoreToParse = $True
$FilePathEntryIndex = 0
$FilePathList = while ($MoreToParse) {
# Parse the EFI_DEVICE_PATH_PROTOCOL struct.
$DevicePathType = $DevicePathTypeMapping[$FilePathListBytes[$FilePathEntryIndex]]
$Length = [BitConverter]::ToUInt16($FilePathListBytes, $FilePathEntryIndex + 2)
[Byte[]] $DataBytes = $FilePathListBytes[($FilePathEntryIndex + 4)..($FilePathEntryIndex + $Length - 1)]
switch ($DevicePathType) {
'MEDIA_DEVICE_PATH' {
$DeviceSubType = $MediaDeviceSubTypeMapping[$FilePathListBytes[$FilePathEntryIndex + 1]]
switch ($DeviceSubType) {
'MEDIA_HARDDRIVE_DP' {
$PartitionNumber = [BitConverter]::ToUInt32($FilePathListBytes, $FilePathEntryIndex + 4 + 0)
$PartitionStart = [BitConverter]::ToUInt64($FilePathListBytes, $FilePathEntryIndex + 4 + 4)
$PartitionSize = [BitConverter]::ToUInt64($FilePathListBytes, $FilePathEntryIndex + 4 + 4 + 8)
$SignatureIndex = $FilePathEntryIndex + 4 + 4 + 8 + 8
[Byte[]] $SignatureBytes = $FilePathListBytes[$SignatureIndex..($SignatureIndex + 16 - 1)]
$MBRType = @{ [Byte] 1 = 'MBR_TYPE_PCAT'; [Byte] 2 = 'MBR_TYPE_EFI_PARTITION_TABLE_HEADER' }[$FilePathListBytes[$SignatureIndex + 16]]
$SignatureType = @{ [Byte] 0 = 'NO_DISK_SIGNATURE'; [Byte] 1 = 'SIGNATURE_TYPE_MBR'; [Byte] 2 = 'SIGNATURE_TYPE_GUID' }[$FilePathListBytes[$SignatureIndex + 16 + 1]]
$DeviceInfo = [PSCustomObject] @{
PartitionNumber = $PartitionNumber
PartitionStart = $PartitionStart
PartitionSize = $PartitionSize
Signature = ($SignatureBytes | ForEach-Object {$_.ToString('X2')}) -join ':'
MBRType = $MBRType
SignatureType = $SignatureType
}
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
'MEDIA_FILEPATH_DP' {
$PathName = [Text.Encoding]::Unicode.GetString($DataBytes).TrimEnd(@(0))
$DeviceInfo = [PSCustomObject] @{ PathName = $PathName }
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
'MEDIA_PIWG_FW_VOL_DP' {
$DeviceInfo = [PSCustomObject] @{ FvName = [Guid] $DataBytes }
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
'MEDIA_PIWG_FW_FILE_DP' {
$DeviceInfo = [PSCustomObject] @{ FvFileName = [Guid] $DataBytes }
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
default {
$DeviceSubType = $FilePathListBytes[$FilePathEntryIndex + 1].ToString('X2')
$DeviceInfo = [PSCustomObject] @{ RawDeviceBytes = $DataBytes }
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
DeviceInfo = $DeviceInfo
}
}
}
}
'END_DEVICE_PATH_TYPE' { }
default {
# Until other subtypes are added, just supply the bytes.
$DeviceSubType = $FilePathListBytes[$FilePathEntryIndex + 1].ToString('X2')
[PSCustomObject] @{
Type = $DevicePathType
SubType = $DeviceSubType
Length = $Length
Data = ($DataBytes | ForEach-Object {$_.ToString('X2')}) -join ':'
}
}
}
$FilePathEntryIndex = $FilePathEntryIndex + $Length
$MoreToParse = $null -ne $FilePathListBytes[$FilePathEntryIndex]
}
}
$OptionalData = $null
# The remaining bytes in the load option descriptor are a binary data buffer that is passed to the loaded image.
# If the field is zero bytes long, a NULL pointer is passed to the loaded image. The number of bytes in OptionalData
# can be computed by subtracting the starting offset of OptionalData from total size in bytes of the EFI_LOAD_OPTION.
if (($VariableDataBytes.Count - ($FilePathListEndIndex + 1)) -gt 0) { $OptionalData = $VariableDataBytes[($FilePathListEndIndex + 1)..($VariableDataBytes.Count - 1)] }
if ($OptionalData) { $OptionalData = ($OptionalData | ForEach-Object {$_.ToString('X2')}) -join ':' }
$VariableData = [PSCustomObject] @{
Attributes = $Attributes
FilePathListLength = $FilePathListLength
Description = $Description.TrimEnd(@(0))
FilePathList = $FilePathList
OptionalData = $OptionalData
}
} else {
$VariableData = $BinaryReader.ReadBytes($VariableDataLength)
}
$Event = [PSCustomObject] @{
PSTypeName = 'TCGUEFIVariable'
VariableGUID = $VariableName
VariableName = $UnicodeName
VariableData = $VariableData
}
}
default {
$Event = ($BinaryReader.ReadBytes($EventSize) | ForEach-Object {$_.ToString('X2')}) -join ':'
}
}
[Ordered] @{
PCR = $PCRIndex
EventType = $EventType
Digest = $Digests
Event = $Event
}
}
$BinaryReader.Close()
$PCRTemplate = [Ordered] @{
PCR0 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR1 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR2 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR3 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR4 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR5 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR6 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR7 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR8 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR9 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR10 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR11 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR12 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR13 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR14 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR15 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR16 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCR23 = (New-Object 'System.Collections.Generic.List[PSObject]')
PCRMinusOne = (New-Object 'System.Collections.Generic.List[PSObject]')
}
foreach ($PCRMeasurement in $Events) {
if ($PCRMeasurement['PCR'] -eq -1) {
$PCRMeasurement.Remove('PCR')
$PCRTemplate['PCRMinusOne'].Add(([PSCustomObject] $PCRMeasurement))
} else {
$PCRNum = $PCRMeasurement['PCR']
$PCRMeasurement.Remove('PCR')
$PCRTemplate["PCR$($PCRNum)"].Add(([PSCustomObject] $PCRMeasurement))
}
}
foreach ($Key in $PCRTemplate.GetEnumerator().Name) {
if ($PCRTemplate[$Key].Count -eq 0) { $PCRTemplate[$Key] = $null }
if ($PCRTemplate[$Key].Count -eq 1) { $PCRTemplate[$Key] = $PCRTemplate[$Key][0] }
}
$TCGEventLog = [PSCustomObject] @{
PSTypeName = 'TCGLog'
LogPath = $LogFullPath
Header = $TCGHeader
Events = ([PSCustomObject] $PCRTemplate)
}
$TCGEventLog
}
Export-ModuleMember -Function Get-TCGLogContent, ConvertTo-TCGEventLog, Get-TPMDeviceInfo