Linux 调试 gdb 堆栈信息缺失

文章探讨了在x86和x64架构下函数调用时堆栈的布局,以及如何处理堆栈信息缺失或被破坏的情况。通过使用gdb调试工具,结合-fstack-protector编译选项来增强栈保护,以及AddressSanitizer等工具,可以有效地分析和解决堆栈破坏导致的崩溃问题。堆栈的内存分布包括返回地址、输入参数和局部变量,而堆栈破坏可能由于内存越界或重复释放导致。
摘要由CSDN通过智能技术生成

目的是 是解决 堆栈信息缺失的情况

本文是以gdb 的内存分布以x86 elf 的格式为例

通常

当前堆栈的输入 参数 为 rdi rsi , (从第一个参数开始) 在x86中,函数的所有参数都是一个个通过push指令压至栈中。
而在x64中,通常函数的前6个参数会优先存放在寄存器中,分别是rdi, rsi, rdx, rcx, r8d和r9d,超过6个的则放在栈中。
x86 常用的汇编寄存器和指令在以下链接 :

x86和x64通用寄存器在汇编中的特殊默认用法
https://zhuanlan.zhihu.com/p/575201221?utm_id=0
https://github.com/GreyZhang/g_unixC
堆栈的内存分布图

一个完整的堆栈 应该包括 old esp ,old rip(返回地址) ,输入参数,localvar其实堆栈被破坏是分几种情况的。
1. 当前的堆栈不完整, bt 出来的符合都是 ???
2. 堆栈被破坏了,内存越界了,后面的出现随机乱地出现崩溃的情况。
3. 崩溃,但是属于存在release版本 找到对应的符合 和 无法进行 i locals和 i args ,无法定位到具体的行号
4. 内存越界,数组越界

https://zhuanlan.zhihu.com/p/575201221?utm_id=0

通俗的崩溃情况:
当内存出现了覆盖,例如写一个内存区域时没控制好长度,越界了,把其他字段的值破坏了,这个时候再使用这个被破坏的字段就会出现崩溃;
当内存被重复释放,一块内存已经被释放了,但是因为逻辑问题,再次尝试释放这块内存,这个时候也会出现崩溃,再次尝试释放不一定是用户主动行为,可能是编译器偷偷安排的工作,例如析构函数的调用。

个人的应对方案

    1. 当前的堆栈不完整, bt 出来的符合都是 ??? 编译的时候添加编译选项:-fstack-protector 和 -fstack-protector-all 这两个选项指示编译器开启栈保护,再在崩溃的位置 反汇编 ,根据栈搜索找到有符号的指令地址,从而跟踪到是具体哪个函数。接着可以采用方案3进行进一步定位
    1. 可先采用在编译器先添加内存 编译的时候添加编译选项:-fstack-protector 和 -fstack-protector-all 这两个选项指示编译器开启栈保护,这样在栈乱序的第一时间可以dump出来现场。

可加在Makefile里面。
1. 可以通过在反汇编里面查找 寄存器的参数,和 已了解到的断点反汇编的函数符合。从而判断该函数是否被正常使用。
2. acan ,采用 AddressSanitizer

gdb 的堆栈破坏实例
https://blog.csdn.net/weixin_37357702/article/details/121804164?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-12-121804164-blog-115440377.235v32pc_relevant_default_base3&spm=1001.2101.3001.4242.7&utm_relevant_index=15

从上述图可见,栈底的地址 保存的是返回指令地址

实例 从x86_64 位的数据来看,这除了86 32位 的架构外,64 位系统是带有其他的保留预留地址存在

—> ebp 在未受改变之前始终指向栈帧的开始,也就是栈底,所以ebp的用途是在堆栈中寻址用的。
—> esp是会随着数据的入栈和出栈移动的,也就是说,esp始终指向栈顶。
见下图,假设函数A调用函数B,我们称A函数为"调用者",B函数为“被调用者”则函数调用过程可以这么描述:
(1)先将调用者(A)的堆栈的基址(ebp)入栈,以保存之前任务的信息。
(2)然后将调用者(A)的栈顶指针(esp)的值赋给ebp,作为新的基址(即被调用者B的栈底)。
(3)然后在这个基址(被调用者B的栈底)上开辟(一般用sub指令)相应的空间用作被调用者B的栈空间。
(4)函数B返回后,从当前栈帧的ebp即恢复为调用者A的栈顶(esp),使栈顶恢复函数B被调用前的位置;然后调用者A再从恢复后的栈顶可弹出之前的ebp值(可以这么做是因为这个值在函数调用前一步被压入堆栈)。这样,ebp和esp就都恢复了调用函数B前的位置,也就是栈恢复函数B调用前的状态

https://blog.csdn.net/luoganttcc/article/details/126624500

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值