GDB中x(examine memory)查看64位程序栈内存问题咨询
关于GDB x命令的64位格式说明符
x/10xg中的g本身就是GDB定义的64位(8字节,quadword)值格式说明符,不存在“无64位展示格式”的问题。认为输出拆成两个32位值属于视觉误判:
- 输出每行开头的地址间隔为0x10(16字节),默认每行并排展示2个内存单元值,每个值都是16位十六进制数(对应64位二进制长度),比如第一行的
0x0000000000000000、0x00007ffff7dd80b3都是完整64位值,没有做32位拆分。 - 如果要取消并排排版、每行只显示1个64位值,直接用命令
x/10xgz $rsp即可,末尾加的z参数会强制每个值独占一行,不会再有拆分的错觉。
当前断点位置的栈实际状态
断在<main+8>即mov eax,0x0处时,对栈内容的判断存在两个认知偏差,先梳理已执行的指令流:
命中该断点时,函数前序的push rbp、mov rbp,rsp两条指令已经执行完毕,后续的call <func>指令还未执行,因此call指令需要压入的返回地址(0x555555555182,即call指令的下一条指令地址)尚未入栈,自然不可能在当前rsp附近找到main函数内部的指令地址。
此时rsp值为0x7fffffffdd30,栈从低地址到高地址(栈向低地址增长,低地址方向为新入栈内容)的对应关系为:
0x7fffffffdd30(rsp当前指向位置):存储刚执行push rbp时压入的旧rbp值,对应输出第一个值0x00000000000000000x7fffffffdd38(rsp+8偏移位置):存储__libc_start_main调用main函数时压入的返回地址,即main执行ret指令后要跳转回的libc代码段地址,对应输出中的0x00007ffff7dd80b3,这是Linux用户态程序启动后的标准栈布局,不属于main函数内部压入的值- 更高地址位置的内容是libc启动阶段残留在栈上的环境变量、命令行参数、栈帧链数据,其中出现的
0x0000555555555170是main函数入口地址,属于启动传参残留值,和当前执行流的压栈操作无关。
另外注意:rip寄存器存储当前待执行指令地址,正常执行流中只有call指令会将下一条指令地址压栈,其余时间栈上不会自动存储当前/下一条指令地址,不要把rsp的功能和rip混淆。
构建完整程序栈布局的实操方法
要梳理全执行流程的栈变化,不要只在main入口打单个断点,按以下步骤操作即可:
- 用
si命令做单指令步进,每执行一条指令就查看rsp值和对应栈内容 - 遇到
push、call这类会修改rsp、执行压栈操作的指令时,提前记录当前rsp值,指令执行后对比rsp偏移和新入栈的值 - 遇到
pop、ret这类出栈指令时,观察rsp的自增幅度和值的流向 - 随时用
info registers rsp rbp rip确认三个核心寄存器的当前值,避免把栈上残留的旧值、环境数据和当前函数栈帧的有效内容混淆 - 64位环境下栈固定按8字节对齐,每个栈单元长度为8字节,计算栈偏移时按8的倍数递推即可,不会出现错位。
内容的提问来源于stack exchange,提问作者user13675319
相关产品推荐
相关产品推荐

