當你像這樣得到一個垃圾回溯時,它幾乎肯定意味着你的堆棧被搗毀,並且實際的返回地址和堆棧幀指針已被覆蓋。
值0xbffff118
幾乎肯定是您的堆棧中的地址。我相信,在許多x86 Linux編譯器上,編譯器在虛擬地址0xc0000000
處啓動堆棧,並且從那裏向下增長,因此以0xbfff
開頭的任何地址都可能是堆棧地址。
通常,指令指針不應該在堆棧中。通常發生的方式是指向堆棧的值覆蓋存儲在堆棧中的返回地址,然後在當前函數返回時返回到被覆蓋的值。如果堆棧不可執行,就像它應該那樣,這會立即產生一個信號;如果堆棧是可執行的,那麼它可能不會崩潰,除非您被惡意利用,在這種情況下,您將會遇到不好的時間。
一旦你的堆棧被砸,backtrace
命令不會有用,因爲你發現了。找出發生什麼事的最好方法是手動檢查堆棧並搜索可能的返回地址和幀指針。您可以使用x
命令轉儲內存區域(有關詳細信息,請運行help x
)。我喜歡使用x/<NUMBER>wx
轉儲爲4字節的十六進制值。所以,這裏是如何轉儲一組數據開始堆棧指針$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
這裏,$esp
是0xbffff7c0
,並$eip
是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
,除非你真的有這樣做的充分理由。
這意味着應用程序被隔離。通過您的評論來判斷,您可能會覆蓋堆棧或在其後面寫作。如果你使用警告('-Wall -Wextra')和調試符號('-g')編譯你的應用程序,它也會有幫助。另外你爲什麼要禁用堆棧保護機制?它確保您的程序安全。 – Nobilis
我開始使用-g標誌,我現在使用了-Wall和-Wextra,並消除了所有的警告,但仍然是一樣的...... – zabeltech
如果使用'-g',GDB應該能夠給你名稱和行失敗的功能。 – Nobilis