0

プログラムを gcc でコンパイルしてから、a.out ファイルに対して gcc を実行します。gdb 内でプログラムを実行すると、次のエラーが発生します。

Program received signal SIGSEGV, Segmentation fault.
0xbffff118 in ?? ()
(gdb) backtrace
#0  0xbffff118 in ?? ()
#1  0x00000000 in ?? ()

これは何を意味するのでしょうか?また、i の前に、プログラムを正常に実行しようとしたときに、次のエラーが発生しました。

*** stack smashing detected ***: ./benchmark terminated
Aborted (core dumped)

だから私はこれがバッファオーバーフローの保護であることを読んだ、私はすべてを再確認し、すべてがうまくいくはずなので、フラグを使ってこれを無効にした:

-fno-stack-protector

編集:コードを機能させるのではなく、gdbの使用方法を理解したいので、コードを投稿していません。だから私はgdbは初めてですが、今では数回使用して成功しています。それでも私は何を理解できませんか?? つまり、gdb がコード内の対応する呼び出しではなく、エラーを引き起こし、フレーム 1 のアドレスが 0x0 である理由を示すことができるのはどのような場合ですか?

4

2 に答える 2

5

このようなジャンク バックトレースが発生した場合は、スタックがなんらかの形で破壊され、実際のリターン アドレスとスタック フレーム ポインターが上書きされたことをほぼ確実に意味します。

0xbffff118はほぼ確実にスタック内のアドレスです。多くの x86 Linux コンパイラーでは、コンパイラーは仮想アドレス0xc0000000でスタックを開始し、そこから下に向かって成長するため、 で始まるアドレス0xbfffはスタック アドレスである可能性が非常に高いと思います。

通常、命令ポインターはスタック内にあるべきではありません。通常、スタックを指している値がスタックに格納されている戻りアドレスを上書きし、現在の関数が戻ると、上書きされた値に戻ります。スタックが実行可能でない場合は、本来あるべきように、すぐにシグナルが発生します。どういうわけかスタックが実行可能である場合、悪意を持って悪用されていない限り、数命令後までクラッシュしない可能性があります。

あなたが知っているように、スタックが破壊backtraceされると、コマンドは役に立たなくなります。そこから何が起こっているのかを把握する最善の方法は、スタックを手動で調べて、おそらくリターン アドレスとフレーム ポインターを検索することです。このコマンドを使用しxてメモリ領域をダンプできます (詳細については実行help xしてください)。を使用してx/<NUMBER>wx、4 バイトの 16 進値としてダンプするのが好きです。したがって、スタックポインタから始まる一連のデータをダンプする方法は次の$espとおりです。

(gdb) x/64wx $esp
0xbffff7c0: 0x00000073  0xbffff9e9  0x0000000b  0x00000012
0xbffff7d0: 0xbffff9e8  0x0be04aa0  0xbffff7f8  0x000018aa
0xbffff7e0: 0x0be04aa0  0xbffff9e8  0x00000000  0x00000002
0xbffff7f0: 0xbffff9e7  0x09a0bb10  0xbffff818  0x000018aa
0xbffff800: 0x09a0bb10  0xbffff9e7  0x00000002  0x0000000e
0xbffff810: 0xbffff9e6  0x015377f0  0xbffff838  0x000018aa
0xbffff820: 0x015377f0  0xbffff9e6  0x00000008  0x00000011
0xbffff830: 0xbffff9e5  0x01537860  0xbffff858  0x000018aa
0xbffff840: 0x01537860  0xbffff9e5  0x00000003  0x0000000f
0xbffff850: 0xbffff9e4  0x001ddbc0  0xbffff878  0x000018aa
0xbffff860: 0x001ddbc0  0xbffff9e4  0x00000018  0x00000017
0xbffff870: 0xbffff9e3  0x00177c50  0xbffff898  0x000018aa
0xbffff880: 0x00177c50  0xbffff9e3  0xbffff8b8  0x00000000
0xbffff890: 0xbffff9e2  0x00176050  0xbffff8b8  0x000018aa
0xbffff8a0: 0x00176050  0xbffff9e2  0xbffff9e1  0x0000000c
0xbffff8b0: 0xbffff9e1  0x00174920  0xbffffb08  0x00001b8a

ここでは、$espis 0xbffff7c0、および$eipis 0x00001870(コマンドを使用して取得しましたが、すべてのレジスタを取得するためにp/x $eipで確認することもできます)。info regsしたがって、すべてのスタック フレームは、スタック ( ) の上位にあるポインタであり、0xbfff....その後に のようなアドレスが続きます0x00001870。メモリ ダンプでこれらを探すと、これらがスタック フレームであることはほぼ確実です。

0xbffff7f8  0x000018aa
0xbffff818  0x000018aa
0xbffff838  0x000018aa
(etc.)

これは、私が横たわっていた非常に再帰的なプログラムから得た例です。そのため、戻りアドレスはすべて同じです。破壊されていない適切なスタック フレームをいくつか見つけたら、フレーム ポインターを自分でたどることができます。

(gdb) x/2wx 0xbffff7f8
0xbffff7f8: 0xbffff818  0x000018aa
(gdb) x/2wx 0xbffff818
0xbffff818: 0xbffff838  0x000018aa
(gdb) x/2wx 0xbffff838
0xbffff838: 0xbffff858  0x000018aa
(gdb) x/2wx 0xbffff858
0xbffff858: 0xbffff878  0x000018aa
...

そして、命令アドレスをシンボル名に変換したい場合は、再度xコマンドを使用できます。デバッグ シンボルがある場合、gdb は問題なくシンボル名を出力します。

(gdb) x 0x000018aa
0x18aa <add_word+154>:  0x5d18c483

これは、スタックが破壊されたときに実際に役立つスタック トレースを取得する方法の簡単な入門書です。もちろん、最初からこれを避けるのが最善です。コードをコンパイルすることを強くお勧めします-Wall -Wextra -Werror-pedantic可能であれば)。もちろん、本当に正当な理由が-fno-stack-protectorない限り、使用しないでください。

于 2013-07-05T02:55:49.157 に答える