x64架构栈溢出测试中无法控制RIP寄存器的原因排查
x64环境下栈溢出无法覆盖RIP的问题排查
可能的原因及解决方法
1. 编译选项未关闭栈保护/PIE
默认GCC编译会启用栈金丝雀(Stack Canary)和位置无关可执行文件(PIE):
- 栈金丝雀会在栈帧中插入随机值,溢出覆盖到该值时,程序会直接触发
__stack_chk_fail崩溃,根本无法触及返回地址(RIP)。 - PIE会随机化程序内存地址,但你的问题是RIP未被覆盖,所以核心问题是栈保护。
解决:用以下命令重新编译程序,关闭栈保护和PIE,禁用优化:
gcc -fno-stack-protector -no-pie -O0 vuln.c -o vuln
2. 偏移量计算错误
你计算的偏移量15不符合x64栈帧的基本布局:
x64栈帧中,局部变量s[8]之后是旧RBP(8字节),再往上才是返回地址(RIP的目标值,8字节)。理论上,要覆盖返回地址,至少需要填满s的8字节 + 旧RBP的8字节,共16字节填充,第17-24字节才会覆盖RIP。
错误的偏移量可能来自:
- 使用
pattern_create时生成的字符串长度不足,未覆盖到返回地址区域; - 崩溃时错误读取了RSP而非RBP/返回地址的内容来计算偏移。
解决:
- 生成足够长的pattern(比如32字节):
pattern_create 32; - 在GDB中运行程序,输入该pattern触发崩溃;
- 查看崩溃时
rip寄存器的值,或查看rbp+8位置的内存(返回地址存在这里); - 用
pattern_offset <获取到的pattern片段>重新计算偏移。
3. 栈对齐导致的填充字节
x64要求栈指针RSP保持16字节对齐,编译器可能会在局部变量和旧RBP之间插入填充字节,导致实际偏移量大于理论值。
解决:手动查看栈布局确认偏移:
- 在
vgetstr(s);处设置断点:b main:10(对应代码行号); - 运行到断点后,执行
p &s获取s的地址,p $rbp获取当前RBP值; - 计算
$rbp - &s得到s到旧RBP的字节数,再加上8(旧RBP的长度)就是到返回地址的偏移; - 也可以用
x/20x $rbp-0x20查看栈内存,直观确认各区域的位置。
4. 代码逻辑的隐性问题
你的vgetstr函数中,for循环的写法虽然语法正确,但要确认字符写入逻辑无问题:
for(p = s; (c = getchar()) != EOF && c != '\n'; p++) *p = c;
这段代码会正确将每个输入字符写入s及后续栈区域,溢出逻辑是成立的,无需修改。
内容的提问来源于stack exchange,提问作者KmerPadreDiPdor
相关产品推荐
相关产品推荐

