天天看點

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頭

PE檔案結構與函數導出表——詳解與執行個體

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辨別

PE檔案結構與函數導出表——詳解與執行個體

是DOWRD大小的PE辨別。

第二部分标準頭

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檔案結構與函數導出表——詳解與執行個體

第三部分是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頭後緊跟着節目錄表。

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。

PE檔案結構與函數導出表——詳解與執行個體

導出目錄的具體結構為

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、導出函數名稱位址表

PE檔案結構與函數導出表——詳解與執行個體

由位址0x3678開始周遊,得到0x4956, 0x496c, 0x4980等等虛拟偏移,對應的實際偏移為0x3d56, 0x3d6c, 0x3d80等等,查找這些位置的名稱字元串如下。

PE檔案結構與函數導出表——詳解與執行個體

4、導出序号位址表

導出序号 AddressOfNameOrdinals 指向的也是到處序号清單位址,其中的每個序号,與導出函數名稱一一對應,代表了這個函數名稱對應的函數在導出函數清單裡序号。

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

需要注意的是,這個下标隻有在>=0時才有效,并且,當函數名對應的有效函數位址數目(下表>=0)達到 NumberOfNames的時候,之後的有效函數就是無名稱函數。

PE檔案結構與函數導出表——詳解與執行個體

5、導出函數在記憶體中的位址

如果在Xp系統中,導出函數AppCacheCheckManifest 在記憶體的位址隻需加上推薦的程式裝在位址 ImageBase(1.2PE頭的可選頭裡的字段)即可。

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

至此完成

PE檔案結構與函數導出表——詳解與執行個體

~~

繼續閱讀