Win32 PE文件格式簡要總結

PE文件的內容分為不同的塊/節(jié)(Section),塊中包含代碼或數(shù)據,各個塊按頁邊界對齊,塊沒有大小限制,是一個連續(xù)結構。
PE文件不是作為一個整體被載入內存的而是分節(jié)加載的。PE裝載器不會對PE文件頭作特殊處理,但裝載各個塊的時候會根據塊的屬性做不同的處理:
1. 內存頁的屬性
對于磁盤來說,所有的頁都是按照磁盤映射文件函數(shù)指定的屬性設置的,但是在裝載可執(zhí)行文件的時候,與塊對應的內存頁屬性要按照塊的屬性來設置,所以,在同屬于一個模塊的內存頁中,從不同塊映射過來的內存頁的屬性是不同的。
2. 塊的偏移地址
塊的起始地址在磁盤文件中是按照IMAGE_OPTIONAL_HEADER32結構的 FileAlignment 字段的值進行對齊的,而當被加載道內存中時是按照同一結構中的SectionAlignment字段的值設置對齊的,兩者的值可能不同。所以一個塊表被裝載到內存后相對于文件頭的偏移地址和磁盤中的偏移地址可能是不同的。(塊表事實上就是相同屬性數(shù)據的組合,當塊表再入到內存中的時候,相同一個快比愛所對應的內存頁都將被賦予相同的頁屬性,事實上Windows 系統(tǒng)對內存屬性的設置是以頁為單位的進行的,塊表在內存的對齊的單位必須是一個頁的大小或者是一個頁的正整數(shù)倍)。在磁盤中沒有這個限制,因為磁盤中排放的是以空間為主導,在磁盤知識存放,不是使用,所以不用設置那么詳細的屬性。
3. 塊表的尺寸
主要有2個方面:
對齊:磁盤映像和內存映像中塊表對齊存儲單位的不同而導致了長度不同;
初始化:某些數(shù)據沒有初始化,那么沒有必要為其在磁盤中浪費空間資源,但是在內存中不同,程序一旦運行,之前沒有初始化的數(shù)據便有可能要被賦值初始化,那么就必須為他們留下空間。
4. 不進行映射的塊表
有些塊表并不需要映射到內存中,例如 .reloc 塊表,重定位數(shù)據對于文件的執(zhí)行代碼是透明的,它只是提供Windows裝載器使用執(zhí)行代碼根本不會去訪問他們,所以沒有必要將他們映射到物理內存中。

各種地址概念

虛擬地址(VA):
每個進程都有自己獨立的4G虛擬內存空間,對應的地址為VA
基址:
PE映象文件被裝入內存的地址,在windows中也就是hModule的值,同時也指向PE頭
相對虛擬地址 RVA(Relative Virtual Addresses):
PE文件可以被加載到進程地址空間的任何位置;RVA是相對于基址的偏移,即VA=基址+RVA
文件偏移地址:
和內存無關,指距離文件頭的偏移

重定位

1.每個PE文件可以定義一個默認的基址,但當它被裝載時該基址有可能已經被其他模塊占用
2.鏈接器生成PE文件時會假設指令中用到的地址都基于默認的基地址
3.假如該模塊被迫加載到非默認的基址上,此時就需要根據重定位表進行地址的修復
*.由于一般exe是第一個加載的,所以一般exe不需要重定位表,但不代表所有exe都沒有重定位表

常見section

.text:雖然叫text,但實際上它主要保存編譯后的源碼指令,是只讀字段
.data:保存數(shù)據,對應初始化后的非const的全局變量變量或者局部static變量
.rdata:保存只讀數(shù)據,對應C語言中的const變量
.bss:未初始化的非const全局變量和局部static變量
.reloc:重定位段。如果加載PE文件失敗,將基于此段進行重新調整
.rsrc:資源

PE頭

通過GetModuleHandle得到的HMODULE實際上就指向IMAGE_DOS_HEADER,也就是PE文件頭的特殊標記MZ。通過IMAGE_DOS_HEADER可以進一步獲取到IMAGE_FILE_HEADER,并依此訪問各個節(jié)的數(shù)據。相關代碼網上一搜就有,不過要注意32位與64位平臺的差異。

導入表, 導出表

導入表記錄一個exe/dll所用到的其他模塊導出的函數(shù);導出表則記錄了導出符號的信息;日常開發(fā)中常用的工具exescope/dumpbin/depends等就是從導入表/導出表中獲取的相應信息。
exe很少有導出表;大多數(shù)dll都有導出表;某些僅用于存放資源的dll可以沒有導出表;
可以根據PE結構解析出已經被加載到內存中的某個模塊的導出表的地址并對其進行修改以達到hook某個函數(shù)的目的,這就是IAT Hook。

應用

為什么要學習PE格式,學了這個可以做什么?列舉幾個例子:

  1. IAT hook
  2. chrome_elf
    chrome_elf模塊為chrome瀏覽器提供以下支持:初始化crashpad(dump抓取);攔截第三方模塊注入;
    為了確保抓取到某些早期邏輯導致的崩潰,也為了攔住在早期就注入到進程里的第三方模塊,chrome_elf的執(zhí)行非常早,早于一般程序的入口點winMain,也早于全局對象的初始化,它基于隱式鏈接動態(tài)庫的DllMain函數(shù)來實現(xiàn)。為了確保它在程序啟動的最早時機被執(zhí)行,編譯chrome.exe之后會由某個腳本修改chrome.exe的導入表,將chrome_elf.dll置于第一個,因為隱式加載dll的順序是按導入表的順序執(zhí)行的,這樣就可以確保chrome_elf.dll被第一個加載到進程里,并首先運行chrome_elf.dll里的DllMain。
  3. 注入exe
    這個主要為某些病毒程序所用,將其源碼插入到正常程序中,或者將其dll插入到exe導入表中,達到跟隨正常程序啟動的目的;
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容