部分的な MD4 チェックサムを計算する PowerShell スクリプト

部分的な MD4 チェックサムを計算する PowerShell スクリプト

入力ファイルを読み込み、ファイルの最後まで9728000バイトのチャンクごとにMD4チェックサムを計算するスクリプトが必要です。私は以前、まず9728000バイトのチャンクをすべて個別のファイルとして抽出し、次にコマンドラインツールでそれぞれのMD4チェックサムを計算することでこれを実行しました。フサムしかし、特に大きなファイルの場合、これは便利ではありません。

私が収集した情報によると、PowerShell は MD5 チェックサムをネイティブに計算できますが、MD4 は計算できません。

私は以下のスクリプトを使ってテストを行った。これは入手したコードを元にしたものである。そこには、そして私はまだそれを完全に理解していません。

$input = Read-Host "File name"
$file = Get-Item -LiteralPath $input
$offset = 0
$length = $file.length
$name = $file.name
$hasher = [System.Security.Cryptography.HashAlgorithm]::Create('MD5')
for ($offset -lt $length) {
    $buffer = [Byte[]]::new(9728000)
    $stream = [System.IO.FileStream]::new($file.FullName, 'Open', 'Read')
    $stream.Position = $offset
    $readSize = $stream.Read($buffer, 0, 9728000)
    $md5 = $hasher.ComputeHash($buffer, 0, $readSize)
    $stream.Dispose()
    echo "$md5 $offset" >> "$name MD5.txt"
    $offset = $offset + 9728000
}

結果は以下のとおりです。

78 224 90 67 158 49 86 127 118 179 251 89 133 103 144 25 0
190 40 170 149 51 93 153 191 212 194 162 165 38 124 57 96 9728000
160 20 197 150 189 246 209 156 212 84 154 21 200 7 148 228 19456000
...

しかし、1) これは必要なレイアウトではありません、2) 特に MD4 が必要です、3) もう 1 つの問題は、条件にもかかわらず、読み取るデータがなくなってもループが停止しないことです$offset -lt $length(間違っているに違いありません)。

必要なのは通常32バイトのMD4文字列です:

908CB75033311ADCB0FBCD0DCD869050 0
A1211C1B77C0EFFA98DB8F719AB30A93 9728000
D9719A4CB32F2D350CB39A0CB790424B 19456000
...

そして今日私は見つけたこのMD4スクリプト上記のスクリプトを応用して使ってみましたが、失敗したミセバリー。また、スクリプトの内容全体 ( で始まるfunction Get-MD4...) を自分のスクリプト内にコピーした場合、関数は認識されませんでした。スクリプトを「ドット ソース」で呼び出した場合にのみ機能します (この作業に約 2 時間費やし、PowerShell に関する 400 ページを超える PDF ブックを数冊調べているうちに発見した概念ですが、それ以外ではほとんど役に立ちませんでした)。これは、書式設定の問題によるものでしょうか ? (md4.ps1 スクリプトを Notepad2 で開くと、行末が「LF」であると表示されますが、私のスクリプトの行末は「CR+LF」です。これは主な問題ではありませんが、今日の午後は本当に腹が立ったので、何が起こっているのか知りたいです。)

READMEファイルそのスクリプトには以下が含まれています:

 This is a MD4 algorithm function wrote in powershell.
 
 PS> Get-MD4 -String 'abc' -UpperCase
 A448017AAF21D8525FC10AE87AA6729D
 
 PS> $b = @('abc'.ToCharArray() | %{[int]$_})
 PS> Get-MD4 -bArray $b
 a448017aaf21d8525fc10ae87aa6729d

で始まる行が理解できず$b =、PDF ブックや Web 検索で関連する情報を見つける方法がまったくわかりません。またしても、途方に暮れています。

一度にサイズが定義されたチャンクを 1 つずつファイルを読み取り、それを関数の入力として渡し、$offset 値を増分するなどして、ファイルの最後まで実行する最も効率的な方法は何でしょうか。またはGet-MD4を使用すればよいでしょうか。-String-bArray

目標は、それが重要であれば、eDonkey ネットワークで共有されているが ED2K チェックサムが一致しないファイルをチェックすることです。たとえば、4GB の MKV ファイルがあり、eMule で検索すると、サイズはまったく同じだが ED2K チェックサムが異なる MKV ファイルを見つけたので、どの部分が異なるのかを知りたいと思います (ED2K チェックサムは、メタデータ ファイルにある 9728000 バイトのチャンクごとの MD4 文字列のリストの MD4 チェックサムです)。その後、それらの部分をダウンロードして、そのファイルの破損バージョンが自分のファイルなのか、現在共有されているファイルなのかをチェックします。これでおわかりいただけたでしょうか。そうでなければ、気にせず、本題である「部分的な MD4 チェックサムの計算」に集中しましょう。

答え1

上記のコードは、ループの繰り返しごとに新しい巨大なバッファと新しいストリームが作成されるため、非常に非効率的です。ストリームすでにオフセットやその他の処理が行われているため、ループ本体は実際には非常にシンプルです。

Param (
    [parameter(Mandatory)][string]$InputFile,
    [int]$ChunkSize = 9728000
)

. .\md4.ps1
# Or copy the md4.ps1 content here, or add to the $PROFILE

class Md4Info {
    [string]$Checksum
    [int]$Offset
}

$stream = [IO.FileStream]::new($InputFile, [IO.FileMode]::Open, [IO.FileAccess]::Read)
$buffer = [Byte[]]::new($ChunkSize)
while (($readSize = $stream.Read($buffer, 0, $ChunkSize)) -gt 0) {
    [Md4Info]@{
        Checksum = Get-MD4 -bArray $buffer[0..$readSize]
        Offset = $stream.Position
    }
}
$stream.Dispose()

これはあなたのコードに比べて大きな改善ですが、明らかに最も効率的な方法なぜならms4.ps1 の翻訳です。上記のスクリプトは非常に古いバージョンの PowerShell 用に書かれたもので、非常に非効率的です。また、Unicode を正しく処理しないため、を使用すると-String壊れた出力が返される可能性があります。とにかく、上記の更新されたコードを などの *.ps1 ファイルとして保存して試すことができますGet-ChunkMd4.ps1。サンプル出力:

PS D:\test> .\Get-ChunkMd4.ps1 D:\test\file.txt

Checksum                         Offset
--------                         ------
11cf3ecf1a3a9d1b270c9e313901441d      0

PS D:\test> .\Get-ChunkMd4.ps1 D:\test\file.mp4 -ChunkSize 1MB

Checksum                          Offset
--------                          ------
8932ec620ef5df53f519e6271931bc0d       0
92a8f97be075655bfd1e8670368ff2a3 1048576
c6ec8e0d67b42cc3a9a1bc9d5c9fa8f0 2097152
1339bac99b94397b5848d1d86b0cc49e 3145728
fd643f329daeb73e07e24194fd1b0a31 4194304

そうは言っても、ほとんどハードウェアアクセラレーションやSIMDを使った非常に効率的な並列コードを書いてネイティブコードにコンパイルしない限り、効率的な方法とは言えません。ネイティブMD4ライブラリは、上記のスクリプトよりも数千倍または数百万倍高速に実行できます。実際、AVX-512を使用した優れたハッシュライブラリは、3~30 GB/秒またはそれ以上ですが、md4.ps1私のマシン(Ryzen 5 4600H)では8.2MBの小さなファイルをハッシュするのに4分もかかります。約29.5KB/秒!!!動画ファイルをダウンロードするときにハッシュ化が十分速くできないことは間違いありません。サードパーティのライブラリ/ツールを見つけて、上記のループでデータを入力する必要があります


また、関数を頻繁に使用する場合は、次のようにプロファイルに追加する必要があります。

Add-Content -Path $profile -Value ". .\md4.ps1"

. .\md4.ps1PowerShellが実行されるたびに関数が読み込まれるので、スクリプト内のを削除するだけで済みます。

関連情報