PE文件格式(加密與解密3)(一)


本次的了解主要講解 PE的基本概念、MS-DOS文件頭、PE文件頭、區塊、輸入表、輸出表等。

這里我將會結合一個簡單的小程序來加深我對PE文件結構的了解。

 

使用學習工具:有StudyPE、LordPE、PEID。

 學習PE建議看書。。和自己動手。。。

 

PE文件:

  在WIN上,32位的可執行文件是PE文件,64位的是PE32+文件 ,DLL文件的格式和PE格式差不多,唯一的區別是PE和DLL的有一個字段標識這個文件是EXE還是DLL。

  

如上就是一個PE文件的結構圖,PE文件使用的是一個平面地址空間,所有的數據都融合在一起,文件的內容又被分割為不同的區塊(Section),

各個區塊按頁的邊界來對齊。每個塊都有自己的屬性(是否可讀,是否可寫,是否可執行等等)。

 

基地址:

      當PE文件被裝載器裝載了之后,內存中的板塊被稱為模塊。映射文件的起始地址被稱為模塊句柄---內存中的模塊代表這進程從這個可執行文件中所需要的代碼、數據、資源、輸入表、輸出表及其他東西所使用的東西放在一個連續的內存塊中。在裝載中,PE文件的一個字段會告訴系統把文件映射到內存需要多少內存,不能被映射的數據被放置在文件的尾部。

      在WIN32中,可以使用HMODULE GetModuleHandle(LPCTSTR lpModuleName)來獲得一個模塊的名稱。當傳遞一個可執行文件或者DLL作為參數,

如果系統成功找到這個文件,就會返回該可執行文件或者DLL文件映像加載到的基地址。

      在PE文件中,有一個字符設置了基地址,VC++建立的exe文件的基地址是0x00400000h,DLL文件的基地址是0x10000000h。

 

相對虛擬地址:

    為了讓程序的載入更加的靈活-也為了在PE文件中出現有確定的內存地址,出現了相對虛擬地址(Relative Vritual Address, RVA),當你的程序加載后,假設你的text塊的RVA = 0x00001000h,映射到程序中時,VA(虛擬地址) = ImagineBase(基地址)+RVA(相對虛擬地址),你的代碼區塊在內存中就開始與0x00401000h。

 

文件偏移地址:

    因為我們的文件是存儲在磁盤上的,某個數據相對於文件頭的偏移量就是這個數據的偏移地址,稱為文件偏移地址(File Offset)或者物理地址(RAW Offset),偏移地址的起始值是0。

 

MS-DOS頭部(IMAGE_DOS_HEADER):

   每個PE文件是以一個DOS程序開始的,還有MZ header之后的DOS stub(DOS塊)。如果這個可執行文件不能被這個系統支持,會打印一串提示符

"This program cannot be run is MS-DOS mode",DOS頭部中主要是WORD e_magic和 LONG e_lfanew這個字段比較重要。這些數據結構可以在winnt.h中找到。

#define IMAGE_DOS_SIGNATURE 0x5A4D
#define IMAGE_OS2_SIGNATURE 0x454E
#define IMAGE_OS2_SIGNATURE_LE 0x454C
#define IMAGE_VXD_SIGNATURE 0x454C
#define IMAGE_NT_SIGNATURE 0x00004550

#include "pshpack2.h"
//這里就是IMAGE_DOS_HEADER的結構了。
    typedef struct _IMAGE_DOS_HEADER {
      WORD e_magic; // DOS可執行文件標記“MZ”
      WORD e_cblp;
      WORD e_cp;
      WORD e_crlc;
      WORD e_cparhdr;
      WORD e_minalloc;
      WORD e_maxalloc;
      WORD e_ss;
      WORD e_sp;
      WORD e_csum;
      WORD e_ip;       //DOS代碼入口IP
      WORD e_cs;      //DOS代碼的入口CS
      WORD e_lfarlc;
      WORD e_ovno;
      WORD e_res[4];
      WORD e_oemid;
      WORD e_oeminfo;
      WORD e_res2[10];
      LONG e_lfanew;   // 指向PE文件頭, “PE”,0,0
    } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER;

 LONG e_lfanew是指向PE文件頭的一個地址,它的文件偏移地址是0x3C--也就是60字節開始,然后占據了4個字節,指向PE文件頭,具體看下圖

我們可以看到0x000000F8在偏移60字節的地方,看到PE文件頭在0x000000F8,具體可以自己找一個文件來測試(加深印象)

 DOS文件頭就到這里了,接下來繼續介紹PE頭,也就是IMAGE_NT_HEADER,下面的代碼是對於定義32還是64的PE頭,我們可以看到相應的宏語句

和對應的數據結構定義。

#ifdef _WIN64             //如果采用64的架構
    typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER;
    typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER;
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR64_MAGIC
#else  /* _WIN64 */      //如果不是采用64而是32位的架構
    typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER;
    typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER;
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR32_MAGIC
#endif /* _WIN64 */ //上面的typedef都是改變結構體的名稱

    typedef struct _IMAGE_NT_HEADERS64 {//這里是64位的PE頭的結構體的定義
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER64 OptionalHeader;
    } IMAGE_NT_HEADERS64,*PIMAGE_NT_HEADERS64;

    typedef struct _IMAGE_NT_HEADERS {//這里是32位的PE頭的結構體的定義
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER32 OptionalHeader;
    } IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;

在PE的文件頭中,第一個DWORD Signature, 被定義為了0x00004550h,也就是"PE\0\0"這四個字符。這個標志沒有什么作用。(DOS中指向的地方)。

主要是IMAGE_FILE_HEADER和IMAGE_OPTIONAL_HEADER這兩個結構體中的幾個字段重要。

我么接下來觀看PE文件頭中的IMAGE FILE_HEADER FileHeader這個結構體

typedef struct _IMAGE_FILE_HEADER {
      WORD Machine;                     //這里定義的是運行平台,i386= 0x014Ch這個值,還有其他平台,看書吧。。
      WORD NumberOfSections;       //這個是標識區塊的數目,緊跟在PE頭的后面,也就是IMAGE_NT_HEADERS的后面
      DWORD TimeDateStamp;
      DWORD PointerToSymbolTable;
      DWORD NumberOfSymbols;
      WORD SizeOfOptionalHeader;     //這里表明了IMAGE_NT_HEADERS中的大小(RAW SIZE),32位一般是0x00E0, 64位PE+一般是0x00F0
      WORD Characteristics;       //普通的EXE是0x010fh, DLL文件是0x210Eh
    } IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;

接下來我們看一下IMAGE_OPTIONAL_HEADER這個結構體,一樣下面的是這個結構體在winnt.h中的定義,下面這個是32位的,還有64位的,但是差不多的,可以看winnt.htypedef struct _IMAGE_OPTIONAL_HEADER

typedef struct _IMAGE_OPTIONAL_HEADER {

      WORD Magic;
      BYTE MajorLinkerVersion;
      BYTE MinorLinkerVersion;
      DWORD SizeOfCode;           //這里定義了包含代碼區塊的大小
      DWORD SizeOfInitializedData;    //這里定義了已經初始化的變量的區塊的大小
      DWORD SizeOfUninitializedData;   //這里是未初始化的變量的區塊的大小
      DWORD AddressOfEntryPoint;     //這里是程序入口的RVA(相對虛擬地址)
      DWORD BaseOfCode;          //這里是程序代碼塊的起始RVA
      DWORD BaseOfData;          //這里是數據塊起始RVA
      DWORD ImageBase;           //這里是程序默認裝入的基地址(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;   //這里定義了數據目錄表的項數,一直保持為16
      IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //這個是數據目錄表,指向輸入、輸出表、資源塊等數據,很重要
    } IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;

 這個IMAGE_OPTIONAL_HEADER只需要關注一些關鍵字段就行了,記住。。

接下來我么就看一看這個數據目錄表,數據目錄表簡單點說就是一個長度為16的IMAGE_DATA_DIRECTORY結構體數組而已

typedef struct _IMAGE_DATA_DIRECTORY {
      DWORD VirtualAddress;            //數據塊的其實RVA,很重要
      DWORD Size;             //數據塊的長度
    } IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;

這是一個16位的數組,最有一個數組元素作為保留,全部為0,其他的從開頭一直到倒數第二個數據都是已經規定好了的,我們看一下這個數據目錄表成員

#define IMAGE_DIRECTORY_ENTRY_EXPORT 0              //Export Table
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1        //Import Table              輸入表這里比較重要
#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_ARCHITECTURE 7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
#define IMAGE_DIRECTORY_ENTRY_TLS 9
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
#define IMAGE_DIRECTORY_ENTRY_IAT 12          //IAT (import address table), 這里也很重要
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14

還有也可以使用LordPE的PE editor來查看這個目錄,現在的查看目錄表的工具很多。。。。。

PE的第一階段到這里,接下來會繼續學習,增加熟悉度。。。。。。。。。。-----------好好學習,天天向上!

 


免責聲明!

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



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