zoukankan      html  css  js  c++  java
  • 汇编基础四 --函数调用与堆栈平衡

    函数

    将高级语言中定义的函数,被编译位汇编代码执行时,会被编译为一堆指令的集合,用来实现特定的功能,并获得执行后的结果。如果不关注函数中的具体实现,就可以将一个函数看作一个整体,函数调用过程等同于执行了一个操作,只不过这个操作比较复杂而已。

    汇编中实现一个函数可以使用JMP 和 CALL 指令完成。

    函数是一堆完成特定功能的指令集,这些指令集同样需要按照顺序依次执行,所以只要知道函数执行的第一条指令的地址(函数的首地址),函数将会依次执行这些指令,完成函数。

    计算两数之和

    我们使用C语言实现简单的加法函数

    函数分为带参函数和无参函数,对于无参函数,直接使用CALL指令跳转到函数首地址然后开始执行即可,并且CALL指令会将下一行指令地址入栈保存,用于函数结束返回时跳回到原地址。

    // 函数体
    首地址         指令
    00401019     ADD  EAX, 4
    0040101D RETN // 将栈中的地址
    00401038 取出, 赋值给ESI,下次执行回到00401038地址处执行
    ...
    ...
    
    00401034    CALL  00401019       // 将下一行指令地址 00401038存入栈空间,然后执行 00401019地址处的函数
    00401038 ...

    如果函数带参数,我们使用C语言实现简单的加法函数为例

    int  add(int x, int y)
    {
      int  z = 0;
      z = x + y;
      return z;
    }
    
    void main()
    {
      int x = 1;
      int y = 2;
      int z;
      z = add(x, y)
    }

    该函数执行时可以分为以下的步骤。

    1. add函数需要x, y两个参数至,所以在调用函数前,将参数push 到栈中,为了在函数中使用,多个参数多次执行push即可。 push 1, push  2
    2. 执行CALL 指令,该指令将下一指令地址存入栈中,并将函数首地址写入EIP寄存器,下一次将会执行函数体中的指令。
    3. 函数中的指令开始执行,但是执行我们加法逻辑前,会执行一些操作
      1. 首先进行堆栈提升,并开启一段缓冲区空间用于储存函数中的临时变量,使用 SUB ESP, 40h指令,将ESP向上偏移40个数据宽度。堆栈提升后,使用EBP位置进行寻址。
      2. 将三个寄存器中的值写入栈中,函数执行的过程中会覆盖寄存器的值,保存在栈中后,函数结束时可以从该处恢复。
      3. 开始执行函数的主逻辑
    4. 获取栈中 x,y的值进行加法操作。通过EBP寄存器中保存的位置,函数参数在栈中的位置分别为,EBP+8和EBP+C(16进制)
    5. 执行加法操作
      1. MOV EAX, 0                            -- EAX寄存器中的值设置为0
      2. ADD EAX, ptr ds:[ EBP+8 ]     -- EAX寄存器中的值 + 内存地址 EBP +8位置值,结果保存在EAX中
      3. ADD EAX, ptr ds:[ EBP+C]   -- EAX寄存器中的值 + 内存地址 EBP +C 位置值,结果保存在EAX中
      4. 执行结束后,结果被保存到了EAX寄存器中。
    6. 函数执行结束,开始恢复堆栈,清除函数栈中的信息,保证函数执行前的堆栈信息和函数执行后的堆栈相同,也就是满足堆栈平衡。
      1. 恢复三个寄存器edi, esi, ebx中值,pop edi,   pop esi,    pop ebx
      2. 清除缓冲区,这里的清除并不删除其中的数据,将ESP指针恢复即可,这样缓冲区空间会被作为未使用区域,新的数据写入时候,将原来的数据覆盖。由于ESP提升执行了SUB,所以ADD ESP, 40h即可
      3. EBP恢复到原EBP位置,此时栈中保存了原EBP中的位置,所以 POP EBP ,将堆栈中的原EBP地址保存到EBP中,EBP地址恢复。
      4. 下一行指令地址赋值给EPI寄存器。到此为止,函数执行结束,并回到了CALL指令的下一行指令,但是函数参数空间还没有清除,所以需要在函数外部恢复堆栈平衡。
      5. CALL指令的下一行,清除函数参数。同样的,ESP向下移动,ADD EBP, C 即可。

       

     上面是执行过程,汇编代码为。

    // 主程序入口
    。。。
    push 1
    push 2
    call  0040107D            -- 调用函数
    add esp, 8                   -- 清楚堆栈中的参数值,恢复堆栈

    -- 执行结果在eaxz中,需要使用时,获取即可 。。。
    // 此处为函数首地址为 0040107D push ebp -- 堆栈提升,保存原ebp值,然后将esp赋值给esp mov ebp, esp sub esp, 40h push ebx push esi push edi mov eax, 0 add eax, ptr ds:[ebp+8] add eax, ptr ds:[ebp+c] pop edi pop esi pop ebx add esp, 40h cmp ebp esp -- 比较esp和ebp是否相同,清除栈信息后应该相,否则说明栈中的内容没有被清除。堆栈不平衡 pop ebp -- 从栈中取出原ebp值,存入ebp即恢复原ebp值 ret

    函数执行过程中的堆栈空间是在函数执行时才分配的,这里发生了一次堆栈提升,所以我们总是说函数执行时有独立的栈空间,也是通过这种方式实现。函数执行前后始终需要保证堆栈平衡。

    总结

    总结函数的执行过程

    • 参数入栈
    • 保存当前执行指令的地址,入栈
    • 进入函数,
      • ESP 和 EBP 分别进行堆栈提升
      • sub esp 开辟缓冲区空间,缓冲区空间地址为 EBP + 4  -> ESP
      • 三个寄存器值保存到栈中
    • 执行函数中内容,EBP为基址,获取通过+8 +C获取函数参数,+4位置为函数返回时跳转的地址
    • 函数执行结束:
      • 恢复三个寄存器
      • add esp恢复 缓冲区,
      • ESP和EBP位置恢复
      • ret
    • 函数外部add ebp  恢复参数空间。
    • 在eax中获取函数返回值即可。
  • 相关阅读:
    SQL性能优化思路
    EF Migraiton错误解决
    How to resolve the 403 error when send POST request from Postman
    Disable trigger to avoid the ID is auto-updated
    MBG(Mybatis Generator)配置
    Publish Web Site To IIS From VS
    quickSort算法导论版实现
    Clang与libc++abi库安装
    Clang与libc++abi库安装
    整数中1 的个数
  • 原文地址:https://www.cnblogs.com/k5210202/p/13368280.html
Copyright © 2011-2022 走看看