棧溢出攻擊原理


棧溢出原理

馬上軟考了,補一補

寄存器分配 ESP、EBP、EIP

以32位x86架構為基礎,Windows提供三個寄存器幫助定位棧和函數調用--ESP、EBP、EBP

ESP

ESP 用來存儲函數調用棧的棧頂指針,指向棧區中最上一個棧幀的棧頂

EBP

EBP 用來存儲當前函數狀態的基地址,即棧底指針,指向棧區中最上一個棧幀的棧底

EIP

EIP 用來存放下一個執行語句的地址的指令寄存器

函數調用步驟

下面讓我們來看看發生函數調用時,棧頂函數狀態以及上述寄存器的變化。變化的核心任務是將調用函數(caller)的狀態保存起來,同時創建被調用函數(callee)的狀態。

所謂被調用函數,就是函數中的函數 Caller's Caller's ebp=Callee's ebp

void caller(arg1,arg2,....argn){
    callee(arg1,arg1,arg2,....argn);
}

Step1.參數入棧

首先將被調用函數(callee)的參數按照逆序依次壓入棧內 argn......arg2,arg1

image

這個棧是從下往上壓的,也就是esp位置的先出棧

Step2.返回地址入棧

將調用函數(caller)進行調用之后的下一條指令地址作為返回地址壓入棧內。這樣調用函數(caller)的 EIP(指令地址)保存在 Return Address中。這里的地址就是callee()運行完接着caller()下一條指令執行的地址

image

Step3.調用函數的EBP入棧

將當前的 EBP 寄存器的值(也就是調用函數caller()的基地址)壓入棧內

並將EBP(棧底)的值更新為當前ESP(棧頂)的地址 → mov esp, ebp

image

Step4.被調用函數局部變量入棧

到這一步,被調用函數callee的函數幀結構就完整了

在壓棧的過程中,esp 寄存器的值不斷減小,棧內數據不斷變大(對應於棧從內存高地址向低地址生長)。

壓入棧內的數據包括:調用參數(argn...arg1)、返回地址(Return Address)、調用函數的基地址(新EBP),以及局部變量(Local Variables)

其中調用參數以外的數據共同構成了"被調用函數(callee)的狀態"。在發生調用時,程序還會將被調用函數(callee)的指令地址存到 EIP 寄存器內,這樣程序就可以依次執行被調用函數的指令了。

image

看過了函數調用發生時的情況,就不難理解函數調用結束時的變化。變化的核心就是丟棄被調用函數(callee)的狀態,並將棧頂彈出恢復為調用函數(caller)的狀態。

Step5.將被調用函數的局部變量彈出棧外

首先被調用函數的局部變量會從棧內直接彈出,棧頂會指向調用函數(caller)的基地址。

image

Step6.彈出調用函數EBP

然后將基地址內存儲的調用函數(caller)的基地址從棧內彈出,並存到 ebp 寄存器內。這樣調用函數(caller)的 ebp(基地址)信息得以恢復。此時棧頂會指向返回地址。下圖容易誤解,其實這里的Caller's Caller's ebp已經變成了Caller's ebp

image

Step7.彈出返回地址,存入EIP

再將返回地址從棧內彈出,並存到 eip 寄存器內。這樣調用函數(caller)的 eip(指令)信息得以恢復到caller()的執行狀態。

image

至此調用函數的狀態恢復,之后繼續執行調用函數的指令

棧溢出攻擊

什么是棧溢出呢?

棧溢出是指向向棧中寫入了超出限定長度的數據,溢出的數據會覆蓋棧中其它數據,從而影響程序的運行。如果我們計算好溢出的長度,編寫好溢出數據,讓我們想要的地址數據正好覆蓋到函數返回地址

那么被調函數調用完返回主函數時,就會跳轉到我們覆蓋的地址上。所以控制程序執行指令最關鍵的寄存器就是 EIP,我們的目標就是讓EIP 載入攻擊指令的地址。通過這樣改變程序流程,接下來我們就可以干很多壞事了!

該怎么構造呢,這里有個簡單的緩沖區溢出的例子

void f(char *str){
    char buf[16];
    strcpy(buf,str);
}
void main(){
    char buf[128];
    for(i=0;i<=127;i++)
        buf[i]='A';
    f(buf);
    print("It's Buffer OverFlow!");
}

這是執行被調用函數f()的stack

image

EIP(Return Address)

其中的buf是局部變量,但是我們從程序中可以看出,128位的*str輸入明顯越界,會導致數據溢出,溢出就會覆蓋EBP、EIP

image

我們通過精心構造一個位數精准的數據,使得EIP處正好被我們溢出的那幾位數據覆蓋為AAAA,其中A對應ASCII 0x41

AAAA對應EIP地址為0x41414141

0x41414141覆蓋了原來EIP的位置時,f函數在返回時就會將0x41414141彈出來給EIP,假如這時shell程序調用地址是0x41414141,那么main()接下來就會去執行shell,我們就能運行shell實施攻擊

(這里全填A是為了方便理解,實際情況構造shell地址肯定不是AAAA哈,自己根據shell地址構造就行了)

防范緩沖區溢出策略

常用的函數有strcpy()sprintf()strcat()vsprintf()gets()scanf(),以及在循環內的函數getc()fgetc()getchat()等都非常容易導致溢出

防范策略有:

系統管理防范策略:關閉不必要的特權程序、及時打系統補丁

開發時的防范策略:注意危險函數的調用、緩沖區不允許執行、靜態分析檢查指針、堆棧向高地址方向增長等


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM