当你得到这样的垃圾回溯时,几乎可以肯定的是,你的堆栈以某种方式被破坏了,并且实际的返回地址和堆栈帧指针已被覆盖。
该值0xbffff118
几乎可以肯定是堆栈中的地址。我相信在许多 x86 Linux 编译器上,编译器从 virtual address 开始堆栈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
is0xbffff7c0
和$eip
is 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
,除非您有非常非常好的理由这样做。