プログラミング
バイナリファイルの可視化
Visualizing Binary Files (movq.de)
要約
この記事では、バイナリファイルを理解しやすくするための2つの可視化手法を紹介しています。一つ目は、テキストエディタのASCII表示部分に特殊文字を用いて非ASCIIバイトやNULバイトを強調する手法です。二つ目は、バイナリファイル全体を画像(PGM形式)として解釈し、バイト値をピクセル値にマッピングすることで、ファイル構造の全体像を視覚的に把握する手法です。後者の手法は、特に大規模ファイルの分析において、その特性を直感的に理解するのに役立ちます。
全文翻訳
これは私のヘックスエディタ bine です。基本的に、すべて白黒です。
しばらく前に、Vim の xxd は出力に色を付ける機能(私が使用している Vim Classic には含まれていません)を獲得しました。私も bine にこの機能を持たせたいと思いました。そこで実装を開始しました。
初期のドラフトはここで見ることができます。
これはすでに改善だと考えています。なぜなら、ほとんどASCIIテキストの範囲をより簡単に見つけられるからです。しかし、それほど素晴らしいものではありません。もっと多くの色が必要になり、おそらく16進数の列がスペースで区切られているのではなく、互いに隣接している方が良いでしょう。
私のTUIフレームワーク movwin は、内部で ncurses を使用しています(これは完全に意図的です。なぜなら、私は ncurses の機能の安定性を高く評価しているからです)。フレームワーク自体は Python で書かれており、ncurses は C ライブラリです。
Python から ncurses への呼び出しが非常に高価になることがあることに、すでに気づいていました(これが一般的な問題なのか、Python + ncurses だけの問題なのか、あるいは ncurses 自体の問題なのかは分かりませんし、重要ではありません)。色がない場合、bine は1回の curses 呼び出しで完全な16進数行を書き込みます。色があれば、行ごとに多くの呼び出しが必要になります。それは高すぎます。
そして、フレームワーク自体も、多くの異なる色を高速に処理するのに特に適していません。階層的なパレットシステムがあり、それらすべてが高価です。また、movwin はデフォルトでダークテーマとライトテーマの両方をサポートしているため、少なくとも2つのバージョンのカラーパレットを維持する必要があります。
最終的に、次のような考えに至りました。
16進数カラムを色付けする代わりに、bine はASCIIカラムに特殊文字を使用します。通常そこには現れない文字なので、競合はありません。NULバイトは U+2593 (▓) を使用し、0x20 <= バイト <= 0x7E の範囲外のバイトは U+2593 (░) を使用し、残りはそのまま表示される印刷可能なASCII文字です。これは xxd がカバーするすべてのケースを網羅しているわけではありませんが、十分です。「プレーンテキスト」であるものとそうでないものの概要を素早く把握でき、NULバイトが際立ちます。実装は非常に簡単で、実行時のコストも低いです。
また、大きなファイル全体の同様の可視化機能も欲しくなりました。bine の小さなASCIIペインでは、それほど多くを表示できません。
これは数メガバイト、あるいはギガバイトのサイズのものに対して必要でした。私の可視化は基本的に画像です。ですから…なぜコンピューターに既存のバイナリファイルを画像として解釈させるように強制しないのでしょうか?処理は何もせず、0x00 から 0xFF の範囲のすべてのバイトを取得し、それらをピクセルにマッピングします。
Portable Graymap (PGM) はまさにそれです。Wikipedia の例は、このフォーマットのASCIIバージョンを示していますが、バイナリバージョンもあります。ですから、私がしなければならなかったことは、基本的にこれだけです。
printf 'P5\n%s %s 255\n' "$width" "$height"
cat "$infile"
完了。
私が実装した唯一の「ロジック」は、適切な幅と高さを決定することでした。結果は(LinuxとBSDでは)どの画像ビューアでも表示できます。
GORILLAS.BAS の EXE ファイルの可視化です(拡大なし):
(はい、これがゲーム全体です。この画像を取得して、EXE ファイルに変換することもできます。)
ここで何が見えるでしょうか?興味深いセクションをいくつか注釈付けさせてください。
上部には、ほぼ「ランダム」な領域があります。非常に暗いピクセル、非常に明るいピクセル、灰色のピクセル、すべてがあります。これはコードです。
中央には、非常に規則的なパターンがあります。それが何であるかはまだ解明していませんが、それはまた別の日の話です。(リロケーションテーブルのように見えますが、位置が間違っています。しかし、このファイルは EXEPACK ファイルであり、事態を複雑にしています。見てみましょう。)
末尾には、「暗い灰色」の長いセクションがあります。比較的均一です。これはASCIIテキストです。
ハイビットは設定されていないため、これらの値はすべてコードバイトよりも明らかに小さいです。(プログラムファイルは、末端にこのようなセクションを持つことがよくあります。文字列リテラルが格納されている場所です。)
前処理が全くないのは良いことだと思います。これらはすべてデータの特性を利用しています。
より「明示的な」例として、2つのファイル(テキストファイルと画像ファイル)が入ったtarballを以下に示します。
テキスト領域の「パターン」にも気づきましたか?タイルの壁紙のように見えますか?それはテキストが繰り返されているからです。
さらにいくつかの例です。
ruff の縮小された可視化です。
上部にあるほとんど NUL バイトの黒い帯の拡大されていない抜粋です。
たくさんのきれいに配置されたデータとテーブル。
ruff バイナリは 25 MB のサイズで、結果の PGM ファイル(4096x6225 ピクセル)も同様なので、縮小しました。ブログにそんな大きなファイルを永続的に残したくないからです。しかし、GIMP や nsxiv は PGM ファイルを簡単に扱え、パフォーマンスや使いやすさの問題は全くありません。
690 MB の .iso ファイルの可視化です(もちろん、大幅に縮小されています)。
PGM ファイルの作成には 0.2 秒かかり、ImageMagick を使用した画像のサイズ変更には 6 秒かかります。これはこのアプローチの限界を示しています。
4 GB のファイルでこれを試しましたが、ImageMagick は過剰な RAM を必要としました。理論的には、画像を段階的にサイズ変更すること(一度に数行だけメモリに入れる)、次に小さいバージョンを圧縮することで、これを機能させることができると思います。まだそのようなツールは書いていませんし、ImageMagick はこれを実装していないようです(または、私が知らないだけです。検索していませんが)。
それでも、探しているデータの種類によっては、すでに役立つツールになり得ます。ギガバイト範囲の大きなファイルを検査することは、めったに必要ありません。必要であれば、簡単に小さな部分に分割できます。
デモとして、古い 8 GB のディスクイメージを以下に示します。
見るのは少し面白いですが、前述したように、何を探しているかによっては役立つことがあります。
今のところはこのままにしておくつもりです。