上篇笔记:https://bbs.kanxue.com/thread-281876.htm,整理了单步执行的基本原理,本篇记录调用栈的推导原理。
原理很简单,直接参考:Unwinding a Stack by Hand with Frame Pointers and ORC
关键要清楚一点,栈帧始终存在,只不过记录方式不同。
只要知道每个函数的栈桢大小,就可以根据最后一层函数的栈帧位置,逐层推导调用函数的栈帧,并不是必须,在每个函数入口,将调用函数的实际栈帧地址,保存到栈中。
不使用rbp记录栈帧的优点:
1. 每个函数入口,省掉了执行rbp入栈的指令,同时将rbp寄存器节约出来;
2. 基于rbp推导的调用栈,有些情况会不完整,参考:
消失的调用栈帧-基于fp的栈回溯原理解析
当没有了frame pointer我们该如何栈回溯?
3. 对于32位linux系统,个人认为还有一个原因,就是ebp寄存器,用于系统调用传参。
不如看一下32位glibc库函数:
ar -x /usr/lib64/libc.a objdump -S printf.o
可以看出,入口没有enter、返回值也没有leave,参数/局部变量根据esp寄存器的相对位置获取。
学习这些有什么用途?
分析vmcore、core文件时,有些情况crash、gdb解析不了参数或局部变量的值,需要人工去别的栈帧找:http://blog.chinaunix.net/uid-14528823-id-4358785.html
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
https://blog.tartanllama.xyz/writing-a-linux-debugger-setup/