【入門編】【ファイルハッシュ検証】CertUtil コマンド連携による SHA256 ハッシュ値を用いたファイル同一性判定 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptでファイルハッシュ検証!CertUtilコマンド連携でファイル破損・改ざんをガッチリ検知!

皆さん、こんにちは!VBScriptの世界へようこそ。マクロの記録だけでは物足りなくなってきた、そんなあなたを「ここをクリアすれば、VBScriptの基本はバッチリですよ!」と温かく導く、あなたの先輩エンジニアです。

今回は、ファイル転送時の「あれ?ファイル壊れてないかな?」「誰か触った?」なんて不安を、バシッと解消してくれる、とっておきのテクニックをご紹介します。それは、Windowsに標準搭載されている `CertUtil` コマンドVBScript を連携させた SHA256ハッシュ値によるファイル同一性判定 です!

なぜハッシュ値でファイルの同一性を検証するの?

ファイルって、ネットワークで送ったり、コピーしたりする際に、ごく稀にデータが壊れてしまうことがあります。また、悪意のある第三者によって改ざんされてしまう可能性もゼロではありません。

そこで登場するのが「ハッシュ値」です。ハッシュ値とは、ファイルの内容を元に計算される、「指紋」 のようなもの。ファイルの内容が1ビットでも変われば、ハッシュ値は全く別のものになります。

今回使うのは、SHA256という、現在主流の強力なハッシュアルゴリズムです。このSHA256ハッシュ値を、ファイル転送前と転送後で比較することで、ファイルが完全に同じ状態であるかを、ほぼ確実に検証できるんです。

VBScriptと`CertUtil`コマンドの出会い

「でも、ハッシュ値の計算って難しそう…」と思ったあなた、ご安心ください!Windowsには、このハッシュ値計算を簡単に実行してくれる `CertUtil` コマンド という便利なツールが標準で備わっています。

そして、この `CertUtil` コマンドをVBScriptから呼び出し、その結果をVBScriptで受け取って比較するのが、今回の主役です。

「VBScriptからコマンドを実行するなんて、難しそう…」なんて思わないでくださいね。VBScriptの `WScript.Shell` オブジェクトを使えば、まるで手でコマンドを入力するのと同じ感覚で、外部コマンドを実行できるんです。

さあ、実践!VBScriptでハッシュ値検証コードを書いてみよう!

まずは、準備として、検証したいファイルを用意しましょう。ここでは例として、`C:\temp\my_important_file.txt` というファイルがあるとして進めます。

1. `CertUtil`コマンドでSHA256ハッシュ値を取得するVBScript関数

まずは、指定したファイルのSHA256ハッシュ値を取得するVBScript関数を作成します。

‘===============================================================================
‘ 関数名: GetFileSHA256Hash
‘ 機能: 指定されたファイルのSHA256ハッシュ値を取得する
‘ 引数: filePath (String) – ハッシュ値を取得したいファイルのフルパス
‘ 戻り値: String – ファイルのSHA256ハッシュ値。エラー時は空文字列を返す。
‘===============================================================================
Function GetFileSHA256Hash(filePath)
Dim objShell
Dim objExec
Dim strCommand
Dim strOutput
Dim arrLines
Dim strHashLine
Dim strHashValue

On Error Resume Next ‘ エラー発生時も処理を続行させる

‘ WScript.Shellオブジェクトを作成
Set objShell = WScript.CreateObject(“WScript.Shell”)

‘ CertUtilコマンドを構築
‘ certutil -hashfile <ファイルパス> SHA256
‘ /C オプションは、コマンドプロンプトのウィンドウを表示しないようにします。
‘ cmd.exe /C は、コマンドを実行して終了させるためのもの。
strCommand = “cmd.exe /C certutil -hashfile “”” & filePath & “”” SHA256″

‘ コマンドを実行し、標準出力を取得
Set objExec = objShell.Exec(strCommand)

‘ コマンドの完了を待つ
Do While objExec.Status = 0
WScript.Sleep 100 ‘ 100ミリ秒待機
Loop

‘ 標準出力の内容をすべて読み込む
strOutput = objExec.StdOut.ReadAll

‘ エラーが発生していないか確認
If Err.Number <> 0 Then
WScript.Echo “エラー: CertUtilコマンドの実行中に問題が発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “エラー説明: ” & Err.Description
GetFileSHA256Hash = “” ‘ エラー時は空文字列を返す
Set objExec = Nothing
Set objShell = Nothing
Exit Function
End If

‘ 取得した出力からハッシュ値の行を探し出す
arrLines = Split(strOutput, vbCrLf) ‘ 改行コードで分割
strHashValue = “” ‘ 初期化

For Each strLine In arrLines
‘ ハッシュ値は通常、数字とアルファベットの羅列で、行の先頭にハッシュ値が記載される
‘ CertUtilの出力形式に依存しますが、一般的にこのパターンで取得できます。
‘ 具体的な出力例: “xxxxxx sha256 hash of the file:” のような行の前にハッシュ値があります。
‘ 厳密には、CertUtilの出力は “XXXXXX\r\n\r\nCertUtil: -hashfile command completed successfully.\r\n0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\r\nCertUtil: -hashfile command completed successfully.\r\n” のような形式になることがあります。
‘ そのため、行を1つずつ確認し、ハッシュ値らしき文字列を見つけます。

‘ より堅牢な方法として、ハッシュ値のパターン(16進数64文字)を正規表現でマッチさせることも可能ですが、
‘ 初学者向けに、ここでは比較的シンプルな方法で取得します。
‘ CertUtilの出力には、ハッシュ値の行の前に「SHA256 hash of the file:」のような文字列が含まれることがあります。
‘ しかし、言語設定によっては異なる場合もあるため、ここではハッシュ値らしき文字列を抽出します。

‘ ハッシュ値は、空白を挟んで数値とアルファベットの羅列(64文字)であることが多いです。
‘ ここでは、行の先頭から64文字が16進数(0-9, a-f, A-F)で構成されているものをハッシュ値とみなします。
‘ 簡易的なチェックとして、文字列の長さを確認します。
If Len(Trim(strLine)) = 64 Then
‘ ここで、その文字列が本当に16進数かどうかのチェックを入れるとより堅牢になります。
‘ 例:”^([0-9a-fA-F]{64})$” のような正規表現でチェック
‘ 今回は簡易的に、64文字であればハッシュ値として採用します。
strHashValue = Trim(strLine)
Exit For ‘ 見つかったらループを抜ける
End If
Next

‘ ハッシュ値が見つからなかった場合
If strHashValue = “” Then
WScript.Echo “警告: 指定されたファイル ‘” & filePath & “‘ からSHA256ハッシュ値を取得できませんでした。” & vbCrLf & “CertUtilの出力内容:” & vbCrLf & strOutput
GetFileSHA256Hash = “”
Else
‘ 取得したハッシュ値を大文字に統一して返す
GetFileSHA256Hash = UCase(strHashValue)
End If

Set objExec = Nothing
Set objShell = Nothing

End Function

‘ — ここまでがハッシュ値取得関数 —

‘ === メイン処理 ===
Sub Main()
Dim sourceFile ‘ 元ファイル
Dim destinationFile ‘ 転送後(またはコピー後)のファイル

‘ — 設定 —
sourceFile = “C:\temp\my_important_file.txt” ‘ ★検証したい元のファイルのパスを指定してください
destinationFile = “C:\temp\my_important_file_copied.txt” ‘ ★検証したい先のファイルのパスを指定してください
‘ —————-

Dim sourceHash
Dim destinationHash

‘ 元ファイルのハッシュ値を取得
WScript.Echo “元のファイル ‘” & sourceFile & “‘ のハッシュ値を計算中…”
sourceHash = GetFileSHA256Hash(sourceFile)

If sourceHash = “” Then
WScript.Echo “エラー: 元ファイルのハッシュ値を取得できませんでした。処理を中断します。”
Exit Sub
Else
WScript.Echo “元のファイルのSHA256ハッシュ値: ” & sourceHash
End If

WScript.Echo vbCrLf ‘ 見やすくするために改行

‘ 転送後(またはコピー後)のファイルのハッシュ値を取得
WScript.Echo “先のファイル ‘” & destinationFile & “‘ のハッシュ値を計算中…”
destinationHash = GetFileSHA256Hash(destinationFile)

If destinationHash = “” Then
WScript.Echo “エラー: 先のファイルのハッシュ値を取得できませんでした。処理を中断します。”
Exit Sub
Else
WScript.Echo “先のファイルのSHA256ハッシュ値: ” & destinationHash
End If

WScript.Echo vbCrLf ‘ 見やすくするために改行

‘ ハッシュ値を比較
If sourceHash = destinationHash Then
WScript.Echo “【検証結果】ファイルは同一です。データ破損や改ざんの可能性はありません。”
Else
WScript.Echo “【検証結果】★★★警告★★★ ファイルが異なります! データ破損または改ざんの可能性があります。”
WScript.Echo “元のハッシュ値: ” & sourceHash
WScript.Echo “先のハッシュ値 : ” & destinationHash
End If

End Sub

‘ メイン処理の実行
Main

コードの解説

  • `GetFileSHA256Hash(filePath)` 関数:
  • `WScript.CreateObject(“WScript.Shell”)`: VBScriptから外部コマンドを実行するための「Shellオブジェクト」を作成します。これが、コマンドを実行する際の司令塔となります。
  • `cmd.exe /C certutil -hashfile “”” & filePath & “”” SHA256`: ここが、実際に `CertUtil` コマンドを実行する部分です。
  • `cmd.exe /C`: コマンドプロンプトを起動し、指定したコマンドを実行した後、すぐに閉じるように指示します。これにより、コマンドプロンプトのウィンドウが画面に表示されるのを防ぎます。
  • `certutil -hashfile`: `CertUtil` コマンドで、指定したファイルのハッシュ値を計算するオプションです。
  • `””” & filePath & “””`: ここで、検証したいファイルのパスをコマンドに渡しています。ファイルパスにスペースが含まれる場合でも正しく渡せるように、ダブルクォーテーションで囲んでいます。VBScriptでは、文字列内のダブルクォーテーションを表現するために `””` と二重に記述します。
  • `SHA256`: 計算したいハッシュアルゴリズムを指定します。
  • `objShell.Exec(strCommand)`: 構築したコマンドを実行します。`Exec` メソッドは、コマンドの実行プロセスを管理するためのオブジェクト (`objExec`) を返します。
  • `Do While objExec.Status = 0 … WScript.Sleep 100`: コマンドの実行が完了するまで待機します。`Status = 0` は実行中であることを意味します。
  • `strOutput = objExec.StdOut.ReadAll`: コマンドの実行結果(標準出力)をすべて読み込みます。`CertUtil` のハッシュ値はこの標準出力に含まれています。
  • `Split(strOutput, vbCrLf)`: `CertUtil` の出力は複数行にわたることがあるため、改行コード (`vbCrLf`) で文字列を分割し、配列に格納します。
  • `If Len(Trim(strLine)) = 64 Then … strHashValue = Trim(strLine)`: 分割した各行をチェックし、長さが64文字(SHA256ハッシュ値の長さ)の文字列を見つけます。これがハッシュ値であると判断し、`strHashValue` に格納します。`Trim()` は文字列の前後の不要な空白を取り除きます。
  • `UCase(strHashValue)`: 取得したハッシュ値を大文字に統一して返します。これにより、大文字・小文字の違いによる比較ミスを防ぎます。
  • `On Error Resume Next`: 万が一、ファイルが存在しない、アクセス権がないなどのエラーが発生した場合でも、スクリプトが途中で停止しないようにします。エラーが発生した場合は、後続の `Err.Number` でチェックできます。
  • `Main` サブルーチン:
  • `sourceFile` と `destinationFile` に、検証したいファイルのパスを設定します。ここをあなたの環境に合わせて書き換えてくださいね!
  • `GetFileSHA256Hash` 関数を呼び出して、それぞれのファイルのハッシュ値を取得します。
  • 取得したハッシュ値を比較し、一致すれば「ファイルは同一」、異なれば「ファイルが異なる」というメッセージを表示します。

実行前の準備

1. 検証したい2つのファイルを用意します。例えば、`C:\temp\my_important_file.txt` と、それをコピーしたり転送したりして作成された `C:\temp\my_important_file_copied.txt` です。
2. 上記のVBScriptコードを、拡張子 `.vbs` で保存します(例: `CheckHash.vbs`)。
3. コード内の `sourceFile` と `destinationFile` のパスを、あなたの環境に合わせて正しく設定します。

実行方法

保存した `.vbs` ファイルをダブルクリックして実行します。コマンドプロンプトのウィンドウがパッと表示されて消え、その後、VBScriptのメッセージボックス(またはコマンドプロンプトの出力ウィンドウ)に結果が表示されます。

陥りやすいエラーとその対策

  • 「ファイルが見つかりません」エラー:
  • 原因: 指定したファイルパスが間違っている、ファイルが存在しない、またはスクリプトを実行しているユーザーにファイルへのアクセス権がない。
  • 対策: ファイルパスを再度確認し、タイプミスがないか、ファイルが実際に存在するか確認してください。また、エクスプローラーでそのファイルにアクセスできるか試してみましょう。
  • 「CertUtilコマンドの実行中に問題が発生しました」:
  • 原因: `CertUtil` コマンドが見つからない(非常に稀ですが)、またはコマンドの実行権限がない。
  • 対策: ほとんどの場合、`CertUtil` は標準で利用できます。もしこのエラーが出る場合は、Windowsのシステムファイルに問題があるか、非常に特殊な環境かもしれません。管理者権限でスクリプトを実行してみてください。
  • ハッシュ値が取得できない、または不正な値になる:
  • 原因: `CertUtil` の出力形式が、スクリプトが想定しているものと異なる。これは、Windowsのバージョンや言語設定によって微妙に変わることがあります。
  • 対策: 実際に `CertUtil` コマンドをコマンドプロンプトで実行してみて、どのような出力になるか確認してください。その出力に合わせて、VBScriptコード内のハッシュ値抽出ロジック(`If Len(Trim(strLine)) = 64 Then` の部分)を調整する必要があります。より堅牢にするには、正規表現を使って「64文字の16進数」というパターンを正確にマッチさせる方法があります。

VBScriptのオブジェクトライフサイクルとパフォーマンスについて

今回、`WScript.Shell` オブジェクトを使いましたね。このオブジェクトは、外部コマンドの実行やレジストリ操作など、Windowsのシステムリソースにアクセスする際に非常に便利です。

しかし、オブジェクトの生成と破棄には、それなりのコストがかかります。特に、ループの中で何度も `WScript.CreateObject` を実行すると、パフォーマンスが低下する可能性があります。

今回のコードでは、`GetFileSHA256Hash` 関数内で `objShell` を作成し、関数の終了時に `Set objExec = Nothing`、`Set objShell = Nothing` で明示的に解放しています。これにより、オブジェクトが不要になったらきちんとメモリから解放され、リソースの無駄遣いを防いでいます。

もし、大量のファイルを連続で処理する必要がある場合は、`Main` サブルーチンの中で一度だけ `WScript.Shell` オブジェクトを作成し、それを `GetFileSHA256Hash` 関数に渡すようにコードを修正すると、より効率的になります。

ここまでクリアすれば、VBScriptの基本はバッチリ!

どうでしたか? `CertUtil` コマンドとVBScriptを組み合わせることで、ファイルの同一性を正確に検証できる、強力なツールが手に入ったはずです。

今回学んだ `WScript.Shell` オブジェクトの使い方や、コマンド実行結果の取得、そしてオブジェクトのライフサイクル管理は、VBScriptで様々な自動化を実現するための基礎となります。

この知識があれば、ファイル転送だけでなく、バックアップファイルの整合性チェック、設定ファイルの変更検知など、応用範囲はグッと広がります。

ぜひ、あなたの業務にこのコードを応用して、より安全で効率的な環境を築いていってくださいね。応援しています!