天天看點

函數調用過程(棧桢)

棧桢

首先來看一段代碼

#include<stdio.h>
int add(int x, int y)
{
    int z = x + y;
    return z;
}
int main()
{
    int a = ;
    int b = ;
    int ret = add(a, b);
    printf("ret = %d\n",ret);
    return ;
}
           
函數調用過程(棧桢)

      此處是為了給a,b分别開辟空間,這時棧桢如圖所示

函數調用過程(棧桢)
函數調用過程(棧桢)

     兩條push指令将a,b變量入棧儲存

函數調用過程(棧桢)

     接下來通過call指令,将call指令的下一條指令的位址入棧,目的是為了恢複,其次call指令通過jmp指令修改eip寄存器的值,進而達到跳轉的目的

函數調用過程(棧桢)
函數調用過程(棧桢)
函數調用過程(棧桢)

     通過push指令将此時的ebp壓棧保護(此時的ebp是main函數的ebp),再通過move将esp和ebp的指向變得相同(都指向esp指向的位置),再将esp減44,至此形成新的棧桢結構

函數調用過程(棧桢)

     接下來,通過以下幾條指令計算出a + b,并将其儲存,同時通過pop指令出棧,再用move指令将esp和ebp指向相同位置再通過ret将目前棧頂的資料彈回到eip(ret将目前棧頂資料彈回到eip 中)

函數調用過程(棧桢)
函數調用過程(棧桢)

     對應彙編代碼如下

函數調用過程(棧桢)

     此時對應的棧桢結構如圖所示

函數調用過程(棧桢)

     此時已經回到初始狀态,通過add和move指令達到将ebp傳回到main函數的ebp,再将a+b的内容放到ebp-12的那塊空間,如圖所示.

函數調用過程(棧桢)
函數調用過程(棧桢)

     至此完成所棧桢結構,以及臨時變量的釋放      再來看一段棧桢應用的代碼:

#include<stdio.h>
#include<windows.h>

void bug();
int my_add(int x, int y);

void* ret = NULL;
void bug()
{
    int x = ;
    int* q = &x;
    q += ;
    *q = ret;
    printf("bug: hehe i am a bug!\n");
    Sleep();
}

int my_add(int x, int y)
{
    //printf("my_add:");
    int* p = &x;
    p--;
    ret = *p;
    *p = bug;
}

int main()
{
    int a = ;
    int b = ;
    int c = my_add(a, b);
    __asm
    {
        sub esp,
    }
    printf("main: you should run here\n");
    return ;
}
           
函數調用過程(棧桢)

繼續閱讀