binparser.py 技術ドキュメント

プログラムの動作

binparser.py は、Pythonで記述されたバイナリファイル解析ツールです。このプログラムの主な目的は、バイナリファイルから特定のデータ形式(数値や文字列)を読み取ったり、ファイル内で特定の値を持つデータを探し出すことです。

主な機能は以下の通りです。

  • バイナリファイルの読み込み: 指定されたパスのバイナリファイルをバイト列としてメモリに読み込みます。

  • データ抽出: 指定されたオフセット、バイトサイズ、データ型、エンディアンに基づいて、バイナリデータから単一の値を抽出します。サポートされる型は浮動小数点数(float32, float64)、符号なし整数(uint16, uint32, uint64)、符号付き整数(int16, int32, int64)、およびASCII文字列です。

  • データ検索: バイナリデータ内の指定された範囲から、特定の型と値を持つデータを探し出し、そのオフセットを返します。浮動小数点数の検索では、許容誤差(tolerance)を設定して厳密な一致でなくても見つけられるようにします。ASCII文字列の検索も可能です。

  • ASCII文字列の自動検出: ファイル全体から一定の長さ以上の連続したASCII印字可能文字を抽出し、そのオフセットと値を特定します。

このプログラムは、バイナリファイルに含まれる未知のデータ構造を調査したり、特定の値がファイル内のどこに存在するかを効率的に特定したりする際に役立ちます。

原理

binparser.py は、Python標準ライブラリの struct モジュールと re モジュールを主要なツールとして利用し、バイナリデータを解析します。

  1. バイナリデータの読み込み: load_bin_file 関数は、open(file_path, 'rb') を使用してファイルをバイナリ読み込みモードで開き、f.read() でファイルの内容全体をバイト列 (bytes オブジェクト) としてメモリに読み込みます。

  2. 構造化データの抽出と検索:

    • struct モジュール: バイナリデータから特定の型の値を読み出す(アンパック)ために使用されます。type_map 辞書は、開発者が使いやすい型名(例: 'float32')を、struct モジュールが認識するフォーマットコード(例: 'f')とバイトサイズにマッピングします。

    • エンディアン指定: struct モジュールでは、フォーマット文字列の先頭に endian_prefix を付加することでエンディアンを指定します。'<' はリトルエンディアン、'>' はビッグエンディアンを示します。

    • データ抽出: get_data 関数では、struct.unpack_from(endian_prefix + fmt, bin_data, offset)[0] を使用して、指定されたオフセットからバイト列を解釈し、対応するPythonの数値型に変換します。

    • データ検索: search_data 関数は、指定された範囲をsizeバイトずつ繰り返し読み込み、それぞれを struct.unpack_from で値に変換します。

    • 浮動小数点数の比較: 浮動小数点数はコンピュータ内部での表現に限界があるため、厳密な等価比較は推奨されません。代わりに、許容誤差 (tolerance) を用いて比較します。検索対象の値 \(Y\) とバイナリから読み取った値 \(X\) の差の絶対値が許容誤差 \(\epsilon\) 以下である場合に一致とみなします。 $\(|X - Y| \le \epsilon\)$

    • 整数型の比較: 整数型は厳密な値の比較 (==) を行います。

  3. ASCII文字列の操作:

    • ASCII文字列の抽出 (extract_ascii_prefix): 指定されたオフセットから始まるバイト列を順に走査し、ASCII印字可能文字(0x20から0x7E)が続く限り文字を収集します。非ASCII文字が現れた時点で抽出を停止し、収集したバイト列を ascii エンコーディングでデコードします。

    • ASCII文字列の検索 (find_ascii_strings): re (正規表現) モジュールを使用します。正規表現パターン rb'[\x20-\x7E]{%d,}' は、指定された最小長以上のASCII印字可能文字の連続をバイナリデータから検索します。

    • 文字列検索 (search_datavartype == 'str'): 指定されたサイズで区切られたバイト列チャンクを utf-8 でデコードし、検索対象の文字列がデコード結果に含まれるかどうかを確認します。デコードエラーは errors='ignore' で無視されます。

これらの原理に基づいて、binparser.py はバイナリデータから情報を効率的に抽出し、検索する機能を提供します。

必要な非標準ライブラリとインストール方法

binparser.py は、Pythonの標準ライブラリのみを使用しており、非標準ライブラリは一切必要ありません。そのため、追加のインストール作業は不要です。

使用されている標準ライブラリは以下の通りです。

  • sys: コマンドライン引数の処理に使用されます。

  • struct: バイナリデータを特定のデータ型にアンパック(読み出し)するために使用されます。

  • re: 正規表現を用いた文字列の検索に使用されます。

必要な入力ファイル

プログラムの主な入力は、解析対象となるバイナリファイルです。

  • ファイル形式: 特定の形式は問いません。画像、音声、実行ファイル、カスタムデータ形式など、任意のバイナリデータを解析できます。

  • データ構造: プログラム自体は特定のデータ構造を仮定しません。解析を行うユーザーは、ファイル内のデータのオフセット、サイズ、データ型、エンディアンに関する知識を持っている必要があります。これらの情報は、プログラムの関数呼び出し時に引数として渡されます。

  • ファイル名: コマンドライン引数で指定します。引数が省略された場合、test.raw という名前のファイルをデフォルトとして使用します。

生成される出力ファイル

binparser.py は、実行時に出力ファイルを生成しません

全ての解析結果(検索で見つかったオフセット、エラーメッセージなど)は、プログラムの実行中に標準出力(コンソール)に表示されます。

コマンドラインでの使用例 (Usage)

binparser.py は、コマンドライン引数を受け取って実行できます。

python binparser.py [バイナリファイル名] [エンディアン]
  • [バイナリファイル名]:

    • 解析対象のバイナリファイルのパスを指定します。

    • この引数を省略した場合、プログラムはデフォルトで test.raw という名前のファイルを読み込もうとします。

  • [エンディアン]:

    • バイナリデータのバイト順序(エンディアン)を指定します。

    • 有効な値は 'little' (リトルエンディアン) または 'big' (ビッグエンディアン) です。

    • この引数を省略した場合、プログラムはデフォルトで 'little' を使用します。

コマンドラインでの具体的な使用例

ここでは、test.raw というバイナリファイルが存在し、以下のような内容(リトルエンディアンのデータ)が含まれていると仮定します。

  • オフセット 0x000000008.0 (float32)

  • オフセット 0x00000004120.0 (float32)

  • オフセット 0x000000205601 (int16)


実行例1: デフォルト引数での実行

解析対象ファイルとして test.raw を使用し、エンディアンをリトルエンディアンとします。

python binparser.py
  • 説明: binparser.pytest.raw ファイルをリトルエンディアンとして解析し、main 関数内で指定されている浮動小数点数 (8.0, 120.0, 0.02, 514.902, 552.835) と整数 (5601) を検索します。

  • 実行結果の例:

    offset 00000000 for 8.0
    offset 00000004 for 120.0
    No data found for 0.02
    No data found for 514.902
    No data found for 552.835
    offset 00000020 for 5601
    

    (出力されるオフセットは、test.raw ファイルの実際の内容によって異なります。)


実行例2: 特定のバイナリファイル (my_data.bin) をビッグエンディアンで解析

my_data.bin というファイルが存在し、これがビッグエンディアンで記述されている場合を想定します。

python binparser.py my_data.bin big
  • 説明: binparser.pymy_data.bin ファイルをビッグエンディアンとして解析し、main 関数内で指定されている値を検索します。my_data.bin には test.raw とは異なるデータが含まれているため、検索結果も異なります。

  • 実行結果の例:

    No data found for 8.0
    No data found for 120.0
    offset 00000010 for 0.02
    offset 00000014 for 514.902
    offset 00000018 for 552.835
    No data found for 5601
    

    (出力されるオフセットは、my_data.bin ファイルの実際の内容とエンディアンによって異なります。)