小甲魚PE詳解之區塊表(節表)和區塊(節)(PE詳解04)

PE文件到內存的映射


在執行一個PE文件的時候,windows 並不在一開始就將整個文件讀入內存的,而是採用與內存映射文件類似的機制。也就是說,windows 裝載器在裝載的時候僅僅建立好虛擬地址和PE文件之間的映射關係。
當且僅當真正執行到某個內存頁中的指令或者訪問某一頁中的數據時,這個頁面纔會被從磁盤提交到物理內存,這種機制使文件裝入的速度和文件大小沒有太大的關係。


但是要注意的是,系統裝載可執行文件的方法又不完全等同於內存映射文件。
當使用內存映射文件的時候,系統對“原著”相當忠實,如果將磁盤文件和內存映像比較的話,可以發現不管是數據本身還是數據之間的相對位置它丫丫的都是完全相同的。
而我們知道,在裝載可執行文件的時候,有些數據在裝入前會被預處理,如重定位等,正因此,裝入以後,數據之間的相對位置可能發生微妙的變化。


Windows 裝載器在裝載DOS部分、PE文件頭部分和節表(區塊表)部分是不進行任何特殊處理的,而在裝載節(區塊)的時候則會自動按節(區塊)的屬性做不同的處理。
一般情況下,它會處理以下幾個方面的內容:

  • 內存頁的屬性;
  • 節的偏移地址;
  • 節的尺寸;
  • 不進行映射的節。


內存頁的屬性:

對於磁盤映射文件來說,所有的頁都是按照磁盤映射文件函數指定的屬性設置的。但是在裝載可執行文件時,與節對應的內存頁屬性要按照節的屬性來設置。所以,在同屬於一個模塊的內存頁中,從不同節映射過來的的內存頁的屬性是不同的。


節的偏移地址:

節的起始地址在磁盤文件中是按照 IMAGE_OPTIONAL_HEADER32 結構的 FileAlignment 字段的值進行對齊的,而當被加載到內存中時是按照同一結構中的 SectionAlignment 字段的值對其的,兩者的值可能不同,所以一個節被裝入內存後相對於文件頭的偏移和在磁盤文件中的偏移可能是不同的。


注意,節事實上就是相同屬性數據的組合!當節被裝入到內存中的時候,相同一個節所對應的內存頁都將被賦予相同的頁屬性, 事實上,Windows 系統對內存屬性的設置是以頁爲單位進行的,所以節在內存中的對齊單位必須至少是一個頁的大小。(小甲魚溫馨提示:對於32位操作系統來說,這個值一般是4KB==1000H; 對於64位操作系統這個值一般是8KB==2000H)


在磁盤中就沒有這個**,因爲在磁盤中排放是以什麼爲主?肯定是以空間爲主導,在磁盤只是存放,不是使用,所以不用設置那麼詳細的屬性。試想想看,如果在磁盤中都是以4KB爲大小對齊的話,不夠就用0來填充,那麼一個只佔20字節的數據就要消耗4KB的空間來存放,是不是浪費?有木有??


節的尺寸:

對節的尺寸的處理主要分爲兩個方面:

第一個方面,正如剛剛我們所講的,由於磁盤映像和內存映像中節對齊存儲單位的不同而導致了長度擴展不同(填充的0數量不同嘛~);

第二個方面,是對於包含未初始化數據的節的處理問題。既然是未初始化,那麼沒有必要爲其在磁盤中浪費空間資源,但在內存中不同,因爲程序一運行,之前未初始化的數據便有可能要被賦值初始化,那麼就必須爲他們留下空間。


不進行映射的節:

有些節並不需要被映射到內存中,例如.reloc節,重定位數據對於文件的執行代碼來說是透明的,無作用的,它只是提供Windows 裝載器使用,執行代碼根本不會去訪問到它們,所以沒有必要將他們映射到物理內存中。


好了,上邊的一些知識相信又是對我們之前學習的一點補充和擴展。大家可能對上邊的知識覺得又是眼熟,但又覺得有幾分陌生。那是當然哈,小甲魚教學遵循的思路就是今天的知識今天學好它,明天的東西明天再學!一下子你肯定不能接受那麼深的東西,像上邊的東西如果放在第一講來講解,那麼恐怕很多朋友不會繼續往下看(一頭霧水看下去只能兩頭霧水,哈~),所以咱學習要遵循循環漸進,有些重點分開來重複講解,雖然重複,但每次都會往下加深一點來讓大家容易接受哈。


我們可以繼續了,接下來是節表,也稱爲區塊表:



節表(區塊表):


PE文件中所有節的屬性都被定義在節表中,節表由一系列的IMAGE_SECTION_HEADER結構排列而成,每個結構用來描述一個節,結構的排列順序和它們描述的節在文件中的排列順序是一致的。全部有效結構的最後以一個空的IMAGE_SECTION_HEADER結構作爲結束,所以節表中總的IMAGE_SECTION_HEADER結構數量等於節的數量加一。節表總是被存放在緊接在PE文件頭的地方。

另外,節表中 IMAGE_SECTION_HEADER 結構的總數總是由PE文件頭 IMAGE_NT_HEADERS 結構中的 FileHeader.NumberOfSections 字段來指定的。


typedef struct _IMAGE_SECTION_HEADER 

{

        BYTE Name[IMAGE_SIZEOF_SHORT_NAME];     // 節表名稱,如“.text” 

        //IMAGE_SIZEOF_SHORT_NAME=8

        union
         {

                DWORD PhysicalAddress;        // 物理地址

                DWORD VirtualSize;                // 真實長度,這兩個值是一個聯合結構,可以使用其中的任何一個,一

                                                              // 般是取後一個

        } Misc;

        DWORD VirtualAddress;              // 節區的 RVA 地址

        DWORD SizeOfRawData;            // 在文件中對齊後的尺寸

        DWORD PointerToRawData;        // 在文件中的偏移量

        DWORD PointerToRelocations;     // 在OBJ文件中使用,重定位的偏移

        DWORD PointerToLinenumbers;   // 行號表的偏移(供調試使用地)

        WORD NumberOfRelocations;      // 在OBJ文件中使用,重定位項數目

        WORD NumberOfLinenumbers;    // 行號表中行號的數目

        DWORD Characteristics;              // 節屬性如可讀,可寫,可執行等} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 

0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章