池袋バイナリ勉強会(8)に参加しました
11月25日(日)に開催された池袋バイナリ勉強会(8)に参加しました。PDP-11のバイナリ解析に取り組んでいます。a.outを逆アセンブルする自作スクリプトを作成しながら、平行してそれを実行するインタプリタを作ろうとしている段階です。
逆アセンブラの作成
以下のhello.cを逆アセンブルするプログラムの作成に取り組んでいます。
$ cat hello.c main() { write(0, "hello\n", 6); }
たったこれだけのコードですが、hello.cから生成されるa.outをpdp11-aout-objdump
で逆アセンブルしてみると、いろんな処理が行われているのが分かります。逆アセンブル結果が以下になります。
$ v6cc hello.c # コンパイル $ v6strip a.out # シンボルテーブル削除 $ pdp11-aout-objdump -d a.out # 逆アセンブル a.out: ファイル形式 a.out-pdp11 セクション .text の逆アセンブル: 00000000 <.text>: 0: f009 setd 2: 1180 mov sp, r0 4: 1226 mov (r0), -(sp) 6: 0bd0 tst (r0)+ 8: 1036 0002 mov r0, 2(sp) c: 09f7 0008 jsr pc, 0x18 10: 100e mov r0, (sp) 12: 09df 0052 jsr pc, *$122 16: 8901 sys 1 18: 0977 0040 jsr r5, 0x5c 1c: 15ce 0006 mov $6, (sp) 20: 15e6 0086 mov $206, -(sp) 24: 0a26 clr -(sp) 26: 09df 0030 jsr pc, *$60 2a: 2596 cmp (sp)+, (sp)+ 2c: 0077 003a jmp 0x6a 30: 1166 mov r5, -(sp) 32: 1185 mov sp, r5 34: 1d40 0004 mov 4(r5), r0 38: 1d77 0006 0052 mov 6(r5), $0x90 3e: 1d77 0008 004e mov 10(r5), $0x92 44: 8900 sys 0 46: 008e .word 216 48: 8602 bcc 0x4e 4a: 0077 002a jmp 0x78 4e: 1585 mov (sp)+, r5 50: 0087 rts pc 52: 1166 mov r5, -(sp) 54: 1185 mov sp, r5 56: 1d40 0004 mov 4(r5), r0 5a: 8901 sys 1 5c: 1140 mov r5, r0 5e: 1185 mov sp, r5 60: 1126 mov r4, -(sp) 62: 10e6 mov r3, -(sp) 64: 10a6 mov r2, -(sp) 66: 0be6 tst -(sp) 68: 0048 jmp (r0) 6a: 1141 mov r5, r1 6c: 1844 mov -(r1), r4 6e: 1843 mov -(r1), r3 70: 1842 mov -(r1), r2 72: 1146 mov r5, sp 74: 1585 mov (sp)+, r5 76: 0087 rts pc 78: 1037 0018 mov r0, $0x94 7c: 15c0 ffff mov $-1, r0 80: 1146 mov r5, sp 82: 1585 mov (sp)+, r5 84: 0087 rts pc
自作の逆アセンブラによる出力は以下になります。Pythonで作成しています。
$ python ./script/dump.py a.out text size: 134 data size: 14 0: f009 setd 170011 2: 1180 mov sp, r0 010600 4: 1226 mov (r0), -(sp) 011046 6: 0bd0 tst (r0)+ 005720 8: 1036 0002 mov r0, 0o2(sp) 010066 000002 c: 09f7 0008 jsr pc, 0o10(pc) => $0x18 004767 000010 10: 100e mov r0, (sp) 010016 12: 09df 0052 jsr pc, @(pc)+ => 82(0o122 0x52) 004737 000122 16: 8901 sys 1 104401 18: 0977 0040 jsr r5, 0o100(pc) => $0x5c 004567 000100 1c: 15ce 0006 mov $6, (sp) 012716 000006 20: 15e6 0086 mov $206, -(sp) 012746 000206 24: 0a26 clr -(sp) 005046 26: 09df 0030 jsr pc, @(pc)+ => 48(0o60 0x30) 004737 000060 2a: 2596 cmp (sp)+, (sp)+ 022626 2c: 0077 003a jmp 0o72(pc) => $0x6a 000167 000072 30: 1166 mov r5, -(sp) 010546 32: 1185 mov sp, r5 010605 34: 1d40 0004 mov 0o4(r5), r0 016500 000004 38: 1d77 0006 0052 mov 0o6(r5), 0o122(pc) => $0x90 016567 000006 000122 3e: 1d77 0008 004e mov 0o10(r5), 0o116(pc) => $0x92 016567 000010 000116 44: 8900 sys 0 104400 46: 008e .word 0o216 000216 48: 8602 bcc 0x4e 103002 4a: 0077 002a jmp 0o52(pc) => $0x78 000167 000052 4e: 1585 mov (sp)+, r5 012605 50: 0087 rts pc 000207 52: 1166 mov r5, -(sp) 010546 54: 1185 mov sp, r5 010605 56: 1d40 0004 mov 0o4(r5), r0 016500 000004 5a: 8901 sys 1 104401 5c: 1140 mov r5, r0 010500 5e: 1185 mov sp, r5 010605 60: 1126 mov r4, -(sp) 010446 62: 10e6 mov r3, -(sp) 010346 64: 10a6 mov r2, -(sp) 010246 66: 0be6 tst -(sp) 005746 68: 0048 jmp (r0) 000110 6a: 1141 mov r5, r1 010501 6c: 1844 mov -(r1), r4 014104 6e: 1843 mov -(r1), r3 014103 70: 1842 mov -(r1), r2 014102 72: 1146 mov r5, sp 010506 74: 1585 mov (sp)+, r5 012605 76: 0087 rts pc 000207 78: 1037 0018 mov r0, 0o30(pc) => $0x94 010067 000030 7c: 15c0 ffff mov $177777, r0 012700 177777 80: 1146 mov r5, sp 010506 82: 1585 mov (sp)+, r5 012605 84: 0087 rts pc 000207
pdp11-aout-objdump
の出力結果は、8, 10, 16進数が混ざっていて混乱するので、自作の逆アセンブラでは、8, 10, 16進数にはプレフィックス(0x, 0o)を付けました。- オペコードはPDP-11の資料を見ながら一つ一つ解析しています。
- PDP-11の資料は、UNIXv6 ハードウェア資料 - 驟雨のカーネル探検隊(只今遭難中w で紹介されているものを参考にしています。
mov の引数のパターン
Mode | Name | Symbolic | Description |
---|---|---|---|
0 | register | R | Rの値 |
1 | register deferred | (R) | Rの値をアドレスと見なし、そのアドレスの値 |
2 | auto-increment | (R)+ | Rの値をアドレスと見なし、そのアドレスの値; R+=2 |
3 | auto-incr deferred | @(R)+ | Rの値をアドレスと見なし、そのアドレスの値をさらにアドレスとみなし、そのアドレスの値; R+=2 |
4 | auto-decrement | -(R) | R-=2; Rの値をアドレスと見なし、そのアドレスの値 |
5 | auto-decr deferred | @-(R) | R-=2; Rの値をアドレスと見なし、そのアドレスの値をさらにアドレスと見なし、そのアドレスの値 |
6 | index | X(R) | R+Xの値をアドレスと見なし、そのアドレスの値 |
7 | index deferred | @X(R) | R+Xの値をアドレスと見なし、そのアドレスの値をさらにアドレスと見なし、そのアドレスの値 |
表のSymbolicの括弧の意味は、C言語のポインタのデリファレンスと同じと捉えて良いようです。また、@ も同様でポインタのデリファレンスと同じ意味になります。ですから、@ ( R )+
は、( ( R ) )+
のように括弧が2重になったものと解釈すると良いと教えていただきました。C言語のポインタのポインタですね。
(今後の課題)条件式と符号なし整数
『Lions’ Commentary on UNIX (Ascii books)』の278ページには以下の記述があります。
Cでは条件式を使うことができる。aとbが整数変数ならば、
(a > b ? a : b)はaとbの大きなほうを値として持つ式である。ただし、これはaとbが符号なし整数とみなされる場合には機能しない。したがって、次の手続きを使用する。
6326 max (a, b) char *a, *b; { if (a > b) return (a); return (b); }ここでのトリックは、文字へのポインタとして宣言されたaとbが比較のために符号なし整数として扱われるということである。
aとbが符号なしの場合、条件式がなぜ機能しないのか疑問に思いました。その理由を勉強会で教えていただいたのですが、よく理解出来ませんでした……。まだまだ学ぶべきことが沢山あるので、今後の課題となります。理解できたら改めてブログに書くつもりです。