binspect

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 のダンプの段階で適当にいじれば色々遊べるわけです。コード埋めたりとかですね。

プラグインは今のところ GIFBMP と 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
なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h