初識PE文件--dos頭、pe頭


初識PE文件

0x00 前言

PE(Portable Executable),即可移植的執行體。

Linux平台:ELF(Executable and Linking Format)文件結構。

一般在Windows平台下,所有的可執行文件諸如:exe、dll、sys、ocx、com等均適用PE文件結構。這些使用PE文件結構也被稱為PE文件。

0x01 PE 結構

PE 結構是由若干個復雜的結構體組合而成的,不是單單的一個結構體那么簡單,它的結構就像文件系統的結構是由多個結構體組成的。

PE 結構包含的結構體有 DOS 頭、PE 標識、文件頭、可選頭、目錄結構、節表等。

image

Windows下如何判斷文件是否是PE文件?

1.通過導入文件到c32asm等工具,觀察MZ頭。

image

2.通過lordpe等工具。

image

從 數據管理的角度來看,可以把 PE 文件大致分為兩部分,DOS 頭、PE 頭和節表屬於 PE 文件的數據管理結構或數據組織結構部分,而節表數據才是 PE 文件真正的數據部分,其中包含着代碼、數據、資源等內容。

image

從PE結構圖中可以看出,PE 結構主要分為 4 大部分(DOS頭、PE頭、節表、節表數據),其中每個部分又進行了細分,存在若干個小的部分。

DOS頭:

無論是32位或64位可執行文件,其文件的頭部必定是IMAGE_DOS_HEADER.

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頭又分兩部分:

MZ文件頭和Dos Stub

MZ文件頭:IMAGE_DOS_HEADER 結構體,其大小占64個字節,並且該結構中的最后一個LONG類型e_lfanew成員指向PE文件頭的位置為中的PE文件頭標志的地址。

這里有兩個比較有用的成員信息:

1、e_magic,用於判斷PE文件的標識。如果不是MZ即不是十六進制值:0x5A4D。計算機存儲順序是低位在前高位在后,所以存儲為:0x4D5A。

2、e_lfanew,這里是指pe的偏移量,用於找到pe頭的位置。

如下陰影區域:

image

DOS stub:dos存根,在IMAGE_DOS_HEADER和IMAGE_NT_HEADERS之間存在一DOS存根,這其實是一段匯編代碼:

PE文件是運行在32位或64位操作系統下的。

其功能是當該EXE運行在16位環境下,輸出一段文字:“This program cannot be run in DOS mode”,然后並退出該進程。

在pe文件利用的時候,我們可以把payload寫入到當前區域,諸如存放我們的shellcode,在讀取時,獲取dos頭字節數,減去MZ頭字節數,即為dos存根字節大小。然后拿去操作加載shellcode等。

image

PE頭:

在MS-DOS頭下main,就是PE頭,PE頭是PE相關結構NT映像頭(IMAGE_NT_HEADER)的簡稱,其中包含許多PE裝載器用到的重要字段。

IMAGE_NT_HEADER數據結構定義:

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

參數具體含義:

Signature

將文件標識為 PE 映像的 4 字節簽名。字節為“PE\0\0”。這個字段是PE文件的標志字段,通常設置成00004550h,其ASCII碼為PE00,這個字段是PE文件頭的開始,前面的DOS_HEADER結構中的字段e_lfanew字段就是指向這里。

image

FileHeader

指定文件頭的 IMAGE_FILE_HEADER結構。

IMAGE_FILE_HEADER結構

這個字段也是包含幾個字段結構,它包含了PE文件的一些基本信息,最重要的是其中一個域指出了IMAGE_OPTIONAL_HEADER的大小。

typedef struct _IMAGE_FILE_HEADER {

WORD Machine;//運行平台

WORD NumberOfSections;//文件的區塊數目

DWORD TimeDateStamp;//文件創建的用時間戳標識的日期

DWORD PointerToSymbolTable;//指向符號表(用於調試)

DWORD NumberOfSymbols;//符號表中符號的個數

WORD SizeOfOptionalHeader;//IMAGE_OPTIONAL_HEADER32結構大小

WORD Characteristics;//文件屬性

} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

在PE文件頭的后面,1234567個框分別對應_IMAGE_FILE_HEADER結構的七個參數位置以及各自的值。我們需要判斷運行平台,就可以通過第一個參數位置的值來判斷。

上述e_lfanew中,可以在下圖中看到,e_lfanew的值為0080,這里可以看到PE頭就在0080h。

常見標識如下,比如這里的014c,就是在Intel I386機器上運行。

機器 標識

Intel I386 14ch

MIPS R3000 162h

Alpha AXP 184h

Power PC 1F0h

MIPS R4000 184h

image

2)NumberOfSection,標識區塊的數目,關於區塊后面會詳細講。

3)TimeDateStamp

這個字段沒啥好說的,指的就是PE文件創建的事件,這個時間是指從1970年1月1日到創建該文件的所有的秒數。

4)PointerToSymbolTable。這個字段用的比較少,略

5)NumberOfSymbol。這個字段也用得很少,略

6)SizeOfOptionalHeader:緊跟着IMAGE_FILE_HEADER后面的數據大小,這也是一個數據結構,它叫做IMAGE_OPTIONAL_HEADER,其大小依賴於是64位還是32位文件。32位文件值通常是00EOh,對於64位值通常為00F0h。

7)Characteristics:文件屬性,普通EXE文件這個字段值為010fh,DLL文件這個字段一般是0210h。

IMAGE_OPTIONAL_HEADER結構

OptionalHeader

指定可選文件頭的 IMAGE_OPTIONAL_HEADER結構。

這個結構是IMAGE_FILE_HEADER結構的補充。這兩個結構合起來才能對整個PE文件頭進行描述。左邊的16位字符表示相對於文件頭的偏移量。

typedef struct _IMAGE_OPTIONAL_HEADER 
{
  //
  // Standard fields. 
  //
+18h  WORD  Magic;     // 標志字, ROM 映像(0107h),普通可執行文件(010Bh)
+1Ah  BYTE   MajorLinkerVersion;   // 鏈接程序的主版本號
+1Bh  BYTE   MinorLinkerVersion;   // 鏈接程序的次版本號
+1Ch  DWORD  SizeOfCode;   // 所有含代碼的節的總大小
+20h  DWORD  SizeOfInitializedData;  // 所有含已初始化數據的節的總大小
+24h  DWORD  SizeOfUninitializedData; // 所有含未初始化數據的節的大小
+28h  DWORD  AddressOfEntryPoint;  // 程序執行入口RVA
+2Ch  DWORD  BaseOfCode;   // 代碼的區塊的起始RVA
+30h  DWORD  BaseOfData;   // 數據的區塊的起始RVA
  //
  // NT additional fields.  以下是屬於NT結構增加的領域。
  //
+34h  DWORD  ImageBase;   // *********程序的首選裝載地址
+38h  DWORD  SectionAlignment;   // *********內存中的區塊的對齊大小
+3Ch  DWORD  FileAlignment;   // *********文件中的區塊的對齊大小
+40h  WORD  MajorOperatingSystemVersion; // 要求操作系統最低版本號的主版本號
+42h  WORD  MinorOperatingSystemVersion; // 要求操作系統最低版本號的副版本號
+44h  WORD  MajorImageVersion;    // 可運行於操作系統的主版本號
+46h  WORD  MinorImageVersion;    // 可運行於操作系統的次版本號
+48h  WORD  MajorSubsystemVersion; // 要求最低子系統版本的主版本號
+4Ah  WORD  MinorSubsystemVersion; // 要求最低子系統版本的次版本號
+4Ch  DWORD  Win32VersionValue;    // 莫須有字段,不被病毒利用的話一般為0
+50h  DWORD  SizeOfImage;    // 映像裝入內存后的總尺寸
+54h  DWORD  SizeOfHeaders;    // 所有頭 + 區塊表的尺寸大小
+58h  DWORD  CheckSum;    // 映像的校檢和
+5Ch  WORD  Subsystem;    // 可執行文件期望的子系統
+5Eh  WORD  DllCharacteristics;    // DllMain()函數何時被調用,默認為 0
+60h  DWORD  SizeOfStackReserve;    // 初始化時的棧大小
+64h  DWORD  SizeOfStackCommit;    // 初始化時實際提交的棧大小
+68h  DWORD  SizeOfHeapReserve;    // 初始化時保留的堆大小
+6Ch  DWORD  SizeOfHeapCommit;    // 初始化時實際提交的堆大小
+70h  DWORD  LoaderFlags;    // 與調試有關,默認為 0 
+74h  DWORD  NumberOfRvaAndSizes; // 下邊數據目錄的項數,這個字段自Windows NT 發布以來    // 一直是16
+78h  DWORD  DataDirctory[16];     // ********* 數據目錄表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

這里總共31個字段。通常用的就是加*字段

字段6:AddressOfEntryPoint 表 程序入口RVA,即OEP:
   ``EOP:程序入口點,殼相關概念
   ``OEP:原本的程序入口點(實際為偏移,+模塊基址=實際入口點)
   ``EP: 被加工后的入口點
字段9:ImageBase 表 模塊加載基地址,exe默認0x400000,dll默認0x10000000
   ``建議裝載地址:exe映射加載到內存中的首地址= PE 0處,即實例句柄hInstance
   ``一般而言,exe文件可遵從裝載地址建議,但dll文件無法滿足
尾字段:DataDirectory 表 數據目錄表,用來定義多種不通用處的數據塊。
   ``存儲了PE中各個表的位置,詳情參考IMAGE_DIRECTORY_ENTRY...系列宏

這里可以知道我們確定的PE文件頭在0080h處,通過偏移計算,我們可以得到IMAGE_OPTIONAL_HEADER結構的的首個字段在80h+18h=98h的地方。這里直接通過c32asm跳轉到對應位置。如圖所示:

image

然后比較重要的就是最后一個成員,即數據目錄表,大小為16,每個元素都是一個IMAGE_DATA_DIRECTORY結構體,這里看到是一個數組類型。

它的定義如下:

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD VirtualAddress; //所指向的數據結構的虛擬地址
    DWORD Size; //數據結構的大小
} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;

在winnt.h中的定義:

#define IMAGE_DIRECTORY_ENTRY_EXPORT		   //0 導出表
#define IMAGE_DIRECTORY_ENTRY_IMPORT		   //1 導入表 
#define IMAGE_DIRECTORY_ENTRY_RESOURCE		 //2 資源目錄
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION		 //3 異常目錄
#define IMAGE_DIRECTORY_ENTRY_SECURITY		 //4 安全目錄
#define IMAGE_DIRECTORY_ENTRY_BASERELOC	   //5 重定位基本表
#define IMAGE_DIRECTORY_ENTRY_DEBUG		     //6 調試目錄
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT		 //7 描術字串
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR		 //8 機器值
#define IMAGE_DIRECTORY_ENTRY_TLS		       //9 TLS目錄
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG	 //10 載入配值目錄
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT //11 綁定輸入表
#define IMAGE_DIRECTORY_ENTRY_IAT		       //12 導入地址表
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT //13 延遲載入描述
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR     //14 COM信息

在這個數據目錄結構體中只有兩個成員VirtualAddressSize,這兩個成員的含義比較簡單,VirtualAddress指定了數據塊的相對虛擬地址(RVA)。Size則指定了該數據塊的大小,有時並不是該類型數據的總大小,可能只是該類型數據一個數據項的大小。這兩個成員(主要是VirtualAddress)成為了定位各種表的關鍵,所以一定要知道每個數組元素所指向的數據塊類型,以下表格就是它的對應關系:

image

下面是DataDirctory[16]即數據目錄表的各個成員

索 引 索引值在Windows.inc中的預定義值 對應的數據塊 偏移量
0 IMAGE_DIRECTORY_ENTRY_EXPORT 導出表 78h
1 IMAGE_DIRECTORY_ENTRY_IMPORT 導入表 80h
2 IMAGE_DIRECTORY_ENTRY_RESOURCE 資源 88h
3 IMAGE_DIRECTORY_ENTRY_EXCEPTION 異常(具體資料不詳) 90h
4 IMAGE_DIRECTORY_ENTRY_SECURITY 安全(具體資料不詳) 98h
5 IMAGE_DIRECTORY_ENTRY_BASERELOC 重定位表 A0h
6 IMAGE_DIRECTORY_ENTRY_DEBUG 調試信息 A8h
7 IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 版權信息 B0h
8 IMAGE_DIRECTORY_ENTRY_GLOBALPTR 具體資料不詳 B8h
9 IMAGE_DIRECTORY_ENTRY_TLS Thread Local Storage C0h
10 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 具體資料不詳 C8h
11 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 具體資料不詳 D0h
12 IMAGE_DIRECTORY_ENTRY_IAT 導入函數地址表 D8h
13 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 具體資料不詳 E0h
14 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 具體資料不詳 E8h
15 未使用 保留

對於安全人員來說,通常需要了解比較重要的導出表和導入表。這里放在下篇學習。

0x03 參考

https://www.cnblogs.com/a-s-m/p/12251728.html

https://www.cnblogs.com/2f28/p/9816419.html

https://blog.csdn.net/obuyiseng/article/details/50231631


免責聲明!

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



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