1. 程式人生 > >PE檔案詳解------PE檔案結構剖析

PE檔案詳解------PE檔案結構剖析

一.PE檔案結構縱覽


PE檔案的結構如上圖所示,由低地址到高地址分別為:Dos頭,PE頭,塊表,塊,除錯資訊。其中真正的PE檔案頭是位於Dos頭的後面的部分。


上圖為利用PE工具開啟的一個可執行檔案在磁碟中的對映,這就是PE檔案的內部資訊。

可以看到這個檔案的錢兩個位元組為”4D 5A”,表示的就是Dos頭的第一個欄位的額資訊,標示Dos的頭部。在Dos頭結構的最後一個欄位是一個指標,這個指標指向PE頭。在上圖中可以看到00 00 00 60表示的就是這個指標的值,它指向這個PE檔案的真正PE頭。根據這個地址可以找到PE頭開始的地方,45 50表示的就是PE頭結構的而第一個欄位,標示的就是PE頭。可以看到,

PE檔案的結構十分分明,沒有一塊多餘的地方,各個部分排列緊湊,井井有條。我們獲取PE檔案的資訊就是通過讀取這個2進位制檔案。


二.Dos 頭(Dos Header)

Dos頭中的資訊儲存在IMAGE_DOS_HEADER中。這個結構的定義為:

IMAGE_DOS_HEADER STRUCT

{

+0hWORDe_magic //Magic DOS signature MZ(4Dh 5Ah) DOS 可執行檔案標記

+2h WORD e_cblp //Bytes on last page of file

+4hWORD e_cp //Pages in file

+6hWORD e_crlc //Relocations

+8hWORD e_cparhdr //Size of header in paragraphs

+0ahWORD e_minalloc //Minimun extra paragraphs needs

+0chWORD e_maxalloc //Maximun extra paragraphs needs

+0ehWORD e_ss //intial(relative)SS value DOS程式碼的初始化堆疊SS

+10hWORD e_sp //intial SP value DOS 程式碼的初始化堆疊指標SP

+12hWORD e_csum //Checksum

+14hWORD e_ip // intial IP value DOS程式碼的初始化指令入口

[指標IP]

+16hWORD e_cs //intial(relative)CS value DOS 程式碼的初始堆疊入口

+18hWORD e_lfarlc //File Address of relocation table

+1ahWORD e_ovno // Overlay number

+1chWORD e_res[4] //Reserved words

+24hWORD e_oemid // OEM identifier(for e_oeminfo)

+26hWORD e_oeminfo // OEM information;e_oemid specific

+29hWORD e_res2[10] // Reserved words

+3chDWORD e_lfanew //Offset to start of PE header 指向PE 檔案頭

} IMAGE_DOS_HEADER ENDS

在Image_Dos_Header 中有幾個成員是很重要的,第一個magic,這個欄位是來標示是否為Dos 程式的,如果是的話那麼就會有MZ 的標記,16 進製為4Dh 5Ah,一個字母佔一個位元組,一共是兩個位元組。還有一個是最後一個欄位,e_lfanew,這是一個指向PE 檔案頭的指標,指出了PE 檔案頭在PE 檔案中的偏移。如果這個值為0,那麼說明這個exe 檔案是一個Dos 程式,windows 會啟動Dos 子系統來執行它。

三.Dos Stub

Dos Stub部分其實就是一段windows下的一段程式碼,當windows程式在Dos系統下執行的時候,那麼系統就會返回一段資訊,表示這個程式是一個windows下的可執行檔案,不能在Dos系統下執行。


四.PE頭(PE Header )

這部分是PE檔案真正的PE頭,它儲存在一個結構體中,它的型別為IMAGE_NT_HEADER


1.PE頭的結構 IMAGE_NT_HEADER 

PE頭實際上是一個IMAGE_NT_HEADER結構,它的定義為:

IMAGE_NT_HEADERS STRUCT{

+0hDWORDSignature //

+4h IMAGE_FILE_HEADER FileHeader //

+18hIMAGE_OPTIONAL_HEADER32OptionalHeader //

} IMAGE_NT_HEADERS ENDS

緊接著Image_Dos_Header 的結構是Image_NT_Header,這是PE 真正的標頭檔案。這個結構包含了三個欄位,第一個是Siganture,第二個是一個結構Image_FILE_Header,第三個是Image_Optional_Header。

在一個有效的PE 檔案裡,Signature 欄位被設定為00004550h, ASCII 碼字元是“PE00”。標誌這PE 檔案頭的開始。“PE00” 字串是PE 檔案頭的開始, DOS 頭部的e_lfanew 欄位正是指向這裡。

Image_FILE_Header 結構為PE 映像頭,而Image_Optional_Header 是PE 擴充套件頭部。


2.PE檔案映像頭結構 IMAGE_FILE_HEADER

它的定義為:

typedef struct _IMAGE_FILE_HEADER

{

+04h WORD Machine; // 執行平臺

+06h WORD NumberOfSections; // 檔案的區塊數目

+08h DWORDTimeDateStamp; // 檔案建立日期和時間

+0Ch DWORD PointerToSymbolTable; // 指向符號表(主要用於除錯)

+10hDWORD NumberOfSymbols; // 符號表中符號個數(同上)

+14h WORD SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32結構大小

+16h WORD Characteristics; // 檔案屬性

} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

以下為對IMAGE_FILE_HEADER部分資料成員做出解釋:

Machine:可執行檔案的目標CPU 型別。

NumberOfSection: 區塊的數目。(注:區塊表是緊跟在IMAGE_NT_HEADERS 後邊的)

TimeDataStamp: 表明檔案是何時被建立的。

這個值是自1970 年1 月1 日以來用格林威治時間(GMT)計算的秒數,這個值是比檔案系統( FILESYSTEM ) 的日期時間更加精確的指示器。如何將這個值翻譯請看:

PointerToSymbolTable: COFF 符號表的檔案偏移位置,現在基本沒用了。

NumberOfSymbols: 如果有COFF 符號表,它代表其中的符號數目,COFF 符號是一

個大小固定的結構,如果想找到COFF 符號表的結束位置,則需要這個變數。

SizeOfOptionalHeader: 緊跟著IMAGE_FILE_HEADER 後邊的資料結構(IMAGE_OPTIONAL_HEADER)的大小。(對於32 位PE 檔案,這個值通常是00E0h;對於64 位PE32+檔案,這個值是00F0h )。

Characteristics: 檔案屬性,有選擇的通過幾個值可以運算得到。( 這些標誌的有效值是定義於winnt.h 內的IMAGE_FILE_** 的值,具體含義見下表。普通的EXE 檔案這個欄位

的值一般是0100h,DLL 檔案這個欄位的值一般是210Eh。)


3.可選的PE頭結構 IMAGE_OPTIONAL_HEADER

它的定義為:

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; // D

llMain()函式何時被呼叫,預設為0

+60h DWORD SizeOfStackReserve; // 初始化時的棧大小

+64h DWORD SizeOfStackCommit; // 初始化時實際提交的棧大小

+68h DWORD SizeOfHeapReserve; // 初始化時保留的堆大小

+6Ch DWORD SizeOfHeapCommit; // 初始化時實際提交的堆大小

+70h DWORD LoaderFlags; // 與除錯有關,預設為0

+74h DWORD NumberOfRvaAndSizes; // 下邊資料目錄的項數,這個欄位自Windows NT

釋出以來// 一直是16

+78h IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENT

RIES];

// 資料目錄表

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

這其中的大部分結構都不重要,不過有幾個是很重要的。

(1).AddressOfEntryPoint 欄位

指出檔案被執行時的入口地址,這是一個RVA 地址(RVA 的含義在下一節中詳細介紹)。如

果在一個可執行檔案上附加了一段程式碼並想讓這段程式碼首先被執行,那麼只需要將這個入口

地址指向附加的程式碼就可以了。

(2). ImageBase 欄位

指出檔案的優先裝入地址。也就是說當檔案被執行時,如果可能的話,Windows 優先將檔案裝入到由ImageBase 欄位指定的地址中,只有指定的地址已經被**模組使用時,檔案才被裝入到**地址中。連結器產生可執行檔案的時候對應這個地址來生成機器碼,所以當檔案被裝入這個地址時不需要進行重定位操作,裝入的速度最快,如果檔案被裝載到**地址的話,將不得不進行重定位操作,這樣就要慢一點。對於EXE 檔案來說,由於每個檔案總是使用獨立的虛擬地址空間,優先裝入地址不可能被**模組佔據,所以EXE 總是能夠按照這個地址裝入,這也意味著EXE 檔案不再需要重定位資訊。對於DLL 檔案來說,由於多個DLL 檔案全部使用宿主EXE 檔案的地址空間,不能保證優先裝入地址沒有被**的DLL 使用,所以DLL 檔案中必須包含重定位資訊以防萬一。因此,在前面介紹的IMAGE_FILE_HEADER 結構的Characteristics 欄位中,DLL 檔案對應的IMAGE_FILE_RELOCS_STRIPPED 位總是為0,而EXE 檔案的這個標誌位總是為1。在連結的時候,可以通過對link.exe 指定/base:address 選項來自定義優先裝入地址,如果不指定這個選項的話,一般EXE 檔案的預設優先裝入地址被定為00400000h,而DLL 檔案的預設優先裝入地址被定為10000000h。

(3). SectionAlignment 欄位和FileAlignment 欄位

SectionAlignment 欄位指定了節被裝入記憶體後的對齊單位。也就是說,每個節被裝入的地址必定是本欄位指定數值的整數倍。而FileAlignment 欄位指定了節儲存在磁碟檔案中時的對齊單位。

(4).Subsystem 欄位

指定使用介面的子系統,在windows下可選的子系統為CUI和GUI。這個欄位決定了系統如何為程式建立初始的介面,連結時的/subsystem:**選項指定的就是這個欄位的值,在前面章節的程式設計中我們早已知道:如果將子系統指定為Windows CUI,那麼系統會自動為程式建立一個控制檯視窗,而指定為Windows GUI 的話,視窗必須由程式自己建立。

4.資料目錄(Data Directory)

在結構IMAGE_NT_HEADER 中有一個結構IMAGE_OPTINAL_HEADER 結構,在IMAGE_OPTINAL_HEADER 中又有一個欄位,這個欄位是一個結構陣列IMAGE_DATA_DIREECTORY[]。這個結構陣列中的資料的型別全部都是IMAGE_DATA_HEADER 結構,這個結構中有兩個成員,一個是RVA(相對虛擬地址),一個是Size(大小)。它的定義如下:

Typedefe IMAGE_DATA_DIRECTORT

{

DWORD VirtualAddress ;

DWORD Size ;

} IMAGE_DATA_DIRECTORT ;

在windows 系統撞在PE 可執行檔案時,往往需要很快摘到一些裝載需要的資料結構,比如匯入表,匯出表,資源,重定位表等等。這些常用的資料結構的位置和長度都被儲存在IMAGE_DATA_HEADER 結構裡。在這個數組裡面,每一個元素都對應一個一個包含一定資訊的表。比如陣列的第一個元素就是記錄的是匯出表的地址的長度。


5.匯入表(Import table )

在程式碼分析或程式設計中經常遇到“輸入函式(Import Functions,也稱匯入函式)”的概念。這裡我們就來解釋下,輸入函式就是被程式呼叫但其執行程式碼又不在程式中的函式,這些函式的程式碼位於相關的DLL 檔案中,在呼叫者程式中只保留相關的函式資訊(如函式名、DLL 檔名等)就可以。

對於磁碟上的PE 檔案來說,它無法得知這些輸入函式在記憶體中的地址,只有當PE 檔案被裝入記憶體後,Windows 載入器才將相關DLL 裝入,並將呼叫輸入函式的指令和函式實際所處的地址聯絡起來。這就是“動態連結”的概念。動態連結是通過PE 檔案中定義的“輸入表”來完成的,輸入表中儲存的正是函式名和其駐留的DLL 名等。

在PE 檔案頭的IMAGE_OPTIONAL_HEADER 結構中的DataDirectory(資料目錄表) 的第二個成員就是指向輸入表的。而輸入表是以一個IMAGE_IMPORT_DESCRIPTOR(簡稱IID)

的陣列開始。每個被PE 檔案連結進來的DLL 檔案都分別對應一個IID 陣列結構。在這個

IID 陣列中,並沒有指出有多少個項(就是沒有明確指明有多少個連結檔案),但它最後是以

一個全為NULL(0) 的IID 作為結束的標誌。

IMAGE_IMPORT_DESCRIPTOR STRUCT

{

union

Characteristics DWORD ?

OriginalFirstThunk DWORD ?

ends

TimeDateStamp DWORD ?

ForwarderChain DWORD ?

Name DWORD ?

FirstThunk DWORD ?

}

IMAGE_IMPORT_DESCRIPTOR ENDS 



五.塊表(Section Table)

緊接著PE檔案的頭部就是section table(段表)了,它記錄著pe檔案中每一個區(段)的資訊。塊在映像中是按起始地址排(RVA)列的。它的定義如下:

typedef struct _IMAGE_SECTION_HEADER {BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//節表名稱,如“.text” //IMAGE_SIZEOF_SHORT_NAME=8union {DWORD PhysicalAddress;//實體地址DWORD VirtualSize;//真實長度,這兩個值是一個聯合結構,可以使用其中的任何一個,//一般是節的資料大小} Misc;DWORD VirtualAddress;//RVADWORD SizeOfRawData;//物理長度DWORD PointerToRawData;//節基於檔案的偏移量DWORD PointerToRelocations;//重定位的偏移DWORD PointerToLinenumbers;//行號表的偏移WORD NumberOfRelocations;//重定位項數目WORD NumberOfLinenumbers;//行號表的數目DWORD Characteristics;//節屬性 如可讀,可寫,可執行等} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 

1.VirtualSize:該塊的真實長度。和SizeOfRawData不一樣,是塊對齊前的長度。

2. VirtualAddress:該塊裝載到記憶體中的RVA。這個地址是按照記憶體頁對齊的,它的數值總是SectionAlignment的整數倍。

3. SizeOfRawData:該塊在磁碟中所佔的大小。在可執行檔案中,該欄位包含經過FileAlignment調整後的塊的長度。

4.PointerToRawData:該塊在磁碟中的偏移。程式經過編譯或彙編後生成原始資料,這個欄位用於給原始資料在檔案中的偏移

5.Characteristics:塊屬性。該欄位是一組指出塊屬性(如程式碼/資料/可讀/可寫等)的標誌。


六.塊(Section)結構

1.text塊

.text是在編譯或者彙編結束時產生的一種塊。它的內容全是指令程式碼。PE檔案執行在32方式下,不受16位的約束,所以沒有理由把程式碼放到不同的sections中。連結器把所有目標檔案的.text塊連結成一個大塊的.text中。

2.data塊

如同.text是預設的程式碼塊一樣,.data是初始化的資料塊。這些資料包括編譯時被初始化的global和static變數,也包括字串。連結器把obj及lib檔案的.data結合成一個大的.data。Local資料可以放在一個線性的堆疊中,不佔.data和.bbs的空間。

 3.idata

.idata包含其他外來的DLL的函式及資料資訊,即輸入表。該功能與NE檔案的模組引用表類似,關鍵的差異在於PE檔案中的每一個輸入函式都明確地列於該塊中。要在NE格式中找到相同的資訊,必須從各段的重定位資料中查詢。

4 .rsrc

.rsrc包含模組的全部資源資料,如圖表,選單,點陣圖等。

5.reloc

.reloc儲存基地址重定位表。當裝載程式不能按連結器所指定的地址裝載檔案時,需要對指令或已初始化的變數進行調整,基址重定位表包含調整所需的資料,如果裝載程式能正常裝載檔案,它就忽略.reloc中的重定位資料。

6.edata

.edata是該PE檔案的輸出表,以供其他模組引用。PE格式檔案沒有必要輸出一個函式,所以通常只是在DLL檔案中才能看到.edata塊。

7.rdata

.rdata塊通常是在.data或.bbs中間,但程式很少用到該塊中的資料。

8.tls

TLS的意思是”執行緒區域性儲存器。

其他常見的塊:.debug$s , .bbs等。

關於PE檔案的詳解,大家看以參考魚C論壇小甲魚的加解密系統篇的視訊,講的很贊。




相關推薦

PE檔案------PE檔案結構剖析

一.PE檔案結構縱覽 PE檔案的結構如上圖所示,由低地址到高地址分別為:Dos頭,PE頭,塊表,塊,除錯資訊。其中真正的PE檔案頭是位於Dos頭的後面的部分。 上圖為利用PE工具開啟的一個可執行檔案在磁碟中的對映,這就是PE檔案的內部資訊。 可以看到這個檔案的錢兩個

redis配置檔案配置檔案redis.conf

配置檔案redis.conf daemonize yes #---預設值no,該引數用於定製redis服務是否以守護模式執行。--- pidfile /var/run/redis.pid #預設值/var/run/redis.pid,指定redis服務的程序號檔案路徑

PE檔案(一)--資料結構欄位

typedef struct _IMAGE_OPTIONAL_HEADER{//// Standard fields.//+18h WORD Magic; // 標誌字, ROM 映像(0107h),普通可執行檔案(010Bh)+1Ah BYTE MajorLinkerVersion; // 連結程式的主版

PE檔案(二)--節表和節

SizeOfRawData:該區塊在磁碟中所佔的大小。在可執行檔案中,該欄位是已經被FileAlignment 潛規則處理過的長度。PointerToRawData:該區塊在磁碟中的偏移。這個數值是從檔案頭開始算起的偏移量哦。PointerToRelocations:這哥們在EXE檔案中沒有意義,在OBJ

MyBatis的體系結構和配置檔案

一、SqlSessionFactory MyBatis 的應用都是以一個 SqlSessionFactory 的例項為中心的,它是單個數據庫對映關係經過編譯後的記憶體映象;SqlSessionFactory 的例項可以通過 SqlSessionFactoryBuilder 獲得。而 SqlSes

Linux樹狀結構檔案

Linux樹狀檔案詳解 /           根目錄 /bin        存放使用者可執行程式 /boot       存放系統啟動時所需檔案、核心檔案 /dev      

三大框架(ssh)學習——配置檔案(一)

配置檔案詳解 指定web應用預設字符集 <constant name="struts.i18n.encoding" value="gbk" /> 此配置相當於: request.setCharacterEncoding(“gbk”); r

三大框架(ssh)學習——配置檔案(二)

多配置檔案實現 專案經常需要多人協作開發,但是如果大家都是用同一個struts.xml檔案,會互相影響不利於開發的正常開展。這樣,我們可以通過<include>元素指定多個配置檔案。 可以在src下面建立多個struts配置檔案。然後再struts.xml中分別引入: str

xxx.launch檔案(部落格學習筆記)

ROS筆記(一)xxx.launch檔案詳解 .launch檔案是ROS中用於同時啟動多個節點的重要檔案,在大型的ROS專案中使用頻繁,所以掌握其主要元素與屬性對ROS系統的應用至關重要: launch標籤(元素)說明 launch拓展說明 parameter說明

Nginx配置配置檔案

文章目錄 配置檔案 nginx.conf配置檔案詳解 用於除錯、定位問題的配置引數 正常執行必備的配置引數 優化效能的配置引數 事件相關配置 Fastcgi相關配置引數 常需要調整的引數 nginx作為web伺服器時使

Django中static(靜態)檔案以及{% static %}標籤的使用

想要深入學習Django的可以看一些這個視訊:超細講解Django打造大型企業官網 在一個網頁中,不僅僅只有一個html骨架,還需要css樣式檔案,js執行檔案以及一些圖片等。因此在DTL中載入靜態檔案是一個必須要解決的問題。在DTL中,使用static標籤來載入靜態檔案。要使用stat

Hibernate_day01---Hibernate環境搭建、配置檔案、核心api介紹

JavaEE三層結構對應的框架 1) web層:struts2框架 2) service層:spring框架 3)dao層:hibernate框架 -- 對資料庫進行crud操作 什麼是框架: 可複用的設計構件 作用:可以少寫一部分程式碼。使用框架寫程式,會幫我們實現一部

Linux(CentOS)開機自動掛載與fstab檔案

摘要: Linux中我們分完區,並做好檔案系統格式化,掛載(mount)之後才可以使用磁碟裝置。/etc/fstab是用來存放檔案系統的靜態資訊的檔案, 當系統啟動的時候,系統會自動地從這個檔案讀取資訊,並且會自動將此檔案中指定的檔案系統掛載到指定的目錄。 Linux中我們分完區,並做

build.gralde檔案

AS中APP所有的配置盡在一個build.gradle檔案中,打包的時候也是解析build.gralde檔案來打包的,所以搞懂build.gradle檔案是至關重要的,結構如下所示 1、apply plugin用來指定用的是哪個外掛,

scrapy settings配置檔案

# -*- coding: utf-8 -*- # Scrapy settings for step8_king project # # For simplicity, this file contains only settings considered important or # comm

nsswitch.conf檔案

Linux系統下的/etc/nsswitch.conf檔案 轉載自:https://www.cnblogs.com/besharp/p/8351227.html 一、什麼是nsswithch.conf(服務搜尋順序)檔案呢?       &n

keepalived介紹及配置檔案

keepalived介紹 Keepalived軟體起初是專為LVS負載均衡軟體設計的,用來管理並監控LVS集群系統中各個服務節點的狀態,後來又加入了可以實現高可用的VRRP功能。因此,Keepalived除了能夠管理LVS軟體外,還可以作為其他服務(例如:Nginx、Hapr

爬蟲Scrapy框架的setting.py檔案

  # -*- coding: utf-8 -*-   # Scrapy settings for demo1 project # # For simplicity, this file contains only setting

Maven的pom.xml配置檔案

轉自: Maven的pom.xml配置檔案詳解  <!--父專案的座標。如果專案中沒有規定某個元素的值,那麼父專案中的對應值即為專案的預設值。 座標包括group ID,artifact ID和 version。-->  &

Spring MVC 配置檔案dispatcher-servlet.xml 檔案

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframew