文章目录
运行时堆栈
运行时堆栈是内存数组,CPU用ESP(堆栈指针寄存器)对其进行直接管理,32位模式下,ESP寄存器存放的是堆栈中某个位置的32位偏移量.ESP基本上不会直接被程序员控制,它是用CALL,RET,PUSH和POP等指令间接进行修改.
运行时堆栈工作于系统层,处理子程序调用
如下图所示:ESP中保存的是刚压入堆栈数值(00000001)的偏移量。当指针数值减少时,栈顶也随之下移。
入栈操作(PUSH)
步骤:
- 把栈顶指针减4
- 将数值复制到栈顶指向的堆栈位置
下图是将数值0000B1压入堆栈的结果
出栈操作(POP)
步骤:
- 先将数值弹出堆栈
- 增加栈顶指针(按堆栈元素大小)
堆栈的应用
关于堆栈的应用可参考:https://blog.csdn.net/qq_43313035/article/details/90215278
函数调用中的相关指令
PUSH指令
PUSH指令首先减少ESP的值,再将源操作数复制到堆栈。操作数是16位的,则ESP减2,操作数是32位的,则ESP减4。
POP指令
POP指令首先把ESP指向的堆栈的元素复制到一个16位或者32位的目的操作数中去,再增加ESP的值。操作数是16位的,则ESP加2,操作数是32位的,则ESP加4。
PUSHFD和POPFD指令
- PUSHFD指令把32位EFLAGS寄存器内容压入堆栈
- POPFD指令则把栈定元素弹出到EFLAGS寄存器
不能用MOV指令把标识寄存器内容复制给一个变量,因此,PUSHFD可能就是保存标志位的最佳途径。有时候保存
标志寄存器的副本是非常有用的,这样之后就可以恢复标志寄存器原来的值。
通常会用PUSHFD和POPFD封存一段代码:
pushfd ;保存标志寄存器
;
;任意语句序列
;
popfd ; 恢复标志寄存器
当用这种方式使用入栈和出栈指令时,必须确保程序的执行路径不会跳过POPFD指令。当程序随着时间不断修改时,很难记住所有入栈和出栈指令的位置。
一种不容易出错的保存和恢复标识寄存器的方法是:将它们压入堆栈后,立即弹出给一个变量:
.data
saveflags DWORD ?
.code
pushfd ;标识寄存器内容入栈
pop saveflags; ;复制给一个变量
下述语句从同一个变量中恢复标识寄存器的内容
push saveflags ;被保存的标识入栈
popfd ;复制给标识寄存器
PUSHAD,PUSHA,POPAD,POPA
-
PUSHAD 指令按照 EAX、ECX、EDX、EBX、ESP(执行 PUSHAD 之前的值)、EBP、ESI 和 EDI 的顺序,将所有 32 位通用寄存器压入堆栈。
-
POPAD 指令按照相反顺序将同样的寄存器弹出堆栈。
-
PUSHA 指令按序(AX、CX、DX、BX、SP、BP、SI 和 DI)将 16 位通用寄存器压入堆栈。
-
POPA 指令按照相反顺序