美文网首页
反汇编01

反汇编01

作者: wyrover | 来源:发表于2016-12-04 16:56 被阅读129次

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

    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

    相关文章

      网友评论

          本文标题:反汇编01

          本文链接:https://www.haomeiwen.com/subject/lomhmttx.html