【テクニカル・上級編】【バイナリデータの16進数ダンプ表示】ADODB.Streamで取得したバイト配列を16進表記(Hex Dump)に整形・出力する開発支援モジュール – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【バイナリデータの16進数ダンプ表示】ADODB.Streamで取得したバイト配列を16進表記に整形・出力する極限のVBScriptモジュール

レガシーシステムの深部、あるいは閉じたイントラネット環境において、VBScriptとWSH(Windows Script Host)はいまだにインフラの血液として静かに鼓動を打っている。GUIを持たず、.NET Frameworkの依存関係すら超越してどのWindowsマシンでも即座に稼働するこの軽量ランタイムは、システム管理やデータ解析の現場において唯一無二の武器だ。

日々の運用保守の中で、文字化けする不明なログ、独自のバイナリプロトコルで通信する古いデバイス、あるいは破損したファイルフォーマットの解析に直面したことはないだろうか。
「中身のバイト列はどうなっているのか?」
それを確かめるために、わざわざ巨大なIDEを立ち上げる必要はない。VBScriptの極限まで最適化されたメモリ操作とCOMコンポーネントの組合せにより、数行かつ高パフォーマンスな「16進数ダンプ(Hex Dump)ツール」を構築できる。

本稿では、`ADODB.Stream`のバイナリモードを駆使し、VBScriptの弱点である文字列結合のパフォーマンス問題を完全に克服した、実務で即座に使えるプロフェッショナル向けのバイナリ解析モジュールを公開する。

—

1. VBScriptにおけるバイナリ処理のパラダイムシフト

VBScriptはネイティブの「バイト型配列(Byte Array)」を直接操作する強力な構文を持たない。Variant型の中に安全に隠蔽された安全な配列として扱われるため、これを効率的に処理するにはCOMの力学を理解する必要がある。

バイナリデータをファイルからストリームとして読み込む際、`ADODB.Stream`の右に出るものはいない。
しかし、読み込んだバイナリを単に16進数に変換していく過程で、多くのプログラマブル・初心者は致命的な罠に陥る。

‘ 【アンチパターン】絶対にやってはいけない文字列結合
Dim hexStr, i
For i = 0 To UBound(byteArray)
‘ ループのたびにメモリ再割り当てが発生し、O(N^2)のパフォーマンス劣化を引き起こす
hexStr = hexStr & Right(“0” & Hex(byteArray(i)), 2)
Next

VBScript(内部的にはCOMのBSTR)において、ループ内での文字列の連結 (`&`) は、データサイズが大きくなるにつれて指数関数的なメモリ再割り当てを引き起こし、プロセスをフリーズさせる。数メガバイトのファイルを読み込んだ瞬間、CPUはスパイクし、スクリプトは沈黙するだろう。

この極限の課題を突破するのが、`System.Collections.ArrayList` または `Scripting.Dictionary` をバッファとして活用し、最後に一括で結合するアプローチ、あるいは `ADODB.Stream` 自体をテキストバッファとして再利用するアーキテクチャである。

—

2. 実装:HexDump.vbs 開発支援モジュール

以下のコードは、指定したバイナリファイルを読み込み、お馴染みの16進数ダンプ形式(アドレス、16進表記、ASCII文字一覧)で標準出力(コンソール)へ流し込む完全なモジュールだ。

‘ ==============================================================================
‘ File Name: HexDump.vbs
‘ Description: ADODB.Streamを用いた高速バイナリ16進数ダンプ出力モジュール
‘ Author: Chief Architect (Legacy Systems Integration)
‘ ==============================================================================

Option Explicit

‘ 実行のエントリポイント
Call Main()

Sub Main()
Dim args
Set args = WScript.Arguments

If args.Count < 1 Then WScript.Echo "Usage: cscript.exe HexDump.vbs ”
WScript.Quit(1)
End If

Dim targetPath
targetPath = args(0)

‘ バイナリダンプの実行
Call DumpBinaryFile(targetPath)
End Sub

Sub DumpBinaryFile(filePath)
Dim stream
Set stream = CreateObject(“ADODB.Stream”)

On Error Resume Next
‘ 1. ストリームの初期化とバイナリモード設定
stream.Type = 1 ‘ adTypeBinary
stream.Open
stream.LoadFromFile filePath

If Err.Number <> 0 Then
WScript.Echo “[ERROR] ファイルの読み込みに失敗しました: ” & filePath & ” (” & Err.Description & “)”
Set stream = Nothing
Exit Sub
End If
On Error GoTo 0

‘ 2. バイナリデータをバイト配列(Variant)として取得
Dim binData
binData = stream.Read()
stream.Close
Set stream = Nothing

Dim lngSize
lngSize = LenB(binData)

If lngSize = 0 {
WScript.Echo “[INFO] ファイルサイズが0バイトです。”
Exit Sub
}

WScript.Echo “Dump: ” & filePath & ” (” & lngSize & ” bytes)”
WScript.Echo String(76, “-“)

Dim i, j, offset
Dim lineHex, lineAscii, ascCode, b
Dim arrBuffer
Set arrBuffer = CreateObject(“System.Collections.ArrayList”)

‘ 3. 16バイト単位でループ処理
For i = 0 To lngSize – 1 Step 16
lineHex = “”
lineAscii = “”

‘ オフセットアドレスの生成 (8桁固定 16進数)
offset = Right(“00000000” & Hex(i), 8)

For j = 0 To 15
If (i + j) < lngSize Then ' 中身のバイト値を取得 (AscBを使用) b = AscB(MidB(binData, i + j + 1, 1)) ' 16進数2桁に整形 lineHex = lineHex & Right("0" & Hex(b), 2) & " " ' ASCII文字の判定 (制御文字はドットに置換) If b >= 32 And b <= 126 Then lineAscii = lineAscii & Chr(b) Else lineAscii = lineAscii & "." End If Else ' パディング(最終行の端数処理) lineHex = lineHex & " " lineAscii = lineAscii & " " End If ' 8バイト目に視認性向上のためのスペース挿入 If j = 7 Then lineHex = lineHex & " " End If Next ' バッファに蓄積(メモリ効率の最大化) arrBuffer.Add offset & " " & lineHex & " | " & lineAscii ' バッファが一定数たまってきたらコンソールに出力してメモリを解放 If arrBuffer.Count >= 1000 Then
WScript.Echo Join(arrBuffer.ToArray(), vbCrLf)
arrBuffer.Clear()
End If
Next

‘ 残余バッファのフラッシュ
If arrBuffer.Count > 0 Then
WScript.Echo Join(arrBuffer.ToArray(), vbCrLf)
End If

Set arrBuffer = Nothing
WScript.Echo String(76, “-“)
WScript.Echo “Dump Complete.”
End Sub

—

3. チーフアーキテクトが解説するコードの急所

このスクリプトは単なる「動くコード」ではない。レガシー環境において数ギガバイト、あるいは数メガバイトのファイルを安全に捌くための知見が凝縮されている。

① `MidB` と `AscB` による高速バイナリ走査

VBScriptの文字列関数には `Mid` や `Asc` といったマルチバイト対応関数が存在するが、バイナリ解析においてこれらを使用すると、システムのコードページ(ANSI等)に基づく意図しない文字変換が発生し、データが破壊される。
バイナリを扱う際は、常にバイト単位で処理を行う `MidB` と `AscB` を使用し、配列のインデックス計算を厳密に行う必要がある。

② `System.Collections.ArrayList` によるメモリ汚染の回避

前述の通り、VBScriptの `&` 演算子による文字列結合は、巨大データの処理でメモリを枯渇させる。
ここでは .NET Frameworkの `System.Collections.ArrayList` をCOM経由でインスタンス化し、行ごとに文字列をプッシュ(Add)していき、最後に `Join` 関数で一括結合する手法をとっている。これにより、ガベージコレクションの負荷を最小限に抑え、圧倒的な処理速度を実現している。

③ 堅牢なオブジェクトライフサイクル管理

`ADODB.Stream` や `ArrayList` などのCOM/CLRオブジェクトは、スコープを抜けるだけでは即座にメモリから解放されないケースがある(特にWSHの長寿プロセスやループ内)。
そのため、使用後は明示的に `Set stream = Nothing` を記述し、リソースのリークを完全に防ぐ設計を徹底している。

—

4. 実行方法と実務での活用シーン

このスクリプトは、GUIを持たないWindows Serverのメンテナンスや、開発環境でのバイナリ検証に最適だ。コマンドプロンプト(`cmd.exe`)から `cscript` エンジンを指定して実行する。

cscript //nologo HexDump.vbs “C:\app\data\unknown_format.dat”

出力イメージ

Dump: C:\app\data\unknown_format.dat (48 bytes)
—————————————————————————-
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 | .PNG……..IHDR
00000010 00 00 02 00 00 00 01 f4 08 06 00 00 00 1a b6 c3 | …………….
00000020 ea 00 00 00 0d 49 44 41 54 78 5e 63 60 18 05 | …..IDATx^c`..
—————————————————————————-
Dump Complete.

このような形で、ファイルヘッダ(マジックナンバー)の確認や、通信キャプチャデータのパケット構造の解析を、サードパーティ製のツールを一切インストールできないセキュアな環境下でも瞬時に行うことが可能となる。

—

総括

VBScriptは「古い言語」として片付けられがちだが、その本質は 「OSの基盤に直結した、最も身軽で強力なオートメーション・レイヤー」 である。
COMコンポーネントの特性を熟知し、メモリの挙動をコントロール下におくことで、現代の重厚長大なフレームワーク顔負けの高効率なユーティリティを作り上げることができる。

現場のインフラを支えるエンジニア諸君、レガシーを恐れるな。真の技術力とは、環境の制約を優美にハックし、目的を完遂することにあるのだから。