跳轉至內容

逆向工程/棧溢位

來自Wikibooks,開放世界中的開放書籍

我們經常聽到惡意程式碼導致一個非常模糊的問題,稱為棧溢位。本頁將討論什麼是棧溢位以及如何防止它。

什麼是棧溢位

[編輯 | 編輯原始碼]

基於棧的溢位攻擊是指向緩衝區寫入過多資訊以覆蓋返回地址並劫持控制流的行為。覆蓋的返回地址在大多數情況下會指向程式地址空間中的某個函式。此函式可能已在應用程式中定義,或者駭客可以透過將程式碼注入到棧中輕鬆定義它。

如果我們記得關於棧的那一章,我們知道當我們進入一個新函式時關於棧的一些基本事實

  1. 棧“向下”增長。
  2. 區域性資料被推送到棧頂。
  3. bp的舊值儲存在區域性資料下方
  4. 返回地址儲存在舊bp值下方

考慮以下有錯誤的C程式碼片段

void MyFunction(void)
{
   int a[100];
   int i;
   for(i = 0; i <= 100; i++)
   {
      a[i] = 0;
   }
   ...

當i達到100時會發生什麼?如前所述,我們知道區域性陣列是在棧上建立的。如果我們嘗試寫入“a”的上界以上,我們將覆蓋棧上的先前值:a[100]覆蓋bp,a[101]覆蓋返回地址。

程式流程隨後將重定向到我們放置的新地址。這是一個棧溢位漏洞,它源於程式設計師在向陣列寫入資料之前沒有檢查陣列邊界的錯誤程式設計。

發現漏洞

[編輯 | 編輯原始碼]

逆向工程師如何發現棧溢位漏洞?讓我們看一些示例ASM程式碼

push ebp
mov ebp, esp
sub esp, 100

這是一個標準的入口序列,我們可以看到此函式在棧上分配了100位元組的資料。要麼是25個整數值得資料,要麼是某種陣列。我們檢查函式的其餘部分,並檢視它是什麼型別的資料

call _gets
push eax
push esp
call _strcpy
...

顯然,我們正在訪問棧上的資料作為陣列,特別是字元陣列。上面的彙編程式碼片段從控制檯獲取文字字串,並將該資料複製到棧上的區域性變數中。

不幸的是,我們正在使用的標準C庫字串函式存在一個眾所周知的漏洞:它們不檢查輸入引數的邊界。事實上,<string.h>函式很少甚至要求程式設計師提供陣列的大小或最大可用記憶體大小!

一些最常見的棧漏洞源於此事實。需要注意的違規者是strcpystrcatsprintf,這些函式的輸出字串引數可能大於提供的緩衝區以容納它們。

區域性變數只有100個字元(1個字元=1個位元組)寬。如果我們輸入一個100個字元長的字串會發生什麼?請記住,ASCIIZ字串以空字元(00h)結尾,這需要陣列中的一個額外槽。這意味著第101個字元將為空位元組,並且ebp的儲存值將丟失。現在想象一下,如果我們輸入104個字元甚至108個字元(足以覆蓋返回地址)會發生什麼。輸入正確值的攻擊者可以將程式執行重定向到可能幫助接管計算機的惡意函式。

進一步閱讀

[編輯 | 編輯原始碼]

"為了樂趣和利益而粉碎棧",Aleph One,Phrack,7(49),1996年11月。

華夏公益教科書