我一直在尝试添加另一个命令,并将其输出以显示SHA1和SHA256。同时检查文件是否使用Get-AuthenticatedSignature进行了签名,但似乎无法确定。
Get-ChildItem C: -Recurse -File -Force -ea SilentlyContinue -ev errs | % { Get-FileHash $_.Fullname -Algorithm MD5 } | Select-Object Hash,Path,@{Name='Name';Expression={[System.IO.Path]::GetFileName($_.Path)}} | Out-File C:$env:COMPUTERNAME.csv -Encoding UTF8 -Force
例如。
Column1 Column2 Column3 Column4 Column5
MD5 SHA1 SHA256 Sign/Unsigned Path
您想要执行的操作将花费大量时间,因此我建议不要在完整的C:\驱动器上尝试,而是循序渐进。。
为了加快每个文件所需的3个不同文件哈希,我建议以字节数组的形式读取文件,并计算这些字节上的哈希,这样可以避免反复读取同一文件。
类似这样的东西:
# create the 3 different hasher objects
$md5Hasher = [System.Security.Cryptography.MD5]::Create()
$sha1Hasher = [System.Security.Cryptography.SHA1]::Create()
$sha256Hasher = [System.Security.Cryptography.SHA256]::Create()
# Loop through the files in the root path
Get-ChildItem -Path 'C:SomePath' -File -Recurse -Force -ErrorAction SilentlyContinue |
ForEach-Object {
# read the content of the file as byte array only once
$bytes = [System.IO.File]::ReadAllBytes($_.FullName)
# output an object with all properties you need
[PsCustomObject]@{
MD5 = ($md5Hasher.ComputeHash($bytes) | ForEach-Object { '{0:X2}' -f $_ }) -join ''
SHA1 = ($sha1Hasher.ComputeHash($bytes) | ForEach-Object { '{0:X2}' -f $_ }) -join ''
SHA256 = ($sha256Hasher.ComputeHash($bytes) | ForEach-Object { '{0:X2}' -f $_ }) -join ''
Signed = (Get-AuthenticodeSignature -Content $bytes -SourcePathorExtension $_.FullName).Status
Path = $_.FullName
}
} | Export-Csv -Path "D:Test$env:COMPUTERNAME.csv" -Encoding UTF8 -NoTypeInformation
# clean op the hasher objects when done
$md5Hasher.Dispose()
$sha1Hasher.Dispose()
$sha256Hasher.Dispose()
Theo的绝妙答案是:对大文件进行即时哈希是一项缓慢的操作,您可能需要优化从磁盘的读取操作。对于较小的文件,这将是最佳的,但它不适用于大于2GB的文件。
问题是[System.IO.File]::ReadAllBytes($_.FullName)
需要将整个文件读取到内存中的字节数组中,而.NET中的数组最多只能容纳21亿个项目(~2GB(。
您可以通过将文件流传递给$hasher.ComputeHash()
来绕过这个限制——唯一需要注意的是;再风";文件流,然后将其传递给下一个算法。
如果只期望文件的一小部分具有签名,另一个(不太重要的(优化是选择性地筛选调用Get-AuthenticodeSignature
的扩展名:
function Get-FileMultiHash
{
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[System.IO.FileInfo]$InputObject,
[Parameter(Mandatory = $false)]
[string[]]$SignatureExtension
)
begin {
$hashers = @(
($md5Hasher = [System.Security.Cryptography.MD5]::Create())
($sha1Hasher = [System.Security.Cryptography.SHA1]::Create())
($sha256Hasher = [System.Security.Cryptography.SHA256]::Create())
)
# Prepend any extension filter with `.` if not already present
$SignatureExtensionFilter = $SignatureExtensionFilter -replace '^(?!.)','.'
}
process {
try{
$shouldCheckSignature = -not$PSBoundParameters.ContainsKey('SignatureExtensionFilter') -or $InputObject.Extension -in $SignatureExtensionFilter
# Open file stream, compute md5
$fs = $InputObject.OpenRead()
$md5sum = $md5Hasher.ComputeHash($fs)
# Rewind file stream, compute sha1
[void]$fs.Seek(0, [System.IO.SeekOrigin]::Begin)
$sha1sum = $sha1Hasher.ComputeHash($fs)
# Rewind file stream, compute sha256
[void]$fs.Seek(0, [System.IO.SeekOrigin]::Begin)
$sha256sum = $sha256Hasher.ComputeHash($fs)
}
finally{
# Dispose of file stream
if($fs){$fs.Dispose()}
}
# Output checksums and signature status
[pscustomobject]@{
MD5 = [BitConverter]::ToString($md5sum).Replace('-', '')
SHA1 = [BitConverter]::ToString($sha1sum).Replace('-', '')
SHA256 = [BitConverter]::ToString($sha256sum).Replace('-', '')
Signed = if($shouldCheckSignature){(Get-AuthenticodeSignature -LiteralPath $_.FullName).Status}else{'Skipped'}
Path = $_.FullName
}
}
end {
# Dispose of hash algo instances
$hashers |ForEach-Object Dispose
}
}
然后使用类似:
Get-ChildItem -Path C:pathtorootfolder -Recurse -File -Force -ea SilentlyContinue -ev errs |Get-FileMultiHash
或带有扩展过滤器:
Get-ChildItem C:WindowsSystem32 -File |Get-FileMultiHash -SignatureExtensionFilter exe,dll