【入門編】【上級プロフェッショナル向け】マジックバイト(ヘッダー情報)とGetExtensionNameの不一致から不審な「拡張子偽装」を特定するセキュリティスクリプト – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは! VBScriptの世界へようこそ。
業務の自動化を進めていくと、「フォルダの中にあるファイルを自動でチェックしたい」「怪しいファイルがないか見張りいたい」という実践的な課題にぶつかりますよね。

今回は、プログラミング初学者の方から「マクロの記録や単純な自動化は卒業したぞ!」というステップアップ層に向けて、ファイルシステムオブジェクト(FSO)と、ちょっとディープな「ファイルの正体(マジックバイト)」を暴くセキュリティスクリプトの作り方を解説します。

「ここをクリアすれば、VBScriptのファイル操作の基本はバッチリですよ!」と温かく導きますので、ぜひ最後までついてきてくださいね。

—

1. なぜ「拡張子」だけを見てはいけないのか?

Windowsを使っていると、ファイル名の後ろにある「.txt」や「.jpg」「.exe」といった拡張子を見て、私たちはファイルの性質を判断しますよね。

しかし、悪意あるユーザーや、セキュリティ意識の低いユーザーが、

  • 本当は実行ファイル(.exe)なのに、名前を `report.txt.exe` ではなく `report.txt` に偽装する
  • 危険なスクリプトを画像ファイルに見せかける

といった「拡張子偽装」を行うことがあります。これをVBScriptの標準機能である `GetExtensionName` だけで見ていると、「あ、これはテキストファイルですね」と騙されてしまいます。

そこで登場するのが、ファイルが持つ「マジックバイト(ヘッダー情報)」です。ファイルの中身の先頭数バイトには、「私はJPEG画像です」「私はZIPアーカイブ(またはOfficeファイル)です」という絶対に偽装できない固有の署名(シグネチャ)が刻まれています。

今回は、このマジックバイトと拡張子を突き合わせ、「おや、名前はテキストだけど中身はexeじゃないか!」と検知するプロ仕様のスクリプトを作ってみましょう。

—

2. スクリプトの全体像:何をするプログラムか?

今回作成するVBScriptは、指定したフォルダ内を巡回し、以下のチェックを行います。

1. FSO(FileSystemObject)を使ってファイルの拡張子を取得する。
2. ADODB.Streamを使って、ファイルの先頭数バイト(バイナリデータ)を読み込む。
3. マジックバイトのパターンと照合し、本来のファイル形式を特定する。
4. 「自称する拡張子」と「本当のファイル形式」が一致しているか監査し、不一致なら警告ログを吐く!

それでは、実際のコードを見てみましょう。現場でそのままコピー&ペーストして使える実用的なコードです。

—

3. 実装コード:拡張子偽装ハンター・スクリプト

以下のコードをメモ帳などに貼り付け、拡張子を `.vbs` (例: `audit_files.vbs`)として保存してください。

‘ ==============================================================================
‘ スクリプト名: CheckFileSpoofing.vbs
‘ 概要: マジックバイトと拡張子の不一致(拡張子偽装)を検出するセキュリティスクリプト
‘ 対象読者: VBScriptの基本から一歩進んだファイル操作を学びたい方
‘ ==============================================================================

Option Explicit

‘ メイン処理の実行
Call AuditFolder(“C:\Work\CheckTarget”)

Sub AuditFolder(targetFolderPath)
Dim fso, folder, file, ext, magicBytes, actualType
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ フォルダが存在するかチェック
If Not fso.FolderExists(targetFolderPath) Then
WScript.Echo “エラー: 指定されたフォルダが存在しません: ” & targetFolderPath
Exit Sub
End If

Set folder = fso.GetFolder(targetFolderPath)

WScript.Echo “— ファイル監査を開始します: ” & targetFolderPath & ” —”

‘ フォルダ内の全ファイルをループ処理
For Each file in folder.Files
‘ 1. FSOの GetExtensionName で「自称する拡張子」を取得
ext = LCase(fso.GetExtensionName(file.Name))

‘ 2. ファイルの先頭バイト(マジックバイト)を読み取って「本当の形式」を判定
actualType = GetFileMagicType(file.Path)

‘ 3. 監査ロジック(拡張子と中身の乖離をチェック)
Call CheckConsistency(file.Name, ext, actualType)
Next

WScript.Echo “— ファイル監査が完了しました —”

‘ オブジェクトの解放(メモリ管理の基本!)
Set folder = Nothing
Set fso = Nothing
End Sub

‘ ——————————————————————————
‘ 関数: GetFileMagicType
‘ 役割: ADODB.Streamを使ってファイルの先頭数バイトを読み込み、マジックバイトを判定する
‘ ——————————————————————————
Function GetFileMagicType(filePath)
Const adTypeBinary = 1
Dim stream, bytes, hexHeader

On Error Resume Next
Set stream = CreateObject(“ADODB.Stream”)
If Err.Number <> 0 Then
GetFileMagicType = “ERROR”
Exit Function
End If

stream.Type = adTypeBinary
stream.Open
stream.LoadFromFile filePath

‘ 先頭から4バイトを読み込む
bytes = stream.Read(4)
stream.Close
Set stream = Nothing
On Error GoTo 0

‘ バイト配列を16進数の文字列に変換する(例: “4D5A9000″)
hexHeader = BytesToHex(bytes)

‘ マジックバイトによる判定
If Left(hexHeader, 4) = “4D5A” Then
GetFileMagicType = “EXE” ‘ Windows実行ファイル (MZ…)
ElseIf Left(hexHeader, 8) = “504B0304” Then
GetFileMagicType = “ZIP” ‘ ZIP圧縮ファイル(Officeドキュメント含む)
ElseIf Left(hexHeader, 6) = “FFD8FF” Then
GetFileMagicType = “JPG” ‘ JPEG画像
ElseIf Left(hexHeader, 8) = “89504E47” Then
GetFileMagicType = “PNG” ‘ PNG画像
ElseIf Left(hexHeader, 8) = “25504446” Then
GetFileMagicType = “PDF” ‘ PDF文書
Else
GetFileMagicType = “UNKNOWN”
End If
End Function

‘ ——————————————————————————
‘ 補助関数: BytesToHex
‘ 役割: バイナリデータを16進数文字列に変換する
‘ ——————————————————————————
Function BytesToHex(bytes)
Dim i, stream, result
Set stream = CreateObject(“ADODB.Stream”)
stream.Type = 1 ‘ adTypeBinary
stream.Open
stream.Write bytes
stream.Position = 0
stream.Type = 2 ‘ adTypeText
stream.Charset = “iso-8859-1”
result = stream.ReadText
stream.Close
Set stream = Nothing

Dim hexStr
hexStr = “”
For i = 1 To Len(result)
Dim h
h = Hex(Asc(Mid(result, i, 1)))
If Len(h) = 1 Then h = “0” & h
hexStr = hexStr & h
Next
BytesToHex = hexStr
End Function

‘ ——————————————————————————
‘ 監査ロジック: 乖離チェック
‘ ——————————————————————————
Sub CheckConsistency(fileName, ext, actualType)
Dim isSuspicious
isSuspicious = False

‘ 例: 拡張子が txt なのに、中身が EXE だった場合
If ext = “txt” And actualType = “EXE” Then
isSuspicious = True
‘ 例: 拡張子が jpg なのに、中身が EXE だった場合
ElseIf (ext = “jpg” Or ext = “jpeg”) And actualType = “EXE” Then
isSuspicious = True
End If

If isSuspicious Then
WScript.Echo “[警告!] 拡張子偽装の疑いがあります!” & _
” ファイル名: ” & fileName & _
” (自称拡張子: ” & ext & ” / 本当の中身: ” & actualType & “)”
Else
WScript.Echo “[正常] ” & fileName & ” (拡張子: ” & ext & ” / 判定: ” & actualType & “)”
End If
End Sub

—

4. コードの重要ポイントと「初心者が陥りやすい罠」

ここまで読んだ方の中には、「うわっ、ADODB.Streamとか出てきて難しそう……」と思った方もいるかもしれません。でも大丈夫!ここを押さえればVBScriptの視野が一気に広がります。

① `FileSystemObject` (FSO) の限界を知る

FSOはファイルやフォルダを作る・消す・調べる(`GetExtensionName` など)には最高の相棒ですが、「ファイルの中身(バイナリ)」を直接読み解く能力はありません。
だからこそ、テキストファイルを扱うときは `TextStream` を使い、今回のようなバイナリ(マジックバイト)を扱うときは `ADODB.Stream` を組み合わせるという、適材適所のオブジェクト使い分けが必要になります。これがプロの技術です。

② メモリの解放(`Set … = Nothing`)をサボらない

VBScriptでは、スクリプトが終了すれば使ったメモリは自動で回収されますが、長時間のループや巨大なフォルダを巡回するスクリプトでは、使い終わったオブジェクト(`Set folder = Nothing` や `Set stream = Nothing`)をこまめに解放することが、メモリリークを防ぐ重要なマナーです。

③ なぜ `Option Explicit` を書くのか?

コードの2行目にある `Option Explicit`、これ実はめちゃくちゃ重要です。
これを書いておくと、変数の宣言し忘れ(タイポによるバグ)を強制的にエラーにしてくれます。 「あれ、動かないな…あ、変数名を打ち間違えてた!」という無駄なデバッグ時間をゼロにしてくれるので、VBScriptを書くときは必ずおまじないのように先頭に書きましょう。

—

5. まとめ

今回は、FSOによるファイル操作の基本を押さえつつ、一歩進んだ「マジックバイトによるファイル監査」のスクリプトをご紹介しました。

  • FSOは「外側(名前や拡張子、サイズ)」を見る得意技がある。
  • ADODB.Streamは「内側(バイナリ・中身の正体)」を暴く得意技がある。

この2つを組み合わせることで、業務自動化だけでなく、ちょっとしたセキュリティチェックツールまでVBScriptで自作できるようになります。

「ここをクリアすれば、VBScriptの基本はバッチリですよ!」
ぜひご自身の環境でも試して、自動化マスターへの階段を一段上ってみてくださいね。それでは、次回の応用編もお楽しみに!

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