最近學校講到了棧溢位,並有一道很基礎的題,在此可以記錄一下,感興趣的人可以瞭解瞭解.
/
棧溢位:
/
首先談到棧溢位,要先說windows系統典型漏洞分析。
/
棧溢位也是其中的一種。
/
要了解棧溢位,要先了解:
棧(音:zhan四聲)是一種特殊的線性計算機內部儲存結構,服從先進後出的一種特殊線性結構,如同彈夾裡的子彈,先放入的子彈,在最下面。棧的結構使其只能在棧的頂端進行增加資料和刪除資料的操作,壓入資料稱為(push),彈出資料稱為(pop).
/
在win32環境下,由高階語言生成的可執行檔案,即PE檔案,(Portable Executable)檔案。在執行可執行檔案時,系統會將其載入到記憶體,並對映出4GB的虛擬儲存空間,然後繼續執行,形成所謂的程序空間。(教材原話)
/
在win32中將程序使用的記憶體按功能可以分為4個區域,如下圖:
/
名稱 | 作用 | |
---|---|---|
棧區(stack) | 用於動態儲存函數之間的呼叫關係 | 低地址 |
堆區 | 該記憶體區域由程序利用相關函數和運運算元動態申請 | |
程式碼區 | 存放程式組合之後的機械程式碼和唯讀程式,計算機執行程式,會在該區域讀取命令並執行。 | |
資料區 | 用於儲存全域性變數和靜態變數 | 高地址 |
/
其在計算機記憶體中的結構也如下圖:
棧區 | ↑棧增長方向 |
堆區 | ↓堆增長方向 |
程式碼區 | |
資料區 |
/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void attack()
{
printf("TRY IT!.\n"); //attack函數
}
void func() //func函數
{
char password[6] = "ABCDEF";
char str[6];
FILE *fp;
if(!(fp=fopen("D:\\password.txt","r"))) //開啟D槽的password.txt檔案
exit(0);
fscanf(fp,"%s",str); //將str的內容寫入fp
str[5]='\0';
if(strcmp(str,password)==0) //判斷str是否與password相同
printf("OK.\n");
else
printf("NO.\n");
}
int main()
{
func(); //執行func函數
return 0;
}
/
/
在D槽的password.txt中輸入一定量的字串,執行程式,AAAAA…與指定的password並不相同,所以返回了「NO」。
現在開啟IDA.
得到如下:
/
/
現在展示的IDA適用靜態偵錯,左邊可以清晰地看到函數體,這是IDA比較清晰簡便的一個優點。
點選左側的函數名可以進入。右側也可以檢視十六進位制檢視:
IDA快捷鍵,按F5可以檢視虛擬碼。虛擬碼可以讓人較為清晰地瞭解函數的部分構造,但不是能執行的程式碼。在這裡,用滑鼠左鍵雙擊左側函數名稱中的func(),再按F5可以進入func函數檢視虛擬碼:
在上圖中點選各個變數,可以檢視棧。
檢視到str,password,fp在棧中的位置
/
注意到最下面有一個"r"
/
這裡"r"指的是return adress,這裡簡述為ret,ret指的是返回地址,ret在組合中也有涉及。
這裡的ret主要功能是返回下一個函數的地址,用於在一個函數執行完後跳轉到下一個函數,棧溢位攻擊在這裡執行的原理是,通過文字中讀入過量的資料,從而,是資料在棧上溢位,使其覆蓋正常的資料,從而引起漏洞與異常。
/
如果,溢位的資料覆蓋了ret地址,那麼函數可以跳轉到一個指定的函數,即上文的「attack」函數。
/
所以在這裡開始,找到attack函數的地址:
然後,只要將attack()的地址,輸入到文字的末尾即可。但是,要滿足其剛好能將ret的地址覆蓋,使函數跳轉異常,從而跳轉到指定的攻擊函數。
/
那麼,來計算一下,在文字中輸入多少個字元會剛剛覆蓋到「r」,處:
/
這裡檢視到這個資訊: [esp+10h] [ebp-18h] (如下圖)
/
由於記憶體棧區的地址由高地址向低地址增長,當4個位元組壓入棧幀時,即為 ESP=ESP-4, 有4個位元組彈出棧幀時, ESP=ESP+4.
ESP(extended stack point)是擴充套件棧指標暫存器,其存放地址指向這個棧幀的棧頂
/
EBP(extended base point)是擴充套件基址指標暫存器,其存放地址指向這個棧幀的棧底。
/
ESP與EBP之間是當前棧幀的空間。
上圖意為,從前棧底到str存放的位置還有18個位元組。即[ebp-18h]的含義。
其實從IDA中也能清晰看出來:
上圖中,可以看到變數str,password,fp存放在棧中的位置,可以看到上圖用紅色框框選的,有18個位元組,也就是上文的[ebp-18h],下面藍色框有10個位元組,即到r為止。
/
資料從上方加入,那我們只要在文字前加入(10+18)=28個位元組,就可以剛剛覆蓋到r處,r是執行完後要跳轉的函數地址,那我們在28個直接之後加入,攻擊函數attack()的地址即可。
/
將D槽的password.txt檔案拖入軟體「HxD」中,一種文字編輯軟體,notepad++也可。
輸入28個字母,之後將attack()的地址加入,即"00 40 13 50",但這裡是小端序。
這裡講一講:
位元組序即為多位元組物件儲存在記憶體中的位元組順序,有兩種不同的儲存方案:大端法和小端法。現代的處理器大多為雙端法,大小端都支援,可以設定稱大端法或者小端法。
/
大端序:(Big-endian):高位位元組存入低地址,低位位元組存入高地址
小端序:(Little-endian):低位位元組存入低地址,高位位元組存入高地址
一般x86位的CPU都為小端序:所以在編輯時將地址反著寫,寫作"50 13 40 00",
然後,點選「儲存」檔案。
/
再開啟編譯器,執行:
/
執行結果出現了TRY IT!,明顯這是attack()函數的內容,已經完成了棧溢位,使函數執行出了指定的內容,這就是用IDA實現的簡單的棧溢位攻擊實驗。
/
通過棧上資料的溢位,覆蓋了,正確的r返回地址,從而使函數跳轉出現失誤,或指向性的跳轉。可以加以利用。