http://shinh.skr.jp/dat_dir/binspect.tgz
ずっと放置するよりは…と思って出すことに。JVM Golf をやった時に作った、汎用バイナリフォーマット解析コマンド&ライブラリ、を作りたかったものです。 binareal 見て 1 年後にちょっと作ってみたものを 2 年してから出すというようなそういう。
% ./binspect.rb hello.gif hello.p
などとすると Ruby の pp で GIF をダンプしたものが出力されます。
[[:fileHdr, [[[:signature, "GIF"], [:version, "89a"]]]], [:logicalScreenDescriptor, [[[:width, 35], [:height, 100], [:packed, 128], [:bgcolor, 1], [:aspect_ratio, 0], [:globalColorTable, [[[:r, 0], [:g, 0], [:b, 0]], [[:r, 255], [:g, 255], [:b, 255]]]]]]], [:data, [[[:tag, 33], [:extension, [[[:label, 254], [:commentExtension, [[[:commentData, [[[:size, 45], [:data, "\n;BEGIN{print\"Hello, world!\\n\";exit}\n__END__\n"]], [[:size, 0], [:data, ""]]]]]]]]]]], [[:tag, 44], [:imageDescriptor, [[[:left, 0], [:top, 0],
こういうのがえんえんと出力される。 -y つければ YAML 。余談ですが YAML 書きやすいって言うけどこういうリストだけの構造は徹底的に書きにくいですね。 XML の方がマシなくらい。
出力されたものを
% ./binspect.rb hello.p hello2.gif
とかすれば元に戻ったりもします。ちゃんと戻るのは GIF だけですが。 pp のダンプの段階で適当にいじれば色々遊べるわけです。コード埋めたりとかですね。
プラグインは今のところ GIF と BMP と ELF と class があります。 GIF 以外はヘッダ以外はダンプしないので、見るだけに使うか、ライブラリとして使って内容は自分で加える必要があります。まぁもともとそいう用途を想定してます。
プラグイは、例えば ELF のプログラムヘッダはこんな感じで書けます。
class Phdr < Binformat int :type; int :offset; addr :vaddr; addr :paddr; int :filesz; int :memsz; int :flags; int :align; end
サイズ情報があってからそのサイズだけバイナリが入ってくる…とかいうフォーマットは多いのですが、そういうのはこんなかんじ。
class PascalString < Binformat int :size byte :buf, :size end
サイズに計算結果を入れることもできます。
class NullTerminatedPascalString < Binformat int :size byte :buf, proc {size + 1} end
タグによって union 的に型が変わったり、ターミネータがあるまで繰り返し…っていうようなものは、
class GIFData < Binformat byte :tag some { case tag when 0x2c struct GIFImageDescriptor, :imageDescriptor when 0x21 struct GIFExtension, :extension when 0x3b struct GIFTrailer, :trailer else raise "Unknown separator: #{tag}" end } end class GIF < Binformat struct GIFFileHeader, :fileHdr struct GIFLogicalScreenDescriptor, :logicalScreenDescriptor repeat GIFData, :data, proc{|data| data.assoc(:tag)[1] == 0x3b } end
とかそんな感じ。
ライブラリとして使うサンプルとしては、 bmp にコードを埋めてみた、下記みたいなものがあります。
require 'bmp' fileHdr = [:fileHdr, [[ [:type, "BM"], [:size, 35 + 256], [:reserved1, 0], [:reserved2, 0], [:offbytes, 160], ]]] infoHdr = [:infoHdr, [[ [:size, 40], [:width, 16], [:height, 16], [:planes, 1], [:bits, 4], [:compression, 0], [:sizeImage, 128], [:xPixPerMeter, 2834], [:yPixPerMeter, 2834], [:clrUsed, 2], [:clrImportant, 2], ]]] palette = [:palette, [ [[:r, 0], [:g, 0], [:b, 0], [:unused, 0]], [[:r, 255], [:g, 255], [:b, 255], [:unused, 0]], ]] bmp = [fileHdr, infoHdr, palette] bin = BMP.new.dump(bmp) bin += %Q( BEGIN{puts'Hello, world!';exit} __END__ ).ljust(98) 128.times{|i| bin << i } bin << 0 bin << 0 bin << 0 print bin