可移植可執行
此條目沒有列出任何參考或來源。 (2017年5月11日) |
副檔名 |
.acm, .ax, .cpl, .dll, .drv, .efi, .exe, .mui, .ocx, .scr, .sys, .tsp |
---|---|
網路媒體型式 |
application/vnd.microsoft.portable-executable |
開發者 | Microsoft |
格式類型 | 二進制可執行文件、目標代碼、函式庫 |
延伸自 | DOS MZ可執行文件 COFF |
可移植性可執行文件(英語:Portable Executable,縮寫為PE)是一種用於可執行文件、目標文件和動態連結庫的文件格式,主要使用在32位和64位的Windows作業系統上。「可移植的」是指該文件格式的通用性,可用於許多種不同的作業系統和體系結構中。PE文件格式封裝了Windows作業系統加載可執行程序代碼時所必需的一些信息。這些信息包括動態連結庫、API導入和導出表、資源管理數據和線程局部存儲數據。在Windows NT作業系統中,PE文件格式主要用於EXE文件、DLL文件、.sys(驅動程序)和其他文件類型。可擴展固件接口(EFI)技術規範書中說明PE格式是EFI環境中的標準可執行文件格式。開頭為DOS頭部。
PE格式是由Unix中的COFF格式修改而來的。在Windows開發環境中,PE格式也稱為PE/COFF格式。
在Windows NT作業系統中,PE格式目前支持IA-32、IA-64和x86-64(AMD64/Intel64)的指令系統。在Windows 2000之前,Windows NT還支持MIPS、Alpha和PowerPC的指令系統。由於Windows CE也在使用PE文件格式,因此PE仍然支持幾種不同型號的MIPS、ARM(包括Thumb)和SuperH指令系統。
PE文件格式的主要競爭對手是可執行與可連結格式(ELF)(使用於Linux和大多數Unix版本中)和Mach-O(使用於Mac OS X中)。
佈局結構
[編輯]文件頭部
[編輯]MS-DOS頭與MS-DOS stub
[編輯]MS-DOS頭和MS-DOS Stub只存在於映像文件中。在MS-DOS下運行該應用程式,默認的Stub會打印出消息"This program cannot be run in DOS mode"。 MS-DOS頭的偏移位置0x3C處包括指向PE簽名的文件指針,用於定位PE的開始位置。
COFF file header
[編輯]PE簽名是一個4位元組的項:字符P和E,隨後是2個空字節。
在Winnt.h中定義的標準COFF頭的結構_IMAGE_FILE_HEADER
optional header
[編輯]Object文件不含這部分,所以稱為「可選頭」。
在Winnt.h中定義的optional header的結構_IMAGE_OPTIONAL_HEADER
Standard COFF Fields
[編輯]Windows Specific Fields
[編輯]Data Directories
[編輯]- Export Table: .edata Section
- Import Table: .idata Section.
- Resource Table: .rsrc Section.
- Exception Table: .pdata Section.
- Certificate Table: 指向Attribute Certificate Table(由用於文件驗證的屬性證書組成的表). 屬性證書不會作為映像文件的一部分加載到內存。同樣,這個地址項的第一個字段是文件指針而不是RVA。屬性證書表的每個項都包括了一個4位元組的文件指針,指向各自的屬性證書,並具有4位元組的大小。
- Base Relocation Table: .reloc Section
- Debug: .debug Section. 調試數據輸出到PDB文件中,因此這個Data directory要麼全都是0,或者只指向一個類型為2(IMAGE_DEBUG_TYPE_CODEVIEW)、30個字節的調試目錄項,而這個項又指向一個包括PDB文件路徑在內的、CodeReview風格的頭。
- Architecture: 保留,必須為0
- Global Ptr: 在全局指針寄存器中存儲的RVA值。該結構的大小必須設置為0。如果目標架構沒有使用全局指針的概念,這個數據目錄就全都設置為0(例如I386或AMD64)。
- TLS Table: .tls Section.
- Load Config Table: Load Configuration Structure. 特定於Window NT家族作業系統的數據(例如,GlobalFlag值)。
- Bound Import: 由綁定導入描述符組成的數組,其中的每個描述符都描述了一個DLL。在創建映像的時候,該映像與DLL綁定在一起。描述符中還攜帶這些綁定的時間戳,如果這些綁定是最新的,那麼作業系統加載程序就會使用這些綁定以作為API導入的"快捷方式"。否則,加載程序就會忽視這些綁定並通過導入表解析這些導入API。
- IAT: Import Address Table. 這個表(IAT)會在導出目錄表(第1個數據目錄)中被引用。
- Delay Import Descriptor: Delay-Load Import Tables. 包括一個由32位ImgDelayDescr結構體組成的數組,每個結構體都描述了延遲加載的導入。延遲加載(delay load)的導入是這樣一些DLL,它們被描述為隱式的導入,而作為顯式的導入進行加載(通過對LoadLibrary這個API的調用)。動態庫的延遲加載是根據需要--在第一次調用這樣一個DLL的時候執行的。這與隱式的導入不同,後者在導入的可執行體初始化的時候就立即被加載。
- CLR Runtime Header: .cormeta Section (Object Only).
- 保留: 必須全為0
節
[編輯]節(section)是PE文件中存儲數據的劃分。通常,加載到內存後,同一節的數據具有相同的內存訪問屬性(可讀/可寫/可執行等)。
節表緊隨文件頭部。由於沒有文件頭具有直接指向節表的指針,所以節表的位置被計算為文件頭的大小再加上1。
COFF頭的NumberOfSections字段,定義了節表中節的數量。在節頭表中,節的索引是基於1,並且節的順序是由連結器確定。節按照節表中定義的順序連續存放,起始RVA根據PE頭的SectionAlignment字段值進行對齊。
節頭是一個定義在Winnt.h中的40位元組的結構IMAGE_SECTION_HEADER:
- Name: 8位元組的ASCII字符串。表示節的名稱。節名稱開始於一個點號(例如,.reloc)。如果節名稱正好包含8個字符,就會省略null終結符。如果節名稱小於8個字符,就會使用null字符來填充數組Name。映像文件不能使用超過8個字符的節名稱。然而,在對象文件中,節名稱可以更長一些, 在這種情況下,名稱被放置在字符串表中,並且字段的第一個字節中包括了字符"/",隨後是一個ASCII字符串,包含有字符串表中相應偏移量的十進制表示。
- VirtualSize: 4位元組無符號整型的Union。在映像文件中,這個字段保存了節中的代碼或數據裝入內存的實際(未對齊的)字節大小。如果改制大於本節的SizeOfRawData, 本節由0填充. 對於object文件該節為0.
- VirtualAddress: 4位元組無符號整型。在映像文件中,為本節裝入內存後相對於image base的偏移.
- SizeOfRawData: 4位元組無符號整型。在映像文件中,這個字段保存了磁盤上需要初始化的數據的字節大小,向上捨入為FileAlignment的倍數。如果SizeOfRawData小於VirtualSize,那麼裝入內存時使用0來填充節的剩餘部分。對於不需要初始化的數據,這個字段的值為0.
- PointerToRawData: 4位元組無符號整型。保存了指向節的第一頁的文件指針。在映像文件中,向上捨入為FileAlignment的倍數。 對於不需要初始化的數據,這個字段的值為0.
- PointerToRelocations: 4位元組無符號整型。這是一個文件指針,指向了節的重定位項的起始位置。在映像文件中,不使用這個字段,應該設置為0。
- PointerToLinenumbers: 4位元組無符號整型。這個字段保存了一個文件指針,指向節的行號項的起始位置。在映像文件中, 該字段已經過時了,應設置為0
- NumberOfRelocations: 2位元組無符號整型。節的重定位項的數量. 在映像文件中,設置為0。
- NumberOfLinenumbers: 2位元組無符號整型. 節的行號條目的數量. 在映像文件中, 該字段已經過時了,應設置為0
- Characteristics: 4位元組無符號整型。這個字段指定了映像文件的特徵,並保存了這些二進制標誌的位或運算值。
- .text: 只讀的節,包括了CLR頭、元數據、IL代碼、託管異常處理信息以及資源。
- .sdata: 可讀寫的節,與GP相關的已初始化數據
- .reloc: 只讀的節,基址重定位表包含了鏡像中所有需要重定位的內容。NT頭中的數據目錄中的Base Relocation Table(基址重定位表)域給出了基址重定位表所佔的字節數。基址重定位表被劃分成許多塊,每一塊表示一個4K頁面範圍內的基址重定位信息,它必須從32位邊界開始。
- .rsrc: 只讀的節,包括了非託管的資源目錄。
- .tls: 可讀寫的節,包括了TLS數據。
- .bss(Block Start with Symbol):未初始化全局變量
- .textbss:未初始化的可執行代碼節。也即這個節具有可執行屬性,在PE文件中未實際佔用硬盤文件空間,在加載到內存時未填充數據。這用於支持Visual Studio在Debug模式下動態編譯代碼功能,也即「Edit and Continue」功能。例如,一個函數在Visual Studio中設斷點或單步調試,這時該函數在.text節中;修改原始碼後繼續執行這個函數,Visual Studio會重新編譯這個函數並把它加載到.textbss節中的未利用地址空間(原為padding的部分),並修改對這個函數調用的跳轉(jmp)表條目以及當前EIP寄存器值。
- .data 代碼節
- .edata 導出表
- .idata 導入表
- .idlsym 包含已註冊的SEH,它們用以支持IDL屬性
- .pdata 64位程序的異常處理器的地址表 NT頭中的Exception Table(異常表)域指向它。
- .rdata 只讀的已初始化數據(用於常量)
- .sbss 與GP相關的未初始化數據
- .srdata 與GP相關的只讀數據
- .text 默認代碼節
符號表
[編輯]COFF File Header中的字段PointerToSymbolTable給出了符號表地址,字段NumberOfSymbols給出了符號表條目數量。對於映象文件,COFF調試信息是過時的,因此該字段為空。
typedef struct {
union {
char e_name[E_SYMNMLEN];
struct {
unsigned long e_zeroes;
unsigned long e_offset;
} e;
} e;
unsigned long e_value;
short e_scnum;
unsigned short e_type;
unsigned char e_sclass;
unsigned char e_numaux;
} SYMENT;
符號表包含了所有符號與元符號的條目。
- e.e_name - 內聯的符號名字(小於等於8位元組)。
- e.e.e_zeroes - flag,用於判斷是內聯符號名字還是在字符串表中的符號名字
- e.e.e_offset - 字符串表中的符號名字的偏移值
- e_value - 符號的值。如表示函數的符號,值為函數的地址。還有相對於%ebp的變量地址、寄存器變量的寄存器號、結構成員相對偏移值、枚舉成員值、struct/union/enum的尺寸
- e_scnum - 符號所屬的節的編號。節表中的節從1開始編號。節號0表示未定義(外部)符號;-1表示絕對符號(e_value是個常量而非地址);-2表示調試符號。
- e_type - 符號類型。由基類型與派生類型組成,如「指針到整型」。
- e_sclass - 存儲類。C_FCN,值101,".bf"或".ef" - 函數的開始/結束。C_FILE,值103,表示函數名。
- e_numaux - 輔助條目(18個字節長)的數量(通常為0或1)。符號表中的符號允許緊隨其後有額外的輔助條目。
字符串表
[編輯]字符串表保存長度大於8的符號。字符串表緊隨符號表。首先讀出4個字節,為符號表的字節長度。隨後的4個字節總為0。到符號表的引用地址總是從這4個0位元組開始。示例代碼如下:
int i;
char *s;
read(fd, &i, 4);
s = (char *)malloc(i);
memset(s, 0, 4);
read(fd, s+4, i-4);
行號
[編輯]typedef struct {
union {
unsigned long l_symndx; /* function name symbol index */
unsigned long l_paddr; /* address of line number */
} l_addr;
unsigned short l_lnno; /* line number */
} LINENO;
每個可執行的節有自己的行號表. 節中的每個函數獨立編號,函數的首行(有左花括號的行)編號為1. 每個函數在行號表中有一個條目, 其l_lnno為0, l_symndx為符號表中該函數. 其後是該函數每一行的條目, l_lnno甚至為該函數內的行號(1..N), l_paddr設置為該行的第一條匯編代碼的地址.
為得到絕對行號, 需要在符號表中找到該函數的"beginning of function" symbol (類型C_FCN)為該函數的絕對行號,然後加上函數內的相對行號.
參見
[編輯]外部連結
[編輯]- Microsoft Portable Executable and Common Object File Format Specification(頁面存檔備份,存於互聯網檔案館) (latest edition, OOXML format)
- Microsoft Portable Executable and Common Object File Format Specification (1999 edition, .doc format)
- The original Portable Executable article (頁面存檔備份,存於互聯網檔案館) by Matt Pietrek (MSDN Magazine, March 1994)
- Part I. An In-Depth Look into the Win32 Portable Executable File Format(頁面存檔備份,存於互聯網檔案館) by Matt Pietrek (MSDN Magazine, February 2002)
- Part II. An In-Depth Look into the Win32 Portable Executable File Format by Matt Pietrek (MSDN Magazine, March 2002)
- The .NET File Format by Daniel PistelliArchive.is的存檔,存檔日期2013-01-30
- Ero Carrera's blog describing the PE header and how to walk through(頁面存檔備份,存於互聯網檔案館)
- PE Internals provides an easy way to learn the Portable Executable File Format (頁面存檔備份,存於互聯網檔案館)