2

私はこのC関数を持っています:

void hello(char * src) {
  char buffer[64];
  strcpy(buffer, src);
  printf("Hello %s !\n", buffer);
}

(私が認識しているセキュリティ上の問題が含まれています)

そのためのx86アセンブリは

push   ebp
mov    ebp,esp
sub    esp,0x58

なぜ 0x58 (つまり 88) なのですか? 私は 64 + 4 + 4 + 4 (ローカル変数バッファー + 引数 + 古い + 戻りアドレス) または何かを期待しますebpが、何が欠けていますか?

4

3 に答える 3

4

これはアーキテクチャとコンパイラ フラグに大きく依存するため、ここで 1 つのことを指摘して「これに違いない」と言うのは不可能です。ただし、役立つと思われるヒントをいくつか紹介できます。

まず、スタック境界について考えます。-mpreferred-stack-boundary=XGCC へのフラグについて聞いたことがあるかもしれません。そうでない場合は、基本的に、スタック上の値をそれぞれ 2 Xバイトにすることをコンパイラに指示します。コンパイラは、これらの値がスタックにできるだけ収まるようにプログラムを最適化しようとします。一方、 などの GCC 修飾子__packed__を使用すると、コンパイラはスタック内のデータを可能な限り密に収めようとします。

スタックプロテクターもあります。基本的に、GCC はダミーの値をスタックに配置し、バッファー オーバーフローがプログラムのセグメンテーション違反以外に害を及ぼさないようにします (これは楽しいことではありませんが、攻撃者が命令ポインターの制御を追跡するよりはましです)。これは簡単に試すことができます。GCC の最近のバージョンを使用して、ユーザーにバッファをオーバーフローさせます。プログラムが「スタック破壊が検出されました、終了しました」という行に沿ったメッセージで終了することに注意してください。を使用してプログラムをコンパイルしてみてください-fno-stack-protector。スタックに割り当てられたローカル メモリはおそらく少なくなります。

最後に、cdecl 呼び出し規則がどのように機能するかについて、あなたが誤解している小さな詳細がいくつかあります。引数は、関数を呼び出す前にスタックにプッシュされます。つまり、引数はスタックのメモリの上位にあることを意味します (スタックはメモリの下位に成長することに注意してください)。以下は、3 つの引数を必要とし、2 つのローカル整数変数を割り当てる関数の非常に単純化された例です。

# First we push three arguments on the stack in reverse order as they 
# appear in C. The values don't matter here.
pushl $0xc
pushl $0xb
pushl $0xa

# A CALL instruction comes in here to get in the function. The return 
# address is placed on the stack.

# Assume we are in the function now. This function first saves the base 
# pointer, then sets the base pointer to the address in the stack pointer.
pushl %ebp
movl %esp, %ebp

# Now we can allocate our local variables. We need 8 bytes of space for 
# those 2 integer variables (note that this is an extremely simplified 
# example that doesn't consider what I just told you above).
subl $0x8, %esp
# Let's just put 1 and 2 in those variables.
movl $0x1, -4(%ebp)
movl $0x2, -8(%ebp)

# We're done. Put a return value in EAX, then restore the stack- and 
# base pointers.
movl $0x0, %eax
movl %ebp, %esp
popl %ebp
ret

基本的に、スタックは次のようになります。

16(%ebp)     -> Argument 3
12(%ebp)     -> Argument 2
8(%ebp)      -> Argument 1
4(%ebp)      -> Return address
%ebp         -> Old %ebp pushed on the stack by function
-4(%ebp)     -> Local variable 1
-8(%ebp)     -> Local variable 2

つまり、ローカル変数のみがベース ポインターよりも下位のメモリに存在します。正直なところ、スタック上のローカル変数のサイズに影響を与える可能性のあるものが他にもいくつかありますが、含めるのを忘れていましたが、これが少し役立つことを願っています. プログラムをハッキングし続けると、それがわかるでしょう。:)

于 2013-11-01T22:52:04.140 に答える
0

わかりました、これはばかげた推測ですが、旗竿にそれを走らせて、何が起こるか見てみましょう.

おそらく、コンパイラはスペースを最適化していません。これは、クワッドワード境界を越えてレジスタをロードするためのシフト ワードを節約するためのワード アラインメント調整です。

値、0x58 & 8bytes -> 次のクワッドワード境界 96 0x60 を見てください。メモリ内のクワッドワードラインの重要な最小値からebpをポップする方がはるかに簡単です(または、最もエンディアンですか?;); 前向きな考え方とそのすべて。

編集:まさに!(彼が言った事...)

于 2013-11-01T23:15:24.720 に答える