棧溢出的初步利用
我們接着上面的棧溢出原理來進行講解棧溢出的利用,首先我們不會接着上一篇的文章的例子來進行講解,我會再寫一個C語言的例子來進行講解。再進行講一遍棧溢出的原理。更加熟悉棧溢出的原理能夠讓我們更好地利用棧溢出。
下面的例子代碼如下:(代碼很簡單我不做解釋)
#include <stdio.h> #include<string.h>
#define PASSWORD "qqqqqqq"
int verify_password(char *password) { int authenticated; char buffer[8]; authenticated=strcmp(password,PASSWORD); strcpy(buffer,password); //構造棧溢出
return authenticated; } int 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!\n\n"); } else { printf("Congratulation! you have passed the Verification !\n"); break; } } getchar(); char i; scanf("%s",&i); }
從上面的例子可以看出是密碼有效性的驗證的一個例子,是通過輸入密碼進行驗證密碼是否輸入正確,首先我們現在運行下程序。

通過輸入正確密碼和不正確密碼的區別;
輸入7個'q'程序正常運行時的棧狀態。
如下圖可以看到棧的內部情況:

如果繼續增加輸入的字符,那么超出buffer[8]邊界的字符將依次淹沒authenticated、前棧幀EBP、返回地址。也就是說,控制好字符串的長度就可以讓字符串中相應位置字符的ASCII碼覆蓋掉這些棧幀狀態值。
按照上面對棧幀的分析,可以得出以下的結論:
(1)輸入11個'q',第9-11個字符連同NULL結束符將authenticated沖刷為0x00717171。
(2)輸入15個'q',第9-12個字符將authenticated沖刷為0x71717171;第13-15個字符連同NULL結束符將前棧幀EBP沖刷為0x00717171。
(3)輸入19個'q',第9-12字符將authenticated沖刷為0x71717171;第13-16個字符連同NULL結束符將前棧幀EBP沖刷為0x71717171;第17-19個字符連同NULL結束符將返回地址沖刷為0x00717171。
這里用19個字符作為輸入,看看淹沒返回地址會對程序產生什么影響。出於雙字對齊的目的,我們輸入的字符串按照"4321"為一個單元進行組織,最后輸入的字符串為"4321432143214321432"(測試)。
<用OllyDbg加載程序,在字符串拷貝函數調用結束后觀察棧狀態。>
下面來進行分析:
當輸入7個q時,觀察程序返回時堆棧的里面的內容。

下面執行這段代碼:4321432143214321432
實際的內存狀況和我們分析的結論一致,此時的棧狀態見。請見下表的內容:

接下來我們用OD來調試下程序的運行過程:

返回地址用於在當前函數返回時重定向程序的代碼。在函數返回的"retn"指令執行時,棧頂元素恰好是這個返回地址。"retn"指令會把這個返回地址彈入EIP寄存器,之后跳轉到這個地址去執行。
在這個例子中,返回地址本來是0x040FACB,對應的是main函數代碼區的指令,現在我們已經把這個地址用字符的ASCII碼覆蓋成了0x00323334。
我們可以從調試器中的顯示看出計算機中發生的事件。
(1)函數返回時將返回地址裝入EIP寄存器。
(2)處理器按照EIP寄存器的地址0x00323334取指。
(3)內存0x00323334處並沒有合法的指令,處理器不知道該如何處理,故報錯。
由於0x00323334是一個無效的指令地址,所以處理器在取指的時候發生了錯誤使程序崩潰。但如果這里我們給出一個有效的指令地址,就可以讓處理器跳轉到任意指令區去執行(比如直接跳轉到程序驗證通過的部分),也就是說,我們可以通過淹沒返回地址而控制程序的執行流程。
這時候我們會想到這段代碼是進行對密碼的驗證,如果對了就顯示正確信息,如果不對彈出錯誤消息,這時候我們可以修改這個函數返回地址,直接執行正確信息這樣就達到了我們的目的;接下來我們要對程序進行一定的改進。
程序代碼如下所示:
#include <stdio.h>
#define PASSWORD "1234567"
int verify_password (char *password) { int authenticated; char buffer[8]; authenticated=strcmp(password,PASSWORD); strcpy(buffer,password);//over flowed here!
return authenticated; } main() { int valid_flag=0; char password[1024]; FILE * fp; if(!(fp=fopen("password.txt","rw+"))) { exit(0); } fscanf(fp,"%s",password); valid_flag = verify_password(password); if(valid_flag) { printf("incorrect password!\n"); } else { printf("Congratulation! You have passed the verification!\n"); } fclose(fp); }
這段代碼主要改進部分是密碼是從txt文件中讀取出來的不是手動輸入了,這樣的好處就是能夠用UE輸入一些16進制來進行修改數據;首先我們將程序載入到OD中,看一下我們要跳轉的地方地址在哪里,我們看下面這張圖,我們可以清晰的看到我們要返回的地址應該是0x401122這個地址

這時候我們在程序的同目錄下面創建一個password.txt首先前面的東西可以隨便輸入,還是4321432143214321后面這個就要輸入0x401122如下圖所示:

這時候可以看一下棧幀情況,棧幀的情況如下圖所示:

運行程序看一下結果

OK我們就可以看到我們想要的效果,當然這樣修改會有問題,因為沒有通過一些手段進行處理,代碼里面還存在一些非法的指令因為我們修改了程序的返回地址。由於棧內EBP等被覆蓋為無效值,使得程序在退出時堆棧無法平衡,導致崩潰。雖然如此,我們已經成功地淹沒了返回地址,並讓處理器如我們設想的那樣,在函數返回時直接跳轉到了提示驗證通過的分支。
上一篇文章鏈接地址:(初步認識棧溢出漏洞)http://www.cnblogs.com/dwlsxj/p/StackOverflow.html
如果有寫錯的地方希望各位前輩及時指正我並改正。
