《網絡滲透技術》學習筆記(1)--Windows平臺上一個最簡單溢出程序的調試 zz

http://blog.sina.com.cn/s/blog_492101c7010002st.html

例子的C程序是這樣的(寫簡單的C/c++程序我喜歡用CFree,因爲這個工具簡單易用,並且可以隨時補全已定義過的或者C裏面自帶的標識符,比如變量、函數名等)

//simple_overflow.c
#include <stdio.h>
#include <string.h>

char largebuff[]="1234512345123456====ABCD";

int main(){

char smallbuff[16];
strcpy(smallbuff,largebuff);

}

第一眼看上去,往一個較小的字符數組寫入一個較長的字符串,用的又恰恰是strcpy,而strcpy不會進行越界檢查,於是肯定會引起問題,的確,此程序一運行就會崩潰。這個問題也就是我們經常聽到的“緩衝區溢出”。

如果你裝了VC,那麼你可以直接用cl編譯該程序,而不必爲此也去動用你的VC。
cl simple_overflow.c
結果就是生成了simple_overflow.exe(當然還有simple_overflow.obj,這是中間結果,我們不必關心)
ok,運行一下試試,程序崩潰了,我們在程序中沒有告訴windows如何處理這種異常,於是windows用自己默認的辦法來處理,其結果就是你看到了一個提示你運行程序出錯,是否要發送錯誤的窗口(XP下)。

說到這不得不再扯一扯。今天看了《異形星球》,講的是地球上的科學家向達爾文四號星球發射了探測器,並且探測器已經相當成功地滿足了科學家們的好奇心。其中一個科學家的一段話給我印象深刻,大意是“有人會說,'你們發佈這個探測器,有什麼用處呢?能給你帶來吃的還是喝的?'。地球這個事情目前來看沒太多實際意義,但是人是需要有好奇心的,如果沒有,就不能稱之爲人了。”。同樣,如果沒有搞溢出的前輩當初的那種好奇心,那種鑽研精神,我們到今天也只能是習以爲常地看着程序崩潰了。是不是說的有點牽強?不管了,轉入正題。

下面我們用OllyDbg跟蹤一下程序是怎麼崩潰的。
OllyDbg打開我們的simple_overflow.exe。CPU窗口當前所停位置我目前還不清楚是什麼地方,我只知道我們需要的main函數的彙編代碼在CPU窗口的最頂部。於是,我們就在最頂部看到了下面這段彙編代碼(OllyDbg給出的main函數對應的彙編代碼):

00401000 /$ 55 PUSH EBP
00401001 |. 8BEC MOV EBP,ESP
00401003 |. 83EC 0C SUB ESP,10
00401006 |. 68 30504000 PUSH simple_o.00405030 ; ASCII "1234512345123456====ABCD"
0040100B |. 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-10]
0040100E |. 50 PUSH EAX
0040100F |. E8 0C000000 CALL simple_o.00401020
00401014 |. 83C4 08 ADD ESP,8
00401017 |. 8BE5 MOV ESP,EBP
00401019 |. 5D POP EBP
0040101A \. C3 RETN

其中第一列是代碼地址,第二列是代碼機器碼,第三大列是彙編代碼,;後是OllyDbg自動加的註釋。這段代碼比較簡單,下面我們分析一下:


PUSH EBP

;在main函數被調用前夕,主調函數就把main的返回地址(其實也就是old eip)push到棧中。那麼進入main後,main所做的第一件事情就是把主調函數的棧底指針保存起來,其實也是存到了棧中。這樣的話,old ebp跟old eip是僅挨着的,並且均是4字節,old ebp在old eip之上(這在下面的程序運行時棧組成示例圖中可以看出)。


MOV EBP,ESP

;將當前棧頂作爲自己的棧頂。因此上面old ebp實際上是保存到了main的主調函數的棧中。下面對棧的操作纔是針對main的棧(下稱main棧)的。


SUB ESP,10

;main棧擴展領域。windows中棧的生長方向是高址向低址。即如果你看到對ESP的減操作,則說明是在擴展棧,相反,若是加操作,則是收縮棧。同樣PUSH和POP分別隱含了對ESP的減和加操作。
;這裏我原來就有一個疑惑。一方面,我記得看過的文章中說函數局部變量是分配在棧中的,另一方面我印象中對棧的操作只有PUSH和POP,而此二操作均可自動調整ESP,因此類似的操作到底是怎麼回事呢?通過這個例子我想明白了,這裏的棧其實跟我們平時數據結構中所看到的棧有所不同,這裏的棧更靈活。棧中一開始其實是局部變量的空間,這部分空間過後才屬於“真正”棧元素的空間。
;由此我們還可以想到另外一個事情。我們經常可以在彙編程序中看到用[EBP+?]來訪問一個函數參數,而用[EBP-?]來訪問一個自己的局部變量。爲什麼呢?對於後者,從我們上面的分析自然可以理解。而一個函數被調用前主調函數實際已經將其參數PUSH到了自己的棧中,而這些參數剛好在被調函數EBP下面不遠處。
;具體地說,這裏是給smallbuff分配的空間。這裏的10是16進制的,因此是10進制的16,也就是說給smallbuff分配了16字節。由於VC編譯器是4字節對齊,因此,如果定義smallbuff大小不是16,而是13,那麼編譯器一樣給它分配16字節,而定義smallbuff大小是11,那麼編譯器會給它分配12字節,也就是說此處就應該是SUB ESP,0C了。

;===以上三條語句也是函數開始處常見的經典組合。


PUSH simple_o.00405030

;通過OllyDbg給出的提示,我們已經清楚,這裏是把largebuff(也就是那個全局字符串的地址)作爲參數PUSH到棧中


LEA EAX,DWORD PTR SS:[EBP-10]

;看到了吧,這裏剛好就是把smallbuff(也就是那個局部字符數組的地址,4個字節)放到了EAX,要幹什麼呢?往下看


PUSH EAX

;o,又一個PUSH,

;如此一來,不就剛好把strcpy的參數按照從右向左的順序入棧了嘛,看來是要進行函數調用了。


CALL simple_o.00401020

;的確,緊接着就是一個CALL,不必說,這個CALL肯定就是調用strcpy了
;strcpy會做些什麼呢?很簡單,它就是把以其第二個參數爲始址的一個字符串老老實實地COPY到以其第一個參數爲始址的空間。
;其第一個參數不就是剛纔我們用SUB ESP,10給分配的空間麼?而這個空間下面緊挨着的不就是我們的old ebp,old eip麼?這下好了,你應該知道strcpy後發生了什麼事情了。
;我們的largebuff共16+4+4=24個字節。前面16個剛好放我們爲smallbuff分配的空間中,而接下來的4個=就覆蓋了我們的old ebp,再接下來的4個字節ABCD就剛好覆蓋了我們的old eip,也就是main函數的返回地址,不多不少。


ADD ESP,8

;我們剛不是爲了給strcpy傳遞參數進行了兩個PUSH麼?那好,現在strcpy已經完成,參數可以丟掉了。當然此處也可以執行兩次POP操作。此操作執行結束後ESP應該是指向smallbuff的。


MOV ESP,EBP

;剛進入函數的時候我們保存了主調函數的棧底,並把主調函數的棧頂做了我們的棧底,現在main函數已經執行完成,該恢復主調函數的棧了。
;EBP的數據是保存在寄存器中的,因此無聊如何溢出也不會被覆蓋的,因此執行此語句後,ESP正確指向函數開始處MOV EBP,ESP執行前夕ESP的值。此時剛執行了PUSH EBP,因此MOV ESP,EBP後,ESP也就指向了old ebp。


POP EBP

;既然現在ESP也就指向old ebp,那麼我們POP EBP應該是非常正確的,可惜的是old ebp已經被溢出的數據覆蓋,它已經面目全非了。


RETN

;從main返回。本條語句實際做了什麼呢?它其實就是從當前棧頂彈出自己的返回地址到eip,然後開始從eip中的地址處繼續執行。
;而這個返回地址也就是old eip已經被我們的溢出數據覆蓋了。就本程序來說,eip的值已經變成了44434241(也就是ABCD的ASCII碼倒過來,因爲x86是little-endian的,也就是低字節存在地址,高字節存在高址)如果這個地址在本程序的地址空間內程序還不會立即崩潰,但邏輯已經變了。但如果這個地址不在本程序地址空間內,那麼當CPU按照新的eip中的地址去取指令的時候必將引發一個內存訪問越界異常。

;===以上四條語句也是函數結束處常見的經典組合。

羅嗦了半天,還是畫個圖清楚一些。

內存低址

|           ...                 |
.
.
+------------------------+ <----  esp
|smallbuff的地址(4B)  |
+------------------------+
|largebuff的地址(4B)  |
+------------------------+
|smallbuff的空間(16B)|
+------------------------+ <----- ebp
|old ebp(4B)             |
+------------------------+
|old eip(4B)              |
+------------------------+
.
.
|           ...                 |


內存高址


我們可以畫出更一般的棧結構:

內存走向
------------>
...[local1][local2]...[localn][old ebp][old eip][parm1][parm2]...[parmn] ... 
|              | 
esp            ebp


程序執行中“顛峯時刻”棧的構成情況(不考慮strcpy的執行情況)


發佈了7 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章