函數調用棧
博客網址:www.shicoder.top
微信:18223081347
歡迎加群聊天 :452380935
這個分欄我們開始學習PWN,當然PWN也是自己的興趣愛好,所以可能博客更新較慢,不好意思啦
前置知識
每個函數都有自己的棧幀,棧底用ebp,棧頂用esp,還有一個eip,表示即將執行的指令的地址,下面是棧的圖示,是從高地址向低地址增長

函數的棧幀包括
- 函數的返回地址和參數
- 本地變量
- 調用前後上下文
因此比如一個main函數執行時候,ebp指向其棧幀底部,然後在函數運行時候隨時push和pop,導致其esp不斷變化,當要調用子函數func時候,會先push子函數所需要的參數,然後進入func之後,首先push ebp,因為此時的ebp是main函數的,為了在func函數結束後,找到main函數的ebp,所以先保留,然後mov ebp,esp,這時候ebp就會被賦予新的值,這個值就是func函數棧幀的底部,然後就是一些func函數自己的操作,當要使用傳進來的參數時候,就去當時在main調用func前push的參數去取,當執行func函數之後,需要返回到main,此時分兩種情況
func中沒有使用過局部變量,則進入func之後,ebp和esp始終是一樣的,所以直接pop ebp,此時ebp中就存放了call調用前,main的ebpfunc中使用過局部變量,則進入func之後,首先會sub esp,一些空間來給func使用,然後結束時候,使用leave => mov esp,ebp、pop ebp,先將esp值變為ebp的值,此時兩個一樣,然後pop ebp之後,ebp中就存放了call調用前,main的ebp
簡單的就是如下代碼
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

