【テクニカル・上級編】【スクリプト自己改ざん検知】自身のファイルハッシュと属性を起動時に検証するセーフティロジック – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【スクリプト自己改ざん検知】自身のファイルハッシュと属性を起動時に検証するセーフティロジック

レガシーシステムの基盤、あるいは各端末に点在するバッチ処理の自動化において、VBScript(Visual Basic Scripting Edition)とWSH(Windows Script Host)のコンビネーションはいまだに現場のインフラを陰で支え続けている。

しかし、テキストベースで平文のまま配置される.vbsファイルは、悪意ある第三者による改ざんだけでなく、現場のオペレーターによる偶発的な誤操作(エディタでの意図しない上書きや文字コードの破壊)というリスクと常に隣り合わせだ。

「起動したスクリプトが、本当に信頼できるオリジナルティを保っているか?」

これを実行プロセスの初期段階で自己診断し、異常があれば即座に安全停止(Fail-Fast)するセーフティロジックの構築法を、Windows APIとWSHの深層を知り尽くした視点から解説する。

—

1. VBScriptにおける「自己改ざん検知」のアーキテクチャ

VBScript自体には、暗号学的ハッシュ関数をネイティブで計算するクラスは存在しない。また、実行中のスクリプトが「自分自身のファイルパス」を正確に特定し、それをIOストリームとして安全に読み込むには、WSHのランタイム環境とCOMコンポーネントのライフサイクルを正確に理解しておく必要がある。

今回のセーフティロジックの要件は以下の通りだ。

1. 自身のパスの動的特定: `WScript.ScriptFullName`を用い、実行中の実体ファイルのフルパスを取得する。
2. ファイルハッシュの算出: COMオブジェクト(例: .NETの `System.Security.Cryptography.SHA256Managed` や WMI、あるいはCertUtilのラッパー)を活用し、SHA-256ハッシュ値を算出する。
3. 期待値との照合: ハードコーディングされた、あるいは外部の安全なストアから読み込んだ「正当なハッシュ値」と比較する。
4. メモリの厳密な解放: VBScript特有のガベージコレクションの遅延を回避するため、生成したCOMオブジェクトはスコープ終了時に明示的に `Nothing` を代入して破棄する。

—

2. 実装コード:自己診断セーフティロジック

以下のコードは、スクリプトの先頭に配置し、本処理の実行前に改ざんを検知するイミュータブル・ガードの完全実装である。ハッシュアルゴリズムには耐衝突性の高いSHA-256を採用している。

Option Explicit

‘ —————————————————————–
‘ メインエントリーポイント
‘ —————————————————————–
Call Main()

Sub Main()
‘ 1. 自己改ざん検知の実行
If Not VerifySelfIntegrity() Then
Wscript.Echo “[CRITICAL ERROR] スクリプトの整合性チェックに失敗しました。” & vbCrLf & _
“ファイルが改ざんされているか、破損しています。処理を中断します。”
Wscript.Quit 99 ‘ 異常終了コード
End If

‘ ————————————————————-
‘ ここから下 本処理
‘ ————————————————————-
Wscript.Echo “[INFO] 整合性検証を通過しました。メイン処理を開始します。”

‘ (ここに実際の業務ロジックを記述)

End Sub

‘ —————————————————————–
‘ 自身のファイルハッシュを計算し、期待値と比較検証する関数
‘ —————————————————————–
Function VerifySelfIntegrity()
Dim targetPath, expectedHash
Dim fso, shell, exec, stream, hashResult

‘ 自身ファイルのフルパスを取得
targetPath = WScript.ScriptFullName

‘ 【重要】ここに事前に算出した正しいSHA-256ハッシュ値を記述する
‘ 初回作成時や改修時は、ダミー値で実行して出力されたハッシュ値をここに埋め込むこと
expectedHash = “E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855” ‘ ※これは空文字のハッシュ

‘ 開発・初期導入時のハッシュ確認モード(運用時はFalseにすること)
Const DEBUG_MODE = True

On Error Resume Next

‘ PowerShellを隠蔽実行し、.NETのクラスライブラリを利用して高速にSHA-256を算出する
‘ ※ VBScript単体ではバイナリ読み込みとハッシュ計算のコストが重いため、WSH経由で最適化されたCLRをたたく
Set shell = WScript.CreateObject(“WScript.Shell”)

Dim psCommand
psCommand = “powershell -NoProfile -ExecutionPolicy Bypass -Command “”” & _
“$sha256 = [System.Security.Cryptography.SHA256]::Create();” & _
“$file = [System.IO.File]::Open(‘” & targetPath & “‘, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite);” & _
“$hash = $sha256.ComputeHash($file);” & _
“$file.Close();” & _
“$sha256.Dispose();” & _
“[System.BitConverter]::ToString($hash) -replace ‘-‘””” & _
“”””

Set exec = shell.Exec(psCommand)
hashResult = Trim(exec.StdOut.ReadAll())

If Err.Number <> 0 Then
‘ 実行時例外の捕捉
Wscript.Echo “[ERROR] ハッシュ計算プロセスで例外が発生しました: ” & Err.Description
VerifySelfIntegrity = False
Exit Function
End If

On Error GoTo 0

‘ デバッグモード時は現在のハッシュ値を画面に出力して終了する(初回ハッシュ取得用)
If DEBUG_MODE Then
Wscript.Echo “[DEBUG] 現在のファイルパス: ” & targetPath
Wscript.Echo “[DEBUG] 算出されたSHA-256: ” & hashResult
End If

‘ 期待値との厳密な比較(大文字小文字を区別しない)
If StrComp(hashResult, expectedHash, vbTextCompare) = 0 Then
VerifySelfIntegrity = True
Else
VerifySelfIntegrity = False
End If

‘ オブジェクトの明示的解放(メモリリーク・プロセス滞留の防止)
Set exec = Nothing
Set shell = Nothing
Set fso = Nothing
End Function

—

3. チーフアーキテクトが解説する実装の急所とパフォーマンス最適化

なぜVBScript単体ではなくPowerShell(.NET Framework)をインラインで呼び出すのか?

VBScriptの標準機能(ADODB.Streamなど)を用いてバイナリファイルを読み込み、自前でハッシュアルゴリズムを実装しようとすると、インタープリタ言語特有の圧倒的な速度低下を招く。特に数MBを超えるスクリプトや、依存リソースを含む複合スクリプトの場合、起動に数秒から十数秒の遅延が発生し、バッチ処理のUXを著しく損なう。

バックグラウンドで `WScript.Shell.Exec` を通じて最適化されたコンパイル言語(.NETの `SHA256` クラス)に処理を委譲することで、数ミリ秒オーダーでの高速なハッシュ算出と、メモリ消費の最小化を両立させている。

ファイル共有モード(FileShare.ReadWrite)の配慮

運用環境において、ログ監視エージェントや他のプロセスが該当の `.vbs` ファイルに何らかのアクセス(読み取り等)を行っている瞬間にスクリプトが多重起動されるケースがある。
PowerShell経由でファイルストリームを開く際に `[System.IO.FileShare]::ReadWrite` を明示的に指定しているのは、「他プロセスによるファイルロック競合による予期せぬスクリプト異常終了(BSTRエラー)」を確実に回避するための極めて実務的な知見である。

オブジェクトライフサイクルの厳格な管理

VBScriptの参照カウント方式のガベージコレクションは、スクリプト終了時に自動解放されるとはいえ、長寿命のプロセスや、連鎖的にスクリプトを呼び出すオーケストレーション環境においては、不要になったCOMオブジェクト(`WScript.Shell` など)を即座に `Set obj = Nothing` で解放することが鉄則だ。これを怠ると、ゾンビプロセスの遠因となり、Windowsのリソース枯渇を招く。

—

4. 運用上の注意点とさらなる堅牢化に向けて

1. 期待値ハッシュの管理:
ソースコード内に期待値 (`expectedHash`) をハードコーディングする場合、コードを改ざんする攻撃者はこの期待値の文字列自体も同時に書き換える可能性がある。より強固なセキュリティが求められる環境では、期待値ハッシュをアクセス権が厳しく制限された外部の読み取り専用INIファイル、レジストリ、あるいはデジタル署名(SignTool.org等による Authenticode署名)から動的に読み込む設計に昇華させるべきである。
2. デジタル署名との使い分け:
厳密な改ざん検知の王道はOS標準の Authenticode 署名(.pfx等によるコード署名)だが、社内ニッチな環境や、証明書インフラストラクチャが整備されていないレガシー現場においては、本記事で紹介した「ファイルハッシュの起動時即時検証ロジック」の方が、依存関係なく即座に導入できる現実的な解となる。

レガシーな技術であっても、アーキテクトの知見を正しく適用すれば、モダンなセキュリティ基準に匹敵する堅牢性を引き出すことが可能だ。現場のインフラを守る最後の砦として、このセーフティロジックを日々の自動化基盤に組み込んでいただきたい。

タイトルとURLをコピーしました