Windows安全之PE結構(三) 之 區塊,區塊的對齊值,RVA和物理內存地址的轉揍



區塊:
------------------------------------------------------------------------------------------------
一般PE文件 至少有兩個section(區塊/節)

.text段 - 預設的區塊代碼,它的內容全是指令代碼,linker會將所有目標文件的.text塊連接成一個大的
.text區塊,如果使用borland c++ 這個段叫.code
------------------------------------------------------------------------------------------------

.data段 - 預設的讀/寫數據區塊,全局變量,靜態變量一般都放在這裡

------------------------------------------------------------------------------------------------

.rdata段 - 預設只讀數據區塊,至少有兩個情況要用到.rdata , 一是在Microsoft的連接器產生的EXE文件中, 用於存放調試目錄; 二是用於存放說明字符串

------------------------------------------------------------------------------------------------

.idata段 - 預設包含其他外來DLL的函數及數據信息,一般俗稱輸入表(Import Address Table , IAT) ,

將idata區塊合拼到另一個區塊已成慣例,一般會與.rdata段合拼.edata段 - 輸出表啦這個就是, (Export Address Table , EAT) , 當創建一個輸出api或者數據的可執行文件時,linker 會創建一個.EXP文件,這個.EXP文件就包含了一個.edata區塊, 其會被加入到最後的可執行文件中, 與.idata區塊類似, .edata段經常被合拼到到.text / .tdata 區塊中

------------------------------------------------------------------------------------------------

.reloc - 可執行文件的基址重定位, 基址重定位一般是DLL文件才需要的, 在release模式, linker並不

------------------------------------------------------------------------------------------------

給EXE文件加上基址重定位, 重定位可以在鏈接時侯通過/fixed開關


在C/C++語言中 我們可以透過以下語法聲明一個區塊/節, 編譯器(complier) 就會給文件做一個預處理.
#pragam data_msg("Kelvin_data")


#是宏定義的意思,什麼是宏? 簡單來說就是編譯的時侯(在整個文件翻譯之前,所謂預處理),由編譯器直接進行翻譯,或許說按照指定的格式機械轉換

以上語句是令編譯器將數據都放進一個叫Kelvin_data的區塊內, 而不是預設的.data區塊, 區塊一般是

從.obj文件開始, 被編譯器設置的,linker的工作就是合拼左右的obj和庫中需要的塊,使其成為一個最終

合適的區塊,linker會有一套規則,判斷哪些區塊將被合拼及如何合拼

合拼區塊:
鏈接器的一個有趣特徵是能夠合拼區塊, 如果兩個區塊有相似/一致性的屬性,那麼它們在鏈接時,就能合

拼成一個單一的區塊,這取決於是否開啟編譯器的 /merge 開關, 事實上合拼區塊有一個好處就是可以節

省存放空間,注意: 我們不應將.rsrc/ .reloc / .pdata 合拼


區塊的對齊值:
------------------------------------------------------------------------------------------------------------------------
之前已經說了, 為了符合內存分頁機制, 我們就需要按照CPU本來的分頁對齊值去分虛擬內存

PE文件頭裡的FileAligment(可參考前面筆記),定義了磁盤區塊的對齊值, 每一個區塊從對齊值的倍數的

偏移位置開始存放,而區塊的實際代碼或數據大小不一定剛好能對齊,所以多餘的地方以00H來填充了 這就是區塊間的間隙了

例如在PE文件中,一個典型的對齊值為200H , 十進制為512, 這樣每個區塊都將從200h的倍數的文件偏移位置開始,假設第一個區塊在400h處,大小為90h ,

那麼從400h至490h為這一塊區塊的內容,而由於文件對齊值是200h,所以為了使這一塊區塊長度為FileAlignment的整數倍,490h到600h這一個區間都會被00h所填充.下一個區塊的開始地址為600h 很容易理解吧? 就是說每個區塊大小都是有頁面的倍數(不同的是SectionAlignment與FileAlignment設置的)

PE文件頭裡邊的SectionAlignment 定義了內存中的區塊對齊值, PE文件被映射到內存時,區塊總是至少

從一頁邊界開始.

一般在x86系列的cpu中,頁是4kb(1000h)來排列,在IA-64上,是按8kb(2000h)來排列的,所以在x86系統,pe

文件區塊的內存對齊值一般對於1000h, 每個區塊按1000h的倍數在內存中

RVA和文件偏移的轉換
-------------------------------------------------------------------------------------------------------------------------
在前邊我們探討過RVA這個詞, 但對於初次接觸PE文件的朋友來說, 顯得尤其陌生.

RVA是相對虛擬地址, (Relatvie Virtual Address), 這個是一個相對地址

PE文件中的各種數據結構中涉及地址的字段大部分都是以RVA表示的

更準確來說, RVA是當PE文件被加載到內存後, 某個數據位置相對於文件頭的偏移量。例如,如果Windows

加載器將一個PE文件加載到0x40000000H(這個0x40000000H位置是虛擬地址)的內存中 , 而某個區塊中的

某個數據被裝入至0x0004xxxxh,那麼這個數據的RVA就是0040xxxxh - 40000000h = xxxxh , 反過來說,

將RVA加上文件被裝載的基地址,就可以找到數據在內存中的實際地址.
在內存和磁盤中的偏移地址(RVA)可以一樣,可以不一樣,是在自己設置的.

可是DOS文件頭,PE文件頭,區塊表的偏移位置與大小址沒有變化,而各個區映射內存後其偏移位置就發生了變化.

這裡做一下補充,為什麼需要轉換呢?
DataDirectory數組中不是已經代表了文件偏移量了嗎?
答案是:不是 因為dataDirectory 中存放的是RVA 是在指內存中的位置 而不是磁盤中

Comments

Popular posts from this blog

Android Kernel Development - Kernel compilation and Hello World

How does Nested-Virtualization works?

Understanding ACPI and Device Tree