函数调用时的栈帧变化

Posted 漫小牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数调用时的栈帧变化相关的知识,希望对你有一定的参考价值。

一、程序编译

待分析的程序为如下的c语言程序:

// add.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


int add(int a, int b)
{
	int c = a + b;
	return c;
}

int main(int argc, char* argv[])
{
	int x = 1, y = 2;
	int z = add(x, y);
	printf("The result is %d\\n", z);
	return 0;
}

该程序有main和add两个函数,功能为将1、2两数相加,并将结果在输出窗口中打印出来。通过分析函数调用过程中上下文的每一条语句执行前后的栈帧变化来理解这个过程的原理。
在vc6.0中编译该程序生成可执行文件。在菜单栏点击“文件”——“新建”,切换到“工程”tab页,点击“Win32 Console Application”,并在工程的输入框中输入工程名称“add”,并点击确定按钮。
在这里插入图片描述
选择A “Hello, World” application单选框,点击“完成”按钮。
在这里插入图片描述
将add.cpp文件中的代码替换为待分析的程序代码。在菜单栏点击“编译”——“重建全部”,生成可执行文件。

二、分析过程

1、main函数(调用add前)

将add.exe拖入od,进行动态分析。F8单步调试,参考[ctf逆向003]:win32 application程序入口点解析(结合《加密与解密》4.1.1进行实例分析),3个push后,就是main函数,即入口点:
在这里插入图片描述
call add.0040100A的位置点击F7,进入到该函数。
在这里插入图片描述
在main函数的汇编代码中按F8依次执行每一条语句。

  • 00401060的汇编指令push ebp为上一级调用函数的ebp入栈;
  • 00401061的汇编指令mov ebp, esp将上一级函数调用栈帧的esp赋给ebp,赋值后的ebp为main函数栈帧的ebp;
  • 00401063的汇编指令sub esp, 0x4C为main函数开辟的局部空间,共0x4C个字节。
  • 00401066到00401068的三条push指令保存三个寄存器ebi、esi、edi的值,这是由于main函数中可能会对这三个寄存器进行写入操作,寄存器的值改变后,会在main函数的返回前还原这三个寄存器的值。
  • 00401069到00401076的四条汇编语句,用于将main函数的局部空间从local.19开始(助记符合和栈地址的对应关系可参考:ollydbg中[local.1]、[local.2]、[arg.1]、[arg.2]的含义),循环13次,初始化为0xCCCCCCCC(经典的“烫烫烫烫烫烫烫烫烫烫…”)。在右侧的寄存器窗口可查看EBP寄存器的值为0018FF48,EBP-0x4C=0x0018FEFC,在下面的内存窗口中输入命令db 0x0018fefc,可看到连续0x4C个0xcc:
    在这里插入图片描述
  • 00401078和0040107F的两条mov汇编语句将main函数栈帧中的local.1和local.2两个位置分别赋值为1和2,对应c语言代码int x = 1, y = 2;为了更直观的分析函数调用上下文的栈空间,忽略push的ebx、esi和edi三个寄存器,栈帧情况见下图:
    在这里插入图片描述
  • 00401086到0040108A的4条汇编语句为add函数调用的参数入栈,按照从右到左的顺序入栈,入栈方式为__cdecl(可参考:三种函数调用约定),执行这四条语句后的栈帧情况为:
    在这里插入图片描述
  • 0040108E的汇编语句为add函数的调用语句,跳转到add函数执行,位置为00401005。执行该语句后的栈帧见下图:
    在这里插入图片描述

2、add函数

下图为add函数的汇编代码:
在这里插入图片描述

  • 00401020到00401036的10条汇编指令用于分配局部空间,寄存器入栈和局部空间初始化,与main函数的相应语句类似,不再详述。栈帧变化见下两幅图:
    在这里插入图片描述
    在这里插入图片描述
  • 00401038到00401041的4条汇编指令的功能是将两参数相加保存在eax寄存器中,并保存在add栈帧的第一个局部存储空间local.1。栈帧变化见下图:
    在这里插入图片描述
  • 00401047到0040104A的3条汇编指令用于从add函数的栈帧恢复main函数的栈帧空间,主要变化见下面三幅图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3、main函数(调用add后)

  • main函数00401093处的汇编指令add esp, 0x08用于栈平衡,这是由于 __cdec是C/C++ 缺省调用方式,调用方进行栈平衡,栈平衡后的栈帧见下图:
    在这里插入图片描述

以上是关于函数调用时的栈帧变化的主要内容,如果未能解决你的问题,请参考以下文章

为啥ebx保存在一个简单函数的栈帧中,调用gets?

C语言函数调用及栈帧结构

Linux - 函数的栈帧

c语言函数的栈帧

详解JavaScript调用栈尾递归和手动优化

尾调用优化