「実行中のプロセスのパス名をチェックする」の話

http://d.hatena.ne.jp/y-hamigaki/20061116#1163680455

で実行時にファイルを移動した場合について調べてくださっています。 (deleted) がつくのが面白いです。これ、以前見たことはある気がしますが、いつだったか覚えてない…

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