Linux x86_64で楽しみと利益のためにスタックを破壊するの例 3 を再現しました。ただし、命令をスキップするために戻りアドレスにインクリメントする必要がある正しいバイト数を理解するのに問題があります。
0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
x = 1
そこに指示があると思います。私は次のように書いています。
#include <stdio.h>
void fn(int a, int b, int c) {
char buf1[5];
char buf2[10];
int *ret;
ret = buf1 + 24;
(*ret) += 7;
}
int main() {
int x;
x = 0;
fn(1, 2, 3);
x = 1;
printf("%d\n", x);
}
gdbで逆アセンブルしました。-fno-stack-protector
アドレスのランダム化を無効にし、オプションを指定してプログラムをコンパイルしました。
質問1
以下の逆アセンブラーの出力から、アドレスの命令をスキップしたいことがわかります0x0000000000400595
。戻りアドレスcallq <fn>
とmovl
命令のアドレスの両方です。したがって、戻りアドレスが0x0000000000400595
で、次の命令が の場合、戻りアドレス0x000000000040059c
に 7 バイト追加する必要がありますか?
0x0000000000400572 <+0>: push %rbp
0x0000000000400573 <+1>: mov %rsp,%rbp
0x0000000000400576 <+4>: sub $0x10,%rsp
0x000000000040057a <+8>: movl $0x0,-0x4(%rbp)
0x0000000000400581 <+15>: mov $0x3,%edx
0x0000000000400586 <+20>: mov $0x2,%esi
0x000000000040058b <+25>: mov $0x1,%edi
0x0000000000400590 <+30>: callq 0x40052d <fn>
0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
0x000000000040059c <+42>: mov -0x4(%rbp),%eax
0x000000000040059f <+45>: mov %eax,%esi
0x00000000004005a1 <+47>: mov $0x40064a,%edi
0x00000000004005a6 <+52>: mov $0x0,%eax
0x00000000004005ab <+57>: callq 0x400410 <printf@plt>
0x00000000004005b0 <+62>: leaveq
0x00000000004005b1 <+63>: retq
質問2
戻りアドレスに 7 の代わりに 5 バイトを追加しても、同じ結果が得られることに気付きました。そうするとき、私は命令の途中に飛び込んでいません0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
か? その場合、リターン アドレスに 5 バイトまたは 7 バイトの代わりに 6 バイトを追加した場合のように、プログラムがクラッシュしないのはなぜですか。
質問 3
スタックの buffer1[] の直前は SFP で、その前に戻りアドレスがあります。つまり、4 バイトが buffer1[] の終わりを通過します。ただし、buffer1[] は実際には 2 ワードなので、8 バイトの長さになることに注意してください。したがって、戻りアドレスは buffer1[] の先頭から 12 バイトです。
Aleph 1 の例では、リターン アドレスのオフセットを buffer1[] の先頭から 12 バイトとして計算します。私は x86_32 ではなく x86_64 を使用しているため、戻りアドレスへのオフセットを再計算する必要があります。x86_64 の場合、buffer1[] はまだ 2 ワード、つまり 16 バイトです。SFP と戻りアドレスはそれぞれ 8 バイト (64 ビットを使用しているため) であるため、戻りアドレスは次のとおりbuf1 + (8 * 2) + 8
ですbuf1 + 24
。