這里我們為了到達實驗的要求,先來編寫1個最簡單的存在緩沖區溢出隱患的程序。這個程序我使用VC++6.0進行編寫,并在Windows XP下履行。(這里請大家注意的是,如果你使用的是新版本的VC,由于微軟加入了GS機制來避免緩沖區溢出情況的出現,那末本實驗就沒法實現。)
首先新建1個Win32控制臺利用程序,然后輸入以下C語言代碼:
圖1
可見程序已得到了正確的履行與輸出。但是我在程序中所創建出來的是1個8字節長度的數組,而我在程序中的輸入是7個字節。如果我的輸入超過8個字節會怎樣樣呢?無妨試1下。
這次再次運行程序,嘗試輸入“jiangyejiangye”,運行結果以下:
圖2
可見,程序雖然也能夠正確輸出,但是卻彈出了毛病提示對話框。為何會出現這類情況?我們接下來就來研究1下。
圖3
這些都是系統自動生成的,與我們的實驗無關,我們在此也無需關注這些代碼的功能。對本次實驗來講,我們只要找到main函數,從而進1步分析便可。那末應當如何尋覓main函數呢?固然我們可以不斷地按F8單步履行,通過視察獲得,但是這樣未免需要1定的經驗,而且也比較麻煩。所以這里無妨利用IDA Pro來打開我們的實驗程序,以下圖所示:
圖4
可見,IDA已幫我們獲得了main函數的入口地址,即0x00401010,那末我們此時可以在OD中,跳到該地址,按F2下1個斷點。以下圖所示:
圖5
由上面的截圖,我們除可以知道main函數的位置外,我們還從下面那段話“Jump from 00401005”得知main函數是由位于0x00401005位置處的語句跳過來的。由于緩沖區溢出是與??臻g緊密相干的,所以我們現在應當分析調用(CALL)main函數前后,??臻g的情況,所以這里我們就需要定位究竟是哪條語句調用了main函數。如果僅僅通過OD,我們是比較難定位的,所以這里我還是使用IDA Pro。
由于已知道main函數的地址是0x00401010,那末我們在IDA中,用鼠標在該地址點1下,以后利用快捷鍵“Ctrl+X”打開“交叉援用窗口”,就來到了jmp到此的函數位置:
圖6
然后在0x00401005的地址處,再次利用“交叉援用”功能,我們就可以夠找到調用main函數的位置了:
圖7
現在就已知道,是位于0x00401694處的語句調用了main函數,那末我們下1步的工作就是分析該語句履行前后,堆棧的情況。
圖8
可以看到,CALL下面的語句的地址是0x00401699。這個地址之所以重要,是由于我們的程序在進入每個CALL之前,都會首先將CALL下面那條語句的地址入棧,然后再履行CALL語句。這樣當CALL履行完后,程序再將該地址出棧,這樣就可以夠知道下1步應當履行哪條指令。我們1般也將這個地址稱為“返回地址”,它告知程序:“CALL履行完后,請履行這個地址處的語句?!?/p>
我們先看1下當前棧的情況:
圖9
注意棧空間由下至上是高地址往低地址處走的。然后我們按下F7,步入這個CALL,此時再看1下??臻g:
圖10
可見,返回地址0x00401669已入棧。這就是CALL語句對棧空間的影響,而這個返回地址在后面的漏洞利用中,其影響相當重要,請大家牢記。
圖11
在上圖中,比較重要的是最后兩行。其中最后1行在之前已講過了,是非常重要的返回地址,它決定了當main函數履行終了后,程序所要履行的語句的地址,而倒數第2行是父函數的EBP,關于這個,我們知道便可,再往上,就是我們的main函數的局部變量空間。這里大家可能會有疑惑,既然是分配給我們的空間,那末為何還會有其它的數據呢?關于這個大家不要急,當我們履行完0x0040DA36的語句后,再看1下這段棧的空間:
圖12
可見這段空間都被0xCC填充了。程序為了容錯性與保持本身的硬朗性,因而利用0xCC,即int 3斷點來填充滿這段區域,這樣1來,如果有未知的程序跳到這片區域,就不會出現崩潰的情況,而是直接斷下來了。固然,這個問題與我們的緩沖區溢出沒甚么關系,大家知道便可。
然后繼續履行,查找反匯編代碼strcpy函數的位置,先來看1看正常情況下,履行這個函數前后,堆棧的情況:
圖13
這里可以看到,strcpy的第2個參數,就是所接收的字符串所保存的地址位置,其保存位置為0x0012FF78。接下來看看當“jiangye”這段字符串拷貝到這段區域時,棧中的情況:
圖14
對照上1張圖可以發現,棧中的地址0x0012FF20位置處,保存的是strcpy第2個參數的地址,OD幫我們解析出了,其內容為“jiangye”。而在棧中地址為0x0012FF78處,則是我們真實的保存“jiangye”這段字符串的內存空間。這并沒有甚么問題,程序能夠取得正常履行。那末如果我將strcpy的第1個參數改寫為“jiangyejiangye”會如何呢?利用OD打開OverrunTest_2.exe,來到一樣的位置,以下圖所示:
圖15
可以發現,由于我們所輸入的字符串太長,使得本來位于棧中0x0012FF80處的父函數EBP和本來位于棧中0x0012FF84處的返回地址全都被改寫了。這里我們主要關注位于0x0012FF84處的返回地址,原來它所保存的值為0x00401669,也就告知了程序,在履行完main函數后,需要履行該地址處的指令??墒乾F在那個棧中的內容被破壞了,變成了0x00006579,即當main函數履行終了后,程序會跳到地址為0x00006579處繼續履行。那末會產生甚么問題呢?我們無妨繼續履行看看:
圖16
到這里,main函數需要返回,可以看到它要返回到0x00006579的地址處,來履行該地址處的指令,我們再單步運行1下:
圖17
此時我們發現了兩件事,1件是OD中的反匯編代碼窗口是空的,說明0x00006579地址處不存在指令,或說它就是1個無效地址。第2件事是OD彈出了毛病對話框,提示我們該地址出錯,這與我們直接履行程序時所彈出的毛病對話框有幾分類似。
至此,大家應當已了解了緩沖區溢出漏洞的原理,它就是由于我們輸入了太長的字符,而緩沖區本身又沒有有效的驗證機制,致使太長的字符將返回地址覆蓋掉了,當我們的函數需要返回的時候,由于此時的返回地址是1個無效地址,因此致使程序出錯。
那末根據這個原理,假定我們所覆蓋的返回地址是1個有效地址,而在該地址處又包括著有效的指令,那末我們的系統就會絕不猶豫地跳到該地址處去履行指令。因此,如果想利用緩沖區溢出的漏洞,我們就能夠構造出1個有效地址出來,然后將我們想讓計算機履行的代碼寫入該地址,這樣1來,我們就通進程序的漏洞,讓計算機履行了我們自己編寫的程序。而具體的關于漏洞利用的知識,我會在下1節課中給大家詳細講授。