【C语言】函数栈帧的创建和销毁(一)

简介: 【C语言】函数栈帧的创建和销毁.

前言

🎈大家好,我是何小侠🎈

🍃大家可以叫我**小何或者小侠🍃**

💐希望能通过写博客加深自己对于学习内容的理解💐

🌸也能帮助更多人理解和学习🌸

花不尽,柳无穷。应与我情同。觥船一棹百分空。何处不相逢。

朱弦悄。知音少。天若有情应老。劝君看取利名场。今古梦茫茫。

— 宋代·晏殊《喜迁莺·花不尽》


    这篇博客我们一起来简单的了解一下,在函数传参,调用过程中的种种细节,由于是汇编语言,我自己会用查找的资料和一些讲解或图片展示来

    帮助大家理解,由于这个过程真的很难用博客来讲解,所以我可能讲的不是很好,希望大家谅解。

    前言🍊

    我们先让大家了解一下汇编与反汇编的区别:

    词性 汇编 反汇编
    名词 就是指汇编语言 指的是由机器语言经过反汇编过程生成的汇编语言。
    动词 指的是将汇编语言指令转换为机器语言指令的过程。 指的是由已生成的机器语言(二进制语言)转化为汇编语言的过程,也可以说是汇编的逆向过程

    那么我们再介绍一下什么是机器语言:机器语言是一种由计算机硬件直接执行的编程语言。
    它使用二进制代码表示指令和数据,与计算机的底层硬件架构紧密相关

    机器语言是计算机能够理解和执行的唯一语言,它包括了一系列的机器指令,每个指令对应着计算机硬件中的一个操作。
    由于机器语言使用的是二进制代码,因此对人类来说很难直接阅读和编写,通常需要使用汇编语言或高级编程语言来进行编写和转换。

    但是啊,重点来了:

    反汇编可以将机器语言指令转换为汇编语言指令,也可以将高级语言程序转换为汇编语言代码。这取决于我们想要分析的程序是已经编译成机器语言的可执行文件,还是高级语言的源代码。

    在调试过程中,高级语言通常会被编译成汇编语言代码,然后再被转换成机器语言指令执行。当我们有一个已编译的可执行文件时,反汇编可以将其中的机器语言指令转换为对应的汇编语言指令,使其更具可读性和可理解性。这对于逆向工程、代码审计和漏洞分析等任务非常有用。

    另一方面,当我们有高级语言的源代码时,反汇编可以将其转换为汇编语言代码,以便更深入地理解程序的执行过程和指令的细节。这对于调试、性能优化和代码分析等任务非常有帮助。

    总之,反汇编可以用于将机器语言转换为汇编语言,也可以将高级语言转换为汇编语言,具体取决于我们想要分析的程序的形式和需求。

    而我们今天要介绍的,就是我们调试的过程中的反汇编。

    越高级的编译器就越不容易观察,希望大家不要选择太新的编译器,VS2022是不行的.

    寄存器与函数栈帧🍊

    要了解函数栈帧就必须理解寄存器

    由于32位和64位的寄存器差别很大,而且我也没有时间去了解64位的寄存器所以今天我们只讨论32位的。

    EAX(Extended Accumulator)是累加器寄存器。它在计算过程中常用于存储临时数据、计算结果和函数返回值。

    EBX(Extended Base)是基址寄存器。它通常用于存储指向数据段的指针,或者作为通用寄存器来存储数据。

    ECX(Extended Counter)是计数器寄存器。它常用于循环计数和字符串操作,也可以用作通用寄存器。

    EDX(Extended Data)是数据寄存器。它可以用于存储数据和执行I/O操作,也可以作为通用寄存器使用

    大家只要看看就行,没必要现在就记住。

    每个寄存器都能够存储一个32位的二进制数据(一个地址)。寄存器在计算机中被用来存储和处理指令和数据。也就是说32位下一个寄存器的大小就是4个字节。

    由于栈顶是一个动态变化的,所以我们要知道栈顶指针会经常变动`

    从标题可以看出我们要描述的是函数栈帧,那么函数栈帧到底是什么?

    函数栈帧(Function stack frame)是指在程序执行过程中,每个函数被调用时所创建的一块内存区域,用于保存函数的局部变量、参数、返回地址等信息。这只是简单的描述,不敢太过于复杂。

    大家只要记得是一块在栈区上所开辟的一块内存区域就行。

    这里还有一个重要到底知识点,esp和ebp是用来维护函数栈帧的,也就是说,现在在调用哪个函数我esp,ebp就维护哪个函数的函数栈帧。

    main函数居然被调用!🍊

    我在Microsoft VC上展示的不是很全面:

    这是调试过程中调用堆栈的例子:大家可能不是很理解我再换一张图:

    这里就很明显的能看出特点,我们看到main函数是在Add函数的下面,也就意味着Add函数是后调用的,那么main函数下面还有一个函数,叫main

    CRTStartup,说明这个函数其实是调用了main函数。但是不是这么简单

    这里由于这个软件的原因,实际上是一个叫__tmainCRTStartup调用了main函数,__tmainCRTStartup是被mainCRTStartup调用的。

    内存中的栈区🍊

    这就是我们还未进入到main函数时栈区的情况,我们在开头介绍过栈顶其实是动态的,只要我们往栈区里面放东西,顶就会变高。

    这里再补充说明一下我们所用的代码是这个:

    push

    现在我们调试起来转到反汇编,如果猜的没错你第一眼看到的就是push,

    英文中的push是推动,压,强迫等意思,而在栈区这里的意思就是压栈,

    压栈是指: 压栈就是把数据放如栈中,从栈顶放入。

    既然说到压栈我们就说说出栈,出栈也是从栈顶取出。

    而栈区具有有先进后出的特点!

    就好像往盒子里放东西,先放入的东西后拿出来,后放入的东西先拿出来

    我们来看看这个汇编代码

    push ebp的含义是:

    "push ebp"这一条汇编指令,用于将ebp寄存器的值压入栈中。 这个指令通常出现在函数的开头,用于保存上一个函数的栈帧指针。

    我们知道ebp其实可以理解成指针变量,用来存放地址。

    ebp指向的是栈底。

    这里还有一个补充的知识:

    我们知道内存里的变量能够取地址,但是寄存器是集成在CPU上的,不在内存当中。

    具体的操作步骤如下:

    • 将ebp寄存器的值压入栈中,此时ebp的值被保存在栈的顶部。
      那为什么要这样做呢?
      通过"push ebp"指令,可以将上一个函数的栈帧指针保存在栈中,以便在当前函数执行完毕后能够正确返回到上一个函数的位置。大家只要记得还有一个返回的作用就行了。
      在 这条汇编代码执行后,

      我们一再强调esp是指向栈顶的所以,esp和栈顶都是一起在动态变化的,而这个ebp保存的就是_tmainCRTSartup的地址。

    然后我们再关注下一条指令,

    move🍊

    在汇编语言中,move(或者是mov)是一条指令,用于将数据从一个位置复制到另一个位置。它的语法通常是:

    move destination , source

    其中,destination表示目标位置,source表示源位置。这条指令将源位置的数据复制到目标位置。

    我们在一开始就已经介绍过,ebp和esp都是指针,如果说

    int * p = &a ,那么p中存放的就是a的地址。这里也一样,把esp的地址赋给ebp,也就意味着,ebp指向的位置改变了,但我们之前说这个ebp不是栈底指针吗?怎么还可以移动?别急请听我慢慢道来!

    我们再给出图像

    sub 🍊

    在汇编语言中,sub是一条指令,用于执行减法操作。它的语法通常是:

    sub destination, source

    以下是一些示例:

    将寄存器eax中的值减去立即数5,并将结果存储在eax中:

    sub eax, 5

    将寄存器ebx中的值减去寄存器ecx中的值,并将结果存储在ebx中:

    sub ebx, ecx

    也就是说虽然是减去但不是只有减去,还要减去之后放在一个寄存器中

    那么这行代码呢?

    4Ch是什么呢?

    HEX:hexadecimal,十六进制的;

    打开程序员计算器就可以看到

    也就是说4C就是个16进制数,转换为十进制就是

    4 * 16 ^ 1+12*16 ^ 0= 76

    也就是说这行代码的意思是:将esp减去76,然后再放到esp,

    也就是说esp这个栈顶指针要移动了,为什么是减去呢?

    我们知道在栈区是先使用高地址再使用低地址的。

    那么为什么要移动这么大一块空间,实际上就是为main函数开辟的函数栈帧。

    我们再次给出栈区变化图。

    这个-76个地址画的可能不太准确但是为了方便截图就这样简单画一下。


    目录
    相关文章
    |
    6月前
    |
    存储 安全 C语言
    深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
    深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
    |
    6月前
    |
    存储 编译器 C语言
    深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
    深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
    |
    6月前
    |
    存储 编译器 程序员
    C语言之反汇编查看函数栈帧的创建与销毁(一)
    C语言之反汇编查看函数栈帧的创建与销毁(一)
    C语言之反汇编查看函数栈帧的创建与销毁(一)
    |
    11月前
    |
    编译器 Linux C语言
    函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)(上)
    函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)
    |
    11月前
    |
    存储 C语言
    打通你学习C语言的任督二脉-函数栈帧的创建和销毁(上)
    打通你学习C语言的任督二脉-函数栈帧的创建和销毁(上)
    63 0
    |
    存储 C语言
    C语言-------函数栈帧的创建和销毁------剖析描骨
    C语言-------函数栈帧的创建和销毁------剖析描骨
    |
    3月前
    |
    存储 C语言
    【C语言】——函数栈帧的创建与销毁
    【C语言】——函数栈帧的创建与销毁
    |
    6月前
    |
    存储 编译器 C语言
    C语言:底层剖析——函数栈帧的创建和销毁
    C语言:底层剖析——函数栈帧的创建和销毁
    |
    6月前
    |
    存储 编译器 程序员
    C语言之反汇编查看函数栈帧的创建与销毁(二)
    C语言之反汇编查看函数栈帧的创建与销毁(二)
    |
    11月前
    |
    编译器 C语言
    函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)(下)
    函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)