Last active
February 28, 2022 12:33
-
-
Save potatoqualitee/7b0e8b30c950a82fa218cebcabaa905e to your computer and use it in GitHub Desktop.
Simple file hash comparison tool written in PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Test-FileHash { | |
<# | |
.Synopsis | |
This is a simple file hash comparison tool that writes to Windows Events when changes are detected | |
.Description | |
This is a simple file hash comparison tool that writes to Windows Events when changes are detected | |
.PARAMETER FilePath | |
The path to the file to hash and compare | |
.PARAMETER DictionaryFile | |
The path to the directionary file with previously calculated hashes | |
Defaults to the current directory + compare.xml | |
This file is created if it does not exist | |
.NOTES | |
Copyright: (c) 2022 by Chrissy LeMaire, licensed under MIT | |
License: MIT https://opensource.org/licenses/MIT | |
.Example | |
PS> Test-FileHash -FilePath C:\indexes\index.json -DictionaryFile D:\audit\allhashes.xml | |
Tests to see if the filehash of the file at C:\indexes\index.json has changed since the last time it was hashed. | |
.Example | |
PS> Get-ChildItem -Recurse -Path "C:\Program Files\Microsoft SQL Server" | Test-FileHash -DictionaryFile D:\audit\allhashes.xml | |
Tests to see if the filehash of the file at C:\indexes\index.json has changed since the last time it was hashed. | |
#> | |
[CmdletBinding()] | |
param ( | |
[parameter(ValueFromPipeline, Mandatory)] | |
[Alias("FileName", "FullName")] | |
[psobject[]]$FilePath, # this allows directories, gci and strings to be passed in | |
[System.IO.FileInfo]$DictionaryFile = ".\compare.xml" | |
) | |
begin { | |
# Test for admin -- it's needed to create the event log source | |
$user = [Security.Principal.WindowsIdentity]::GetCurrent() | |
$isadmin = (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) | |
if (-not $isadmin) { | |
throw "You must run this script as administrator" | |
} | |
if ((Test-Path -Path $DictionaryFile)) { | |
$compare = Import-CliXml -Path $DictionaryFile | |
} else { | |
$compare = $null | |
$baseline = $true | |
} | |
if (-not ([System.Diagnostics.EventLog]::SourceExists("FileHash Checker"))) { | |
$null = New-EventLog -LogName Application -Source "FileHash Checker" | |
} | |
$message = New-Object System.Collections.ArrayList | |
$output = New-Object System.Collections.Hashtable | |
} | |
process { | |
foreach ($file in $FilePath) { | |
if ($file.FullName) { | |
$filename = $file.FullName | |
} else { | |
$filename = $file | |
} | |
$current = Get-FileHash -Path $filename -Algorithm MD5 -ErrorAction SilentlyContinue | |
if (-not $baseline -and $current) { | |
$original = $compare[$filename] | |
if ($original -ne $current.Hash) { | |
$currenthash = $current.Hash | |
$oldhash = $original | |
if (-not $original) { | |
$currentmessage = "$filename is a new file with a hash of $currenthash" | |
} else { | |
$currentmessage = "$filename had a previous hash of $oldhash but now has a hash of $currenthash" | |
} | |
$null = $message.Add($currentmessage) | |
Write-Warning -Message $currentmessage | |
} | |
} | |
if ($current) { | |
$null = $output.Add($filename, $current.Hash) | |
$current | |
} | |
} | |
} | |
end { | |
if ($message) { | |
# Just make one entry with all of the filehash failures | |
$params = @{ | |
LogName = "Application" | |
Source = "FileHash Checker" | |
EntryType = "Warning" | |
Category = 0 | |
EventId = 1000 | |
Message = ($message -join [Environment]::NewLine) | |
} | |
Write-EventLog @params | |
} | |
$null = $output | Export-CliXml -Path $DictionaryFile | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment