-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHashChildItems.psm1
201 lines (178 loc) · 6.55 KB
/
HashChildItems.psm1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#Requires -Version 5
function Get-Threads {
[CmdletBinding()]
param (
)
try {
(Get-CimInstance -ClassName CIM_Processor -ErrorAction SilentlyContinue).NumberOfLogicalProcessors
}
catch {
return 1
}
}
function Write-ChildItemHash {
<#
.SYNOPSIS
Create hashes of each file found in a given path.
.DESCRIPTION
This Cmdlet will find all child items of a specified path and write the hash
of each child item to its own file. Calling `Write-ChildItemHash -Path C:\foo`
where C:\foo contains bar1.txt, bar2.txt, and bar3.txt will result in new files
C:\foo\bar1.txt.sha256, C:\foo\bar2.txt.sha256, and C:\foo\bar3.txt.sha256.
.PARAMETER Path
Path to directory containing files to be hashed.
.PARAMETER LogFile
Location log file to be written.
.PARAMETER Algorithm
Hashing Algorithm to use.
.PARAMETER Recurse
If specified will traverse entire directory structure rooted at -Path.
#>
[CmdletBinding()]
param (
$Path = ((Get-Location).Path),
$LogFile = "$($Path)\Write-ChildItemHash.$(get-date -Format FileDateTime).log",
$Algorithm = 'sha256',
[Switch]$Recurse = $false,
$Threads = (Get-Threads),
[Switch]$Force = $false
)
Write-Verbose "Running with $Threads threads."
# Normalize to lower case
$Algorithm = $Algorithm.ToLower()
# Get start time for duration tracking.
$starttime = get-date
# Get items to hash
$children = Get-ChildItem -Path $Path -Recurse:$Recurse
# Stage log file
if (Test-Path $LogFile) {
Write-Verbose "Log file found at: $LogFile"
}
else {
Write-Verbose "Creating log file $LogFile"
New-Item -Path $LogFile -ItemType File -Force -Confirm:$false | Out-Null
}
$tohash = @()
# Iterate through child items determine files to hash.
Foreach($child in $children) {
# Skip files that already have a corresponding hash file.
Write-Verbose "Analyzing: $($child.PSPath)"
if ((Test-Path "$($child.PSPath).$Algorithm") -and (-not ($Force))){
Write-Verbose "Existing hash for: $($child.PSPath)"
continue
}
# Skip directories.
if ((Get-Item $child.PSPath) -is [System.IO.DirectoryInfo]) {
Write-Verbose "Directory detected: $($child.PSPath)"
continue
}
# Skip hash files.
if ($child.Name -match "\.$Algorithm$") {
Write-Verbose "Hash file not hashed: $($child.PSPath)"
continue
}
$tohash += $child
}
foreach ($file in $tohash) {
while ((Get-Job | Where-Object State -eq 'Running').count -eq $Threads) {
Write-Progress -Activity 'Hashing Files'`
-Status "$($(Get-Job | Where-Object State -eq 'Running').Name)"`
-PercentComplete ($($(Get-Job | Where-Object State -eq 'Completed').count / $tohash.count) * 100)
Start-Sleep 1
}
Write-Verbose "Launching thread for $($file.name)"
Start-Job -Name $($file.name) -ArgumentList @($Algorithm,$file,$LogFile) -ScriptBlock {
# Get start time for individual files
$itemstart = Get-Date
# Hash the file, output result to file.
try {
$hash = (Get-FileHash -Algorithm $args[0] -Path $args[1].PSPath).hash
$hash | Out-File "$($args[1].pspath).$($args[0])"
}
catch {
# Try to log any errors
Write-Error $_
$_ | Out-File $args[2] -Append
}
# Build a message for logging
$message = @("Filename: $($args[1].name) ",
"Hash: $hash ",
"Time to hash: $($(get-date) - $itemstart)"
)
# Log success information and run time.
$message -join "`n" | Out-File -FilePath $args[2] -Append
} | Out-Null
}
while ((Get-Job | Where-Object State -eq 'Running').count -ne 0) {
Write-Progress -Activity 'Hashing Files'`
-Status "$($(Get-Job | Where-Object State -eq 'Running').Name)"`
-PercentComplete ($($(Get-Job | Where-Object State -eq 'Completed').count / $tohash.count) * 100)
Start-Sleep 1
}
# Clean up jobs
Get-Job | Remove-Job
# Log total time taken.
$endtime = get-date
Write-Verbose "Total time elapsed: $($endtime - $starttime)"
"Total time elapsed: $($endtime - $starttime)" | Out-File -Force -FilePath $LogFile -Append
}
function Compare-ChildItemHash {
<#
.SYNOPSIS
Compares hashes previously computed and stored using Write-ChildItemHash and
logs files whose current hash does not match their stored hash.
.DESCRIPTION
Iterates through all children of a directory (and optionally recurses through
child directories) validating that the current hash of a file matches the value
stored in the associated hash file. Files that do not match their stored hash
are written to the log file.
.PARAMETER Path
Path to directory containing files to be validated.
.PARAMETER LogFile
Path to logfile to write files which do not match their stored hash to.
.PARAMETER Algorithm
Hashing algorithm to use for laidation; also used to detect stored hashes.
Defaults to sha256.
.PARAMETER Recurse
If specified will traverse entire directory structure rooted at -Path.
#>
[CmdletBinding()]
param(
$Path = ((Get-Location).Path),
$LogFile = "$($Path)\Compare-ChildItemHash.$(get-date -Format FileDateTime).log",
$Algorithm = 'sha256',
[Switch]$Recurse = $false
)
# Normalize to lower case
$Algorithm = $Algorithm.ToLower()
# Get start time for duration tracking.
$starttime = Get-Date
# Get items to hash
$children = Get-ChildItem -Path $Path -Recurse:$Recurse |
Where-Object Name -NotLike ".$Algorithm"
# Iterate through child items and verify hash.
foreach ($child in $children) {
# Only check files for which we have a stored hash.
if (Test-Path -Path "$($child.PSPath).$Algorithm") {
Write-Verbose "Comparing hash of $($child.Name) with hash stored in $($child.Name).$Algorithm"
# Retrieve stored hash and compute current hash; normalize to lowercase.
$storedhash = (Get-Content -Path "$($child.PSPath).$Algorithm").ToLower()
$hash = (Get-FileHash -Algorithm $Algorithm $child.PSPath).hash.ToLower()
# If the hash doesn't match write to log file.
if ($storedhash -ne $hash) {
$message = @( "Failed to validate file: $($child.Name)",
"Stored hash: $storedhash",
"Computed hash: $hash"
)
$message -join "`n" | Out-File -Force -FilePath $LogFile -Append
Write-Verbose $($message -join "`n")
}
}
else {
continue
}
}
$endtime = Get-Date
Write-Verbose "Total time elapsed: $($endtime - $starttime)"
"Total time elapsed: $($endtime - $starttime)" | Out-File -Force -FilePath $LogFile -Append
}