我需要能夠識別一些已在安全服務器之間復制和重命名的大型二進制檔案。為此,我希望能夠散列所有檔案的前 X 個位元組和最后 X 個位元組。我只需要使用標準 Windows 10 系統上可用的內容來執行此操作,無需安裝其他軟體,因此 PowerShell 似乎是正確的選擇。
一些不起作用的事情:
- 我無法讀取整個檔案,然后提取我想要散列的檔案部分。我試圖實作的目標是最小化我需要讀取的檔案量,而讀取整個檔案違背了這個目的。
- 將檔案的較大部分讀入 PowerShell 變數似乎很慢,因此
$hash.ComputeHash($moderatelyLargeVariable)似乎不是一個可行的解決方案。
我很確定我需要$hash.ComputeHash($stream)在$stream只傳輸檔案的一部分的地方做。
到目前為止,我已經嘗試過:
function Get-FileStreamHash {
param (
$FilePath,
$Algorithm
)
$hash = [Security.Cryptography.HashAlgorithm]::Create($Algorithm)
## METHOD 0: See description below
$stream = ([IO.StreamReader]"${FilePath}").BaseStream
$hashValue = $hash.ComputeHash($stream)
## END of part I need help with
# Convert to a hexadecimal string
$hexHashValue = -join ($hashValue | ForEach-Object { "{0:x2}" -f $_ })
$stream.Close()
# return
$hexHashValue
}
方法 0:這有效,但它正在流式傳輸整個檔案,因此不能解決我的問題。對于 3GB 檔案,這在我的機器上大約需要 7 秒。
方法一:$hashValue = $hash.ComputeHash((Get-Content -Path $FilePath -Stream ""))。這也是流式傳輸整個檔案,而且它也需要永遠。對于同一個 3GB 檔案,它需要的時間超過 5 分鐘(我當時取消了,不知道總持續時間是多少)。
方法二:$hashValue = $hash.ComputeHash((Get-Content -Path $FilePath -Encoding byte -TotalCount $qtyBytes -Stream ""))。這與方法 1 相同,只是它將內容限制為$qtyBytes。在 1000000 (1MB) 時需要 18 秒。我認為這意味著方法 1 需要大約 15 個小時,比方法 0 慢 7700 倍。
有沒有辦法做類似方法 2 的事情(限制讀取的內容)但不會減慢速度?如果是這樣,是否有一種好方法可以僅在檔案末尾執行此操作?
謝謝!
uj5u.com熱心網友回復:
您可以嘗試使用以下輔助函式中的一個(或兩者的組合)從檔案開頭或從檔案末尾讀取多個位元組:
function Read-FirstBytes {
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
[Alias('FullName', 'FilePath')]
[ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
[string]$Path,
[Parameter(Mandatory=$true, Position = 1)]
[int]$Bytes,
[ValidateSet('ByteArray', 'HexString', 'Base64')]
[string]$As = 'ByteArray'
)
try {
$stream = [System.IO.File]::OpenRead($Path)
$length = [math]::Min([math]::Abs($Bytes), $stream.Length)
$buffer = [byte[]]::new($length)
$null = $stream.Read($buffer, 0, $length)
switch ($As) {
'HexString' { ($buffer | ForEach-Object { "{0:x2}" -f $_ }) -join '' ; break }
'Base64' { [Convert]::ToBase64String($buffer) ; break }
default { ,$buffer }
}
}
catch { throw }
finally { $stream.Dispose() }
}
function Read-LastBytes {
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
[Alias('FullName', 'FilePath')]
[ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
[string]$Path,
[Parameter(Mandatory=$true, Position = 1)]
[int]$Bytes,
[ValidateSet('ByteArray', 'HexString', 'Base64')]
[string]$As = 'ByteArray'
)
try {
$stream = [System.IO.File]::OpenRead($Path)
$length = [math]::Min([math]::Abs($Bytes), $stream.Length)
$null = $stream.Seek(-$length, 'End')
$buffer = for ($i = 0; $i -lt $length; $i ) { $stream.ReadByte() }
switch ($As) {
'HexString' { ($buffer | ForEach-Object { "{0:x2}" -f $_ }) -join '' ; break }
'Base64' { [Convert]::ToBase64String($buffer) ; break }
default { ,[Byte[]]$buffer }
}
}
catch { throw }
finally { $stream.Dispose() }
}
然后您可以從中計算一個哈希值并根據需要進行格式化。
組合是可能的,例如
$begin = Read-FirstBytes -Path 'D:\Test\somefile.dat' -Bytes 50 # take the first 50 bytes
$end = Read-LastBytes -Path 'D:\Test\somefile.dat' -Bytes 1000 # and the last 1000 bytes
$Algorithm = 'MD5'
$hash = [Security.Cryptography.HashAlgorithm]::Create($Algorithm)
$hashValue = $hash.ComputeHash($begin $end)
($hashValue | ForEach-Object { "{0:x2}" -f $_ }) -join ''
uj5u.com熱心網友回復:
我相信這將是使用System.IO.BinaryReader. 您可以將此函式與您擁有的函式結合使用,它可以讀取所有位元組、最后一個n位元組 ( -Last) 或第一個n位元組 ( -First)。
function Read-Bytes {
[cmdletbinding(DefaultParameterSetName = 'Path')]
param(
[parameter(
Mandatory,
ValueFromPipelineByPropertyName,
ParameterSetName = 'Path',
Position = 0
)][alias('FullName')]
[ValidateScript({
if(Test-Path $_ -PathType Leaf)
{
return $true
}
throw 'Invalid File Path'
})]
[System.IO.FileInfo]$Path,
[parameter(
HelpMessage = 'Specifies the number of Bytes from the beginning of a file.',
ParameterSetName = 'FirstBytes',
Position = 1
)]
[int64]$First,
[parameter(
HelpMessage = 'Specifies the number of Bytes from the end of a file.',
ParameterSetName = 'LastBytes',
Position = 1
)]
[int64]$Last
)
process
{
try
{
$reader = [System.IO.BinaryReader]::new(
[System.IO.File]::Open(
$Path.FullName,
[system.IO.FileMode]::Open,
[System.IO.FileAccess]::Read
)
)
$stream = $reader.BaseStream
$length = (
$stream.Length, $First
)[[int]($First -lt $stream.Length -and $First)]
$stream.Position = (
0, ($length - $Last)
)[[int]($length -gt $Last -and $Last)]
$bytes = while($stream.Position -ne $length)
{
$stream.ReadByte()
}
[pscustomobject]@{
FilePath = $Path.FullName
Length = $length
Bytes = $bytes
}
}
catch
{
Write-Warning $_.Exception.Message
}
finally
{
$reader.Close()
$reader.Dispose()
}
}
}
用法
Get-ChildItem . -File | Read-Bytes -Last 100:讀取100當前檔案夾中所有檔案的最后一個位元組。如果-Last引數超過檔案長度,它將讀取整個檔案。Get-ChildItem . -File | Read-Bytes -First 100:讀取100當前檔案夾中所有檔案的第一個位元組。如果-First引數超過檔案長度,它將讀取整個檔案。Read-Bytes -Path path/to/file.ext:讀取所有位元組file.ext。
輸出
回傳具有屬性FilePath, Length,的物件Bytes。
FilePath Length Bytes
-------- ------ -----
/home/user/Documents/test/...... 14 {73, 32, 119, 111…}
/home/user/Documents/test/...... 0
/home/user/Documents/test/...... 0
/home/user/Documents/test/...... 0
/home/user/Documents/test/...... 116 {111, 109, 101, 95…}
/home/user/Documents/test/...... 17963 {50, 101, 101, 53…}
/home/user/Documents/test/...... 3617 {105, 32, 110, 111…}
/home/user/Documents/test/...... 638 {101, 109, 112, 116…}
/home/user/Documents/test/...... 0
/home/user/Documents/test/...... 36 {65, 99, 114, 101…}
/home/user/Documents/test/...... 735 {117, 112, 46, 79…}
/home/user/Documents/test/...... 1857 {108, 111, 115, 101…}
/home/user/Documents/test/...... 77 {79, 80, 69, 78…}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/374237.html
