PE速查

RVA的含義:相對虛擬地址 = 該數據的虛擬地址 - 映象的裝載基址的虛擬地址.

IMAGE_DOS_HEADER ;DOS頭:
00000000: DB e_magic   "MZ"
0000003C: DD e_lfanew ;PE頭基址,最後一個字段

IMAGE_NT_HEADERS ;PE頭:
00000000: DB Signature "PE",0,0

IMAGE_FILE_HEADER ;文件頭
00000004: DW Machine    ;運行平臺(1)
00000006: DW NumberOfSection   ;文件節數目
00000008: DD TimeDateStamp   ;創建文件的時間   (usually usless)
0000000c: DD PointerToSymbolTable ;指向符號表   (usless)
00000010: DD NumberOfSymbols   ;符號表中符號的數量 (usless)
00000014: DW SizeOfOptionalHeader ;可選頭大小,一般爲E0h
00000016: DW Characteristics   ;文件屬性(2)

IMAGE_OPTIONAL_HEADER32 ;可選頭(其實是必須的)
00000018: DW Magic    ;107h=ROM Image,10bh=exe Image
0000001a: DB MajorLinkerVersion ;連接器主版本號    (usless)
0000001b: DB MinorLinkerVersion ;連接器次版本號    (usless)
0000001c: DD SizeOfCode   ;所有含代碼的節的總大小   (usless)
00000020: DD SizeOfInitializedData ;所有含已初始化節的總大小 (usless)
00000024: DD SizeOfUninitializedData ;所有含未初始化節的總大小 (usless)
00000028: DD AddressOfEntryPoint ;程序執行入口RVA
0000002c: DD BaseOfCode   ;代碼節起始RVA    (usless)
00000030: DD BaseOfData   ;數據節起始RVA    (usless)
00000034: DD ImageBase   ;程序建議裝載地址(虛擬地址)
00000038: DD SectionAlignment ;內存中節對齊粒度,一般是4KB,64位系統是8KB
0000003c: DD FileAlignment   ;文件中節對齊粒度,一般是200h
00000040: DW MajorOperatingSystemVersion ;操作系統主版本號 (usless)
00000042: DW MinorOperatingSystemVersion ;操作系統次版本號 (usless)
00000044: DW MajorImageVersion ;可運行的操作系統的最小版本號,主版本號 (usless)
00000046: DW MinorImageVersion ;可運行的操作系統的最小版本號,次版本號 (usless)
00000048: DW MajorSubsystemVersion ;可運行的操作系統的最小子系統主版本號 (usless)
0000004a: DW MinorSubsystemVersion ;可運行的操作系統的最小子系統次版本號 (usless)
0000004c: DD Win32VersionValue ;未用,Win32VersionValue   (usless)
00000050: DD SizeOfImage   ;內存中整個PE映象的大小
00000054: DD SizeOfHeaders   ;所有頭+節表的大小
00000058: DD CheckSum   ;checksum,在可執行文件中爲0,系統DLL和驅動中爲校驗和,就是將文件的所有內容按單字大小帶進位累加,最後去掉進位加上文件大小.就是校驗和
0000005c: DW Subsystem   ;文件的子系統(3)
0000005e: DW DllCharacteristics
00000060: DD SizeOfStackReserve ;初始化時堆棧大小
00000064: DD SizeOfStackCommit ;初始化時實際提交的堆棧大小
00000068: DD SizeOfHeapReserve ;初始化時堆大小
0000006c: DD SizeOfHeapCommit ;初始化時實際提交的堆大小
00000070: DD LoaderFlags
00000074: DD NumberOfRvaAndSizes ;數據目錄的大小
00000078: QD DataDirectory   ;數據目錄,一般是16個目錄項,每個目錄項8字節(4)
000000f0: QD     ;最後一個目錄項

IMAGE_DATA_DIRECTORY ;數據目錄結構
00000000: DD VirtualAddress   ;數據起始RVA
00000004: DD isize    ;數據塊長度

IMAGE_SECTION_HEADER ;節表
00000000: DB 8 DUP (?) Name1    ;節名稱
00000008: DD   VirtualSize   ;節未對齊時的大小
0000000c: DD   VirtualAddress   ;節在內存中的RVA
00000010: DD   SizeOfRawData   ;節在文件中對齊後的大小
00000014: DD   PointerToRawData ;節在文件中的偏移
00000018: DD   PointerToRelocations ;usless
0000001c: DD   PointerToLinenumbers ;usless
00000020: DW   NumberOfRelocations ;usless
00000022: DW   NumberOfLinenumbers ;usless
00000024: DD   Characteristics   ;節屬性(5)

每個節被加載到內存後,節基址是按頁對齊的,因爲系統管理內存數據是以頁爲單位,而節保存在磁盤中時,節基址是以扇區爲對齊單位的,這是系統管理磁盤數據的普遍規定.
往往一個頁要大於一個扇區的大小,又有在內存中節基址偏移是相對於映象基址的,在磁盤中節基址的偏移是相對於文件基址的.
所以爲了避免當節被加載到內存時要進行對齊轉換,在節表中加入了該節被實際加載到內存後相對於映象基址的按頁對齊的偏移,同時保留該節在磁盤上相對於文件基址的按扇區對齊的偏移.
這樣既方便加載又節省磁盤空間.

雖然方便了加載,但是通過數據的RVA來得到數據在磁盤文件基址的偏移就比較麻煩.
方法是:
先通過該數據的RVA比較各節基址的RVA和各節的大小,看該數據是否落在某個節中,如果落在某個節中,則算出數據相對於節基址的偏移(數據的RVA-節基址RVA)
然後,因爲該數據相對於節基址的偏移在內存中和在文件中都是相同的,所以找到該數據所處的節在文件中的基址,
然後用算出的偏移加上該節在文件中的節基址,就是該數據在文件中的偏移.

節表緊靠在可選頭後面.

;導入表
00000000: DD   ;指向一個雙字的RVA,該雙字描述一個導入函數.若指向的雙字的最高位爲1,則指向的雙字的剩餘數據是該函數的導出編號,否則是一個RVA,這個RVA 指向一個結構,該結構最低字是一個編號,然後是一個以0結尾的字符串,該字符串是導入的函數名.
00000004: DD   ;創建時間,dump一下一般都是0?
00000008: DD   ;dump了一下都是0?
0000000c: DD   ;指向導入庫文件名稱字符串的RVA,該字符串以0結尾.
00000010: DD   ;指向一個雙字的RVA,當映象被加載後,該字段指向的雙字被加載器動態修改爲對應的導入函數首指令所在字節地址.當在程序中動態加載函數後,在調用的時候是通過call,最後轉到一個jmp dword ptr [xxxxxxxx]間接尋址的指令所在的位置,而這個間接地址xxxxxxxx=該RVA+映象基址.

導入表最後要有一個全0表項,指示導入表的結束,00000000指向的偏移處的結構也需要一個全0雙字表示結束.

;導出表
00000000: DD   ;全0
00000004: DD   ;文件產生時間
00000008: DD   ;全0
0000000c: DD   ;指向文件名字符串的RVA,該字符串以0結尾.
00000010: DD   ;導出函數的起始序號
00000014: DD   ;導出函數的總數
00000018: DD   ;以名稱導出的函數的總數
0000001c: DD   ;指向導出函數地址表的RVA,指向的是一個雙字表,該雙字表每個雙字保存函數入口RVA.個數由00000014字段指定
00000020: DD   ;指向導出函數名地址表的RVA,指向的是一個雙字表,該雙字表每個雙字保存函數名字符串.個數由00000018字段指定
00000024: DD   ;指向對照表的RVA,指向的是一個單字表,該單字表每個單字的索引對應導出函數名錶,每個單字的值對應導出函數表的索引.個數由00000018字段指定,該表將導出函數表和導出函數名錶對應起來.

函數的導出序號等於該函數所在的導出函數表索引加00000010字段.
索引都是從0記數.

;重定位表
重定位只是對直接尋址指令進行重定位,積存器尋址不需要.只有在映象基址和建議加載不符時才需要重定位,而實際加載地址是加載時動態確定的,所以重定位表只需保存需要重定位的數值地址.
重定位表是按頁分塊的,這樣不用保存高端地址,只保存頁偏移,當頁內有多個重定位地址時節省空間.
00000000: DD   ;頁起始RVA.
00000004: DD   ;重定位塊長度
通過重定位長度算出該頁內重定位個數,每個重定位項用一個字表示,該字的低12位指定頁內偏移,高4位含義如下:
0:該雙字地址不重定位
1:該雙字地址高16位重定位
2:該雙字地址低16位重定位
3:該雙字地址全部重定位
其他值含義不詳
最後以一個全0的8字節結束

;資源

;調試信息

註釋:
1.運行平臺(WORD):
0 未知平臺
14ch Intel 386
14dh Intel 486
14eh Intel 586
160h R3000 大尾方式
162h R3000 小尾方式
166h R4000 小尾方式
168h R10000 小尾方式
184h Dec Alpha AXP
1f0h IBM Power PC 小尾方式
284h Dec Alpha AXP64

2.文件屬性(WORD):
位數 含義
0 文件中不存在重定位信息
1 文件是可執行的
2 不存在行信息(?)
3 不存在符號信息
7 小尾方式
8 只在32位平臺運行
9 不包含調試信息
10 不能從可移動盤(如軟盤,光盤)運行
11 不能從網絡運行
12 系統文件,不能直接運行
13 這是一個DLL文件
14 文件不能在多處理器機上運行
15 大尾方式

3.使用界面的子系統(WORD):
0 未知子系統
1 不需要子系統(?)
2 Windows圖形界面
3 Windows控制檯界面
5 OS2控制檯界面
7 POSIX控制檯界面
8 不需要子系統(?)
9 WinCE圖形界面

4.數據目錄項的含義:(每個表項佔8字節,低雙字是一個實際數據的RVA,高雙字是實際數據的大小)
0 導出表
1 導入表
2 資源
3 異常(?)
4 安全(?)
5 重定位表
6 調試信息(?)
7 0
8 全局指針(?)
9 Thread Local Storage(?)
10 加載配置表(?)
11 綁定輸入表(?)
12 IAT,導入函數地址表
13 延遲加載的輸入表(?)
14 CLR運行時頭地址
15 0

5.節屬性(DWORD):
位 含義
5 (00000020h)節中包含代碼
6 (00000040h)節中包含已初始化的數據
7 (00000080h)節中包含未初始化的數據
25 (02000000h)節在初始化以後將被丟棄,如重定位節
26 (04000000h)節中的數據不會經過緩存
27 (08000000h)節中的數據不會交到磁盤
28 (10000000h)節中的數據將被不同進程共享
29 (20000000h)映射到內存中頁面包含可執行屬性
30 (40000000h)映射到內存中頁面包含可讀屬性
31 (80000000h)映射到內存中頁面包含可寫屬性

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