1 本章總述
- 通過讓編譯器産生機器級程式的彙編表示, 學習了編譯器及其優化能力, 以及機器、資料類型和指令集;
- 學習了程式如何将資料存儲在不同的記憶體區域中 —— 程式開發人員需要知道一個變量是存儲在運作時棧中, 亦或是在某個動态配置設定的資料結構中, 還是全局程式資料的一部分;
- 程式是以指令序列來表示的, 每條指令都完成一個單獨的操作; 部分程式狀态, 比如寄存器和運作時棧, 對程式開發人員來說是直接可見的;
- 編譯器必須使用多條指令來産生和操作各種資料結構, 以及實作包括條件、循環和過程等在内的控制結構;
2 擴充内容
(1) C 語言中缺乏邊界檢查, 這就是的許多程式容易出現緩沖區溢出 —— 容易受到惡意入侵和攻擊.
(2) 編譯 C++ 和 C 的差別:
C++ 的早期實作隻是簡單地執行了從 C++ 源到 C 源的轉換, 并對結果運作 C 編譯器, 産生目标代碼;
C++ 的對象用結構來表示, 類似于 C 中的
struct
;
C++ 的方法使用指向實作方法的代碼的指針來表示的.
(3) Java 的編譯方式:
Java 的目标代碼是一種特殊的二進制表示 —— Java 位元組碼 —— 可以看作是虛拟機的機器級程式;
Java 用位元組碼作為程式的低級表示, 優勢是: 相同的代碼可以在許多不同的機器上執行, 隻需要在不同的機器上安裝 Java 虛拟機(JVM)即可;
Java 中還有一種稱為 即時編譯(Just-In-Time Compilation) 的方法, 動态地把位元組代碼序列翻譯成機器指令, 當代碼要執行多次時(比如在循環中), 這種方法執行起來更快.
需要注意的是: Java 虛拟機并不是直接用硬體實作的, 而是用軟體解釋器處理位元組碼, 模拟虛拟機的行為.
====== 華麗的分割線 - 第三章第三周作業 - ======
3 一些總結性的知識
生成彙編代碼,
gcc-S
反彙程式設計式, 生成可執行檔案後, 檔案變大 —— 因為包括了啟動、終止程式資訊, 以及作業系統互動資訊.
objdump -d
3.1 機器級程式設計二種重要的抽象
(1) 指令集體系結構, 定義了處理器狀态、指令的格式, 以及每條指令對狀态的影響.
(2) 機器級程式使用的存儲器位址是虛拟位址, 作業系統負責管理虛拟位址空間, 并将虛拟位址翻譯成實際處理器存儲器的實體位址.
3.2 IA32包含8個存儲32位值的寄存器
(1) %eax、%edx、%ecx稱為調用者儲存寄存器, 過程P調用Q, Q可以覆寫他們.
(2) %ebx、%esi、%edi稱為被調用者寄存器, Q須在覆寫前儲存, 并在傳回前恢複.
(3) 棧指針%esp始終儲存着棧頂元素的位址, 壓棧減小棧指針, 棧向下增長; 幀指針%ebp, 大多數資訊的通路都是相對于幀指針的.
3.3 MOV類中指令将源操作數複制到目的操作數
源操作數指定一個立即數, 存儲在寄存器或存儲器中。目的操作數指定一個位置,寄存器或存儲器位址。
(1) 有效位址: 立即數偏移Imm+基址寄存器R[Eb]+變址寄存器R[Ei]*比例因子s(1、2、4);
(2) 限制是兩個操作數不能同時指向存儲器位置;
(3) MOVS和MOVZ将較小的源資料複制到較大的資料位置, 高位符号擴充或零擴充.
4 棧幀結構
4.1 總體描述
IA32的程式使用堆棧支援過程的調用 (函數的調用), 在函數調用時會專門從堆棧中分出一塊記憶體 (稱為幀) 供函數使用.
傳遞給函數的參數由堆棧來儲存, 幀則負責存儲寄存器的狀态、局部變量的記憶體配置設定的相關任務.
如果說函數P調用函數Q, 那麼稱P為調用者 (caller), Q是被調用者 (callee).
根據上述規則, 堆棧會給Q配置設定幀, 并且用兩個指針 (分别存儲在%ebp%ebp和%esp%esp中) 訓示幀的開始和結束的位置.
4.2 其他特性
(1) 棧規則提供了一種機制, 每次函數調用都有它自己私有的狀态資訊 (儲存的傳回位置、棧指針和被調用者儲存寄存器的内容), 是以每次遞歸其實就是一個棧深度不斷增加的過程.
(2) 通路數組元素:
movl (%edx, %ecx, 4)
, %eax 通過首位址+偏移量得到實際位址通路;
(3) 讀存儲器比寫存儲器容易得多, 是以将隻讀變量溢出到存儲器是合适的 —— 寄存器數量有限, 是以才會溢出到存儲器.
(4) 資料對齊的優點:
(a) 對齊限制簡化了形成處理器和存儲器系統之間的硬體設計;
(b) 嚴格對齊後, 可用一個存儲器用作讀或寫;
(c) 對齊資料以提高系統的性能.
(5) 指針的強制轉換, 隻是改變在記憶體中的解讀方式. —— 很重要的一個了解.
5 對抗緩沖區溢出攻擊的幾種方式
(1) 棧随機化;
(2) 棧破壞檢測: 利用一個金絲雀值完成;
(3) 限制可執行代碼區域: 隻儲存編譯器産生的代碼的那部分存儲器是可執行的, 其他部分可以被限制為隻讀、隻寫.
未完待續...