-
Notifications
You must be signed in to change notification settings - Fork 15
Please consider adding these changes. #30
Copy link
Copy link
Open
Description
# License: MIT
# Repository: https://github.com/cjee21/Check-UEFISecureBootVariables
# Check for admin
Write-Host "Checking for Administrator permission..."
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Warning "Insufficient permissions to run this script. Please run as administrator."
Break
} else {
Write-Host "Running as administrator - continuing execution...`n"
}
# Check files
if (-not ((Test-Path -Path "$PSScriptRoot\Check-Dbx-Simplified.ps1" -PathType Leaf) -and `
(Test-Path -Path "$PSScriptRoot\Get-UEFIDatabaseSignatures.psm1" -PathType Leaf) -and `
(Test-Path -Path "$PSScriptRoot\..\dbx_bin\*.bin") -and `
(Test-Path -Path "$PSScriptRoot\..\dbx_info\*.json"))) {
Write-Warning "Some required files are missing. Please re-obtain a copy from https://github.com/cjee21/Check-UEFISecureBootVariables."
Break
}
# Print computer info
Get-Date -Format 'dd MMMM yyyy'
$computer = Get-CimInstance -ClassName Win32_ComputerSystem
$bios = Get-CimInstance -ClassName Win32_BIOS
"Manufacturer: " + $computer.Manufacturer
"Model: " + $computer.Model
$biosinfo = $bios.Manufacturer , $bios.Name , $bios.SMBIOSBIOSVersion , $bios.Version -join ", "
"BIOS: " + $biosinfo
$v = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
"Windows version: {0} (Build {1}.{2})`n" -f $v.DisplayVersion, $v.CurrentBuildNumber, $v.UBR
# Check architecture
$IsArm = $false
$Is64bit = $true
try {
$cpuArch = (Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop).Architecture
# 0 = x86, 9 = x64, 5 = ARM, 12 = ARM64
if ($cpuArch -eq 5 -or $cpuArch -eq 12) {
$IsArm = $true
}
# Windows and UEFI bit-ness should always match on officially supported installs
# since UEFI doesn't support cross-platform boot as of https://learn.microsoft.com/en-us/windows/deployment/windows-deployment-scenarios-and-tools#windows-support-for-uefi
$Is64bit = [Environment]::Is64BitOperatingSystem
} catch {
$IsArm = $false
$Is64bit = $true
Write-Warning "Unable to determine system architecture, proceeding with defaults (x64).`n"
$cpuArch = 9 # default x64
}
$arch = if ($Is64bit -and $cpuArch -eq 9) { # CPU arch x64
"x64"
} elseif ($Is64bit -and $cpuArch -eq 12) { # CPU arch ARM64
"arm64"
} elseif (-not $Is64bit -and ($cpuArch -eq 0 -or $cpuArch -eq 9)) {
"x86" # CPU arch can be x86 or x64, but Windows/EFI arch is x86, thus the one we need.
} elseif (-not $Is64bit -and $IsArm) { # cpu arch check with $IsArm above
"arm"
} else { # any other unsupported CPU architecture
"unsupported"
}
Write-Host "Detected $arch UEFI architecture. Ensure that this is correct for valid DBX results.`n"
# Check for Secure Boot status
Write-Host "Secure Boot status: " -NoNewLine
try {
$status = Confirm-SecureBootUEFI -ErrorAction Stop
if ($status -eq $True) {
Write-Host "$([char]0x1b)[92mEnabled$([char]0x1b)[0m`n"
}
elseif ($status -eq $False) {
Write-Host "$([char]0x1b)[91mDisabled$([char]0x1b)[0m`n"
}
}
catch [System.PlatformNotSupportedException] {
Write-Host "$([char]0x1b)[91mNot available$([char]0x1b)[0m`n"
Break
}
catch {
Write-Host "$([char]0x1b)[91mUnknown$([char]0x1b)[0m`n"
Break
}
$bold = "$([char]0x1b)[1m"
$reset = "$([char]0x1b)[0m"
$check = "$([char]0x1b)[92m$([char]8730)$reset"
$cross = "$([char]0x1b)[91mX$reset"
Import-Module -Force "$PSScriptRoot\Get-UEFIDatabaseSignatures.psm1"
Write-Host $bold'Current UEFI PK'$reset
try {
$pk = Get-SecureBootUEFI -Name pk | Get-UEFIDatabaseSignatures
# ORIGINAL:
# $pk.SignatureList.SignatureData.Subject | ForEach-Object {
# $pk_name = [regex]::Match($_, 'CN=([^,]+)').Groups[1].Value
# Write-Host "$check $pk_name"
# }
# CHANGE: Only process entries where SignatureData is an X509Certificate2.
# This avoids trying to read .Subject from non-certificate entries (e.g. hashes),
# which is what caused the intermittent "CN=..." garbage output on early runs.
$pk.SignatureList |
Where-Object { $_.SignatureData -is [System.Security.Cryptography.X509Certificates.X509Certificate2] } |
ForEach-Object {
$subject = $_.SignatureData.Subject
$pk_name = [regex]::Match($subject, 'CN=([^,]+)').Groups[1].Value
if ($pk_name) {
Write-Host "$check $pk_name"
}
}
} catch {
Write-Warning "Failed to query UEFI variable PK"
}
Write-Host ""
Write-Host $bold'Default UEFI PK'$reset
if ($IsArm) {
Write-Warning "Some ARM-based Windows devices can't retrieve default UEFI variables."
}
try {
$pk_default = Get-SecureBootUEFI -Name PKDefault | Get-UEFIDatabaseSignatures
# ORIGINAL:
# $pk_default.SignatureList.SignatureData.Subject | ForEach-Object {
# $pk_name = [regex]::Match($_, 'CN=([^,]+)').Groups[1].Value
# Write-Host "$check $pk_name"
# }
# CHANGE: Same fix as for current PK — only handle true X509 certificates.
# This keeps behavior consistent and prevents mis-parsing on first run after clean installs.
$pk_default.SignatureList |
Where-Object { $_.SignatureData -is [System.Security.Cryptography.X509Certificates.X509Certificate2] } |
ForEach-Object {
$subject = $_.SignatureData.Subject
$pk_name = [regex]::Match($subject, 'CN=([^,]+)').Groups[1].Value
if ($pk_name) {
Write-Host "$check $pk_name"
}
}
} catch {
Write-Warning "Failed to query UEFI variable PKDefault"
}
function Show-UEFICertIsPresent {
param (
[Parameter(Mandatory)]
[string]$SecureBootUEFIVar,
[Parameter(Mandatory)]
[string]$CertName
)
try {
if ([System.Text.Encoding]::ASCII.GetString((Get-SecureBootUEFI $SecureBootUEFIVar -ErrorAction Stop).bytes) -match $CertName) {
if ($CertName) {
$revoked = $false
try {
$revoked = [System.Text.Encoding]::ASCII.GetString((Get-SecureBootUEFI dbx -ErrorAction Stop).bytes) -match $CertName
} catch {
$revoked = $false
}
Write-Host "$check $CertName (revoked: $revoked)"
} else {
Write-Host "$check $CertName (revoked: Unknown)"
}
} else {
Write-Host "$cross $CertName"
}
} catch {
Write-Warning "Failed to query UEFI variable '$SecureBootUEFIVar' for cert '$CertName'"
}
}
function Show-UEFICertOthers {
param (
[Parameter(Mandatory)]
[string]$SecureBootUEFIVar,
[Parameter(Mandatory)]
[Array]$KnownCerts
)
try {
$certs = Get-SecureBootUEFI -Name $SecureBootUEFIVar | Get-UEFIDatabaseSignatures
# ORIGINAL:
# $cert_names = @()
# $certs.SignatureList.SignatureData.Subject | ForEach-Object {
# $cert_names += [regex]::Match($_, 'CN=([^,]+)').Groups[1].Value
# }
# CHANGE: Only collect CNs from entries whose SignatureData is an X509 certificate.
# This prevents SHA256 hashes or other non-certificate entries from being treated as
# having a Subject, which was the root cause of the intermittent "CN=..." noise.
$cert_names = @()
$certs.SignatureList |
Where-Object { $_.SignatureData -is [System.Security.Cryptography.X509Certificates.X509Certificate2] } |
ForEach-Object {
$subject = $_.SignatureData.Subject
$cn = [regex]::Match($subject, 'CN=([^,]+)').Groups[1].Value
if ($cn) {
$cert_names += $cn
}
}
$cert_names | ForEach-Object {
if ($KnownCerts -notcontains $_) {
# List out all other certs found other than those in known list
# No check for revocation since not all certs have unique CNs and we do not check by thumbprint
Write-Host "$check $_"
}
}
} catch {
Write-Warning "Failed to query UEFI variable '$SecureBootUEFIVar'"
}
}
$KEKCerts = @(
'Microsoft Corporation KEK CA 2011'
'Microsoft Corporation KEK 2K CA 2023'
)
Write-Host ""
Write-Host $bold'Current UEFI KEK'$reset
$KEKCerts | ForEach-Object {
Show-UEFICertIsPresent -SecureBootUEFIVar kek -CertName $_
}
Show-UEFICertOthers -SecureBootUEFIVar kek -KnownCerts $KEKCerts
Write-Host ""
Write-Host $bold'Default UEFI KEK'$reset
if ($IsArm) {
Write-Warning "Some ARM-based Windows devices can't retrieve default UEFI variables."
}
$KEKCerts | ForEach-Object {
Show-UEFICertIsPresent -SecureBootUEFIVar KEKDefault -CertName $_
}
Show-UEFICertOthers -SecureBootUEFIVar KEKDefault -KnownCerts $KEKCerts
$DBCerts = @(
'Microsoft Windows Production PCA 2011'
'Microsoft Corporation UEFI CA 2011'
'Windows UEFI CA 2023'
'Microsoft UEFI CA 2023'
'Microsoft Option ROM UEFI CA 2023'
)
Write-Host ""
Write-Host $bold'Current UEFI DB'$reset
$DBCerts | ForEach-Object {
Show-UEFICertIsPresent -SecureBootUEFIVar db -CertName $_
}
Show-UEFICertOthers -SecureBootUEFIVar db -KnownCerts $DBCerts
Write-Host ""
Write-Host $bold'Default UEFI DB'$reset
if ($IsArm) {
Write-Warning "Some ARM-based Windows devices can't retrieve default UEFI variables."
}
$DBCerts | ForEach-Object {
Show-UEFICertIsPresent -SecureBootUEFIVar dbDefault -CertName $_
}
Show-UEFICertOthers -SecureBootUEFIVar DBDefault -KnownCerts $DBCerts
Write-Host ""
Write-Host $bold'Current UEFI DBX'$reset
$colWidth = 27
function Show-CheckDBX {
param(
[Parameter(Mandatory)][string]$Label,
[Parameter(Mandatory)][string]$File
)
Write-Host ($Label.PadRight($colWidth) + " : ") -NoNewline
try {
$oldPreference = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
& "$PSScriptRoot\Check-Dbx-Simplified.ps1" "$File"
$ErrorActionPreference = $oldPreference
} catch {
Write-Host "ERROR: An exception has occurred while checking DBX" -ForegroundColor Red
}
}
# select the proper bin file for the DBX Update.
# files are copied from https://github.com/microsoft/secureboot_objects/tree/main/PostSignedObjects/DBX
if ($arch -eq "x64") {
# Show-CheckDBX "2023-03-14 " "$PSScriptRoot\..\dbx_bin\x64_DBXUpdate_2023-03-14.bin"
# Show-CheckDBX "2023-05-09 " "$PSScriptRoot\..\dbx_bin\x64_DBXUpdate_2023-05-09.bin"
# Show-CheckDBX "2025-01-14 (v1.3.1)" "$PSScriptRoot\..\dbx_bin\x64_DBXUpdate_2025-01-14.bin"
# Show-CheckDBX "2025-06-11 (v1.5.1)" "$PSScriptRoot\..\dbx_bin\x64_DBXUpdate_2025-06-11.bin"
Show-CheckDBX "2025-10-14 (v1.6.0) [$arch]" "$PSScriptRoot\..\dbx_bin\x64_DBXUpdate_2025-10-14.bin"
} elseif ($arch -eq "arm64") {
Show-CheckDBX "2025-02-25 (v1.4.0) [$arch]" "$PSScriptRoot\..\dbx_bin\arm64_DBXUpdate_2025-02-25.bin"
} elseif ($arch -eq "x86") {
Show-CheckDBX "2025-10-14 (v1.6.0) [$arch]" "$PSScriptRoot\..\dbx_bin\x86_DBXUpdate_2025-10-14.bin"
} elseif ($arch -eq "arm") {
Show-CheckDBX "2025-02-25 (v1.4.0) [$arch]" "$PSScriptRoot\..\dbx_bin\arm_DBXUpdate_2025-02-25.bin"
} else {
Write-Warning "[$arch] architecture."
}
$svn_latest_dbx = "10_14_25"
$svn_json = Get-Content -Path "$PSScriptRoot\..\dbx_info\dbx_info_msft_$svn_latest_dbx.json" -Raw | ConvertFrom-Json
$svn_bootmgr_latest = [version]($svn_json.svns | Where-Object { $_.guid -eq "{9d132b61-59d5-4388-1cab-185c3cb2eb92} == EFI_BOOTMGR_DBXSVN_GUID" }).version
$svn_cdboot_latest = [version]($svn_json.svns | Where-Object { $_.guid -eq "{e8f82e9d-e127-4158-88a4-4c18abe2f284} == EFI_CDBOOT_DBXSVN_GUID" }).version
$svn_wdsmgfw_latest = [version]($svn_json.svns | Where-Object { $_.guid -eq "{c999cac2-7ffe-496f-2781-9e2a8a535976} == EFI_WDSMGR_DBXSVN_GUID" }).version
# ORIGINAL:
# $dbx_bytes = (Get-SecureBootUEFI dbx).Bytes
# CHANGE: Wrap DBX read in try/catch so the script fails gracefully if DBX is missing or unreadable.
# This doesn’t change behavior on healthy systems, but makes the script more robust on broken firmware.
try {
$dbx_bytes = (Get-SecureBootUEFI dbx -ErrorAction Stop).Bytes
} catch {
Write-Warning "Failed to read UEFI variable 'dbx'. SVN checks will report 'None'."
$dbx_bytes = @()
}
$dbx_hex = ($dbx_bytes | ForEach-Object {'{0:x2}' -f $_}) -join ''
function Get-VersionFromHexString {
# SVN_DATA value:
# Byte[0] is the UINT8 version of the SVN_DATA structure.
# Bytes[1...16] are the GUID of the application being revoked. Little endian.
# Bytes[17...18] are the Minor SVN number. Litte endian UINT16.
# Bytes[19...20] are the Major SVN number. Litte endian UINT16.
# Bytes[21...31] are 11 zero bytes padding.
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$HexString
)
$byteArray = -split ($HexString -replace '..', '$& ') | ForEach-Object {
[System.Convert]::ToByte($_, 16)
}
$MinorBytes = $byteArray[17..18]
$svn_ver_minor = [System.BitConverter]::ToInt16($MinorBytes, 0)
$MajorBytes = $byteArray[19..20]
$svn_ver_major = [System.BitConverter]::ToInt16($MajorBytes, 0)
return [version]::new($svn_ver_major, $svn_ver_minor)
}
Write-Host ("Windows Bootmgr SVN".PadRight($colWidth) + " : ") -NoNewline
$svn_bootmgr = [Regex]::Matches($dbx_hex,'01612B139DD5598843AB1C185C3CB2EB92........0000000000000000000000', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase).Value
if ($svn_bootmgr.Count) {
$svn_bootmgr_vers = $svn_bootmgr | ForEach-Object {
Get-VersionFromHexString($_)
}
$svn_bootmgr_ver = ($svn_bootmgr_vers | Measure-Object -Maximum).Maximum
if ($svn_bootmgr_ver -ge $svn_bootmgr_latest) {
Write-Host $svn_bootmgr_ver -ForegroundColor Green
} else {
Write-Host $svn_bootmgr_ver -ForegroundColor Red
}
} else {
Write-Host 'None' -ForegroundColor Red
}
Write-Host ("Windows cdboot SVN".PadRight($colWidth) + " : ") -NoNewline
$svn_cdboot = [Regex]::Matches($dbx_hex,'019D2EF8E827E15841A4884C18ABE2F284........0000000000000000000000', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase).Value
if ($svn_cdboot.Count) {
$svn_cdboot_vers = $svn_cdboot | ForEach-Object {
Get-VersionFromHexString($_)
}
$svn_cdboot_ver = ($svn_cdboot_vers | Measure-Object -Maximum).Maximum
if ($svn_cdboot_ver -ge $svn_cdboot_latest) {
Write-Host $svn_cdboot_ver -ForegroundColor Green
} else {
Write-Host $svn_cdboot_ver -ForegroundColor Red
}
} else {
Write-Host 'None' -ForegroundColor Red
}
Write-Host ("Windows wdsmgfw SVN".PadRight($colWidth) + " : ") -NoNewline
$svn_wdsmgfw = [Regex]::Matches($dbx_hex,'01C2CA99C9FE7F6F4981279E2A8A535976........0000000000000000000000', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase).Value
if ($svn_wdsmgfw.Count) {
$svn_wdsmgfw_vers = $svn_wdsmgfw | ForEach-Object {
Get-VersionFromHexString($_)
}
$svn_wdsmgfw_ver = ($svn_wdsmgfw_vers | Measure-Object -Maximum).Maximum
if ($svn_wdsmgfw_ver -ge $svn_wdsmgfw_latest) {
Write-Host $svn_wdsmgfw_ver -ForegroundColor Green
} else {
Write-Host $svn_wdsmgfw_ver -ForegroundColor Red
}
} else {
Write-Host 'None' -ForegroundColor Red
}
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels