esp:扩展栈指针寄存器,是指针寄存器的一种,用于存放函数栈顶指针(栈顶指针)
ebp:扩展基址指针寄存器,也被称为帧指针寄存器,用于存放函数栈底指针(栈底指针)。

esp和ebp有什么关系呢?

ebp只是存取某时刻的esp,这个时刻就是进入一个函数内后,cpu会将esp的值赋给ebp,此时就可以通过ebp对栈进行操作,比如获取函数参数,局部变量等,实际上使用esp也可以,只是esp可能会变化,去数据的时候很不方便

下面我们通过一个例子来说明:

#include "stdio.h"

int function_add(int a,int b);
int main()
{
	int a=1,b=1,sum=0;
	sum=function_add(a,b);
	printf("sum=%d",sum);
	return 0;
}
int function_add(int a,int b)
{
	return a+b;

}

这段程序的功能是定义一个函数,实现两个参数相加,然后打印出来,下面来分析,

int a=1,b=1,sum=0;
	sum=function_add(a,b);

这两句反汇编如下:

7:  int a=1,b=1,sum=0;
00401038 C7 45 FC 01 00 00 00   mov         dword ptr [ebp-4],1 		;偏移地址为[ebp-4]存放1
0040103F C7 45 F8 01 00 00 00   mov         dword ptr [ebp-8],1		;偏移地址为[ebp-8]存放1
00401046 C7 45 F4 00 00 00 00   mov         dword ptr [ebp-0Ch],0	;偏移地址为[ebp-0ch]存放0
8:        sum=function_add(a,b);
0040104D 8B 45 F8             mov         eax,dword ptr [ebp-8]				;eax的值设置为1
00401050 50                        push        eax											;压入栈
00401051 8B 4D FC             mov         ecx,dword ptr [ebp-4]				;ecx的值设置为1
00401054 51                  	 	push        ecx											;入栈
00401055 E8 AB FF FF FF       call        @ILT+0(function_add) (00401005)
0040105A 83 C4 08             add         esp,8
0040105D 89 45 F4             mov         dword ptr [ebp-0Ch],eax

当执行到这段代码会调到function_add函数执行

call        @ILT+0(function_add) (00401005)

这段一共push两次,假设在没有这两次push前,设esp=M,push两次后,esp=M-8

ESP IDF 文件夹左上角有红色叉 文件esp是什么_寄存器


执行call语句 esp=M-12

function_add反汇编的代码为:

12:   int function_add(int a,int b)
13:   {
004010A0 55                   push        ebp
004010A1 8B EC                mov         ebp,esp
004010A3 83 EC 40             sub         esp,40h
004010A6 53                   push        ebx
004010A7 56                   push        esi
004010A8 57                   push        edi
004010A9 8D 7D C0             lea         edi,[ebp-40h]
004010AC B9 10 00 00 00       mov         ecx,10h
004010B1 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
004010B6 F3 AB                rep stos    dword ptr [edi]
14:       return a+b;
004010B8 8B 45 08             mov         eax,dword ptr [ebp+8]
004010BB 03 45 0C             add         eax,dword ptr [ebp+0Ch]
15:
16:   }
004010BE 5F                   pop         edi
004010BF 5E                   pop         esi
004010C0 5B                   pop         ebx
004010C1 8B E5                mov         esp,ebp
004010C3 5D                   pop         ebp
004010C4 C3                   ret

我们可以看到在函数前面,首先将ebp压入栈,然后将esp的值赋值给ebp,此时
esp=M-16,ebp的值和esp相同

push        ebp
mov         ebp,esp

此时栈里情况为:

ESP IDF 文件夹左上角有红色叉 文件esp是什么_寄存器_02

在函数里,都是通过ebp对栈的数据进行操作的,比如获取参数的值,

004010B8 8B 45 08             mov         eax,dword ptr [ebp+8]
004010BB 03 45 0C             add         eax,dword ptr [ebp+0Ch]

因为在函数里,esp的值可能是变化的,ebp的值不变,通过ebp来操作数据很方便
在最后:

004010C1 8B E5                mov         esp,ebp
004010C3 5D                   pop         ebp

将esp,ebp的值变成调用function_add之前的值,这样看起来只是实现函数的功能,其他并没有啥变化,再使用ret语句,返回去,然后继续往下执行语句

0040105A 83 C4 08             add         esp,8

总结:

  1. esp始终指向栈顶,ebp只要在调用函数时,取值为栈顶,这样可方便对数据的操作
  2. 函数调用时,EBP的值入栈,然后ESP的值传给EBP。函数调用结束后,EBP将值传回ESP,ESP又指向了原来的栈顶地址。这样看起来只是实现函数的功能,其他看起来没有变化