PE文件結構與函數導出表——詳解與實例

                                    PE文件結構與函數導出表——詳解與實例

       隨着windows系統從Xp升級到Win7、Win8, 從32位升級到64位,PE文件結構在整體未變的情況下發生了一些小的變動,一方面是推薦的程序裝載地址未採用,另一方面,導出函數序號不再是簡單的升序,而是一定程度上的進行了亂序。本文首先對PE文件結構進行了詳盡的解說,接着介紹瞭如何得出函數導出表,整個過程採用SysWoW64目錄下的wininet.dll實例進行說明。在介紹過程中,明確指出了Win7、Win8等新系統相對Xp帶來的區別。

文章鏈接:http://blog.csdn.net/typ2004/article/details/44227597

第一部分

1、DOS頭


DOS頭共64字節,最後一個雙字代表PE頭的文件地址。

2、PE頭

WinNT.h 中 PE 頭由三部分構成

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                         //PE ASCII
    IMAGE_FILE_HEADER FileHeader;            //標準頭
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;  //可選頭
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;


第一部分PE標識


是DOWRD大小的PE標識。

第二部分標準頭


是20個字節的PE標準頭。

具體結構爲

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;                  //PE中節的數量
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;              //PE可選頭的長度
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

第三部分可選頭


第三部分是PE可選頭


其中 0x12ch 的DWORD長的 ImageBase(0x63000000)爲程序的建議裝載地址,在XP系統中可能會使用此地址,但在Win7、Win8等系統中,此地址廢棄。

其中 0x16ch 的DWORD長的NumberOfRvaAndSizes(0x00000010,通常爲此值)爲下面數據目錄結構的項目數量。

注:對於64位系統來說,文件中NumberOfRvaAndSizes的位置比32位的系統靠後16個字節(前面SizeOfStackReserver等4個值在64位系統中爲8字節,32位中爲4字節)。

之後的8*IMAGE_NUMBEROF_DIRECTORY_ENTRIES(8*16)大小的字段爲數據目錄字段,定義了導出表、導入表、資源表、異常表等各類地址和大小(共16項地址和大小對)。其中,可以看到0x170h處的地址0x3D44即是導出表的虛擬地址

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;                       //地址
    DWORD   Size;                                 //大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;


PE可選頭的具體結構爲

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    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;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

3、節表

PE頭後緊跟着節目錄表。



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;


此表中包含的的信息如下:


節點名:.text
      虛擬大小:00180454
      虛擬偏移:00001000
      實際大小:00180600
      實際偏移:00000400
      文件特徵:60000020
節點名:.orpc
      虛擬大小:0000009E
      虛擬偏移:00182000
      實際大小:00000200
      實際偏移:00180A00
      文件特徵:60000020
節點名:.wpp_sf
      虛擬大小:00009E3F
      虛擬偏移:00183000
      實際大小:0000A000
      實際偏移:00180C00
      文件特徵:60000020
節點名:.data
      虛擬大小:0000755C
      虛擬偏移:0018D000
      實際大小:00003E00
      實際偏移:0018AC00
      文件特徵:C0000040
節點名:.idata
      虛擬大小:00002444
      虛擬偏移:00195000
      實際大小:00002600
      實際偏移:0018EA00
      文件特徵:40000040
節點名:.didat
      虛擬大小:0000057C
      虛擬偏移:00198000
      實際大小:00000600
      實際偏移:00191000
      文件特徵:C0000040
節點名:.rsrc
      虛擬大小:0002BE40
      虛擬偏移:00199000
      實際大小:0002C000
      實際偏移:00191600
      文件特徵:40000040
節點名:.reloc
      虛擬大小:0000F8BC
      虛擬偏移:001C5000
      實際大小:0000FA00
      實際偏移:001BD600
      文件特徵:42000040


4、各節

節表之後便是各節,需要注意的是,從節表到第一個節直接用0填充,其他節之間同樣用0填充,各節只需要對齊到PE可選頭的文件對齊粒度即可(本例中爲0x0200)。



第二部分

函數導出表

1、導出目錄

從上面已經知道0x3D44即是導出表的虛擬地址,其大小爲0x255F。

依次查看各節的【虛擬偏移,虛擬大小),發現導出表位於.text 節中,.text節的 (虛擬偏移-實際偏移)=0x0C00,所以導出表的實際偏移爲 0x3D44-0x0C00=0x3144。


導出目錄的具體結構爲

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;                   //指向該導出表的文件名字符串
    DWORD   Base;
    DWORD   NumberOfFunctions;      //所有的導出函數數目
    DWORD   NumberOfNames;          //以函數名導出的函數個數
    DWORD   AddressOfFunctions;     // 導出函數地址表 RVA from base of image
    DWORD   AddressOfNames;         // 導出名稱地址表 RVA from base of image
    DWORD   AddressOfNameOrdinals;  // 導出序號地址表 RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

從此結構可以看出,導出函數數目爲0x0143,以函數名導出的函數個數爲0x0123,導出函數地址表的地址爲0x3d6c-0x0c00=0x316c,導出函數名稱地址表的地址爲0x4278-0x0c00=0x3678,導出序號表地址爲0x4704-0x0c00=0x3b04。


2、導出函數地址表

由地址0x316c開始遍歷,0x0FC6A0, 0x154D40, 0x154E30, 0x11C180, 0x11C600, 0x0FEF30, 0x081290等等,即爲地址列表。


3、導出函數名稱地址表


由地址0x3678開始遍歷,得到0x4956, 0x496c, 0x4980等等虛擬偏移,對應的實際偏移爲0x3d56, 0x3d6c, 0x3d80等等,查找這些位置的名稱字符串如下。



4、導出序號地址表

導出序號 AddressOfNameOrdinals 指向的也是到處序號列表地址,其中的每個序號,與導出函數名稱一一對應,代表了這個函數名稱對應的函數在導出函數列表裏序號。

本例中地址爲0x3b04的導出序數表的第一項爲0x0006,代表AppCacheCheckManifest 對應函數地址的下標爲6(從0開始),即地址0x081290。

需要注意的是,這個下標只有在>=0時纔有效,並且,當函數名對應的有效函數地址數目(下表>=0)達到 NumberOfNames的時候,之後的有效函數就是無名稱函數。



5、導出函數在內存中的地址

如果在Xp系統中,導出函數AppCacheCheckManifest 在內存的地址只需加上推薦的程序裝在地址 ImageBase(1.2PE頭的可選頭裏的字段)即可。

但是在Win7、Win8等系統中,導出函數在內存中的地址應加上 真正的程序裝載地址。這個地址可以用 (DWORD) LoadLibrary(DllFilePath)求得。


至此完成微笑~~

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