天天看點

棧溢出筆記1.2 覆寫EIP

1.1節中我們說到可以利用棧溢出來破壞棧中原有的内容,這一節中,我們就來看看如何争奪到傳回位址(EIP),使得我們可以随意控制它的值,這樣我們就可以控制程式。來看一個經典的程式:

棧溢出筆記1.2 覆寫EIP

這個程式的get_print函數中定義了一個大小為11個位元組的數組,正常情況下我們的輸入應該最多為10個字元(還有一個\0結束符),而gets函數沒有明确定義輸入的大小,是以,我們可以輸入超過10個字元,進而造成棧溢出。如下,輸入10個‘A’,一切正常:

棧溢出筆記1.2 覆寫EIP

圖8

當我輸入11個‘A’時,雖然順利列印出來11個‘A’,但是VS2008報了如下錯:

棧溢出筆記1.2 覆寫EIP

圖9

運作時錯誤檢查檢測到棧崩潰,這是Windows為抵抗棧溢出漏洞利用采用的措施。目前我們還不知道如何繞過,先去掉它,在工程屬性中,“C/C++”——“代碼生成”的“基本運作時檢查”選擇為預設值,然後重新編譯。

棧溢出筆記1.2 覆寫EIP

圖10

修改後,輸入11個‘A’貌似是沒什麼問題了,但是輸入12個‘A’的時候,又出來一個這樣的對話框:

棧溢出筆記1.2 覆寫EIP

圖11

緩沖區溢出被檢查到了,這當然不是一個好消息。作為經典的漏洞,Windows自然有多種對付招式,這又是一種叫做棧Cookie的保護方式,可以檢查到棧溢出。同樣,先去掉它,在工程屬性中,“C/C++”——“代碼生成”的“緩沖區安全檢查”選擇為否(GS-, 見圖10),然後重新編譯。這一次,我們輸入一大串‘A’:

棧溢出筆記1.2 覆寫EIP

圖12

什麼?VS2008又出現了彈窗?别緊張,這次是好消息。

棧溢出筆記1.2 覆寫EIP

圖13

看到熟悉的0xC0000005,表明是通路了不該通路的位址。同時,0x41414141不就是“AAAA”嗎?這說明我們輸入的“AAAA”已經以某種方式被程式使用了,這果斷是好消息。

下面,用Immunity Debugger來看看究竟發生了什麼。先找到函數get_print()的代碼,在 MOV EBP, ESP語句上下斷點:

棧溢出筆記1.2 覆寫EIP

圖14

然後運作到這裡,檢視棧内容:

棧溢出筆記1.2 覆寫EIP

圖15

回想1.1中的内容,get_print沒有參數,是以0012FF14(目前ESP)處為儲存的EBP,0012FF18(EBP+4)為傳回位址(重要)。

下面兩句配置設定棧幀的代碼對棧的影響較大:

/*********************************************************/
MOV EBP, ESP
SUB ESP, 4C
/*********************************************************/
           

配置設定了4C(76位元組)大小的空間,是以,下面這段棧空間(get_print)是我們關注的内容:

棧溢出筆記1.2 覆寫EIP

圖16

現在,定位到gets函數,我們關注的不是它的調用過程,而是參數,它位于棧上EBP-C的位置,是以,它緊鄰儲存的EBP。也就是說,這個緩沖區下面是儲存的EBP,再下面是儲存的傳回位址。這很重要,它決定我們需要輸入多少内容才能準确地改寫儲存的傳回位址(EIP)。

我們在gets函數之後設斷點,并輸入以下内容(16個A和4個B):

棧溢出筆記1.2 覆寫EIP

圖17

此時,檢視棧内容:

棧溢出筆記1.2 覆寫EIP

圖18

看到了嗎?儲存的EBP被‘AAAA’覆寫,儲存的傳回位址(EBP+4)被BBBB覆寫。因為,我們知道局部變量的準确位置,是以,可以準确的知道需要多少位元組來覆寫傳回位址的内容。

這樣,我們就從程式手中争奪了EIP,get_print()傳回時,它将跳轉到0x42424242處執行,由于該位址不可通路,是以會出現0xC0000005錯誤。後面,我們将給EIP寫入有意義的位址,進而執行我們自己的内容。

繼續閱讀