首页 > 其他 > 详细

函数调用过程中栈的变化情况

时间:2021-05-04 23:46:48      阅读:40      评论:0      收藏:0      [点我收藏+]

OS:Ubuntu 18.04

编译器:gcc 7.5.0

调试器:gdb 8.1.1

 

test.c源代码:

#include <stdio.h>

void func(int a, int b)
{
    int sum;
    int sub;
    sum = a + b;
    sub = a - b;

}

int main()
{
    int a = 0;
    int b = 0;
    char c = A;

    func(a, b);
    return 0;
}

 

使用gcc编译(编译选项忽视即可),gdb调试

gcc test.c -ggdb -m32 -g -std=c99 -D_GNU_SOURCE -fno-stack-protector -mpreferred-stack-boundary=2 -Wno-format-security -o test

gdb test

 

 

 

技术分享图片     技术分享图片

 

1、push指令:push source 相当于 add esp, 4 mov [esp], source(我在相当长的时间里搞不清到底是先加4还是先传值)

2、调用函数的过程

  1)调用方

    a. 参数压栈,传参结束,这时esp指向栈顶(最左边的参数)

    b. call指令:将call后面的call_ip写入eip,将call的下一条指令地址压栈,这时esp指向这个返回地址

  2)被调用方

    a. push ebp:将ebp压栈,这时,esp指向存储ebp的栈内地址

    b. mov ebp, esp:将esp赋值给ebp,这样ebp的值就是存储着原来的ebp的栈内地址,ebp指向原来的ebp

    c. 将ebp作为栈底,esp作为栈顶,两者中间形成了子函数的栈空间

 

 

 

技术分享图片     技术分享图片

 

3、函数返回的过程

  1)被调用方

    a. leave指令:相当于mov esp, ebp pop ebp,与调用方开始的两条指令刚好相反,作用有两个:还原调用子函数前的ebp,将esp指向返回地址

    b. ret指令:相当于pop eip,将esp指向的地址的内容赋值给eip,这样esp也就刚好指向传参时最左边的参数

    c. 如果有返回值,会存放在eax当中

  2)调用方

    a. 将调用前压入的参数全部出栈,彻底恢复调用函数之前的栈,一般是sub esp, num

    b. 如果有返回值,从eax中取

 

 

4、压入的ebp的作用

压入栈中的ebp代表着调用方的栈底,这与调用方的局部变量的寻址直接相关,当函数调用结束后,调用方还需要借助出栈的原ebp值来确定局部变量的位置。如果原ebp的值被改变,那么子函数调用结束后,虽然会继续执行正确的指令,但其中涉及的局部变量都会错位,进而出现错误。

5、压入的返回地址的作用

这很好理解了,就是函数返回后要执行的那条指令的地址,修改了这个返回地址,相当于修改了接下来要执行的指令,获得了程序控制权。

 

搞清楚了调用函数的过程,也就可以理解缓冲区溢出的原理了,其主要就是通过修改原ebp和返回地址的值来获得程序控制权

函数调用过程中栈的变化情况

原文:https://www.cnblogs.com/lylhome/p/14730119.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!