守護者の設計思想:VBScriptによる「自己防衛型」ファイルシステム監視エンジンの構築
VBScript (Visual Basic Scripting Edition) は、現代のフロントエンド開発やクラウドネイティブなアーキテクチャの文脈では、しばしば「遺物」として片付けられる。しかし、数千のレガシー資産が稼働し続けるエンタープライズの最前線において、このスクリプト言語は依然として強力な、そして極めて軽量な「楔(くさび)」となり得る。
特に、最新のEDR(Endpoint Detection and Response)が導入されていない、あるいは導入の隙間を突くようなレガシーなWindows環境において、ファイルシステムに対する異常な振る舞い——すなわち、ランサムウェアに酷似した短時間での大量ファイル生成——を検知し、即座に物理的な隔離(Lockdown)を行う仕組みを構築することは、システム管理者の責務である。
本稿では、単なる「ファイルの数え上げ」ではない。オブジェクトのライフサイクル、メモリ管理、そしてシステムコマンドへのブリッジングを駆使した、プロフェッショナル向けの「自己防衛型監視スクリプト」の真髄を解説する。
1. アーキテクチャの要諦:検知から隔離への決定論的プロセス
監視ツールを設計する際、最も避けるべきは「検知の遅延」と「監視プロセス自体のリソース枯渇」である。
本システムは、以下の3つのフェーズで構成される。
1. 監視フェーズ (Monitoring Phase): `FileSystemObject (FSO)` を用い、ターゲットディレクトリ内のファイルメタデータをスキャンする。
2. 分析フェーズ (Analysis Phase): 時間軸(Time-window)に基づき、ファイル生成レートを算出する。これは単純な合計数ではなく、「単位時間あたりの増分」を評価するヒューリスティックなアプローチをとる。
3. 介入フェーズ (Intervention Phase): 閾値を超えた場合、`WScript.Shell` を介してWindows OSの低レイヤー属性(Read-only, Hidden, System)を強制適用し、書き込みプロセスを物理的に阻害する。
メモリ管理における致命的な罠
VBScriptにおいて、ループ内で `FileSystemObject` や `File` オブジェクトを生成し続けることは、COMオブジェクトの参照カウントを増大させ、メモリリークを引き起こす。長時間稼働させる監視ツールにおいて、`Set obj = Nothing` による明示的な解放は、単なる作法ではなく、システムの生存条件である。
2. 実装:自己防衛型監視エンジンのコード・プロトタイプ
以下に、実戦投入を想定したコード構造を示す。このコードは、単なるサンプルではなく、エラーハンドリングとオブジェクトのライフサイクル管理を徹底した設計となっている。
' ==============================================================================
' Script Name: SelfDefendingMonitor.vbs
' Description: Ransomware-like mass file creation detection and lockdown.
' Author: Legendary Chief Architect
' ==============================================================================
Option Explicit
' --- 設定定数 ---
Const TARGET_DIR = "C:\Data\CriticalPath" ' 監視対象ディレクトリ
Const THRESHOLD_COUNT = 50 ' 閾値(検知するファイル増分数)
Const TIME_WINDOW = 10 ' 判定時間間隔(秒)
Const CHECK_INTERVAL = 2 ' ループ間隔(秒)
Const LOCKDOWN_DIR = "C:\Quarantine" ' 隔離用ディレクトリ(オプション)
' --- グローバル変数 ---
Dim fso, shell
Dim lastFileCount, lastCheckTime
Set fso = CreateObject("Scripting.FileSystemObject")
Set shell = CreateObject("WScript.Shell")
' 初期化
lastFileCount = GetFileCount(TARGET_DIR)
lastCheckTime = Timer
WScript.Echo "Monitoring started on: " & TARGET_DIR
' --- メインループ ---
Do
WScript.Sleep (CHECK_INTERVAL * 1000)
Dim currentFileCount, currentTime, timeDiff
currentFileCount = GetFileCount(TARGET_DIR)
currentTime = Timer
timeDiff = currentTime - lastCheckTime
' 時間窓(TIME_WINDOW)が経過したか確認
If timeDiff >= TIME_WINDOW Then
Dim growthRate
growthRate = currentFileCount - lastFileCount
' 異常検知ロジック
If growthRate >= THRESHOLD_COUNT Then
ExecuteLockdown TARGET_DIR
WScript.Echo "CRITICAL: Abnormal activity detected! Lockdown initiated."
' 検知後は、被害拡大を防ぐため、あるいは管理者の判断を待つため停止するか、
' 継続する場合は隔離状態を維持するロジックが必要。
WScript.Quit
End If
' 状態の更新
lastFileCount = currentFileCount
lastCheckTime = currentTime
End If
' メモリ最適化の思想:
' ループ内でオブジェクトを生成する場合は、必ずこのスコープの終わりに Nothing を行う。
' 今回はグローバルで保持しているため、ループ内での再生成は避けている。
Loop
' ==============================================================================
' 関数定義
' ==============================================================================
' 指定フォルダ内のファイル数をカウントする
Function GetFileCount(strPath)
Dim folder, fileCount, objFile
fileCount = 0
On Error Resume Next ' ディレクトリへのアクセス権限エラー対策
If fso.FolderExists(strPath) Then
Set folder = fso.GetFolder(strPath)
For Each objFile In folder.Files
fileCount = fileCount + 1
' オブジェクトの参照を最小限にするため、ループ内では最小限の処理に留める
Set objFile = Nothing
Next
Set folder = Nothing
Else
WScript.Echo "Error: Path not found: " & strPath
End If
On Error GoTo 0
GetFileCount = fileCount
End Function
' 異常検知時の緊急ロックダウン処理
Sub ExecuteLockdown(strPath)
Dim folder, objFile, cmd
On Error Resume Next
Set folder = fso.GetFolder(strPath)
' 戦略1: ファイル属性の変更による書き込み阻害 (Read-only, Hidden, System)
' FSOの属性変更よりも、attribコマンドによるOSレベルの属性変更の方が確実かつ高速。
WScript.Echo "Executing Lockdown: Changing file attributes..."
' attrib +r +h +s コマンドを各ファイルに適用(再帰的ではない簡易実装)
' 本来はサブフォルダまで再帰的に処理すべきだが、速度を優先。
For Each objFile In folder.Files
' Windows APIの挙動を模倣し、コマンドライン経由で属性をロック
' +R: Read-only, +H: Hidden, +S: System
shell.Run "attrib +r +h +s """ & objFile.Path & """", 0, True
Set objFile = Nothing
Next
' 戦略2: フォルダ自体のアクセス権限変更(必要に応じて実装)
' ここでは、属性変更による「書き込み不可」状態への移行を主眼とする。
Set folder = Nothing
On Error GoTo 0
End Sub
3. エンジニアリング・インサイト:極限の最適化と信頼性
3.1 属性変更における「コマンド・ブリッジング」の選択
コード内で `shell.Run “attrib …”` を使用している点に注目してほしい。FSOの `File.Attributes` プロパティを使用することも可能だが、大規模なディレクトリに対して大量の属性変更を行う場合、COMインターフェースのオーバーヘッドが無視できなくなる。
Windowsのネイティブコマンドである `attrib` を `shell.Run` で呼び出す手法は、OSのカーネルに近いレイヤーでの処理を期待でき、かつ実行ウィンドウを非表示 (`0`) にすることで、ユーザーへのノイズを最小限に抑えられる。これは、攻撃者に検知を悟らせないための、あるいは業務への影響を最小化するための「静かなる介入」である。
3.2 決定論的なエラーハンドリング
`On Error Resume Next` は、乱用すればコードの整合性を破壊する毒となる。しかし、ファイルシステム監視においては、ロックされているファイルや権限のないファイルに遭遇することは避けられない。
本実装では、`GetFileCount` 内で限定的に使用し、エラーが発生しても監視プロセス自体がクラッシュしない(=監視の空白期間を作らない)よう、堅牢性を担保している。
3.3 時間窓(Time-window)の設計
単なる「ファイル数の増加」を監視すると、通常のバックアップ処理や大量のログ出力によって誤検知(False Positive)が発生する。
プロフェッショナルは、`Timer` 関数を用いて「一定時間内に、どれだけの密度でイベントが発生したか」というレート(変化率)を算出する。これにより、緩やかなファイル生成と、ランサムウェアによる爆発的なファイル生成を数学的に分離することが可能となる。
結語:レガシーを武器に変える
VBScriptは、決して万能ではない。しかし、その制約を知り、Windows APIやネイティブコマンドを適切に制御する術を知るエンジニアにとって、それは「最小のフットプリントで最大の防御力を発揮する」極めて鋭利な武器となる。
システムが複雑化し、モダンなセキュリティ製品がすべてをカバーできない領域において、こうした「低レイヤーの知見に基づいたスクリプト」こそが、最後の防衛線(Last Line of Defense)となるのである。
