函数调用栈是如何在程序执行过程中维护的?

2026-05-27 16:561阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1131个文字,预计阅读时间需要5分钟。

函数调用栈是如何在程序执行过程中维护的?

博客网址:www.shicoder.top微信:18223081347欢迎加群聊天:452380935这个专栏我们从PWN开始学习,虽然PWN也是我的兴趣爱好,但博客更新较慢,不太意思。预设知识:每个函数都有其自白。

博客网址:www.shicoder.top
微信:18223081347
欢迎加群聊天 :452380935

这个分栏我们开始学习PWN,当然PWN也是自己的兴趣爱好,所以可能博客更新较慢,不好意思啦

前置知识

每个函数都有自己的栈帧,栈底用ebp,栈顶用esp,还有一个eip,表示即将执行的指令的地址,下面是栈的图示,是从高地址向低地址增长

函数调用栈是如何在程序执行过程中维护的?

函数的栈帧包括

  • 函数的返回地址和参数
  • 本地变量
  • 调用前后上下文

因此比如一个main函数执行时候,ebp指向其栈帧底部,然后在函数运行时候随时pushpop,导致其esp不断变化,当要调用子函数func时候,会先push子函数所需要的参数,然后进入func之后,首先push ebp,因为此时的ebpmain函数的,为了在func函数结束后,找到main函数的ebp,所以先保留,然后mov ebp,esp,这时候ebp就会被赋予新的值,这个值就是func函数栈帧的底部,然后就是一些func函数自己的操作,当要使用传进来的参数时候,就去当时在main调用funcpush的参数去取,当执行func函数之后,需要返回到main,此时分两种情况

  • func中没有使用过局部变量,则进入func之后,ebpesp始终是一样的,所以直接pop ebp,此时ebp中就存放了call调用前,mainebp
  • func中使用过局部变量,则进入func之后,首先会sub esp,一些空间来给func使用,然后结束时候,使用leave => mov esp,ebp、pop ebp,先将esp值变为ebp的值,此时两个一样,然后pop ebp之后,ebp中就存放了call调用前,mainebp

简单的就是如下代码

main: ... push arg2 push arg1 call func ... ; func函数内没有局部变量 func: push ebp mov ebp,esp 将main的栈中的参数放到寄存器中 一顿操作 pop ebp retn ; func函数内有局部变量 func: push ebp mov ebp,esp sub esp,0x10(就是一段长度,存放局部变量) 将main的栈中的参数放到寄存器中 一顿操作 leave retn

注意:

  • call => 等价 push 返回地址,然后eip跳转到sum开头的地方
  • leave => 等价 mov esp, ebp 然后 pop ebp
  • retn => 等价 pop eip
小试牛刀

以下面的c语言代码进行讲解

#include<stdio.h> int sum(int a,int b) { return a + b; } int main(){ sum(3,4); return 0; }

push ebp mov ebp, esp push 4 push 3 call sum add esp, 8 mov eax, 0 leave retn push ebp mov ebp, esp mov edx, [ebp+8] mov eax, [ebp+0Ch] add eax, edx pop ebp retn

下面一段视频就是其过程,可以看下

后面还有两段有局部变量的,大家也可以自己推导下。

#include<stdio.h> int sum(int a,int b) { int c; c = a + b; return c; } int main(){ int ret; ret = sum(3,4); return 0; }

push ebp mov ebp, esp sub esp, 10h push 4 push 3 call sum add esp, 8 mov [ebp-4], eax mov eax, 0 leave retn push ebp mov ebp, esp sub esp, 10h mov edx, [ebp+8] mov eax, [ebp+0Ch] add eax, edx mov [ebp-4], eax mov eax, [ebp-4] leave retn

#include<stdio.h> int sum(int a,int b) { return a + b; } int main(){ int ret; ret = sum(3,4); return 0; }

push ebp mov ebp, esp sub esp, 10h push 4 push 3 call sum add esp, 8 mov [ebp-4], eax mov eax, 0 leave retn push ebp mov ebp, esp mov edx, [ebp+8] mov eax, [ebp+0Ch] add eax, edx pop ebp retn

本文共计1131个文字,预计阅读时间需要5分钟。

函数调用栈是如何在程序执行过程中维护的?

博客网址:www.shicoder.top微信:18223081347欢迎加群聊天:452380935这个专栏我们从PWN开始学习,虽然PWN也是我的兴趣爱好,但博客更新较慢,不太意思。预设知识:每个函数都有其自白。

博客网址:www.shicoder.top
微信:18223081347
欢迎加群聊天 :452380935

这个分栏我们开始学习PWN,当然PWN也是自己的兴趣爱好,所以可能博客更新较慢,不好意思啦

前置知识

每个函数都有自己的栈帧,栈底用ebp,栈顶用esp,还有一个eip,表示即将执行的指令的地址,下面是栈的图示,是从高地址向低地址增长

函数调用栈是如何在程序执行过程中维护的?

函数的栈帧包括

  • 函数的返回地址和参数
  • 本地变量
  • 调用前后上下文

因此比如一个main函数执行时候,ebp指向其栈帧底部,然后在函数运行时候随时pushpop,导致其esp不断变化,当要调用子函数func时候,会先push子函数所需要的参数,然后进入func之后,首先push ebp,因为此时的ebpmain函数的,为了在func函数结束后,找到main函数的ebp,所以先保留,然后mov ebp,esp,这时候ebp就会被赋予新的值,这个值就是func函数栈帧的底部,然后就是一些func函数自己的操作,当要使用传进来的参数时候,就去当时在main调用funcpush的参数去取,当执行func函数之后,需要返回到main,此时分两种情况

  • func中没有使用过局部变量,则进入func之后,ebpesp始终是一样的,所以直接pop ebp,此时ebp中就存放了call调用前,mainebp
  • func中使用过局部变量,则进入func之后,首先会sub esp,一些空间来给func使用,然后结束时候,使用leave => mov esp,ebp、pop ebp,先将esp值变为ebp的值,此时两个一样,然后pop ebp之后,ebp中就存放了call调用前,mainebp

简单的就是如下代码

main: ... push arg2 push arg1 call func ... ; func函数内没有局部变量 func: push ebp mov ebp,esp 将main的栈中的参数放到寄存器中 一顿操作 pop ebp retn ; func函数内有局部变量 func: push ebp mov ebp,esp sub esp,0x10(就是一段长度,存放局部变量) 将main的栈中的参数放到寄存器中 一顿操作 leave retn

注意:

  • call => 等价 push 返回地址,然后eip跳转到sum开头的地方
  • leave => 等价 mov esp, ebp 然后 pop ebp
  • retn => 等价 pop eip
小试牛刀

以下面的c语言代码进行讲解

#include<stdio.h> int sum(int a,int b) { return a + b; } int main(){ sum(3,4); return 0; }

push ebp mov ebp, esp push 4 push 3 call sum add esp, 8 mov eax, 0 leave retn push ebp mov ebp, esp mov edx, [ebp+8] mov eax, [ebp+0Ch] add eax, edx pop ebp retn

下面一段视频就是其过程,可以看下

后面还有两段有局部变量的,大家也可以自己推导下。

#include<stdio.h> int sum(int a,int b) { int c; c = a + b; return c; } int main(){ int ret; ret = sum(3,4); return 0; }

push ebp mov ebp, esp sub esp, 10h push 4 push 3 call sum add esp, 8 mov [ebp-4], eax mov eax, 0 leave retn push ebp mov ebp, esp sub esp, 10h mov edx, [ebp+8] mov eax, [ebp+0Ch] add eax, edx mov [ebp-4], eax mov eax, [ebp-4] leave retn

#include<stdio.h> int sum(int a,int b) { return a + b; } int main(){ int ret; ret = sum(3,4); return 0; }

push ebp mov ebp, esp sub esp, 10h push 4 push 3 call sum add esp, 8 mov [ebp-4], eax mov eax, 0 leave retn push ebp mov ebp, esp mov edx, [ebp+8] mov eax, [ebp+0Ch] add eax, edx pop ebp retn