p_flags とか

そういえば昨日は喋った後で kik さんに間違ってるところ無いですか?と聞いて、 write が失敗したら EAX が -1 になっちゃって落ちるんだよ、という話と、 p_flags は下 3bit が 3 か 6 か 7 にしなきゃならんという話を補足してもらいました。後者は苦労するのはわかってたけど、 exit のあとは地味気味なので、説明しなくてもまぁいいかなぁと省略してしまったのでした。スライドだけ見るとそれ書いてないので一応補足をここで。 p_flags はロードするメモリ領域の rwx を指定するわけですが、ヘッダを通ってる最中に EAX 付近をいじってしまうので w が必須で、あと r か x が必要だと思います。で、 p_flags のある位置には "o" があるのですが、これは

irb(main):004:0> "%b"%?o
=> "1101111"

とのことですから全 bit 立ってますね。

irb とか

irb はバイナリどうこうするツールとして便利…というような話をスライド中の

irb(main):020:0> [0x00210001^0x6C4D6549].pack"L"
=> "Hell"

の部分で少ししたのですが、もっと色々できるように、ということでとりあえず機械語を命令に変換できるように .irbrc になんか書きました。

require 'tempfile'
def disasm(b, arch = 'i386')
  tmp = Tempfile.new('irb_disasm', '/tmp')
  if b.class == Array
    tmp.print(b.pack("C*"))
  else
    raise "type mismatch (#{b.class})"
  end
  tmp.close
  print `objdump -m #{arch} -b binary -D #{tmp.path}`
  nil
end

例えば別に push で fall through せんでもいい、と言った e_type, e_machine あたりは逆アセすると、

irb(main):001:0> disasm([2,0,3,0])

/tmp/irb_disasm18351.0:     ファイル形式 binary

セクション .data の逆アセンブル:

0000000000000000 <.data>:
   0:   02 00                   add    (%eax),%al
   2:   03 00                   add    (%eax),%eax
=> nil

って感じみたいです。 EAX はどうせ後で 4 入れますしいじっても問題ない、という。

PE Golf

http://d.hatena.ne.jp/firewood/20061116/1163689075

これもなんとも面白そうで。他にそのテのネタは、

  • 可能な限りASCIIでやるELF Golf (どう見ても html です!っていう実行ファイルとかあると楽しそうなんだけど、どうしても制御コードが入ってしまうしなぁ)
  • 他のオブジェクトフォーマット PE とか Mach-O とか。
  • 他のアーキテクチャでゴルフ (RISCは不利だろうなぁ)
  • 同じ ELF バイナリでシステムコール番号が違う環境もサポートとか。

とか。

56B の ELF quine

kik さんが簡単だとおっしゃってたので書いてみました。

i@u ~/wrk/binhacks> ls -l quine_small
-rwxr-xr-x 1 i i 56 2006-11-16 22:41 quine_small*
i@u ~/wrk/binhacks> diff =(./quine_small) ./quine_small
i@u ~/wrk/binhacks>

すごそうに見えますが、実はこれは簡単すぎてつまらない…

        mov     al, 4           ; write = 4
        inc     ebx             ; stdout = 1
        mov     dl, filesize    ; strlen
        int     0x80
        xchg    eax, ebx        ; exit = 1
        xchg    ebx, ecx        ; exit(0)
        int     0x80

みたいなコードを magic の下に埋めてやれば 58B => 56B に簡単にできます。命令的にはスカスカですが、 56B がヘッダの位置的にバイナリサイズの限界です。

一応もっと深く ELF ヘッダとプログラムヘッダを重ねるのを検討したのですが、どうにも無理だと私は判断。一応実行開始までいけるパターンはあるんですが。

a.out で 63Byte Hello, world!

a.out だったら小さくなるかも、って質問に答えたんですが、 a.out ってファイル内オフセットを指定できないから、ヘッダの空間使えないみたいで、たぶんこんなくらいで限界ぽいというかつまらない…

i@u ~/wrk/binhacks> ls -l hello_aout
-rwxr-xr-x 1 i i 63 2006-11-17 05:05 hello_aout*
i@u ~/wrk/binhacks> file hello_aout
hello_aout: Linux/i386 pure executable (NMAGIC), stripped
i@u ~/wrk/binhacks> ./hello_aout
Hello, world!

まぁ、 ELF の 58B の方が短いというのは非常にすごいということが再確認できます。

        BITS    32
        ORG     0
        dw      0x108           ; magic
        db      100             ; 386
        db      0
        dd      textsz
        dd      0
        dd      0
        dd      0
        dd      entry - text
        dd      0
        dd      0
text:
        db      "Hello, world!", 10
entry:
        lea     ebx, [eax+1]
        xor     ecx, ecx
        lea     edx, [eax+14]
        mov     al, 4
        int     0x80
        mov     al, 1
        dec     ebx
        int     0x80
textsz  equ     $ - text

ヘッダ 32B で Hello が 14B で実行コードが 17B 。いくつかどうでもいい知識が身につきました。だいたい a.out なんて私が Linux 触りはじめた時には駆逐されてたしなぁ…

  • a.out はヘッダ→ .text → .data → .bss → relocation関係 と並んでいるだけのえんらい単純なデータ
  • Hello, world!\n はどこを取っても実行コード扱いしてもうまくいかない気がする。
  • a.out で実行すると EAX 以外のレジスタが 0 クリアされてない(だから実行サイズが17Bもある)。
なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h