Skip to content

Instantly share code, notes, and snippets.

@jborean93
Created May 20, 2024 20:13
Show Gist options
  • Save jborean93/ed5bdc739330c7905b458860635b8a48 to your computer and use it in GitHub Desktop.
Save jborean93/ed5bdc739330c7905b458860635b8a48 to your computer and use it in GitHub Desktop.
Gets the signed catalog file details from the provided paths
# Copyright: (c) 2024, Jordan Borean (@jborean93) <jborean93@gmail.com>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
#Requires -Modules Ctypes
#Requires -Version 7.3
Function Get-SignedCatalogFile {
<#
.SYNOPSIS
Gets the .cat file for the specified file.
.DESCRIPTION
Gets the signed catalog file for the provided paths.
This can be used to determine what catalog file and the hash/thumbprint
used when matching the file to a catalog.
.PARAMETER Path
The path to the files to get the catalog info for, this accepts wildcards.
.PARAMETER LiteralPath
The literal path to the file to get the catalog info for without processing
wildcard characters in the path
.EXAMPLE
Get-SignedCatalogFile C:\Windows\cmd.exe
#>
[CmdletBinding(DefaultParameterSetName = "Path")]
param (
[Parameter(
Mandatory = $true,
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
ParameterSetName = "Path"
)]
[SupportsWildcards()]
[ValidateNotNullOrEmpty()]
[String[]]
$Path,
[Parameter(
Mandatory = $true,
Position = 0,
ValueFromPipelineByPropertyName = $true,
ParameterSetName = "LiteralPath"
)]
[Alias('PSPath')]
[ValidateNotNullOrEmpty()]
[String[]]
$LiteralPath
)
begin {
$wintrust = New-CtypesLib Wintrust.dll
ctypes_struct CATALOG_INFO -CharSet Unicode {
[int]$CbStruct
[MarshalAs('ByValTStr', SizeConst = 260)]
[string]$CatalogFile
}
$DRIVER_ACTION_VERIFY = [Guid]::new("F750E6C3-38EE-11d1-85E5-00C04FC295EE")
$catAdmin = [IntPtr]::Zero
$res = $wintrust.SetLastError().CryptCATAdminAcquireContext[bool](
[ref]$catAdmin,
[ref]$DRIVER_ACTION_VERIFY,
0)
if (-not $res) {
$exp = [System.ComponentModel.Win32Exception]::new($wintrust.LastError)
$err = [System.Management.Automation.ErrorRecord]::new(
$exp,
"FailedToOpenCATAdmin",
"NotSpecified",
$null)
$err.ErrorDetails = 'Failed to open CAT Admin Context: Win32 Error 0x{0:X8} - {1}' -f ($exp.NativeErrorCode, $exp.Message)
$PSCmdlet.ThrowTerminatingError($err)
}
}
process {
if ($PSCmdlet.ParameterSetName -eq 'Path') {
$allPaths = $Path | ForEach-Object -Process {
$provider = $null
try {
$PSCmdlet.SessionState.Path.GetResolvedProviderPathFromPSPath($_, [ref]$provider)
}
catch [System.Management.Automation.ItemNotFoundException] {
$PSCmdlet.WriteError($_)
}
}
}
elseif ($PSCmdlet.ParameterSetName -eq 'LiteralPath') {
$allPaths = $LiteralPath | ForEach-Object -Process {
$resolvedPath = $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_)
if (-not (Test-Path -LiteralPath $resolvedPath)) {
$msg = "Cannot find path '$resolvedPath' because it does not exist"
$err = [System.Management.Automation.ErrorRecord]::new(
[System.Management.Automation.ItemNotFoundException]::new($msg),
"PathNotFound",
"ObjectNotFound",
$resolvedPath)
$PSCmdlet.WriteError($err)
return
}
$resolvedPath
}
}
foreach ($filePath in $allPaths) {
try {
$fileStream = [System.IO.File]::OpenRead($filePath)
try {
$hashSize = 0
$hashBuffer = [Array]::Empty[byte]()
$res = $wintrust.SetLastError().CryptCATAdminCalcHashFromFileHandle[bool](
$fileStream.SafeFileHandle,
[ref]$hashSize,
$null,
0)
if ($wintrust.LastError -eq 122) {
$hashBuffer = [byte[]]::new($hashSize)
$res = $wintrust.SetLastError().EntryPoint('CryptCATAdminCalcHashFromFileHandle').CryptCATAdminCalcHashFromFileHandleWithBuffer[bool](
$fileStream.SafeFileHandle,
[ref]$hashSize,
$hashBuffer,
$null)
}
if (-not $res) {
$exp = [System.ComponentModel.Win32Exception]::new($wintrust.LastError)
$err = [System.Management.Automation.ErrorRecord]::new(
$exp,
"FailedToGetFileHash",
"NotSpecified",
$filePath)
$err.ErrorDetails = 'Failed to get file hash for ''{0}'': Win32 Error 0x{1:X8} - {2}' -f @(
$filePath,
$exp.NativeErrorCode,
$exp.Message)
$PSCmdlet.WriteError($err)
continue
}
}
finally {
$fileStream.Dispose()
}
$catHandle = $wintrust.SetLastError().CryptCATAdminEnumCatalogFromHash[IntPtr](
$catAdmin,
$hashBuffer,
$hashBuffer.Length,
0,
$null)
if ($catHandle -eq [IntPtr]::Zero) {
$exp = [System.ComponentModel.Win32Exception]::new($wintrust.LastError)
if ($wintrust.LastError -eq 0x00000490) {
$err = [System.Management.Automation.ErrorRecord]::new(
$exp,
"FileHasNoCatalog",
"ObjectNotFound",
$filePath)
$err.ErrorDetails = 'File ''{0}'' hash ''{1}'' is not present in any system catalog ' -f @(
$filePath,
([Convert]::ToHexString($hashBuffer))
)
}
else {
$err = [System.Management.Automation.ErrorRecord]::new(
$exp,
"FailedToGetCatalogFromHash",
"NotSpecified",
$filePath)
$err.ErrorDetails = 'Failed to get catalog for ''{0}'' with the hash ''{1}'': Win32 Error 0x{2:X8} - {3}' -f @(
$filePath,
([Convert]::ToHexString($hashBuffer)),
$exp.NativeErrorCode,
$exp.Message
)
}
$PSCmdlet.WriteError($err)
continue
}
try {
$catInfo = [CATALOG_INFO]::new()
$catInfo.CbStruct = [System.Runtime.InteropServices.Marshal]::SizeOf[CATALOG_INFO]()
$res = $wintrust.SetLastError().CharSet('Unicode').CryptCATCatalogInfoFromContext[bool](
$catHandle,
[ref]$catInfo,
0)
if (-not $res) {
$exp = [System.ComponentModel.Win32Exception]::new($wintrust.LastError)
$err = [System.Management.Automation.ErrorRecord]::new(
$exp,
"FailedToGetCatalogInfo",
"NotSpecified",
$filePath)
$err.ErrorDetails = 'Failed to get catalog info for ''{0}'': Win32 Error 0x{1:X8} - {2}' -f @(
$filePath,
$exp.NativeErrorCode,
$exp.Message
)
$PSCmdlet.WriteError($err)
continue
}
[PSCustomObject]@{
Path = $filePath
FileHash = [System.Convert]::ToHexString($hashBuffer)
CatalogFile = $catInfo.CatalogFile
}
}
finally {
$null = $wintrust.SetLastError().CryptCATAdminReleaseCatalogContext[bool]($catAdmin, $catHandle, 0)
}
}
catch {
$PSCmdlet.WriteError($_)
}
}
}
clean {
if ($catAdmin -ne [IntPtr]::Zero) {
$null = $wintrust.SetLastError().CryptCATAdminReleaseContext[bool]($catAdmin, 0)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment