1. 程式人生 > 其它 >棧幀與函式呼叫

棧幀與函式呼叫

1.什麼是棧幀

在理解棧幀之前,我們需要理解什麼是棧

棧在資料結構中是一種運算受限的線性表,即我們只能對錶尾進行操作,稱之為棧頂,相對的,另一端稱為棧底。它按照先進後出的原則儲存資料,先進入的資料儲存在棧底,後來的資料儲存在棧頂。

在計算機系統當中,棧是擁有在資料結構當中所有屬性的一塊動態記憶體區域,用來儲存函式內部的區域性變數和返回地址。

而棧幀,就是在程式執行的過程當中,儲存函式呼叫時所需要維護的資訊。

 

棧幀

簡言之,棧幀是利用EBP暫存器來訪問函式內部區域性變數、引數、函式的返回地址等。

棧幀結構

PUSH  EBP                 ;函式開始(使用EBP先把已有值儲存在棧中)
MOV EBP ESP ;儲存當前ESP值到EBP中 ;函式體 *** ;無論ESP值如何變化,EBP值不改變,可以安全訪 問函式內的區域性變數,引數 MOV ESP EBP ;將函式的起始地址(返回地址)返回到ESP中 POP EBP ;彈出儲存在棧中的EBP值 RETN ;函式終止

利用例項來理解棧幀(逆向工程核心原理:棧幀章)

#include<stdio.h>
long add(long a , long b)
{
   long x = a , y = b;

    return (x+y);
    }


int main()
{
    long a = 1 , b = 2 ;
    
    printf(" %d \n" , add( a , b )) ;

    return 0;
    }

 

組合語言如下所示

 

 

①我們先從main()函式來進行程式分析

我們需要密切關注棧的變化

當前ESP、EBP指標如圖所示(如下);

 

棧頂指標儲存19FF2C儲存著main()函式執行完後的返回地址(如下);

 這條指令(如下)把EBP(棧幀指標)儲存在棧中(main()函式執行完畢,返回之前,該值會再次恢復)

 

main()函式的esp的值被儲存在ebp當中(下圖),當做開闢新棧之後的基址(同時作為執行完add()函式的返回地址)。從這條命令開始,ebp與esp持有相同的值,main()函式的棧幀就生成好了。

 

棧內ebp的指標儲存著main()函式開始執行時的初始值

 

 

②設定變數

 

 

 

 sub是減法指令,esp-8意思是在棧內開闢一個八位元組的空間(long 型的a,b一個佔四位元組)

 

 把ebp-4與ebp-8處各有一個四位元組的記憶體空間,用來儲存資料1和2。

執行完後棧內空間如下所示

 

 

 ③呼叫add()函式

 

 

 

 以上五行彙編描述了呼叫main()函式的整個過程

函式add()接收a、b兩個長整型,所以呼叫前先把兩個引數壓入棧,如下圖所示

 

 

返回地址

 

 

執行call命令進入函式之前,需要把函式的返回地址壓入棧。在地址40103C中執行了call,它的下一條指令的地址是401041,即執行完add()函式後程序的執行流接著執行地址401041的命令,此處即為返回地址。

執行完call命令後,棧內如圖所示

 

 

④進入add()函式

 

 

 生成add()函式的棧幀

棧內情況如圖所示:

 

 

 

⑤設定add()函式的區域性變數

 

 

 

 [ebp+8]在棧內表示a的值,[ebp+c]表示b的值,[ebp-8]與[ebp-4]指向add()函式的兩個引數x,y

 

 

⑥add運算

 

 運算完成後值被儲存在eax當中

 

 

 

 

⑦刪除add()函式的棧幀&函式的返回值

在返回值之前,需要先刪除棧幀。

 

 mov指令用於將儲存在ebp當中的main()函式的esp的值恢復到esp當中

pop指令將add()函式開始執行時儲存的ebp的值彈出

這兩條指令完成後,棧內情況如下

 

 

 

 ebp指向main()函式開始執行時的初始值

esp儲存函式執行完畢後的返回地址401041

 

⑧從棧中刪除add()函式的引數

retn執行後,程式重新執行到main()函式內執行add  esp+8

 

 這條命令的作用是刪除引數a,b

 

⑨printf()與return 0;

⑩刪除main()函式的棧幀

 

 執行完後指標與棧內空間如下,main函式棧幀被刪除

 

 

 

 

retn指令執行後,返回到主函式執行完畢後的狀態