淺談C#緩衝區溢位的祕密

iDotNetSpace發表於2010-12-28

開場白

各位朋友們,當你們看到網上傳播關於微軟windows、IE對黑客利用“緩衝區溢位”、0day漏洞攻擊的新聞,是否有過自己也想試試身手,可惜無從下手的感慨?本文將完全使用C#語言,探索那些不為人知的祕密。

1.本文講述在C#中利用堆疊緩衝區溢位動態修改記憶體,達到改變應用程式執行流程的目的。

2.如果你是高手,請指出本文的不足。

3.為了讓本文通俗易懂,程式碼將極盡精簡。

現在開始

我們知道,當陣列下標越界時,.NET會自動丟擲StackOverflowException,這樣便讓我們可以安全的讀寫記憶體,那麼我們有沒有逾越這個自動檢測的屏障,達到我們非常操作的目的呢?答案是有的,而且我們可以修改一些關鍵變數如if、switch的判斷值,for迴圈變數i值,甚至方法返回值,當然理論上還可以注入程式碼、轉移程式碼執行區塊,前提是必須在unsafe程式碼裡。

方法在被呼叫時,系統會進行以下幾項操作:將該方法入棧、引數入棧、返回地址入棧、控制程式碼區入棧(EIP入棧)。我們想要訪問方法的棧內地址,常規的託管程式碼是不行的,只能使用unsafe程式碼,但也並不是說你非要精通C/C++語言和指標操作,本文的例子都非常簡單,完全可以將指標就認為是簡版C#陣列。

改變臨時變數的值

先給出一段程式碼,然後再詳細解釋原理。

  1. static  unsafe void Main(string[] args)  
  2.         {  
  3.             //在棧上申請一個只能儲存一個int32的記憶體段  
  4.             int* p = stackalloc int[1];  
  5.  
  6.             for (var i = 0; i 
  7.             {  
  8.                 System.Threading.Thread.Sleep(200);  
  9.  
  10.                 Console.WriteLine("{0}\n", i);  
  11.                 p[i] = 0;  
  12.             }  
  13.               
  14.             Console.ReadLine();  
  15.         } 

這是一個既簡單,但是對於從沒有嘗試這樣寫過程式碼的開發者來說,又頗耐人尋味,C#(包括C/C++)不會去檢查指標p的偏移量是否越界,那麼這段程式碼將會順利編譯並執行,那麼for迴圈會順利執行30次嗎?還是......

呵呵..大家稍等...

結論是,這將是一個死迴圈,因為p不斷的遞增1偏移,並將附近的記憶體的值全改為0,而區域性變數i是靠p最近的變數,所有當p[i]的偏移地址等於i的地址時,程式碼p[i]=0就等價於i=0,實際上我在測試中i=6的時候i的值就被覆蓋為0了,我在程式碼中新增了Thread.Sleep(200)和Console.WriteLine("{0}\n", i)就是讓大家能更直觀的看到程式的執行過程,當然這裡也可以改為p[i]=1,p[i]=2等數字

搜尋記憶體值並修改

還是先給出程式碼

  1. static  unsafe void Main(string[] args)  
  2.         {  
  3.             Console.WriteLine(Change_Result());  
  4.             Console.ReadLine();  
  5.         }  
  6.  
  7.         static unsafe int Change_Result()  
  8.         {  
  9.               
  10.             int i = 0;  
  11.             //變數result,預設的返回值  
  12.             int result = 123;   
  13.             //申請一段棧記憶體,大小可隨意設定  
  14.             int* p = stackalloc int[1];  
  15. //從當前棧地址開始向下查詢與函式返回值相匹配的地址,一旦匹配則修改為10000  
  16.             while (true)  
  17.             {  
  18.                 if (p[++i] == 123)  
  19.                 {  
  20.                     p[i] = 10000;  
  21.                     break;  
  22.                 }  
  23.             };  
  24.             return result;  
  25.         } 

變數result作為方法的返回值預設為123,並且沒有任何顯式修改其值的程式碼,關鍵在這裡

  1. while (true)  
  2.  
  3.             {  
  4.                 if (p[++i] == 123)  
  5.                 {  
  6.                     p[i] = 10000;  
  7.                     break;  
  8.                 }  
  9.             } 

這段程式碼找到值為123的記憶體地址(也就可能是變數result的地址),然後將其值修改為10000,當然,函式返回值就肯定不會再是原先的123咯

這就是經典的StackOverFlow的兩個例子,希望通俗易懂能讓大家所接受,另外緩衝區溢位並不只是改變記憶體的值,在高手的手裡,他還可以執行任意程式碼,因為方法執行的時候總會有一個指標指向方法即將執行的下一條指令,如果控制了這個指標,就控制了程式。 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-682718/,如需轉載,請註明出處,否則將追究法律責任。

相關文章