羽夏殼世界—— PE 結構(上)

寂靜的羽夏發表於2022-04-10

寫在前面

  此係列是本人一個字一個字碼出來的,包括程式碼實現和效果截圖。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏殼世界——序 ,方便學習本教程。

概述

  在學習PE結構之前,我們來大體看一下它的整體結構:

羽夏殼世界—— PE 結構(上)

  初步學習PE檔案,可能看的比較頭大。如果你學習過程式語言的話,可以這麼說:PE 檔案是由一堆結構體和其他資料堆積起來的 。在學習本教程時,建議安裝010 Editor可以更方便的學習PE結構,不過該軟體是商用的。使用該軟體主要是用了它的模板功能,如果是指定型別的檔案,它也會主動匹配型別並提示使用相應的模板,如下所示是使用模板之後的效果:

羽夏殼世界—— PE 結構(上)

  PE檔案有兩種狀態,一個是在磁碟以檔案的形式進行儲存,另一個就是在記憶體中,如下圖所示:

羽夏殼世界—— PE 結構(上)

  為什麼這麼說呢?是因為有對齊這個概念,什麼是對齊如果不會建議自己進行查閱,這個無論是程式語言還是作業系統基礎中非常重要的概念,我就不在這裡絮叨了。對於為什麼有兩種狀態,之後的博文將會介紹。
  在PE結構中,有一個十分重要的概念就是RVAFOARVA英文全稱為Relative Virtual Address,即相對虛擬地址;FOA英文全稱為File Offset Address,即檔案偏移地址。就是因為PE有兩種狀態,所以會有這兩種偏移地址。當然還有一個VA,英文全稱為Virtual Address,意為絕對的虛擬地址,也就是絕對值,是幾就是幾。而RVA需要基址才能準確定位到VA。有關RVAFOAVA之間的相互轉化,將會到下一篇繼續。

IMAGE_DOS_HEADER

  該結構體的結構如下:

typedef struct _IMAGE_DOS_HEADER {   // DOS .EXE header
    WORD   e_magic;                  // Magic number
    WORD   e_cblp;                   // Bytes on last page of file
    WORD   e_cp;                     // Pages in file
    WORD   e_crlc;                   // Relocations
    WORD   e_cparhdr;                // Size of header in paragraphs
    WORD   e_minalloc;               // Minimum extra paragraphs needed
    WORD   e_maxalloc;               // Maximum extra paragraphs needed
    WORD   e_ss;                     // Initial (relative) SS value
    WORD   e_sp;                     // Initial SP value
    WORD   e_csum;                   // Checksum
    WORD   e_ip;                     // Initial IP value
    WORD   e_cs;                     // Initial (relative) CS value
    WORD   e_lfarlc;                 // File address of relocation table
    WORD   e_ovno;                   // Overlay number
    WORD   e_res[4];                 // Reserved words
    WORD   e_oemid;                  // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                // OEM information; e_oemid specific
    WORD   e_res2[10];               // Reserved words
    LONG   e_lfanew;                 // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

  這個結構體是歷史遺留的產物,因為開始的時候是DOS系統,上面除了最後一個成員e_lfanew,其他的都是給16位DOS程式看的。不過第一個成員e_magic被作為合法PE檔案的校驗值,值為IMAGE_DOS_SIGNATURE/0x5A4D/"MZ"
  e_lfanew為檔案偏移,指向了IMAGE_NT_HEADERS結構,這個就是我們真正可以跑在現在圖形介面的Windows程式必須有的結構,我們來看一個程式與此部分相關的結構示意圖如下:

羽夏殼世界—— PE 結構(上)

DosStub

  接在IMAGE_DOS_HEADER結構體後面是被稱之為DosStub的,我們能夠看到一個字串This program cannot be run in DOS mode,意為該程式不能夠在Dos模式下執行。這裡面其實不是垃圾資料,而是有意義的彙編程式碼,我們用16位反彙編器來看一下:

seg000:0000 seg000          segment byte public 'CODE' use16
seg000:0000                 assume cs:seg000
seg000:0000                 assume es:nothing, ss:seg000, ds:nothing, fs:nothing, gs:nothing
seg000:0000
seg000:0000 ; =============== S U B R O U T I N E =======================================
seg000:0000
seg000:0000                 public start
seg000:0000 start           proc near
seg000:0000                 push    cs
seg000:0001                 pop     ds
seg000:0002                 assume ds:seg000
seg000:0002                 mov     dx, 0Eh
seg000:0005                 mov     ah, 9
seg000:0007                 int     21h             ; DOS - PRINT STRING
seg000:0007                                         ; DS:DX -> string terminated by "$"
seg000:0009                 mov     ax, 4C01h
seg000:000C                 int     21h             ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
seg000:000C start           endp                    ; AL = exit code
seg000:000C
seg000:000C ; ---------------------------------------------------------------------------
seg000:000E aThisProgramCan db 'This program cannot be run in DOS mode.',0Dh,0Dh,0Ah
seg000:000E                 db '$',0
seg000:003A                 align 8
seg000:0040                 db 0A9h, 2Ch, 93h, 71h, 0EDh, 4Dh, 0FDh, 22h, 0EDh, 4Dh
seg000:0040                 db 0FDh, 22h, 0EDh, 4Dh, 0FDh, 22h, 8Fh, 35h, 0FCh, 23h
seg000:0040                 db 0EEh, 4Dh, 0FDh, 22h, 8Fh, 35h, 0F8h, 23h, 0F5h, 4Dh
seg000:0040                 db 0FDh, 22h, 8Fh, 35h, 0F9h, 23h, 0E7h, 4Dh, 0FDh, 22h
seg000:0040                 db 8Fh, 35h, 0FEh, 23h, 0EEh, 4Dh, 0FDh, 22h, 3Eh, 3Fh
seg000:0040                 db 0FCh, 23h, 0E9h, 4Dh, 0FDh, 22h, 0EDh, 4Dh, 0FCh, 22h
seg000:0040                 db 0AAh, 4Dh, 0FDh, 22h, 6Dh, 34h, 0F9h, 23h, 0ECh, 4Dh
seg000:0040                 db 0FDh, 22h, 6Dh, 34h, 2, 22h, 0ECh, 4Dh, 0FDh, 22h, 6Dh
seg000:0040                 db 34h, 0FFh, 23h, 0ECh, 4Dh, 0FDh, 22h, 52h, 69h, 63h
seg000:0040                 db 68h, 0EDh, 4Dh, 0FDh, 22h, 8 dup(0)
seg000:00A8                 db 10h dup(?)
seg000:00A8 seg000          ends
seg000:00A8
seg000:00A8
seg000:00A8                 end start

  注意一定要使用16位的反彙編器,否則無論是在32位是64位下,所有的程式碼都是錯誤的。
  上面的程式碼如果沒有《微機原理》的基礎可能有點難懂,這裡就補充一下:int 21hDos下的中斷,類似表示我要呼叫API了,不同的AH暫存器的值表示呼叫的函式是不一樣的。如果是9,表示在螢幕上列印字串,不過這個字串不是用\0結尾的,而是$,和我們現代作業系統的\0的作用是一樣的。如果AH的值為0x4C,則表示將AL作為返回值結束程式,和我們C語言編寫main函式中的return 0;是一個道理。
  有關該部分就介紹到這裡。

IMAGE_NT_HEADERS

  該結構體在32位和64位程式是有所不同的,只不過是結構體的成員大小的區別,如下所示:

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

  Signature是一個十分重要的成員。PE指紋的第二部分,值為IMAGE_NT_SIGNATURE/0x00004550/"PE"。如果值是錯誤的,同樣被判定為非法PE檔案。
  FileHeader是標準PE頭,大小為20個位元組,可以通過IMAGE_SIZEOF_FILE_HEADER巨集獲取,具體細節後面將會介紹。
  OptionalHeader是擴充套件PE頭,雖然名字帶著可選,但它是必需結構。
  我們再來看看它在二進位制檔案下的位置:

羽夏殼世界—— PE 結構(上)

IMAGE_FILE_HEADER

  該結構體在32位和64位的程式是一樣的,如下所示:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

  Machine指示可以執行在什麼樣的CPU上,它的值如下:

  • 任意CPUIMAGE_FILE_MACHINE_UNKNOWN/0x0000
  • Intel 386以及後續CPU: IMAGE_FILE_MACHINE_I386/0x014C
  • x64:IMAGE_FILE_MACHINE_AMD64/0x8664

下面是其中的所有成員:

#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_TARGET_HOST       0x0001  // Useful for indicating we want to interact with the host and not a WoW guest.
#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_SH3               0x01a2  // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP            0x01a3
#define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5
#define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB             0x01c2  // ARM Thumb/Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT             0x01c4  // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AM33              0x01d3
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP         0x01f1
#define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS
#define IMAGE_FILE_MACHINE_AXP64             IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE           0x0520  // Infineon
#define IMAGE_FILE_MACHINE_CEF               0x0CEF
#define IMAGE_FILE_MACHINE_EBC               0x0EBC  // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R              0x9041  // M32R little-endian
#define IMAGE_FILE_MACHINE_ARM64             0xAA64  // ARM64 Little-Endian
#define IMAGE_FILE_MACHINE_CEE               0xC0EE

  NumberOfSections指示節的數量,它十分重要。
  TimeDateStamp指示編譯器填寫的時間戳與檔案屬性裡面建立時間/修改時間無關,計算的是當前時間與1970年0時0點0分差的秒數。
  PointerToSymbolTable/NumberOfSymbols與除錯相關,不做關注。
  SizeOfOptionalHeader表示擴充套件PE頭的大小,可以修改合適的數值。在預設情況下,32位PE檔案:0xE0,64位PE檔案:0xF0
  Characteristics指示了檔案屬性,它的值有如下:

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved external 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  // Aggressively 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.

羽夏殼世界—— PE 結構(上)

  我們再來看看它在二進位制檔案下的位置:

羽夏殼世界—— PE 結構(上)

IMAGE_OPTIONAL_HEADER

  這個結構體在32位和64位是有區別的,是某些成員的大小區別,它們的結構如下:

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;  
    DWORD   SizeOfUninitializedData; 
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;    //除錯相關
    DWORD   NumberOfRvaAndSizes;    //目錄表的個數(決定DataDirectory陣列長度)
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;
    BYTE        MajorLinkerVersion;
    BYTE        MinorLinkerVersion;
    DWORD       SizeOfCode;
    DWORD       SizeOfInitializedData;
    DWORD       SizeOfUninitializedData;
    DWORD       AddressOfEntryPoint;
    DWORD       BaseOfCode;
    ULONGLONG   ImageBase;
    DWORD       SectionAlignment;
    DWORD       FileAlignment;
    WORD        MajorOperatingSystemVersion;
    WORD        MinorOperatingSystemVersion;
    WORD        MajorImageVersion;
    WORD        MinorImageVersion;
    WORD        MajorSubsystemVersion;
    WORD        MinorSubsystemVersion;
    DWORD       Win32VersionValue;
    DWORD       SizeOfImage;
    DWORD       SizeOfHeaders;
    DWORD       CheckSum;
    WORD        Subsystem;
    WORD        DllCharacteristics;
    ULONGLONG   SizeOfStackReserve;
    ULONGLONG   SizeOfStackCommit;
    ULONGLONG   SizeOfHeapReserve;
    ULONGLONG   SizeOfHeapCommit;
    DWORD       LoaderFlags;
    DWORD       NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

  Magic標識程式的位數。32位程式為值為IMAGE_NT_OPTIONAL_HDR32_MAGIC/0x10b,64位程式為值為IMAGE_NT_OPTIONAL_HDR64_MAGIC/0x20b。它是判斷程式位數的關鍵屬性。
  AddressOfEntryPoint的值表示程式入口RVA,十分重要。
  ImageBase表示該PE比較傾向的記憶體映象載入基址,比較重要。對於DLL這個值通常並不被作業系統採納,如果開啟了隨機基址的EXE也是如此。
  SectionAlignment表示記憶體對齊值,十分重要。
  FileAlignment表示檔案對齊值,十分重要。
  SizeOfImage表示記憶體中整個PE檔案的對映的尺寸,可比實際的值大,必須是SectionAlignment的整數倍,十分重要。
  SizeOfHeaders所有頭和節表按照檔案對齊後的大小,否則載入會出錯,這十分重要。
  CheckSum表示校驗和,一些系統檔案有要求用來判斷檔案是否被修改。
  Subsystem意為子系統,驅動程式值為1,圖形介面值為2,控制檯、DLL值為3。下面是其列舉:

#define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION      10  //
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  11   //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   12  //
#define IMAGE_SUBSYSTEM_EFI_ROM              13
#define IMAGE_SUBSYSTEM_XBOX                 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
#define IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG    17

  DllCharacteristics是檔案特性,不是針對DLL檔案的,該成員是比較重要的,基址重定位就是從這個成員進行設定的。下面是其列舉:

//      IMAGE_LIBRARY_PROCESS_INIT            0x0001     // Reserved.
//      IMAGE_LIBRARY_PROCESS_TERM            0x0002     // Reserved.
//      IMAGE_LIBRARY_THREAD_INIT             0x0004     // Reserved.
//      IMAGE_LIBRARY_THREAD_TERM             0x0008     // Reserved.
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA    0x0020  // Image can handle a high entropy 64-bit virtual address space.
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040     // DLL can move.
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY    0x0080     // Code Integrity Image
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT    0x0100     // Image is NX compatible
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200     // Image understands isolation and doesn't want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH       0x0400     // Image does not use SEH.  No SE handler may reside in this image
#define IMAGE_DLLCHARACTERISTICS_NO_BIND      0x0800     // Do not bind this image.
#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000     // Image should execute in an AppContainer
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER   0x2000     // Driver uses WDM model
#define IMAGE_DLLCHARACTERISTICS_GUARD_CF     0x4000     // Image supports Control Flow Guard.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 

  DataDirectory是儲存表位置大小的陣列。不同的索引代表不同的表的資料,下面是其列舉:

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

羽夏殼世界—— PE 結構(上)

  我們再來看看它在二進位制檔案下的位置:

羽夏殼世界—— PE 結構(上)

IMAGE_SECTION_HEADER

  IMAGE_SECTION_HEADER是節區頭,是重要的結構體,在32位和64位下的程式沒有區別。大小可通過IMAGE_SIZEOF_SECTION_HEADER獲取,在記憶體的展開大小 = Max(Misc,SizeOfRawData)。如果此節為已初始化的變數,則 Misc > SizeOfRawData;若節為未初始化的變數則 Misc < SizeOfRawData。如下是結構體成員:

#define IMAGE_SIZEOF_SHORT_NAME              8
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

  Name表示節區名稱,ASCII字串,可自定義,只擷取8個。
  Misc表示該節在沒有對齊前的真實尺寸,該值可以不準確。
  VirtualAddress是在記憶體中的偏移地址,加上lmageBase才是在記憶體中的真正地址,十分重要。
  PointerToRawData是節區在檔案中的偏移,十分重要。
  PointerToRelocations/PointerToRelocations/NumberOfRelocations/PointerToLinenumbers與除錯相關,不關注。
  Characteristics是節的屬性。下面是其列舉:

//      IMAGE_SCN_TYPE_REG                   0x00000000  // Reserved.
//      IMAGE_SCN_TYPE_DSECT                 0x00000001  // Reserved.
//      IMAGE_SCN_TYPE_NOLOAD                0x00000002  // Reserved.
//      IMAGE_SCN_TYPE_GROUP                 0x00000004  // Reserved.
#define IMAGE_SCN_TYPE_NO_PAD                0x00000008  // Reserved.
//      IMAGE_SCN_TYPE_COPY                  0x00000010  // Reserved.
#define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.
#define IMAGE_SCN_LNK_OTHER                  0x00000100  // Reserved.
#define IMAGE_SCN_LNK_INFO                   0x00000200  // Section contains comments or some other type of information.
//      IMAGE_SCN_TYPE_OVER                  0x00000400  // Reserved.
#define IMAGE_SCN_LNK_REMOVE                 0x00000800  // Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT                 0x00001000  // Section contents comdat.
//                                           0x00002000  // Reserved.
//      IMAGE_SCN_MEM_PROTECTED - Obsolete   0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC          0x00004000  // Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL                      0x00008000  // Section content can be accessed relative to GP
#define IMAGE_SCN_MEM_FARDATA                0x00008000
//      IMAGE_SCN_MEM_SYSHEAP  - Obsolete    0x00010000
#define IMAGE_SCN_MEM_PURGEABLE              0x00020000
#define IMAGE_SCN_MEM_16BIT                  0x00020000
#define IMAGE_SCN_MEM_LOCKED                 0x00040000
#define IMAGE_SCN_MEM_PRELOAD                0x00080000
#define IMAGE_SCN_ALIGN_1BYTES               0x00100000  //
#define IMAGE_SCN_ALIGN_2BYTES               0x00200000  //
#define IMAGE_SCN_ALIGN_4BYTES               0x00300000  //
#define IMAGE_SCN_ALIGN_8BYTES               0x00400000  //
#define IMAGE_SCN_ALIGN_16BYTES              0x00500000  // Default alignment if no others are specified.
#define IMAGE_SCN_ALIGN_32BYTES              0x00600000  //
#define IMAGE_SCN_ALIGN_64BYTES              0x00700000  //
#define IMAGE_SCN_ALIGN_128BYTES             0x00800000  //
#define IMAGE_SCN_ALIGN_256BYTES             0x00900000  //
#define IMAGE_SCN_ALIGN_512BYTES             0x00A00000  //
#define IMAGE_SCN_ALIGN_1024BYTES            0x00B00000  //
#define IMAGE_SCN_ALIGN_2048BYTES            0x00C00000  //
#define IMAGE_SCN_ALIGN_4096BYTES            0x00D00000  //
#define IMAGE_SCN_ALIGN_8192BYTES            0x00E00000  //
// Unused                                    0x00F00000
#define IMAGE_SCN_ALIGN_MASK                 0x00F00000
#define IMAGE_SCN_LNK_NRELOC_OVFL            0x01000000  // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE            0x02000000  // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
#define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
#define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.

羽夏殼世界—— PE 結構(上)

  我們再來看看它在二進位制檔案下的位置:

羽夏殼世界—— PE 結構(上)

IMAGE_DATA_DIRECTORY

  IMAGE_DATA_DIRECTORY是十分重要的結構體,具體重要性的體現已經在IMAGE_OPTIONAL_HEADER介紹過了,如下是其結構:

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

  VirtualAddress是指表在記憶體的RVA,十分重要。
  Size是指IMAGE_DATA_DIRECTORY結構體和其使用的所有資料之和,該值不會影響程式的執行。

  我們再來看看它在二進位制檔案下的位置:

羽夏殼世界—— PE 結構(上)

小結

  本篇我們只介紹了基本結構體,對於後面比較複雜的幾個表和地址轉化,考慮到比較複雜,挪到下一篇進行。
  看完本篇文章一定要把在16進位制下看明白結構,最好用自己熟悉的程式語言寫一個解析上面所述結構體的解析器,以鞏固自己的學習成果。

下一篇

  羽夏殼世界—— PE 結構(下)

相關文章