PE檔案結構學習筆記

來源:互聯網
上載者:User

PE檔案結構

作者:薑江
E-mail:jznsmail@163.net
Blog:http://blog.csdn.net/jznsmail/
QQ:457283

PE檔案布局


                                                             
               
PE表頭(PE Header)
    PE表頭包涵程式碼、資料地區大小、位置、適用的作業系統、堆棧初始大小等重要訊息。PE表頭並非在檔案的最開始。
  檔案最開始數百個單元是DOS stub:一個極小的DOS程式,用來輸出像"This Program cannot be run in DOS mode"這樣的資訊。當Win32載入程式把一個PE檔案對應到記憶體時,記憶體對應檔(Memory mapped file)的第一個位單元對應到DOS Stub的第一個位單元。在DOS Stub表頭中可以通過一個結構找到真正的PE表頭。
  pNTHeader = dosHeader + dosHeader->e_lfanew;
  e_lfanew是一個相對的位移量,指向真正的PE表頭。
  dosHeader是image的基址。
  注意:應為記憶體向上增長,所以加位移量而不是減。
  PE表頭是整個IMAGE_NT_HEADERS,這個結構有一個DWORD和兩個子結構:
  DWORD Signature;
  IMAGE_FILE_HEADER FileHeader;
  IMAGE_OPTIONAL_HEADER OptionalHeader;
  如果e_lfanew指向一個NE Signature而不是PE Signature表示一個Win16 NE可執行檔,如果是LE Signature表示一個VxD文檔。如果是LX Signatrue表OS/2文檔。
 
 
  IMAGE_FILE_HEADER結構如下:
  DWORD Machine;指示使用那種CPU,可以在Winnt.h中找到(我的標頭檔中定義如下)
                #define IMAGE_FILE_MACHINE_UNKNOWN           0
         #define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
         #define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
         #define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
         #define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
         #define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
         #define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
         #define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
         #define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
         #define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
         #define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
         #define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
         #define IMAGE_FILE_MACHINE_THUMB             0x01c2
         #define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64
         #define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS
         #define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS
         #define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS
         #define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64
         #define IMAGE_FILE_MACHINE_AXP64             IMAGE_FILE_MACHINE_ALPHA64
  WORD NumberOfSections;EXE的OBJ中的Sections個數
  DWORD TimeDateStamp;連接器產生此檔案的時間。自從1969年12月31日4:00P.M之後的總秒數。
  DWORD PointerToSymbolTable;COFF符號表的位移位置。只對COFF除錯有用。
  DWORD NumberOfSymbols;COFF符號表中的符號個數。
  DWORD SizeOfOptionalHeader;一個可有可無的表頭大小,在EXE檔案中,這也就是IMAGE_OPTIONAL_HEADER的大小。在OBJ檔案中大多數時候為0。
  WORD Characteristics;描述該檔案的性質。比較重要的性質如下:
             0x0001 檔案沒有重定位
             0x0002 檔案是可執行檔
             0x2000 檔案是動態串連庫   
             下面列出系統定義的所有性質:
             #define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
             #define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).
             #define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
             #define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
             #define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
             #define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
             #define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
             #define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
             #define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from file in .DBG file
             #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, copy and run from the swap file.
             #define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, copy and run from the swap file.
             #define IMAGE_FILE_SYSTEM                    0x1000  // System File.
             #define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
             #define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
             #define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.
  
  
  IMAGE_OPTINAL_HEADER結構   
  該結構是在IMAGE_FILE_HEADER之外的一些附加資訊。
  WORD Magic;定義image的狀態。如0x0107表示一個rom image,0x010B表示一個正常的exe image。
  BYTE MajorLinkerVersion
  BYTE MinorLinkerVersion PE檔案的連接器版本,以10進位表示。
  其它的資訊可以參看Winnt.h標頭檔
  
  Section Table

  內含image的每個sections資訊。section以起始位置排列而不是字母順序。section table的每個地區儲存了一個地址,檔案的原始資料被映射到記憶體。sections是一個個記憶體範圍。程式和作業系統所需要的任何code和data都有一個相應的section儲存。
  
  PE表頭最後是一個IMAGE_SECTION_HEADER結構數組,該數組元素個數紀錄在IMAGE_NT_HEADER.FileHeader.NumberOfSection中。
  
  IMAGE_SECTION_HEADER是EXE檔案或OBJ檔案SECTION的完整資訊。
  BYTE Name[IMAGE_SIZEOF_SHORT_NAME] 是8位的ANSI名稱(沒有NULL結束符),表示SECTION名稱(如.text)。
  union{
     DWORD PhysicalAddress;
     DWORD VirtualSize;
  }Misc;
  在EXE檔案中代表code section或data section的虛擬記憶體大小(未進行alignment)。
  對於OBJ檔案代表SECTION的實際地址。第一個SECTION從0開始。下一個SECTION起始地址為上一個SECTION地址加上SizeOfRawData值(進行調整後的section虛擬記憶體大小)。
  DWORD VirtualAddress;在EXE中代表載入程式將SECTION映射到的虛擬位址。SECTION的真正起始地址是該地址加上基址。這個地址常被編譯器設定成0x1000。
                       在OBJ檔案中沒有意義,總是0。
  DWORD SizeOfRawData; 在EXE檔案中代表section大小被對齊(alignment)後的值。
                       在OBJ中表示由編譯指定的真正SECTION大小。
   DWORD PointerToRawData;從檔案頭開始的位移量,Section的初始資訊可以從這個位置獲得。
   DWORD PointerToRelocations;在EXE中沒有意義,總為0。
                              在OBJ中這是從檔案頭開始的位移量來指向SECTION的重定位資訊。每個OBJ SECTION的重定位資訊緊跟在SECTION資訊後。
   DWORD PointerToLinenumbers;行號表的位移地址。在exe檔案中,行號資訊放在檔案的末尾。在obj檔案中行號表放在每個section的原始資料以及重定位表之後。                          
  WORD NumberofRelocations;重定位表中的重定位項目個數(PointerToRelocations指向)。只用於obj檔案。
  WORD NumberOfLinenumbers;行號表中行號個數(由PointerToLinenumbers指向)。
  DWORD Characteristics; 一組標誌,表示section中的屬性(如code或data可讀、可寫等)。可以參看winnt.h中IMAGE_SCN_XXX_XXX的定義。
  
  
  Sections
  .text section
  包涵所有一般性的程式碼。在.text中除了編譯器產生出來的代碼以及runtime library的代碼外還有一些東西。在PE檔案中,當你調用另一組模組中的函數(如USER32.DLL中的GetMessage),編譯器產生的CALL指令並沒有把控制權直接傳給DLL中的函數,而是傳給一個JMP DWORD PTR [XXXXXXXX]的指令,該指令也在.text中。JMP指令跳轉到.idata中的一個DWORD中。這個DWORD含有真正的函數入口地址。
  例如:

  對DLL的調用方式為什麼要通過這種方式?
  通過對同一個DLL函數的所有調用都集中到一處,載入程式就不需要修改每個調用DLL的指令,只需要把DLL函數的真真實位址放到.idata的那個DWORD中。這樣調用帶來一個缺點,那就是你不能以DLL函數的真正地址來初始化一個變數。
  例如:
     FARPROC pfnGetMessage = GetMessage;
     這個變數實際存放的JMP DWORD PTR [XXXXXXXX]指令地址,而不是實際需要調用的函數地址。
  對於用__declspec(dllimport)修飾的API函數,編譯器不產生JMP DWORD PTR [XXXXXXXX]指令,而是產生CALL DWORD PTR[XXXXXXXX]來調用位於.idata中的XXXXXXXX。
  
  
  .data section
  存放初始化資訊的地方。包括全域變數、字串常量和靜態變數,這些變數在編譯時間期就給定初值。連接器把obj和lib中的所有.data組合起來放到exe的.data中。而變數放在執行過程中的堆棧中。
  
  .bss section
  存放任何未初始化的靜態變數和全域變數。obj和lib中所有.bss組合起來放在exe檔案的.bss中。在section table中.bss的RawDataOffset總為0,表示這個section不佔用任何空間。
  
  .CRT section
  微軟C/C++ runtime library(CRT)所使用的另一個初始化的data section。這裡存放用於在main或WinMan之前執行的靜態C++類的建構函式。
  
  .rsrc section
  存放模組的資源。如.res檔案內容。
  
  .idata section
  包涵有關模組從其他DLLs輸入(import)函數和資料的相關資訊。
  
  .edata section
  存放PE檔案輸出函數的相關資訊。通常只在DLL中才看到.edata。
  
  .reloc section
  存放一個base relocation數組。base relocation是一組指令或者初始設定變數的調整值。如果載入程式沒有辦法把exe或者dll載入預設地址,就必須做這樣的調整。
  
  .tls section
  當使用__declspec(thread)時,定義的資訊並沒有放入.data或.bss,而是有一份拷貝放到.tls中。
  .tls的全稱是thread local storage。每個線程可以擁有自己的一組靜態資料,使用這些資料的程式碼,不需要是那個線程正在執行。假設某個程式有數個線程處理相同的工作。如果聲明一個stl如:
  __declspec(thread) int i = 0; //this is a global variable declaration
  每個線程將因此擁有變數i的一個副本。
  
  
  .rdata section
  至少有4個用途:
                 1.在使用微軟串連程式產生的EXE中,.rdata含有debug directory(obj中不含)。
                 2.如果在程式的.DEF檔案中指定DESCRIPTION,被指定的字串就會出現在.rdata中。
                 3.GUID值都放在EXE或DLL的.rdata中
                 4.放置TLS(Thread Local Storage)的directory。被編譯器的runtime library使用。
  
  
  .drectve section
  只出現在obj檔案中,內含連接器命令參數的文字描述。
  
  
PE檔案的輸入

  在被載入記憶體之前,存放在PE檔案的.idata中的資訊是給載入程式來決定函數地址並且修正它們,以便完成image用的。而在被載入之後,idata內涵的是指標,指向EXE/DLL的輸入函數。
  .idata section(import table)以一個IMAGE_IMPORT_DESCRIPTIOR數組開始,被PE檔案串連的DLL都會在此有一個對應的IMAGE_IMPORT_DESCRIPTOR結構。
   

PE檔案的輸出
  
   PE檔案的輸出函數相關資訊存放在.edata。.edata一開始是一個IMAGE_EXPORT_DIRECTORY結構。   
  
  
PE文檔的基址重定位

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.