はじめに
この記事はうどん Advent Calendar 201813日目の記事です。今のところこの記事のみです。うどんくん頑張って。
最近 Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際 という本を読んでいます。その中で読む前に知っておけばよかったと思う事柄がいくつかあるので、この場でご紹介します。
全くの初心者には難しいかも
この本は「プログラミングとは何か?」から始まり徐々に脆弱性を探っていく構成になっており、丁寧に順序立てされています。冒頭で約10ページ使ってC言語の基本について説明していますが、正直初めてプログラミングをする人には難しいと思います(不可能とは言い切れませんが)。少なくともC言語のポインタあたりまでを別の本で学習してから本題に入っていくことで、理解の速度が劇的に変わるでしょう。
レジスタ名について
64ビット1のOSを使っていると、本書に出てこないレジスタ名に遭遇します。
例として、23ページのfirstprog.cをdisassembleした時のコードを見てみます。
このコードをUbuntu18.04、64ビットで動かします。(私はVirtualBoxで動かしています。仮想環境での学習を強くお勧めします。)
$ objdump -M intel -D a.out | grep -A20 main.:
000000000000063a <main>:
63a: 55 push rbp
63b: 48 89 e5 mov rbp,rsp
63e: 48 83 ec 10 sub rsp,0x10
642: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
649: eb 10 jmp 65b <main+0x21>
64b: 48 8d 3d a2 00 00 00 lea rdi,[rip+0xa2] # 6f4 <_IO_stdin_used+0x4>
652: e8 b9 fe ff ff call 510 <puts@plt>
657: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1
65b: 83 7d fc 09 cmp DWORD PTR [rbp-0x4],0x9
65f: 7e ea jle 64b <main+0x11>
661: b8 00 00 00 00 mov eax,0x0
(後略)
本書27ページと比較してみてください。eから始まるレジスタ名が、rから始まるレジスタ名に置き換わっています。
気にならないのであれば、そのままeから始まるレジスタ名に読み替えて構いません。ここからは気になる方への簡単な解説です。
32ビットのOSでは、基本的にレジスタを32
ビット分使います。同じように64ビットのOSでは基本的にはレジスタを64ビット分使います。しかし、64ビットのOSであっても32ビットやそれ以下のビット数で事足りる場合には、レジスタの下位ビットのみを使用することがあります。例えばraxというレジスタの下位32ビットだけを使う場合には、eaxという名前で指定します。下図はraxレジスタでの例を簡略化したものです。2
32ビットのOS(Ubuntu16.04)で同じプログラムを動かすとこのようになります。
$ objdump -M intel -D a.out | grep -A20 main.:
0804840b <main>:
804840b: 8d 4c 24 04 lea ecx,[esp+0x4]
804840f: 83 e4 f0 and esp,0xfffffff0
8048412: ff 71 fc push DWORD PTR [ecx-0x4]
8048415: 55 push ebp
8048416: 89 e5 mov ebp,esp
8048418: 51 push ecx
8048419: 83 ec 14 sub esp,0x14
804841c: c7 45 f4 00 00 00 00 mov DWORD PTR [ebp-0xc],0x0
8048423: eb 14 jmp 8048439 <main+0x2e>
8048425: 83 ec 0c sub esp,0xc
8048428: 68 d0 84 04 08 push 0x80484d0
804842d: e8 ae fe ff ff call 80482e0 <puts@plt>
8048432: 83 c4 10 add esp,0x10
8048435: 83 45 f4 01 add DWORD PTR [ebp-0xc],0x1
8048439: 83 7d f4 09 cmp DWORD PTR [ebp-0xc],0x9
804843d: 7e e6 jle 8048425 <main+0x1a>
804843f: b8 00 00 00 00 mov eax,0x0
8048444: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
8048447: c9 leave
8048448: 8d 61 fc lea esp,[ecx-0x4]
本書に出てこない命令も出てきますが、見た目は大差がなくなりました。
ここで一つ注意です。32ビットの汎用レジスタの中でもrから始まるものがあります。下図は汎用レジスタの読みかえ表です。
3
前半8つの汎用レジスタはeをrに変えるだけですが、後半8つの汎用レジスタは末尾のdを外すことで区別しています。
実行できないプログラムを実行するには
最後はStach Smashingです。VirtualBoxのUbuntuで実行しようとしても最後まで実行されないプログラムがあります。ここでは138ページのoverflow_example.cを例にとります。
$ gcc -o overflow_example overflow_example.c
$ ./overflow_example 1234567890
[前] buffer_two は 0xbfffeeb4 にあり、その値は 'two' です
[前] buffer_one は 0xbfffeeac にあり、その値は 'one' です
[前] value は 0xbfffeea8 にあり、その値は 5 (0x00000005) です
[STRCPY] 10 バイトを buffer_two にコピーします
[後] buffer_two は 0xbfffeeb4 にあり、その値は '1234567890' です
[後] buffer_one は 0xbfffeeac にあり、その値は 'one' です
[後] value は 0xbfffeea8 にあり、その値は 5 (0x00000005) です
*** stack smashing detected ***: ./overflow_example terminated
Aborted (core dumped)
途中で終了してしまいました。
Stack Smash Protection(SSP)
最近のOSにはSSP4という機能が組み込まれています。簡単に言うと、スタックに対する攻撃を検知するシステムのことです。これが不正を検知したため、結果を見ることができませんででした。
SSPの無効化
SSPを無効化するのは簡単です。gccでコンパイルする時に、-fno-stack-protector
というオプションをつけてみましょう。
$ gcc -fno-stack-protector -o overflow_example overflow_example.c
$ ./overflow_example 1234567890
[前] buffer_two は 0xbfffeebc にあり、その値は 'two' です
[前] buffer_one は 0xbfffeec4 にあり、その値は 'one' です
[前] value は 0xbfffeecc にあり、その値は 5 (0x00000005) です
[STRCPY] 10 バイトを buffer_two にコピーします
[後] buffer_two は 0xbfffeebc にあり、その値は '1234567890' です
[後] buffer_one は 0xbfffeec4 にあり、その値は '90' です
[後] value は 0xbfffeecc にあり、その値は 5 (0x00000005) です
今度は実行することができました!こちらもやはり仮想環境での実行をお勧めします。
おわりに
セキュリティに興味が湧いてこの本を読み始めました。とても面白いですね。この後も詰まったところがあったら更新したいと思います。
-
64ビットアドレッシング ↩
-
http://www.egr.unlv.edu/~ed/x86.html "x86-64 Assembly Language Programming with Ubuntu" 11P
↩ -
http://www.egr.unlv.edu/~ed/x86.html "x86-64 Assembly Language Programming with Ubuntu" 10P ↩
-
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00348.html 開発者は日本人のようです。さらに詳しい説明はgray hat hackingという本で、オプションに関する説明は
$ man gcc
で見ることができます。 ↩