04 | 深入理解栈:从CPU和函数的视角看栈的管理
函数与栈帧
- 深入了解
- 翻译
- 解释
- 总结
本文从函数的层面和CPU的机器指令层面多角度解析了栈的相关概念。首先介绍了函数与栈帧的关系,指出当调用一个函数时,CPU会在栈空间开辟一小块区域,称为栈帧,用于存储函数的局部变量。通过示例代码和递归函数的执行过程,读者能更深刻地理解栈帧与函数的关系。文章通过实际案例和技术原理,帮助读者理解栈的重要性和运行机制,以及如何避免栈溢出和缓冲区溢出带来的安全问题。通过深入理解栈的管理,读者可以更好地排查与栈相关的问题,并提升代码的健壮度和安全性。整体而言,本文通过深入浅出的方式,从CPU和函数的视角全面解析了栈的管理,为读者提供了全面的栈相关知识,使读者能够更深入地理解栈的运行原理和相关概念。 文章还介绍了栈溢出的概念,并通过一个精心构造的例子展示了缓冲区溢出攻击的原理。作者提出了对缓冲区溢出攻击进行防御的两种常见手段,并解释了gcc自带的栈保护机制。此外,文章还通过汉诺塔程序的求解过程分析了栈帧的创建和销毁的过程,以此揭示了函数和栈帧的关系。最后,文章强调了编写健壮而安全的代码的重要性,以及每个程序员都应该关注安全问题。 总的来说,本文通过多个角度深入浅出地解析了栈的管理和相关安全问题,为读者提供了全面的栈相关知识,使读者能够更深入地理解栈的运行原理和相关概念。
《编程高手必学的内存知识》,新⼈⾸单¥59
全部留言(27)
- 最新
- 精选
- 🐮老师,出堆后栈空间里的数据还是保留的啊,是不是叫栈空间扩展和收缩形象点
作者回复: 是的。可以这么说。不过我们在说expand这个词的时候,往往用于栈空间不足了,需要对栈进行动态扩展这种场景。你说的很对,rsp指针的上移并没有真的把栈上的数据清空掉,所以我们在使用局部变量的时候一定要初始化,否则就有可能访问到上次释放的栈内存。你掌握的很好!
2021-11-0114 - 鵼老师好,栈溢出的例子,在栈桢上不是先保存基地址rbp,然后分配rsp保存参数和局部变量吗?所以在参数的栈高位应该还有rbp,然后才是rip。但是代码本地运行一下是可以的,通过objdump看,发现没有push %rbp,mov %rsp,%rbp了。这是因为gcc加了-o1的优化参数。这个是不是有点类似方法内敛呢?不加-o1,就还会先保存rbp了,在执行即使段错误。 然后,思考题答案,通过objdump看,发现参数寄存器rdi和rsi保存的不再是值了,而是通过lea把参数的栈地址传递过去了。因此修改就等于是修改了main的栈桢上的值。
作者回复: 对的。这说明你真的动手实践过了。这就掌握得很好了。你的看法都是对的。
2021-11-0326 - keepgoing又看了一遍老师这一课,没太看懂栈溢出攻击这一块的细节,想多请教一下: 执行test函数后,字符串数组s中从0元素到15元素在栈中存储地址是从高向低的吗?随后call copy方法后,会压栈下一条指令地址到栈上,这条指令存储地址更低。所以最后让栈溢出时多拷贝地址是把地址数值的低位放在内存地址高位、数值的高位放在内存地址低位满足小端序顺利解析到这条地址的数值。 不知道这样理解对吗,因为对老师举的这个例子比较感兴趣,所以想把细节搞清楚,如果没理解对不知道能不能辛苦老师多讲一下,感谢!
作者回复: 是的。是这样的。就是通过把bad函数的地址写到栈上,然后就使得ret指令跑进bad函数里面运行了。两个要素:一是越界读写,一是覆盖栈上的返回地址。
2021-11-0423 - GLswap函数在C传入指针或C++的引用 是拿到了操作数的存放地址 所以可以改变对应的值,Java语言的入参如果基本数据类型是没法改变外部变量的值,如果是引用类型是可以改变引用对象内的属性值。
作者回复: Right! Java没有指向栈上的指针,这个设计很重要。
2021-11-0223 - 威老师您好,缓冲区溢出的Segment Fault,是指一个栈桢里溢出,还是栈桢之间的溢出。我理解是按照文章说的保护机制,应该是溢出到了别的栈桢,才会出现Segment Fault。不知道这样的理解正不正确呢?
作者回复: 往往无意识的缓冲区溢出,因为会拿随机值覆盖有效值,所以会带来segment fault,覆盖了本栈帧的关键数据,或者覆盖了其他栈帧的数据都有可能造成segment fault。但是精心构造的缓冲区溢出,就像课程里对test函数的攻击,是可以把控制流导向恶意代码的。保护机制其实是在关键位置,比如栈帧开始处,编译器自动插入变量,函数结束时再检查一下,如果变化了就主动触发fault,以增强安全性,所以不一定是溢出到别的栈帧,两者之间没有必然联系
2021-11-011 - thomas第 3 行的作用呢,是把栈向下增长 0x10,这是为了给局部变量预留空间。从这里,你可以看出来运行 fac 函数要是消耗栈空间的。 ==========================> 请问栈增长多少是如何预估出来的?
作者回复: 编译器在做编译优化的过程中会计算的。具体地说就是寄存器分配这一步就能统计出来需要多大的栈空间。
2021-12-25 - 拭心看的有点晕,尤其是各个汇编指令和他们操作的寄存器的作用,不知道您是怎么记忆这些晦涩的内容呢?
作者回复: 其实这是本科阶段的三节课:汇编原理,计算机组成和计算机体系结构的内容。我们学了三个学期的呀。一下子记不住很正常。慢慢来。
2021-11-162 - keepgoing老师想提个小建议,能不能把汇编代码也贴上来比较方便理解,n*(n-1)那个例子因为示例代码只有机器码,只能看着您的文字理解,我们这种刚开始入门的同学看着可能比较抽象,不过这一课又把栈更深入地理解了一遍,谢谢老师
作者回复: 汇编代码往右拖😂,我也看不懂机器码,哈哈
2021-11-042 - 柒老师,我觉得你一下用python,一下用c语言,不太好。
作者回复: 其实基本上都是C语言。python呢就当伪代码看吧,你看最后结尾的吊打面试官里,其实也是伪代码。自己转换成可执行的C或者Java代码是个很好的练习哦。
2021-11-03 - Rovebiy老师,是不是曾经在知乎写过专栏进击的Java新人?
作者回复: 嗯,是我。
2021-11-02