棧溢出實驗
利用淹沒返回值控制程序執(zhí)行流程
操作系統(tǒng):Windows XP SP3
開發(fā)環(huán)境:VC++ 6.0
調(diào)試器:Ollydbg
#include <stdio.h>
#include<string.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[8];
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);
return authenticated;
}
main()
{
int valid_flag=0;
char password[1024];
while(1)
{
printf("please input password: ");
scanf("%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!nn");
}
else
{
printf("Congratulation! You have passed the verification!n");
break;
}
}
}
程序未對輸入的密碼進行長度檢測,接收密碼的緩沖區(qū)只有8,而輸入的密碼最長可以輸入1024。判斷密碼是否正確的變量authenticated存儲在棧中,當(dāng)輸入的密碼長度大于8時,輸入的字符串將沖破緩沖區(qū),淹沒authenticated所處的位置。當(dāng)密碼錯誤時authenticated的值是1,正確的時候authenticated的值是0.這就意味著我們可以構(gòu)造一個合適的輸入字符串來改變判斷結(jié)果。
本次的程序與上一節(jié)的程序的區(qū)別為由控制臺輸入改為讀取文件。這是因為很多字符無法由控制臺直接輸入。
本次的程序與上一節(jié)只有讀取文件和控制臺輸入的區(qū)別,故此不再詳細分析。著重分析如何通過覆蓋返回值來控制程序執(zhí)行流程。
1. 首先我們隨便在password.txt中隨便輸入一個字符串保存,然后調(diào)試程序,進入main函數(shù)開始分析。
2. 因為這次的目的是淹沒返回值控制程序流程,所以在進入密碼比對函數(shù)之前,先記錄下函數(shù)位置。
3. 單步進入密碼比對函數(shù),在剛剛進入函數(shù)的時候可以看到ESP的位置就是函數(shù)的返回地址。
4. 在經(jīng)過strcpy函數(shù)之后,可以看到password.txt內(nèi)的字符串成功的覆蓋了函數(shù)比對結(jié)果變量,那么我們可以看到在附近還存儲著函數(shù)返回地址,那么我們是否可以通過加長password.txt內(nèi)的文本內(nèi)容,覆蓋掉返回地址。我們可以看到距離返回地址我們需要12個長度的文本,你那么這次我們將password.txt的內(nèi)容修改為123456781234567812345678,再次調(diào)試程序。
5. 再次調(diào)試可以看到堆棧內(nèi)的函數(shù)返回地址已經(jīng)被成功覆蓋了,但是執(zhí)行后會報錯,因為38373635的位置上并沒有執(zhí)行,是一片非法內(nèi)存。
6. 那么我們可以看到在0012FB20的位置上的字符串是1234,根據(jù)上下文,我們可以看出是第三個1234,那么我們就把它覆蓋成我們想要地址。比如輸出比對成功字符串的位置。
7. 那么我們把password.txt中第三個1234改成輸出字符串的地址,但是需要注意兩點。第一,我們要按照小端序倒著輸入返回地址,第二需要修改的是HEX而不是字符串。因此我們需要使用十六進制編輯器010editer進行如下編輯。
8. 然后我們再次調(diào)試程序??梢钥吹蕉褩V蟹祷氐刂芬呀?jīng)被我們成功覆蓋了。
9. 運行一下,可以看到成功的運行到了我們想要的位置,雖然隨后就因為堆棧不平衡導(dǎo)致程序崩潰,但是這是后續(xù)需要解決的問題,這一節(jié)的目的我們已經(jīng)成功達到了。