栈(Stack)的用途广泛,通常用于存储局部变量、传递函数参数、保存函数返回地址等。调试程序时需要不断查看栈内存,所以掌握栈很重要。
栈是FILO(First In Last Out,后进先出)这个都知道。但是在看汇编的时候一定要注意一些对应的相关细节。比如函数调用的时候,反汇编看到的参数进栈顺序是倒着的等。
栈的特征
内存结构如下:
一个进程中,栈顶指针(ESP)初始状态指向栈底端。执行PUSH命令将数据压入栈时,栈顶指针就会上移到站顶端。执行POP命令从栈中弹出数据时,若栈为空,则指针重新移动到栈底端。栈时一种由高地址向低地址扩展的数据结构,是由下往上扩展的(逆向扩展)。
所以 向栈压入数据时,栈顶指针减小,向低地址移动;从栈中弹出数据时,栈顶指针增加,向高地址移动。初始的时候栈顶指针指向占地。
栈帧(Stack Frame) 栈帧在程序中用于声明局部变量、调用函数。
栈帧就是利用EBP(栈帧指针,请注意不是ESP)寄存器访问栈内局部变量、参数、函数返回地址等的手段。ESP寄存器承担着栈顶指针的作用,而EBP寄存器则负责形式栈帧指针的职能。程序运行中,ESP寄存器的值随时变化,访问栈中函数的局部变量、参数时,若依ESP值为基准编写程序会十分困难,并且也很难使CPU引用到准确的地址。所以,调用某函数时,先要把用作基准点(函数起始地址)的ESP值保存到EBP,并维持在函数内部。这样,无论ESP的值如何变化,以EBP的值为基准(base)能够安全访问到相关函数的局部变量、参数、返回地址,这就是EBP寄存器作为栈帧指针的作用。
提示:某些优化选项可能会优化掉栈帧。
源代码vs2015 C++
#include "stdafx.h"
#include <iostream>
#include <windows.h>
using namespace std;
long add(long a, long b) {
long x = a, y = b;
return (x + y);
}
int main(){
long lNumbera = 1;
long lNumberb = 2;
printf("%d
" ,add(lNumbera , lNumberb));
return 0;
}
编译运行(Debug)之后OD加载上,找到add函数,看下栈帧操作。