-3

みなさん、良い一日を!

バッファオーバーフローがどのように機能するかを理解しようとしています。現在、バッファオーバーフロー攻撃を実行するために変更することになっている関数のリターンアドレスのアドレスを決定しているところです。私はインターネットで読んだ例に基づいて簡単なプログラムを書きました。このプログラムが行うことは、関数の戻りアドレスのアドレスをスタックに格納するための整数ポインターを作成することです。これを行うには(関数/プログラム変数がスタック内でどのように編成されるかを理解していることを認めます)、バッファー変数のアドレスに8を追加し、それをretの値として設定します。ここでは、funcの差出人住所の場所に含まれる住所を変更するようなことは何もしていません。

更新:プログラムを少し変更したので、funcのパラメーターaのスタックアドレスを出力します。ご覧のとおり、aとバッファの間の距離は約8バイトです。つまり、スタックレイアウトに基づいて、保存されたFPと古いEIP(func return address)が間にあることを意味します。私は正しいですか?

プログラムは次のとおりです。

void func( int a){
    char buffer[3];

    int *ret;

    ret = buffer + 11; // this is the configuratio which made the whole program works... This now points to the address containing func's return address

    printf (" address of a is %d\n", &a);

    printf ("address of buffer is %x\n", buffer);

    printf ("address of ret is %x\n", ret);

    printf ("value of ret is %x\n", (*ret));

}

void main(){
    int num;

    num = 0;

    func(num);

    num = 1;

    printf("Num now is %d", num);
}

実行されたときのプログラムの出力:

代替テキストhttp://img20.imageshack.us/img20/2034/72783404.png

ご覧のとおり、変数bufferとretのアドレスを出力しています。ret変数の値を出力するステートメントを追加しました(funcのリターンアドレスの場所を想定しているため、funcが実行から戻った後に実行される次の命令のアドレスを出力する必要があります)。

これは、funcが戻った後に実行される命令の想定アドレスを示すダンプです。(緑色の下線付き)ご覧のとおり、その値は、変数retに含まれている印刷された値とは大きく異なります。

代替テキストhttp://img717.imageshack.us/img717/8273/assemblycodecopy.png

私の質問は、なぜそれらが違うのかということです。(もちろん、私がしたことはすべて正しいと仮定して)。そうでなければ、私は何を間違えましたか?プログラムのランタイムスタックについての私の理解は間違っていますか?これを理解するのを手伝ってください。私のプロジェクトは来週の予定で、まだほとんど触れていません。要求があったらごめんなさい、あなたの助けがひどく必要です。

4

3 に答える 3

1

次のプログラムの場合

int main(int argc, char **argv) {
   int v[2];

   return 0;
}

スタックレイアウトは基本的に次のとおりです。

 
       -------------   
           arg n
       -------------
         ........。
       -------------   
0x1010 arg 0
       -------------
0x100Cretアドレス
       =============
0x1008古いfp
       -------------
0x1004 v [1]
       -------------
0x1000 v [0]
       -------------

v + 3を使用して、mainの差出人住所を見つけることができます。

スタックの左側に配置されたアドレスを想定すると、vのアドレスは0x1000であり、リターンアドレスのアドレスは(v + 3 => 0x1000 + 4 * 3 = 0x100C)です。

于 2010-06-18T07:04:16.947 に答える
1

まず、バッファのアドレスが奇数であることに注意してください。0xbffffd51次に、それに8を追加して。を取得します0xbffffd59。スタックのリターンアドレスが4バイトのアドレスにアラインされていなかったら、私はかなり驚いたでしょう。

コンパイラーによっては、スタックフレームのレイアウトが正確に異なる可能性があるため(たとえば、bufferソースコードの最初にある場合でも、コンパイラーはretスタックの上位に配置される可能性があります)、値を試す必要がある場合があります。私はいくつかのことをします:

  1. バッファを4バイトに変更します。
  2. さまざまなオフセットを試してください。差出人住所を見つけるには、12バイトまたは16バイトを調べる必要があるかもしれないと感じています。
于 2010-06-18T06:31:53.560 に答える
0

もちろん、ポインタを渡さない限り、元のnumを変更することはできません。したがって、メインでは、numは最初は0、次に1であり、funcによって実際に変更されることはありません。funcの(&a)のアドレスは、引数のローカルコピーのアドレス(値による)であり、ほとんどの場合、スタック上のアドレスである可能性があります。そして、retは何を指しますか?3文字のバッファがあり、それを超えるアドレスを取得します。ローカル変数がメモリ内でどのように「編成」されているかに応じて、「興味深い」ものを指している可能性がありますが、これをガベージへのポインタと見なす必要があります。したがって、それが実際に差出人住所を指していることを100%確信することはできません。あなたは次のことを想定しています:

0 4バイト(charの場合、4バイトのアラインメントを想定)
4 4バイト(何でも、多分引数)
8 4バイト(差出人住所)

そしてそれは状況次第です。アーキテクチャによって異なります。これは、コンパイラが関数のコードをどのように「変換」するかによって異なります。x86を想像してみましょう。以下はfuncを行うための合理的な方法です

func:
  ebpをプッシュ; いくつかの登録を保存...
  プッシュeax; またはプーシャと?
  mov ebp、esp
  0を押す; chara[3]の場合
  mov eax、ebp
  eaxを追加、4; -4 + 8
  プッシュeax; int*retの場合
  ; -4(ebp)は
  ; -8(ebp)はint*retを与えます
  ; したがって、ebp-4はaへのポインタです。
  ; 8を追加して、ebp+4を取得します。
  ; 保存されたebpに...retptrがありません
  ; (他のコード...)
  mov esp、ebp
  pop eax; またはポパと?
  ポップebp
  ret

保存された登録がもっと多い場合はどうなりますか?chara[4]とint*retの順序が入れ替わった場合はどうなりますか?どうやって知ることができますか?asmで直接コードを記述しない限り、何も想定できません。この場合、何が起こっているかを正確に制御できます。そうでなければ、あなたが望むことをするために働くCコードは偶然に働くでしょう...

于 2010-06-18T09:31:26.807 に答える