22 1 月 2013

Buffer Overflow 教學(一)

作者:Ben哥|發布日期:2010/07/08

What is a Stack Stack

是一個抽象性的資料類型,普遍的用於電腦科學的領域中;放置於 Stack 中的物件有一個特性,最後一個被放進去的物件就是第一個要移開的物件,像是我們在一個桶子裡面放東西,最先放進去的將是最後被拿出來的;這種特性通常被稱為 Last In, First Out – LIFO。

Stack 定義了好幾種的操作模式;兩個最重要的手續為:PUSH 及 POP,PUSH 把物件放進 Stack 中,POP 則把最上面的物件移開。

Why do we use a stack

現代的電腦在設計的時候就已經考慮到高階電腦語言的使用了;建構高階電腦語言最重要的技巧就是提供 procedure 或是 function,從一個角度來看,一個 procedure呼叫就如 jump 一般改變了控制的流程(flow of control),但是不像 jump 的地方是:當 procedure 呼叫結束他的工作後,一個 function 把控制權還給statement 或是接下來的 instruction,這種高階的抽象性的運作方式必須藉助 stack 的幫忙才能得以實行。

The stack region

一個 stack 就是一個在記憶體內,連續性並相連的資料;一個名為 SP(stack pointer)的 register 指到 stack 最頂端位址,stack 的最底端是一個固定的位址;stack 的大小是由作業系統的核心動態的調整;而 CPU 則負責 PUSH 及 POP 的運作。

stack 是由一些邏輯的 stack frame 所組成,當呼叫一個 function 就會 push stack frame,當執行結果回傳後就 poped;一個 stack frame 包含了:

• 一個 function 的參數
• 他的 local 的變數
• 復原前一個 stack frame 所需要的資料
• 當 function call 的時候那些 instruction pointers 的值

根據 implementation,stack 會變大或變小,在這個範例中,將會使用 stack 變小的方式來示範,這種方式普遍的使用在 Intel, Motorola, SPARC 及 MIPS 的中央處理器上,Stack pointer(SP)也倚賴 implementation 而有所不一樣,他可能會指向 stack 的最後一個位址,或是在 stack 後下一個可用的位址;我們將以指向 stack 的最後一個位址為討論的方向。

stack pointer(SP)指向 stack 的最頂端(最低的數字定址),除了 SP 以外,如果有一個 frame pointer(FP)在 frame 中指向一個固定的位置將會讓操作比較方便,有些文件會把 FP 也講成 local base pointer(LB);理論上來說,local variable 可以經由對 SP 的 offset 值被參照到。然而,當 words 從 stack 被 pushed 或 poped,這些 offset 會一直改變,雖然有些 compiler 比較聰明,會紀錄有多少個word在stack內,因此可以正確的更正 offset,但是有些卻不會;不過在所有的觀念裡,一定程度的人為管理還是必需的,更進一步來說,在某些機器上,如 Intel-based 的中央處理器,從 SP 的一個已知距離存取一個變數也需要數個 instructions 來達成。

因為如此,很多 compiler 使用了第二種 register, FP,因為從 FP 到 local variable 及參數(parameter)的距離不會因為 PUSHes 及 POPes 而改變,在 Intel 的 CPU 上,BP(EBP)正是為了該目的而被使用;在 Motorola 的 CPUs 上,除了 A7(the stack pointer)任何定址的 register 都可被使用;因為我們所討論 stack 變大變小的方式,實際上的參數會有正的 offset,而 local variable 則會有負的 offset from FP.

當一個 procedure 被呼叫後要做的第一件事情就是儲存上一個 FP(所以當 procedure 結束後可被復原),接下來 SP 會被複製到 FP 建立一個新的 FP 並且讓 SP 保留一些空間給 localvariable 使用,這些程序稱之為 procedure prolog;當 procedure 離開後,stack 必須再次被清理,稱為 procedure epilog;Intel 的 ENTER 及 LEAVE instructions 及 Motorola 的 LINK 及 UNLINK instructions 做了大部分的 procedure prolog 及 epilog 並讓他們更有效率。