天天看點

《windows核心程式設計》–Windows記憶體體結構(二)

13.6頁面保護屬性

一些惡意軟體将代碼寫入到用于資料的記憶體區域(比如線程棧上),通過這種方式讓應用程式執行惡意代碼。windows資料執行保護特性提供了對此類惡意攻擊的防護。如果啟用了DEP,那麼隻有對那些真正需要執行的代碼的記憶體區域,作業系統才page_execute_*保護屬性。其它保護屬性(最常 見的就是PAGE_READWRITE)用于隻應該存放資料的記憶體區域。

13.6.1 寫時複制(PAGE_WRITECOPY PAGE_EXECUTE_WRITECOPY)

Windows支援一種機制,允許兩個以上的質監共享同一塊存儲器。是以,如果有10個記事本程式正在運作,所有程序會共享應用程式的代碼頁和資料頁。讓所有的應用程式執行個體共享相同的存儲頁極大的提升了系統的性能。但另一方面,這要求應用程式隻能讀取其中的資料和代碼。如果一個程式修改并寫入一個存儲頁,那麼這等于修改了其它執行個體正在使用的存儲頁,最終将導緻混亂。 為了避免此類錯誤發生,作業系統會給共享存儲頁指定寫時複制屬性。當系統把一個exe或dll遇到 一個位址空間時,系統會計算有多少頁是可以寫的。 當線程試圖寫入一個共享頁面時,系統會介入并執行以下操作 1. 系統在記憶體中找一個閑置頁面。注意,該閑置頁面的後備頁面來自頁交換檔案,它是系統最初将子產品映射到程序位址空間時配置設定的。由于 系統第一次進行映射的時候配置設定了所有可能需要的頁交換檔案空間,這一步不可能失敗。 2.系統把線程想要修改的頁面内容複制到第1步中找到的閑置頁面。系統會給該閑置頁面指定PAGE_READWRITE或PAGE_EXECUTE_READWRITE保護屬性,系統不會對原始頁面的保護屬性和資料做任何修改。 3. 然後系統更新程序頁面表,這樣一來,原來的虛拟位址現在就對應到記憶體中的一個新的頁面了

13.6.2  一些特殊的通路保護屬性标志

使用這些标志時,隻需将它們與除PAGE_NOACCESS之外的任何其它保護屬性進行按位 或 操作就可以 了 PAGE_NOCACHE:禁止對已調撥頁面進行緩存。不建議将該标志用于除驅動程式以外的程式 PAGE_WRITECOMBINE:也是給驅動開發人員用的。它允許把單個裝置的多次寫操作組合在一起。 PAGE_GUARD:使應用程式能夠在頁面中任何一個位元組被寫入時得到通知。

13.7 執行個體分析

以應用程式VMPMap.exe來測試
《windows核心程式設計》–Windows記憶體體結構(二)
以上列出了一個位址空間映射的執行個體

左邊第一列:基位址 

從0x00000000區開始,到可用位址空間最後一個區域為止,最後一個區域的起始位址為0x7FFFe0000。所有區域都是連續的。讀者可能還會注意到所有非閑置區域 的基位址都是64KB的整數倍。這是由系統位址空間配置設定粒度來決定的。如果一個區域的基位址不是64KB的整數倍,這意味着該區域是由作業系統以程序的名義配置設定的。

第二列 區域類型: 

閑置:區域的虛拟位址沒有任何後備存儲器。該位址空間尚未預訂,應用程式即可以從基位址開始預訂區域,也可以從閑置區域的任何地方開始預訂區域 

私有:區域虛拟位址以系統的頁交換檔案為後備存儲器 

映象:區域的虛拟位址一開始以映象檔案(比如.exe和.dll)為後備存儲器,但此後不一定以心映象檔案為後備存儲器。例如:如果程式寫入一個映象檔案中的一個全局變量,那麼寫時複制機制會改用頁交換檔案來作為後備存儲器。 

已映射:區域虛拟位址一開始以記憶體映射檔案為後備存儲器,但此後不一定以記憶體映射檔案為後備存儲器。例如:記憶體映射檔案可能會使用寫時複制保護屬性。任何編寫操作會使對應的頁面改用頁交換檔案為後備存儲器。

第三列 預訂位元組數 

此列始終是CPU頁面大小的整數倍。為了節省磁盤空間,連結器會盡可能的對所生成的PE檔案進行壓縮。但是當windows将PE檔案映射到程序虛拟位址空間時,每 一段必須另起一頁,而且起始位址必須是系統頁面大小的整數倍。這意味着PE檔案所需的虛拟位址空間的大小一般比PE 檔案本身的大小

第四列 所預訂區域内塊的數量 

塊是一些連續的頁面,這些頁面具有相同的保護屬性,并且以相同的實體存儲器為後備存儲器

第五列 區域保護屬性 

E = execute R = read W = write

第六列 描述

13.8 資料對齊的重要性

當通路已對齊的資料時,CPU的執行效率才最高。把資料的位址模除資料的大小,如果結果為0,那麼資料就是對齊的。例如:一個WORD值的起始位址應該能被2整除,一個DWORD的起始位址應該能被4整除,以此類推。 下面的代碼通路了一個錯位的資料 VOID SomeFunc(PVOID pvDataBuffer)  {      //the first byte in the buffer is some byte of information      char c = * (PBYTE) pvDataBuffer;      //increase past the first byte in the buffer      pvDataBuffer = (PVOID) ((PBYTE)pvDataBuffer + 1);      //bytes 2 - 5 contain a double-word value      DWORD dw =  * (DWORD * ) pvDataBuffer;      //the line above raise a data misalignment exception on some CPUS  } CPU處理資料對齊的方式 : x86:  x86 CPU的EFLAGE寄存器内有一個AC标志。預設為0。如果為0,那麼CPU會自動執行必要的操作來通路必要的資料,否則CPU就會觸發INT 17H中斷 。由于  x86  CPU從來不改變這個标志,是以應用程式在X86 運作時從來不會發生資料錯位異常。當應用程式在AMD x86-64處理器上運作時,會有相同的結果。這是因為在預設情況下CPU處理了資料錯位的錯誤。 IA-64:  IA-64 CPU不能自動處理資料錯位的錯誤。當任何代碼要通路錯位資料時,CPU會通知作業系統。Windows然後決定到底是應該抛出資料錯位異常,還是應該沒有任何提示的執行額外指令來修正錯誤并讓代碼繼續執行。在IA-64作業系統裡,window會自動将将資料錯位錯誤轉化成一個EXCEPTION_DATATYPE_MISALIGNMENT異常。但是我們可以通過SetErrorMode來改變這種行為。注意改變這個标志會影響到程序所有的線程,另外值得注意的一點是這個标志會被子程序繼承。IA-64版本的vc++ 編譯器支援一個__unaligned關鍵字。下面的代碼是前面的代碼經過修改代碼的版本。新版使用了__unaligned關鍵字      DWORD dw =  * (__unaligned DWORD * ) pvDataBuffer; 

繼續閱讀