歡迎光臨
每天分享高質量文章

CPU阿甘之緩衝區上限溢位

來自:碼農翻身(微信號:coderising)

我是大家的老朋友CPU阿甘, 每天你一開機,我就忙得不亦樂乎,從記憶體中讀取一條條的指令,挨個執行。

最早的時候我認為程式都是順序執行的,後來發現並不是這樣,經常會出現一條跳轉指令,讓我到另外一個記憶體地址處去下一條指令去執行。

時間久了我就明白這是人類代碼中的if … else ,或者for ,while等迴圈導致的。

這樣跳來跳去,讓我覺得有點頭暈,不過沒有辦法,這是人類做出的規定。

後來我發現,有些指令經常會出現重覆,尤其是下麵這幾個:

pushl    %ebp

movl     %esp %ebp

call  xxxx

ret

正當我疑惑的時候,記憶體炫耀地說:這些指令是為了函式呼叫,建立棧幀所所必需的啊。

“函式呼叫?這是什麼鬼?”

“函式呼叫你都不知道? 我告訴你吧,現在的計算機語言,甭管你是面向物件還是函式式、動態還是靜態、解釋還是編譯,只要想在我們馮諾依曼體系結構下運行,最終都得變成順序、迴圈、分支,以及函式呼叫!”

記憶體說著給我舉了一個例子:

這個例子非常簡單,一看就明白。

“但是棧幀是什麼?”

“阿甘你知道棧是什麼意思吧?”

“不就是一個先進後出的資料結構嗎?”

“對,通俗來說:一個棧幀就是這個棧中的一個元素,表示了一個函式在運行時的結構。” 記憶體繼續給我科普:

“你這種畫法好古怪,怎麼倒過來了,棧底在上方,棧頂反而在下方!”

“這也是人類規定的,一個行程的虛擬記憶體中有個區域,就是棧,這個棧就是從高地址向低地址發展的啊。”

“奧,原來我執行的代碼在一個叫做代碼區的地方存放著啊,執行的時候會操作你的棧,對不對?”

“沒錯,我再給你看看那個棧幀的內部結構吧!”

這張圖看起來很複雜,但是和代碼一對應,還是比較清楚的。

我心中模擬了一下這個執行過程,hello()函式正在被執行,當要呼叫add函式的時候,需要準備引數,即x = 10, y=20 。

還要記錄下傳回地址,即printf(….)這個指令在記憶體的地址。當add函式呼叫完成以後,就可以傳回到這裡執行了。

真正開始執行add函式的時候,也需要給它建立一個棧幀(其中要記錄下上個函式棧幀的開始地址),還有這個函式的引數,在棧幀也會分配記憶體空間,例如sum, buf等。

等到執行結束,add函式的棧幀就廢棄了(相當於從棧中彈出),找到傳回地址,繼續執行printf指令。

hello函式執行完畢,也會廢棄掉,回到上一個函式的棧幀,繼續執行,如此持續下去….

我對記憶體說:“明白了,我已經迫不及待地想執行一下這個函式,看看效果了。”

記憶體說:“真的明白了?正好,操作系統老大已經發出指令,讓我們運行了,開始吧!”

建立hello函式的棧幀,呼叫add函式,建立add棧幀,執行add函式的代碼, 一切都很順利。

add函式中呼叫了scanf ,要求用戶輸入一些資料,人類是超級慢的,我耐心等待。

用戶輸入了8個字符A,我把他們都放到了buf所在的記憶體中:

但是人類還在輸入,接下里是一些很奇怪的資料,其長度遠遠超過了char buf[8]中的8個位元組。

可是我還得把資料給放到記憶體中啊,於是函式棧幀就變成了這個樣子。

(註:用戶輸入的資料是從低地址向高地址存放的。)

我覺得特別古怪的是,這個傳回地址也被衝掉了,被改寫了。

這個用戶到底要乾啥?

add函式執行完畢,要傳回到hello函式了, 我明明知道傳回地址已經被改掉, 可是我沒有選擇,還得把那個新的(用戶輸入的)傳回地址給取出來, 老老實實地去那個地址取出下一條指令去執行。

完了,這根本就不是原來的prinf函式,而是一段惡意代碼的入口!

與此同時….

黑客三兄弟中的老三大叫: 大哥二哥,我的這次緩衝區上限溢位攻擊實驗成功了!

“不錯啊,你是怎麼搞的?” 老大問道。

“正如二哥說的,那個scanf函式沒有邊界檢查,我成功地把代碼註入到了棧幀中,並且修改了傳回地址!於是程式就跳到我指定的地方執行了。”


●本文編號428,以後想閱讀這篇文章直接輸入428即可

●輸入m獲取到文章目錄

推薦↓↓↓

Python編程

更多推薦18個技術類公眾微信

涵蓋:程式人生、演算法與資料結構、黑客技術與網絡安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

赞(0)

分享創造快樂