プログラマをしていると、ちょくちょくバイナリデータから情報を読みたくなりますね。そんな時は、ブツブツ言いながらバイナリエディタと睨めっこすることになるわけですが、これが結構大変なので、何とか楽にならないかなぁと思って探していると、hachoirというナイスなpythonモジュールが見つかりました。このモジュールを使うとバイナリデータをパースして様々なデータを取得できます。かなり多くのデータフォーマットに対応している(現時点で70種類)のが素晴らしいです。
hachoirはいくつかのモジュールに分かれているのですが、大抵は以下をインストールすれば良いと思います。
$ easy_install hachoir_parser $ easy_install hachoir_metadata
このモジュールにはhachoir-metadataというコマンドラインツールが含まれていて、コードを書かなくても基本的な情報を取得できます。早速zipファイルを覗いてみましょう。いい感じにファイルの中身が見れていますね。
$ hachoir-metadata cpsb2cb1.zip Common: - MIME type: application/zip - Endianness: Little endian File "cpsb2cb/": - File name: cpsb2cb/ - File size: 0 bytes - Creation date: 2009-04-05 17:39:42 - Compression: no compression File "cpsb2cb/cpsb2cb.m": - File name: cpsb2cb/cpsb2cb.m - File size: 998 bytes - Compressed file size: 514 bytes - Compression rate: 1.9x - Creation date: 2009-04-02 17:46:30 - Compression: Deflate File "cpsb2cb/makefile": - File name: cpsb2cb/makefile - File size: 242 bytes - Compressed file size: 153 bytes - Compression rate: 1.6x - Creation date: 2009-04-02 04:11:52 - Compression: Deflate
続いてPNGやWAVファイルを覗いてみます。画像の場合はサイズや色数、音の場合はサンプリングレートや再生時間など基本的な情報はだいたい押さえてくれています。
$ hachoir-metadata Default.png Metadata: - Image width: 320 pixels - Image height: 480 pixels - Number of colors: 256 - Bits/pixel: 8 - Pixel format: Color index - Compression rate: 5.3x - Compression: deflate - Producer: Adobe ImageReady - MIME type: image/png - Endianness: Big endian $ hachoir-metadata bgm.wav Common: - Duration: 2 min 41 sec 495 ms - Channel: mono - Sample rate: 22.1 kHz - Bits/sample: 16 bits - Compression rate: 1.0x - Compression: Microsoft Pulse Code Modulation (PCM) - Bit rate: 352.8 Kbit/sec - MIME type: audio/x-wav - Endianness: Little endian
もうこれだけで良いやんと思うわけですが、コマンドラインツールで解析できるフォーマットはhachoirモジュールが対応しているものの一部になっていて、コードを書かないと解析できないフォーマットもあります。iPhoneアプリに含まれているplistなんかがその例で、コマンドラインツールを使うと以下のような親切なエラーを吐きます。
$ hachoir-metadata Info.plist [err!] [/] Hachoir can't extract metadata, but is able to parse: Info.plist
そこで実際にplistを解析するコードを書いてみます。処理は簡単でcreateParser関数にファイル名を渡せばパーサーオブジェクトが作られます。後はそのパーサーを色々いじって情報を取得します。最初はキョドリましたがipythonでタブ補完を使って、簡単に掘り進められました。
#!/usr/bin/python # -*- coding: utf-8 -*- import json from hachoir_parser import * parser = createParser(u'Info.plist') generator = parser.createFields() generator.next() generator.next() generator.next() dict_item = generator.next() dict_item.createDisplay() res = {} for k, v in dict_item.value: res[k.display.strip('"')] = bytes(v) print res['CFBundleSupportedPlatforms'] print res['DTPlatformVersion'] print res['UIDeviceFamily'] print res['LSRequiresIPhoneOS']
上記のコードを走らせた結果が以下になります。思ったとおりの挙動になっていますね。
$ ./hachoir_test.py ["iPhoneOS"] "4.0 GM" [1] True
またパーサーオブジェクトを作る際にファイル名ではなく、ファイルオブジェクトを渡したい時もあります。その場合は以下のようにIOStreamクラスとguessParser関数を使えばOKです。少し探すのに苦労しました。
from hachoir_core.stream import InputIOStream from hachoir_parser import guessParser bplist = open(u'Info.plist') iostream = InputIOStream(bplist) parser = guessParser(iostream)
これでバイナリデータをバンバン覗けるようになりました。最初にも書きましたが多くのフォーマットに対応しているので覚えておくと役に立つ場面も多そうです。
さて、そうこうしてるうちに自然と覗くだけじゃなくて弄りたい、と思うわけですが残念ながらバイナリ編集機能はまだ実験中の様で、かなり限定的なフォーマットにしか対応してません。書き込みたい場合はhachoir-parserモジュール内にある、対象のパーサクラスに書き込み用メソッドを実装する必要があります。
僕は書き込みもしたいので、試しにPNGファイルあたりの書き込みメソッドを実装してみようと思います。

[...] Pythonでいろんなバイナリファイルを覗いてみる « taichino.com (tags: python) [...]