反汇编01

堆栈是连续的地址空间,且向低地址端生长。

esp 是堆栈指针 
ebp 是基址指针 
那两条指令的意思是 将栈顶指向 ebp 的地址 
以下摘自网上一篇文章:

push    ebp           ; ebp 入栈  
mov   ebp, esp        ; 因为 esp 是堆栈指针,无法暂借使用,所以得用 ebp 来存取堆栈  
sub   esp, 4*5        ; 下面的 wsprintf 一共使用了 5 个参数,每个参数占用 4 个字节,所以要入栈 4*5 个字节  
push  1111  
push  2222  
push  3333  
push  offset szFormat  
push  offset szOut  
call  wsprintf         ; 调用 wsprintf  
add   esp, 4*5        ; 堆栈使用完毕,“还” 回 4*5 个字节给系统  
...  
mov   esp, ebp        ; 恢复 esp 的值  
pop   ebp           ; ebp 出栈  
ret 

明白了吗?主要是用来保存 / 恢复堆栈,以便传递参数给函数。  在 MASM 里面,有一条更方便的语句,就是 invoke 使用它后,你就不用自己做这些事情了。 
--------------------------------------------------------------- 
esp 始终指向栈顶,ebp 是在堆栈中寻址用的

我的理解:
调用一个函数时,先将堆栈原先的基址(EBP)入栈,以保存之前任务的信息。然后将栈顶指针的值赋给 EBP,将之前的栈顶作为新的基址(栈底),然后再这个基址上开辟相应的空间用作被调用函数的堆栈。函数返回后,从 EBP 中可取出之前的 ESP 值,使栈顶恢复函数调用前的位置;再从恢复后的栈顶可弹出之前的 EBP 值,因为这个值在函数调用前一步被压入堆栈。这样,EBP 和 ESP 就都恢复了调用前的位置,堆栈恢复函数调用前的状态。

二. 通过 ollydbg 跟踪 esp 和 ebp

       发现文字描述还是太没有快感。上几幅图,来说明这个调试过程更好。此文对于深刻理解 ebp,esp 是具有长远意义的

反汇编01_第1张图片
可以看到,初始情况下,ebp 此时值为 0012FEDC,也就是栈帧的地址,而栈顶地址 esp 值为 0012FDFC。可以看到两个值有一定的关系。而 帧指针的地址较高。
然后我们让它执行前两句,push ebp,mov ebp,esp
反汇编01_第2张图片
可以看到前两句已经执行了,那么 ebp 跟 esp 的值也发生了变化。esp=0012FDF8,ebp=0012FDF8。为神马?一句句解读,push ebp,向栈里面压入了一个东西,那么栈顶此时应该发生变化了,也就是 地址 - 4 字节。为什吗是减法呢?因为是 向低地址增长的,这点一定得注意。所以此时 esp 变化成了 0012FDFC-4=OO12FDF8. 至于 ebp 也等于 0012FDF8 就不解释了。
接着上图不解释:
反汇编01_第3张图片
此时呢,观察现在的值。栈顶 esp=0012FDF4, 而 ebp=0012FDF8; 没啥好说的,此时的栈顶已经又跑上去了,说明又有元素压栈了。那么执行这句 mov esp,ebp 之后,不用说,esp 跟 ebp 都会变成 0012FDF8. 我们重点看下一幅,执行完 pop,让 ebp 出栈,后会发生神马。
反汇编01_第4张图片
此时 ebp 已经出栈了,来看看那他们的值,esp=0012FDFC,ebp=0012FEDC. 首先,ebp 出栈了,这个时候栈空了,所以栈顶会变成初始时的值 001212FDFC。相当于上图中的 esp=0012FDF8+4=0012FDFC.** 注意出栈,则栈顶 + 4,**然后呢。ebp 为啥变成了 0012FEDC 初始的值?ebp 不是一直保存着 esp 的初始地址么?
所以重点就在 pop 这个语句了。pop ebp 究竟表达神马意思?ebp 的值起初存在了栈中,出栈以后,它的值就恢复了原样。所一句灰常重要啊。pop 的意思也许就是把弹出的值赋给我们的变量,pop ebp,也就是把存在栈中的值弹出来赋给 ebp。
所以我在这里总结几句:
1、两句的 mov ebp,esp 实际上是把 ebp 进栈后的栈顶地址给了 ebp。
2、在 ebp 没有出栈钱,它会一直保存 ebp 进栈以后的栈顶值,也就是 1 的值。
3、在 ebp 出栈前,需要把 esp 恢复到只有 ebp 在栈中时的值。
4、出栈后,esp 自然恢复到 ebp 进栈以前的初始值,而 pop ebp 则恢复了 ebp 的初始值。
5、pop 的语义很重要, pop ebp 的意思是把当前栈顶的元素出栈,送入 ebp 中,而不是让 ebp 出栈,这点必须明确!
这下应该明白了吧~~~~
参考网上资源:
http://blog.csdn.net/running_noodle/article/details/2838679
http://hi.baidu.com/anheizzq/item/1c0899622926c81e7ddecca3

ebp-- 栈底指针
esp-- 栈顶指针


反汇编01_第5张图片

如图所示,简化后的代码调用过程如下:
void Layer02()
{
int b = 2;
}

void Layer01()
{
int a = 1;
Layer02();
}

那么函数执行过程中 ebp 和 esp 是如何变化的呢?如下是反汇编后的代码:
void Layer02()
{
00413700 push ebp
00413701 mov ebp,esp
00413703 sub esp,0CCh
00413709 push ebx
0041370A push esi
0041370B push edi
0041370C lea edi,[ebp-0CCh]
00413712 mov ecx,33h
00413717 mov eax,0CCCCCCCCh
0041371C rep stos dword ptr es:[edi]
int b = 2;
0041371E mov dword ptr [b],2
}
00413725 pop edi
00413726 pop esi
00413727 pop ebx
00413728 mov esp,ebp
0041372A pop ebp
0041372B ret
我们看到函数调用开始执行如下的两行代码:
00413700 push ebp
00413701 mov ebp,esp

返回前执行如下代码:
00413728 mov esp,ebp
0041372A pop ebp
0041372B ret
那么这几行代码到底是什么意思呢?首先,如图上所示:
开始两行代码的意思是先将 ebp1 压栈,然后将现在的栈顶 esp1 作为函数调用时的栈底,所以会执行如下语句:
00413701 mov ebp,esp

那么,返回前的几条语句又是什么意思呢?
我想大家已经猜到了,当函数调用执行结束,我们要执行相反的过程:
00413728 mov esp,ebp

还原栈顶指针

0041372A pop ebp

还原栈底指针

0041372B ret
返回到函数调用前的指令继续执行。待续…

ESP,EBP, 栈回溯基本原理

我们看到,尽管可以使用相对于栈顶(ESP 寄存器)的偏移来引用局部变量,但是因为 ESP寄存器经常变化,所以用这种方法引用同一个局部变量的偏移值是不固定的。这种不确定性对于 CPU 来说不成什么问题,但在调试时,如果要跟踪这样的代码,那么很容易就被转得头晕眼花,因为现实的函数大多有多个局部变量,可能还有层层嵌套的循环,栈指针变化非常频繁。

为了解决以上问题,x86 CPU 设计了另一个寄存器,这就是 EBP 寄存器。EBP 的全称是 Extended Base Pointer,即拓展的基址指针。使用 EBP 寄存器,函数可以把自己将要使用的栈空间的基准地址记录下来,然后使用这个基准地址来引用局部变量和参数。在同一函数内,EBP 寄存器的值是保持不变的,这样函数内的局部变量便有了一个固定的参照物。

通常,一个函数在入口处将当时的 EBP 值压入堆栈,然后把 ESP 值(栈顶)赋给 EBP,这样 EBP 中的地址就是进入本函数时的栈顶地址,这一地址上面(地址值递减方向)的空间便是这个函数将要使用栈空间,它下面(地址值递增方向)是父函数使用的空间。如此设置 EBP 后,便可以使用 EBP 加正数偏移来引用父函数的内容,使用 EBP 加负数便宜来引用本函数的局部变量,比如 EBP+4 指向的是 CALL 指令压入的函数返回地址;EBP+8 是父函数压在栈上的第一个参数,EBP+0xC 是第二个参数,一次类推;EBP-n 是第一个局部变量的起始地址(n 为变量的长度)。

因为在将栈顶地址(ESP)赋给 EBP 寄存器之前先把旧的 EBP 值保存在栈中,所以 EBP 寄存器所指向的栈单元中保存的是前一个 EBP 寄存器的值,这通常也就是父函数的 EBP 的值。类似的父函数的 EBP 所指向的栈单元中保存的是更上一层函数的 EBP 值,以此类推,直到当前线程的最顶层函数。这也正是栈回溯的基本原理。

links

  • 堆栈调用, 局部变量

你可能感兴趣的:(反汇编01)