PE文件結構部分解析以及輸入的定位


PE文件定義 

PE 文件(”Portable executable”, 可移植的可執行文件)文件格式,是微軟Windows NT, 中Win32、Win32s中的可執行的二進制的文件格式。 包括:.exe, .dll, .sys, .com, .ocs. PE文件最重要的兩個因素:

1.磁盤上的可執行文件和它被映射到windows內存之后的格式非常相像。

2.對於Win32 來講, 模塊中多使用的所有代碼、數據、資源、導入表、和其他需要的模塊數據結構都在一個連續的內存塊中。因此,只需要知道PE Loader把可執行文件映射到了內存的什么地方(基址),通過作為映像的一部分指針,就可以找到這個模塊的所有不同的塊。

PE文件總覽: 

1. DOS Header: (size:64byte)

_IMAGE_DOS_HEADER結構體:

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

結構體中有兩個重要的數據成員。第一個為e_magic,這個必須為MZ,即0x5A4D。另一個重要的數據成員是最后一個成員e_lfanew,這個成員的值為IMAGE_NT_HEADERS的偏移。其中,*e_lfanew這個字段的值:   PE Header 在磁盤文件中相對於文件開始的偏移地址.

實例截圖:

2.     PE Header: (size: 248bytes)

IMAGE_NT_HEADERS 緊接在DOS Stub之后,位置有e_lfanew所指

1 typedef struct _IMAGE_NT_HEADERS {
2         DWORD Signature;                        //4 bytes PE文件頭標志:(e_lfanew)->‘PE’
3         IMAGE_FILE_HEADER FileHeader;        //20 bytes PE文件物理分布的信息
4         IMAGE_OPTIONAL_HEADER32 OptionalHeader;//224bytes PE文件邏輯分布的信息
5 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

PE Header 總覽

IMAGE_NT_HEADERS結構體成員解析:

2.1.Signature: (4 bytes)

2.2.IMAGE_FILE_HEADER(20 bytes)

1 typedef struct _IMAGE_FILE_HEADER {
2     WORD    Machine;                //運行平台
3     WORD    NumberOfSections;        //文件區塊數目
4     DWORD   TimeDateStamp;            //文件創建日期和時間
5     DWORD   PointerToSymbolTable;    //指向符號表(主要用於調試)
6     DWORD   NumberOfSymbols;        //符號表中符號個數
7     WORD    SizeOfOptionalHeader;        //IMAGE_OPTIONAL_HEADER32 結構大小
8     WORD    Characteristics;            //文件屬性
9 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

IMAGE_FILE_HEADER結構體成員解析:

1). Machine 代表了CPU的類型  //運行平台

#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_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_CEE               0xC0EE
View Code

2)       NumberOfSections: 代表區塊的數目,區塊表緊跟在IMAGE_NT_HEADERS后面, 區塊表大概是一個鏈表結構,鏈表長度由NumberOfSection的數值決定.

3)       TimeDataStamp: 表明文件的創建時間

4)       SizeOfOptionalHeader: 是IMAGE_NT_HEADERS的另一個子結構IMAGE_OPTIONAL_HEADER的大小

5)       Characteristics: 代表文件的屬性EXE文件一般是0100h DLL文件一般是210Eh,多種屬性可以用或運算同時擁有

#define IMAGE_FILE_RELOCS_STRIPPED   0x0001 // 重定位信息被移除 
#define IMAGE_FILE_EXECUTABLE_IMAGE   0x0002 // 文件可執行 
#define IMAGE_FILE_LINE_NUMS_STRIPPED  0x0004 // 行號被移除 
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // 符號被移除 
……..
#define IMAGE_FILE_32BIT_MACHINE  0x0100 // 32位機器 
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // .dbg文件的調試信息被移除 
………………….
#define IMAGE_FILE_SYSTEM       0x1000 // 系統文件 
#define IMAGE_FILE_DLL         0x2000 // 文件是一個dll 
#define IMAGE_FILE_UP_SYSTEM_ONLY    0x4000 // 文件只能運行在單處理器上 
View Code

實例截圖:

2.3. IMAGE_OPTIONAL_HEADER(224 bytes)

緊接IMAGE_FILE_HEADER之后,IMAGE_OPTIONAL_HEADER的大小由IMAGE_FILE_HEADER中倒數第二個成員(SizeOfOptionalHeader)指定. IMAGE_OPTIONAL_HEADER結構體如下:

 1 typedef struct _IMAGE_OPTIONAL_HEADER {
 2     WORD    Magic;    //映像文件的狀態
 3     BYTE    MajorLinkerVersion;    //連接器的主版本號
 4     BYTE    MinorLinkerVersion;    //連接器的次版本號
 5     DWORD   SizeOfCode;    //代碼段的大小,如果有多個代碼段則為總和
 6     DWORD   SizeOfInitializedData; //初始化數據段大小.如果多個則為總和
 7     DWORD   SizeOfUninitializedData;//未初始化數據段大小,.如果多個則為總和.bbs
 8     DWORD   AddressOfEntryPoint;    //PE文件入口地址的RAV:OEP = ImageBase + RAV
 9     DWORD   BaseOfCode;    //代碼塊起始地址的RVA
10     DWORD   BaseOfData;//數據塊的起始地址的RVA
11     //
12     // NT additional fields.
13     //
14     DWORD   ImageBase;    //可執行文件的基址ImageBase
15     DWORD   SectionAlignment; //每一個塊必須保證始於這個值的整數倍
16     DWORD   FileAlignment; //對齊映射文件部分原始數據 2 or 512 or 64: 默認為512
17     WORD    MajorOperatingSystemVersion;//要求的操作系統的主版本號
18     WORD    MinorOperatingSystemVersion;//要求的操作系統的次版本號
19     WORD    MajorImageVersion;//映像的主版本號
20     WORD    MinorImageVersion;//映像的次版本號
21     WORD    MajorSubsystemVersion;//子系統的主版本號
22     WORD    MinorSubsystemVersion;//子系統的次版本號
23     DWORD   Win32VersionValue;//保留值.必須為0
24     DWORD   SizeOfImage;//映像文件的大小
25     DWORD   SizeOfHeaders;
26     DWORD   CheckSum;//映像文件的校驗和
27     WORD    Subsystem;//運行此映像的字系統
28     WORD    DllCharacteristics;//映像文件的DLL特征
29     DWORD   SizeOfStackReserve;//堆棧保留字節. 0x100000
30     DWORD   SizeOfStackCommit;//線程開始提交的初始堆棧大小
31     DWORD   SizeOfHeapReserve;//為初始進程保留的虛擬內存總數
32     DWORD   SizeOfHeapCommit;//進程開始提交初始虛擬內存的大小
33     DWORD   LoaderFlags;
34     DWORD   NumberOfRvaAndSizes; //0x10
35 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
36 //指向第一個IMAGE_DATA_DIRECTORY
37 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

_IMAGE_OPTIONAL_HEADER結構體實例截圖以及成員解析:

1) Magic:32位可執行文件來:0x010B

      64位可執行文件來:0x020B

       0x107

2) SizeOfCode: 代碼段的大小,如果有多個代碼段則為總和

  RAW:經過文件對齊處理后大小(PE文件中的大小)

3) SizeOfInitializedData: 初始化數據段大小.如果多個則為總和

4) ImageBase: 建議的裝載地址

PE建議裝載地址:

實際裝載地址:

5) AddressOfEntryPoint: 程序執行的入口RVA地址

OEP = ImageBase + (AddressOfEntryPoint)RVA

6) SectionAlignment:為內存中節的對齊大小,一般為0×00001000

7) FileAlignment:為PE文件中節的對齊大小

8)  SizeofImage:映像文件的大小

9) DataDirectory為數據目錄表數組,比較重要:共有16個表項

Size = sizeof(_IMAGE_DATA_DIRECTORY) * 16

sizeof(_IMAGE_DATA_DIRECTORY) = 8 bytes

_IMAGE_DATA_DIRECTORY結構體以及成員定義:

 1 typedef struct _IMAGE_DATA_DIRECTORY {
 2     DWORD   VirtualAddress;
 3     DWORD   Size;
 4 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
 5 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16
 6 // Directory Entries
 7 #define IMAGE_DIRECTORY_ENTRY_EXPORT            0   // Export Directory
 8 #define IMAGE_DIRECTORY_ENTRY_IMPORT             1   // Import Directory
 9 #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
10 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
11 #define IMAGE_DIRECTORY_ENTRY_SECURITY          4   // Security Directory
12 #define IMAGE_DIRECTORY_ENTRY_BASERELOC        5   // Base Relocation Table
13 #define IMAGE_DIRECTORY_ENTRY_DEBUG             6   // Debug Directory
14 //      IMAGE_DIRECTORY_ENTRY_COPYRIGHT        7   // (X86 usage)
15 #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE  7   // Architecture Specific Data
16 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR         8   // RVA of GP
17 #define IMAGE_DIRECTORY_ENTRY_TLS                 9   // TLS Directory
18 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10 // Load Configuration Directory
19 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT     11 // Bound Import Directory in headers
20 #define IMAGE_DIRECTORY_ENTRY_IAT              12   // Import Address Table
21 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT    13   // Delay Load Import Descriptors
22 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

Import Table RVA & Size截圖:

3. Section Header:

Section Header 數量:_IMAGE_FILE_HEADER結構體中NumberOfSections成員。

Section Header 定位:緊跟在IMAGE_NT_HEADERS后面

結構體大小:40 bytes

_IMAGE_SECTION_HEADER 結構體以及重要變量的定義:

 1 #define IMAGE_SIZEOF_SHORT_NAME   8
 2 typedef struct _IMAGE_SECTION_HEADER {
 3     BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
 4     union {
 5             DWORD   PhysicalAddress;
 6             DWORD   VirtualSize;
 7     } Misc;
 8     DWORD   VirtualAddress;    //內存中偏移地址
 9     DWORD   SizeOfRawData;    //PE文件中對其之后的大小
10     DWORD   PointerToRawData;//為PE塊區在PE文件中偏移
11     DWORD   PointerToRelocations;
12     DWORD   PointerToLinenumbers;
13     WORD    NumberOfRelocations;
14     WORD    NumberOfLinenumbers;
15     DWORD   Characteristics;    //塊區的屬性:可讀、可寫..
16 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
17 #define IMAGE_SIZEOF_SECTION_HEADER          40

重要數據成員

1)      Name[IMAGE_SIZEOF_SHORT_NAME]:

8字節大小的NAME, 如果節區名稱小於8個字節,則多余的用0填充,否則全部填充節名,末尾不保證有1個0,同樣會被名字填充

2)      PointerToRawData:

為節區在PE文件中的偏移

3)      Characteristics: 為節區的屬性,如可讀、可寫、可執行等

1 #define IMAGE_SCN_CNT_CODE       0x00000020  // Section contains code.
2 #define IMAGE_SCN_LNK_NRELOC_OVFL   0x01000000  // Section contains extended relocations.
3 #define IMAGE_SCN_MEM_DISCARDABLE   0x02000000  // Section can be discarded.
4 #define IMAGE_SCN_MEM_NOT_CACHED    0x04000000  // Section is not cachable.
5 #define IMAGE_SCN_MEM_NOT_PAGED         0x08000000  // Section is not pageable.
6 #define IMAGE_SCN_MEM_SHARED             0x10000000  // Section is shareable.
7 #define IMAGE_SCN_MEM_EXECUTE            0x20000000  // Section is executable.
8 #define IMAGE_SCN_MEM_READ              0x40000000  // Section is readable.
View Code

Section Header 實例截圖:

4.  導入表

_IMAGE_IMPORT_DESCRIPTOR 數據結構:(20 bytes)

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;     // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk; // RVA 指向INT (PIMAGE_THUNK_DATA)
    };
DWORD   TimeDateStamp;    
    DWORD   ForwarderChain;     // -1 if no forwarders
    DWORD   Name;            //dll 名稱
    DWORD   FirstThunk;         //指向引入函數真實地址單元處的RVA  IAT
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

其中OriginalFirstThunk和FirstThunk非常類似,指向兩個本質上相同的數組IMAGE_THUNK_DATA。

1) 定位查找IMAGE_IMPORT_DESCRIPTO結構

A 獲取引入表的RVA.也就是

 data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress 所指的值

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_DIRECTORY_ENTRY_IMPORT             1   // Import Directory

查看 IMAGE_DIRECTORY_ENTRY_IMPORT的值

&data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0x0002D51C

&data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualSize= 0xA0

B. 查找導入表所在的區段:

if(RVA>=SECTION.VirtualAddress && RVA<SECTION.Misc.VirtualSize)

 {

     //處於該節

 }

 else

 {

     //不在該節中

 }

C. 找到引入表所在的節后就可以用該節的VirtualAddress和PointerToRawData兩個域確定引入表在文件中的偏移量:RVA –△k

VirtualAddress = 0x00026000

PointerToRawData = 0x00025000

△k = VirtualAddress – PointerToRawData = 0x1000H

Address = 0x0002D51C - △k = 0x0002C51C

然后定位到文件偏移處:

陰影部分是IID的內容, IID的大小為20h, 陰影部分存在鏈各個IID,最后一個為0000000, 說明此PE文件只有8個IID, 對應8個dll。

0xA0  = 160 bytes = 7 * 20bytes +20bytes(空白)

截圖

第四個變量的地址RVA:0002DB14, 需要轉換成對應的文件偏移

(0x0002DB14 –△k) = 0x0002cb14,定位到文件偏移為0x0002cb14的地方

查看內容,里面記錄的是IMAGE_IMPORT_DESCRIPTOR的第四個成員變量多對應的dll的名字,mfc90u.dll,到此,我們已經找到了這個輸入的dll

2) 獲取dll調用的所有函數

IMAGE_IMPORT_DESCRIPTOR中的第一個參數和最后一個參數,original_first_thunk 和first_thunk分別指向了INT(輸入名稱表)IAT(輸入地址表)這兩個表里面分別記錄了指向調用函數名的地址,和此函數在dll中的序號(序號用來快速索引dll中的函數)

0x0002D85C和0x000262A0是INT 和IAT數組的首地址,下面我們跳到該地址(由於△K=0x1000,故RVA=文件偏移0x0002C85C和0x000252A0)

_IMAGE_THUNK_DATA32數據結構:

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;               //數據
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

a.當一個函數以序號導入.MAGE_THUNK_DATA結構中的AddressOfData最高位被設成1.用16進制表示為(0x80000000),例入一個IMAGE_THUNK_DATA的AddressOfData的值為0x 800010E4在mfc90u.dll數組中.就表示IMAGE_THUNK_DATA將引入mfc90u.dll

中的第10E4號函數

INT數組:

IAT數組:

b.如果一個函數以名稱導入.IMAGE_THUNK_DATA結構中的Ordinal域就包含一個RVA.這個RVA指向一個IMAGE_IMPORT_BY_NAME 結構.該結構保存了一個引入函數的相關信息:例如MSVCR90.dll

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint; //序列號
    BYTE    Name[1];//函數名稱
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

有第四個變量Name定位dll名字

定位INT和IAT:

INT:

IAT

數組里面的值為指向IMAGE_IMPORT_BY_NAME的地址,IMAGE_IMPORT_BY_NAME里面存放的是所調用函數的名字的地址,下來我們選取一個數組里面的值,跳轉到相應的IMAGE_IMPORT_BY_NAME

可以看到,上面的那個圖片中顯示了所調用的函數的名字,名字前面的兩個字節代表的是函數在dll中的序號,方便以后快速索引到

3. 需要注意的地方

INT 和IAT數組在一開始的時候,里面存放的地址都是一樣的,他們都是指向所調用函數的名字的字符串。而在加載到內存的時候,IAT的值會發生變換,它的值存放的是dll中函數實際被調用的地址,在加載到內存后,就只需要保存IAT就可以了,利用它來調用函數

 

今天先到這里.有時間的話在繼續...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM