8
void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   ret = buffer1 + 12;
   (*ret) += 8;//why is it 8??
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}

上記のデモはここからです:

http://insecure.org/stf/smashstack.html

しかし、ここでは機能しません:

D:\test>gcc -Wall -Wextra hw.cpp && a.exe
hw.cpp: In function `void function(int, int, int)':
hw.cpp:6: warning: unused variable 'buffer2'
hw.cpp: At global scope:
hw.cpp:4: warning: unused parameter 'a'
hw.cpp:4: warning: unused parameter 'b'
hw.cpp:4: warning: unused parameter 'c'
1

そして、著者は次のように考えていますが、なぜ8なのかわかりません。

少し計算すると、距離は 8 バイトであることがわかります。

呼ばれる私のgdbダンプ:

Dump of assembler code for function main:
0x004012ee <main+0>:    push   %ebp
0x004012ef <main+1>:    mov    %esp,%ebp
0x004012f1 <main+3>:    sub    $0x18,%esp
0x004012f4 <main+6>:    and    $0xfffffff0,%esp
0x004012f7 <main+9>:    mov    $0x0,%eax
0x004012fc <main+14>:   add    $0xf,%eax
0x004012ff <main+17>:   add    $0xf,%eax
0x00401302 <main+20>:   shr    $0x4,%eax
0x00401305 <main+23>:   shl    $0x4,%eax
0x00401308 <main+26>:   mov    %eax,0xfffffff8(%ebp)
0x0040130b <main+29>:   mov    0xfffffff8(%ebp),%eax
0x0040130e <main+32>:   call   0x401b00 <_alloca>
0x00401313 <main+37>:   call   0x4017b0 <__main>
0x00401318 <main+42>:   movl   $0x0,0xfffffffc(%ebp)
0x0040131f <main+49>:   movl   $0x3,0x8(%esp)
0x00401327 <main+57>:   movl   $0x2,0x4(%esp)
0x0040132f <main+65>:   movl   $0x1,(%esp)
0x00401336 <main+72>:   call   0x4012d0 <function>
0x0040133b <main+77>:   movl   $0x1,0xfffffffc(%ebp)
0x00401342 <main+84>:   mov    0xfffffffc(%ebp),%eax
0x00401345 <main+87>:   mov    %eax,0x4(%esp)
0x00401349 <main+91>:   movl   $0x403000,(%esp)
0x00401350 <main+98>:   call   0x401b60 <printf>
0x00401355 <main+103>:  leave
0x00401356 <main+104>:  ret
0x00401357 <main+105>:  nop
0x00401358 <main+106>:  add    %al,(%eax)
0x0040135a <main+108>:  add    %al,(%eax)
0x0040135c <main+110>:  add    %al,(%eax)
0x0040135e <main+112>:  add    %al,(%eax)
End of assembler dump.

Dump of assembler code for function function:
0x004012d0 <function+0>:        push   %ebp
0x004012d1 <function+1>:        mov    %esp,%ebp
0x004012d3 <function+3>:        sub    $0x38,%esp
0x004012d6 <function+6>:        lea    0xffffffe8(%ebp),%eax
0x004012d9 <function+9>:        add    $0xc,%eax
0x004012dc <function+12>:       mov    %eax,0xffffffd4(%ebp)
0x004012df <function+15>:       mov    0xffffffd4(%ebp),%edx
0x004012e2 <function+18>:       mov    0xffffffd4(%ebp),%eax
0x004012e5 <function+21>:       movzbl (%eax),%eax
0x004012e8 <function+24>:       add    $0x5,%al
0x004012ea <function+26>:       mov    %al,(%edx)
0x004012ec <function+28>:       leave
0x004012ed <function+29>:       ret

私の場合、距離は - = 5 のはずですが、うまくいかないようです..

ローカル変数に56バイトfunctionが必要なのはなぜですか?( )sub $0x38,%esp

4

6 に答える 6

2

joveha が指摘したように、命令によってスタック (戻りアドレス) に保存された EIP の値は、命令をスキップするために7バイトcallずつインクリメントする必要があります ( - = 7 ) ( )。0x004013420x0040133bx = 1;movl $0x1,0xfffffffc(%ebp)

ローカル変数 ( ) 用に 56 バイトが予約されていることは正しいので、欠落している部分は、保存された EIP がスタック上でsub $0x38,%esp何バイト過ぎたかということです。buffer1


少しのテスト コードとインライン アセンブリから、テストのマジック値が28であることがわかります。なぜ28なのかについて決定的な答えを提供することはできませんが、コンパイラがパディングおよび/またはスタックカナリアを追加していると思います。

次のコードは、GCC 3.4.5 (MinGW) を使用してコンパイルされ、Windows XP SP3 (x86) でテストされています。


unsigned long get_ebp() {
   __asm__("pop %ebp\n\t"
           "movl %ebp,%eax\n\t"
           "push %ebp\n\t");
}

void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   /* distance in bytes from buffer1 to return address on the stack */
   printf("test %d\n", ((get_ebp() + 4) - (unsigned long)&buffer1));

   ret = (int *)(buffer1 + 28);

   (*ret) += 7;
}

void main() {
   int x;

   x = 0;
   function(1,2,3);
   x = 1;
   printf("%d\n",x);
}

gdb を使用して、この値を簡単に決定することもできました。

-g(デバッグ シンボルを含めるようにコンパイル)

(gdb) break function
...
(gdb) run
...
(gdb) p $ebp
$1 = (void *) 0x22ff28
(gdb) p &buffer1
$2 = (char (*)[5]) 0x22ff10
(gdb) quit

( 0x22ff28+ 4) - 0x22ff10= 28

(ebp 値 + ワードのサイズ) - buffer1 のアドレス = バイト数


Smashing The Stack For Fun And Profitに加えて、以前の質問への回答で言及した記事や、この件に関する他の資料を読むことをお勧めします。このタイプのエクスプロイトがどのように機能するかを正確に理解することは、より安全なコードを作成するのに役立ちます。

于 2010-03-30T20:42:21.010 に答える
2

buffer1 + 12実際に何を指しているのかを予測するのは困難です。コンパイラは、必要なスタック上の任意の場所に配置できbuffer1buffer2スペースをまったく節約しなくても実行できますbuffer2。どこに行くのかを本当に知る唯一の方法buffer1は、コンパイラのアセンブラ出力を見ることです.異なる最適化設定または同じコンパイラの異なるバージョンでジャンプする可能性は十分にあります.

于 2010-03-30T08:43:28.393 に答える
1

+8バイトの部分は、保存されたEIPをでインクリメントしたい量です。EIPは保存されたので、プログラムは完了後に最後の割り当てに戻ることができfunctionます。保存されたEIPに8バイトを追加して、プログラムをスキップしたいと考えています。

だから彼がしようとしているのは「スキップ」することだけです

x = 1;

あなたの場合、保存されたEIPは、戻り0x0040133b後の最初の命令を指しfunctionます。割り当てをスキップするには、保存されたEIPがを指すようにする必要があります0x00401342。それは7バイトです。

これは、バッファオーバーフローの例ではなく、実際には「RETEIPの混乱」です。

そして、ローカル変数の56バイトに関しては、パディング、スタックカナリアなど、コンパイラが思いついたものであれば何でもかまいません。

編集:

これは、Cでバッファオーバーフローの例を作成することがいかに難しいかを示しています。からの12のオフセットbuffer1は、特定のパディングスタイルとコンパイルオプションを想定しています。GCCは、指示しない限り、スタックカナリア(保存されたEIPを「保護」するローカル変数になります)を喜んで挿入します。また、ジャンプ先の新しいアドレス(printf呼び出しの開始命令)は、実際にはアセンブリから手動で解決する必要があります。彼の場合、彼のマチで、彼のOSで、彼のコンパイラで、その日は....それは8でした。

于 2010-03-30T15:56:38.863 に答える
1

自分のマシンではまだコードをテストしていませんが、メモリ アライメントは考慮されていますか? gcc でコードを分解してみてください。アセンブリ コードを読むと、コードの理解が深まると思います。:-)

于 2010-03-30T08:30:01.743 に答える
1

このコードは、OpenBSD と FreeBSD でも同様に 1 を出力し、Linux ではセグメンテーション違反を引き起こします。

この種のエクスプロイトは、特定のマシンの命令セットと、コンパイラとオペレーティング システムの呼び出し規則の両方に大きく依存しています。スタックのレイアウトに関するすべては、C 言語ではなく、実装によって定義されます。この記事では Linux on x86 を想定していますが、Windows を使用しているようで、システムは 64 ビットである可能性があります-m32

微調整する必要があるパラメーターは、スタックの先端から戻りアドレスまでのオフセットである 12 と、mainジャンプしたいバイト数である 8 です。記事にあるように、gdb を使用して関数の逆アセンブリを検査し、(a) を呼び出したときにスタックがプッシュされる距離functionと、(b) の命令のバイト オフセットを確認できmainます。

于 2010-03-30T08:41:36.897 に答える
0

C++ コンパイラを使用して C プログラムをコンパイルしています。hw.cpp の名前を hw.c に変更すると、コンパイルされることがわかります。

于 2010-03-30T08:34:34.920 に答える