汪道之

有人的地方就有江湖

0%

病毒_DOS_函数调用

定义:是一种用来存储函数调用时的临时信息的结构,如函数调用所传递的参数、函数的返回地址、函数的局部变量等

特性:先进后出(FILO)

基本操作:

  1. PUSH:压栈
  2. POP:弹栈

函数调用对栈的操作

  1. 把指令寄存器EIP(指向当前CPU将要运行的下一条指令的地址)入栈,作为程序的返回地址(一般用RET)表示
  2. 把基址寄存器EBP入栈(保护数据)
  3. 把EBP设为栈顶指针ESP,作为新的基地址
  4. 动态存储分配留出一定空间,即把ESP减去一个适当的值

获取参数值

call指令执行完毕的栈布局:

image-20210511195956130

为了得到参数2,我们使用EBP保存ESP,而为了保护保存ESP的EBP,我们将其入栈,得到如此结构:

image-20210511200219660

获取局部变量值

栈分布如图:

image-20210511200418376

获取返回值

函数返回值通过eax寄存器传递,如果两个返回值还会使用edx:

image-20210511200752137

如果返回值太大、就需要主调函数在call被调函数之前创建临时空间:

image-20210511201041931

然后,在被调函数传递返回值时:

  1. 通过[EBP+偏移]获得返回值的存放地址
  2. 把返回值写入到这个内存
  3. 把返回值写入到EAX,mov eax,[ebp+偏移]

image-20210511201411512

函数调用的扫尾工作

  1. 平衡栈,清除参数传递消耗的栈空间
  2. 只需要改变esp值就可
  3. 可以由调用函数使用ret 参数占用栈大小来清除

栈溢出

例如程序:

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(){
char name[16];
gets(name);
for(int i=0;i<16&&name[i];i++)
printf(“%c”,name[i]);
}

当我们输入的数据大于16个字节时就会产生栈溢出,此时输入的字节会覆盖栈底内容导致出错,这就是栈溢出

image-20210511202236918

例题

  1. 关于函数,下列说法不正确的是( )
    A. 被调函数通过EBP+偏移访问主调函数传递的参数
    B. 被调函数返回后,主调函数不需要再进行清栈的动作
    C. 被调函数通过寄存器将返回值或返回值地址传递给主调函数
    D. 被调函数通过EBP-偏移访问自己的局部变量

参考答案:B

解析:主调函数是否进行清栈是根据被调函数是否清栈来的,而不是一定不清栈