反汇编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 是具有长远意义的

可以看到,初始情况下,ebp 此时值为 0012FEDC,也就是栈帧的地址,而栈顶地址 esp 值为 0012FDFC。可以看到两个值有一定的关系。而帧指针的地址较高。
然后我们让它执行前两句,push ebp,mov ebp,esp
可以看到前两句已经执行了,那么 ebp 跟 esp 的值也发生了变化。esp=0012FDF8,ebp=0012FDF8。为神马?一句句解读,push ebp,向栈里面压入了一个东西,那么栈顶此时应该发生变化了,也就是地址 - 4 字节。为什吗是减法呢?因为是向低地址增长的,这点一定得注意。所以此时 esp 变化成了 0012FDFC-4=OO12FDF8. 至于 ebp 也等于 0012FDF8 就不解释了。
接着上图不解释:
此时呢,观察现在的值。栈顶 esp=0012FDF4, 而 ebp=0012FDF8; 没啥好说的,此时的栈顶已经又跑上去了,说明又有元素压栈了。那么执行这句 mov esp,ebp 之后,不用说,esp 跟 ebp 都会变成 0012FDF8. 我们重点看下一幅,执行完 pop,让 ebp 出栈,后会发生神马。
此时 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-- 栈顶指针


如图所示,简化后的代码调用过程如下:
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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,505评论 1 19
  • 8086汇编 本笔记是笔者观看小甲鱼老师(鱼C论坛)《零基础入门学习汇编语言》系列视频的笔记,在此感谢他和像他一样...
    Gibbs基阅读 36,357评论 8 113
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,669评论 0 27
  • 文/路人锋 什么是对的?什么是错的? 我只知道: 黑夜从来没有月亮光明月亮从来没有玫瑰热情玫瑰从来没有百灵动听百灵...
    路人锋阅读 289评论 16 19
  • 俗不知,学一个工具首先需要知道它可以解决什么问题……然后不假思索的灵活的运用到我们的项目中mycat官网:http...
    goodchax阅读 1,489评论 0 1