Skip to content

Instantly share code, notes, and snippets.

@planetrocky
Created November 15, 2025 04:02
Show Gist options
  • Select an option

  • Save planetrocky/5fc09997022a745772e7410bdd269af6 to your computer and use it in GitHub Desktop.

Select an option

Save planetrocky/5fc09997022a745772e7410bdd269af6 to your computer and use it in GitHub Desktop.
Windows file checksums stored in Alternate Data Stream (ADS)
#!/usr/bin/env pwsh -NoExit
<#
.SYNOPSIS
Adds SHA256 checksums to files as Alternate Data Streams (ADS) and verifies existing checksums.
.DESCRIPTION
This script processes files in the specified directories and either:
- Creates new SHA256 checksums stored in 'hash.sha256' Alternate Data Streams
- Verifies existing checksums against current file content
The script preserves original file timestamps and provides color-coded output
indicating success or failure of checksum operations. Uses PowerShell 7+ features
for improved performance and functionality.
.INPUTS
System.String[]
One or more directory paths to process. If no paths are provided, processes the current directory.
.OUTPUTS
None. Writes results to the console.
.EXAMPLE
add_checksums_to_ads.ps1 C:\MyFiles D:\OtherFiles
Processes all files in C:\MyFiles and D:\OtherFiles directories.
.EXAMPLE
add_checksums_to_ads.ps1 .
Processes all files in the current directory.
.EXAMPLE
Get-ChildItem *.txt | add_checksums_to_ads.ps1
Processes only text files via pipeline input.
.EXAMPLE
add_checksums_to_ads.ps1 -Algorithm SHA512
Uses SHA512 algorithm instead of the default SHA256.
.EXAMPLE
add_checksums_to_ads.ps1 -Force
Forces update of mismatched checksums when verification fails.
.EXAMPLE
add_checksums_to_ads.ps1 -WhatIf
Shows what would happen without making any changes (dry run).
.EXAMPLE
add_checksums_to_ads.ps1 C:\Files -Algorithm SHA384 -Force
Processes C:\Files with SHA384 algorithm and forces updates of mismatched checksums.
.NOTES
Author: PowerShell Script
Requires: PowerShell 7.0 or later
Alternate Data Streams are only supported on NTFS file systems.
File: add_checksums_to_ads.ps1
#>
#Requires -Version 7.0
function Add-ChecksumsToADS {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)]
[string[]]$FilePaths = @("."),
[Parameter(Mandatory=$false)]
[ValidateSet('SHA256', 'SHA384', 'SHA512', 'MD5')]
[string]$Algorithm = 'SHA256',
[Parameter(Mandatory=$false)]
[switch]$Force,
[Parameter(Mandatory=$false)]
[switch]$WhatIf
)
begin {
$processedCount = 0
$verifiedCount = 0
$failedCount = 0
$streamName = "hash.$Algorithm".ToLower()
Write-Verbose "Starting checksum processing with algorithm: $Algorithm"
}
process {
foreach ($path in $FilePaths) {
if (-not (Test-Path -LiteralPath $path)) {
Write-Warning "Path '$path' does not exist. Skipping."
$failedCount++
continue
}
try {
$files = Get-ChildItem -LiteralPath $path -File -Recurse -Attributes !System -ErrorAction SilentlyContinue
}
catch {
Write-Warning "Error accessing path '$path': $($_.Exception.Message)"
$failedCount++
continue
}
if (-not $files) {
Write-Host "No files found in path: $path" -ForegroundColor Yellow
continue
}
foreach ($file in $files) {
$adsPath = "$($file.FullName):$streamName"
if (Test-Path -LiteralPath $adsPath) {
try {
$current_checksum = Get-Content -LiteralPath $file.FullName -Stream $streamName -ErrorAction Stop
$checksum = Get-FileHash -LiteralPath $file.FullName -Algorithm $Algorithm | Select-Object -ExpandProperty Hash
if ($WhatIf) {
Write-Host "[WHATIF] $($file.Name) would verify existing '$streamName'" -ForegroundColor Cyan
continue
}
Write-Host -NoNewline "$($file.Name) already has a '$streamName'"
if ($checksum -eq $current_checksum) {
Write-Host " βœ… $($PSStyle.Foreground.Green)Checksums match$($PSStyle.Reset)" -ForegroundColor Green
$verifiedCount++
} else {
Write-Host " ❌ $($PSStyle.Foreground.Red)Current checksum and computed don't match!!!$($PSStyle.Reset)" -ForegroundColor Red
if ($Force) {
Write-Host " Forcing update of checksum..." -ForegroundColor Yellow
Update-FileChecksum -File $file -Algorithm $Algorithm -StreamName $streamName -WhatIf:$WhatIf
}
$failedCount++
}
}
catch {
Write-Host " ❌ Error reading existing ADS for $($file.Name): $($_.Exception.Message)" -ForegroundColor Red
$failedCount++
}
} else {
if ($WhatIf) {
Write-Host "[WHATIF] $($file.Name) would create new '$streamName'" -ForegroundColor Cyan
continue
}
Write-Host -NoNewline "$($file.Name) generating '$streamName'"
Update-FileChecksum -File $file -Algorithm $Algorithm -StreamName $streamName -WhatIf:$WhatIf
}
$processedCount++
}
}
}
end {
if ($processedCount -gt 0) {
Write-Host "`nProcessing Summary:" -ForegroundColor Magenta
Write-Host " Total files processed: $processedCount" -ForegroundColor White
Write-Host " Checksums verified: $verifiedCount" -ForegroundColor Green
Write-Host " Checksums failed: $failedCount" -ForegroundColor Red
}
}
}
function Update-FileChecksum {
param(
[System.IO.FileInfo]$File,
[string]$Algorithm,
[string]$StreamName,
[bool]$WhatIf = $false
)
$lastWriteTime = $file.LastWriteTime
$checksum = Get-FileHash -LiteralPath $file.FullName -Algorithm $Algorithm | Select-Object -ExpandProperty Hash
if ($WhatIf) {
Write-Host " [WHATIF: $checksum]" -ForegroundColor Cyan
return
}
try {
Set-Content -Force -LiteralPath $file.FullName -Stream $StreamName -Value $checksum
Write-Host " $checksum" -ForegroundColor Cyan
} catch {
Write-Host " Failed to write ADS '$StreamName' to $($file.Name) [$($_.Exception.Message)]" -ForegroundColor Red
return
}
try {
$file.LastWriteTime = $lastWriteTime
} catch {
Write-Host "Failed to reset LastWriteTime on $($file.Name)" -ForegroundColor Yellow
}
}
# Main execution block
if ($MyInvocation.InvocationName -ne '.') {
try {
Write-Host "πŸ” Checksum Processor (PowerShell $($PSVersionTable.PSVersion))" -ForegroundColor Magenta
Write-Host " Algorithm: SHA256 | Stream: hash.sha256" -ForegroundColor Gray
# Handle pipeline input
if ($input.MoveNext()) {
$input.Reset()
$paths = @($input)
Write-Host "Processing pipeline input ($($paths.Count) items)..." -ForegroundColor Cyan
} else {
# Process command line arguments
$paths = $args
if ($paths.Count -eq 0) {
$paths = @(".")
}
Write-Host "Processing paths: $($paths -join ', ')" -ForegroundColor Cyan
}
# Detect parameters from arguments
$paramArgs = @()
$remainingPaths = @()
foreach ($arg in $paths) {
if ($arg -match '^-') {
$paramArgs += $arg
} else {
$remainingPaths += $arg
}
}
if ($remainingPaths.Count -eq 0) {
$remainingPaths = @(".")
}
# Invoke the function with detected parameters
Add-ChecksumsToADS -FilePaths $remainingPaths @paramArgs
Write-Host "`nβœ… Checksum processing completed." -ForegroundColor Green
}
catch {
Write-Error "An error occurred during execution: $($_.Exception.Message)"
exit 1
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment