1. 程式人生 > >《逆向工程核心原理》學習筆記(六)

《逆向工程核心原理》學習筆記(六)

form 定位 ngs release sof 操作系統 mina align get

《逆向工程核心原理》學習筆記(六)

記錄一下關於書中PE文件格式內容的學習(上部分)


PE(Portable Executable)格式,是微軟Win32環境可移植可執行文件(如exe、dll、vxd、sys和vdm等)的標準文件格式。

PE格式衍生於早期建立在VAX(R)VMS(R)上的COFF(Common Object File Format)文件格式。


PE文件種類

技術分享圖片


PE文件的基本結構

技術分享圖片

技術分享圖片

技術分享圖片

.text
默認的代碼區塊,它的內容全是指令代碼,鏈接器把所有目標文件的text塊連接成一個大的.text塊,使用Borland C
++,編譯器產生的代碼存放在CODE的區域裏
.data 默認的讀
/寫數據塊,全局變量,靜態變量一般放在這個區段
.rdata 默認只讀數據區塊,但程序中很少用到該塊中的數據,一般兩種情況用到,一是MS 的鏈接器產生EXE文件中用於存放調試目錄,二是用於存放說明字符串,如果程序的DEF文件中指定了DESCRIPTION,字符串就會出現在rdata中
.idata 包含其他外來的DLL的函數及數據信息,即輸入表,將.idata區塊合並成另一個區塊已成為一種慣例,典型的是.rdata區塊,默認的,鏈接器只在創建一個Release模式的可執行文件時才能將idata合並到另外一個區塊中
.edata
輸出表,當創建一個輸出API或數據的可執行文件時,連接器會創建一個.EXP文件,這個.EXP文件包含一個.edata區塊,其會被加載到可執行文件中,經常被合並到.text或.rdata 區塊中
.rsrc 資源,包括模塊的全部資源,如圖標,菜單,位圖等,這個區塊是只讀的,無論如何不應該把它命名為.rsrc以外的名字,也不能合並到其他的區塊裏
.bss 未初始化的數據,很少在用,取而代之的是執行文件的.data區塊的的VirtualSize被擴展大的空間裏用來裝未初始化的數據.
.crt 用於C
++ 運行時(CRT)所添加的數據
.tls TLS的意思是線程局部存儲器,用於支持通過_declspec(thread)聲明的線程局部存儲變量的數據,這包括數據的初始化值,也包括運行時所需要的額外變量
.reloc 可執行文件的基址重定位,基址重定位一般僅Dll需要的
.sdata 相對於全局指針的可被定位的 短的讀寫數據
.pdata 異常表,包含CPU特定的IAMGE_RUNTIME_FUNTION_ENTRY結構數組,DataDirectory中的IMAGE_DIRECTORY_ENTRY_EXCEPTION指向它.
.didat 延遲裝入輸入數據,在非Release模式下可以找到


關於PE文件中那些"地址"

這裏我就用自己總結的白話來描述

虛擬地址(VA):

在一個程序運行起來的時候,會被加載到內存中,並且每個進程都有自己的4GB,這個4GB當中的某個位置叫做**虛擬地址**,由物理地址映射過來的,4GB的空間,並沒有全部被用到。

基地址(Imagebase):

磁盤中的文件加載到內存當中的時候可以加載到任意位置,而這個位置就是程序的基址。EXE默認的加載基址是400000h,DLL文件默認基址是10000000h。需要註意的是基地址不是程序的入口點。

相對虛擬地址(RVA):

為了避免PE文件中有確定的內存地址,引入了相對虛擬地址的概念。RVA是在內存中相對與載入地址(基地址)的偏移量,所以你可以發現前三個概念的關系:虛擬地址(VA)= 基地址+相對虛擬地址(RVA)

文件偏移地址(FOA):

當PE文件儲存在某個磁盤當中的時候,某個數據的位置相對於文件頭的偏移量。

入口點(OEP):

首先明確一個概念就是OEP是一個RVA,然後使用OEP +Imagebase ==入口點的VA,通常情況下,OEP指向的不是main函數。

PS:32位的windows系統,各進程會分配4gb的虛擬內存,所以VA範圍為00000000-FFFFFFFF




PE頭



DOS頭

每個PE文件都是以DOS頭開始的,IMAGE_DOS_HEADER 結構如下所示:

(最左邊是文件頭的偏移量。)

IMAGE_DOS_HEADER STRUCT
{
+0h WORD    e_magic//   MZ(4Dh 5Ah)     DOS可執行文件標記
+2h     WORD    e_cblp
+4h WORD    e_cp
+6h WORD    e_crlc
+8h WORD    e_cparhdr
+0ah    WORD    e_minalloc
+0ch    WORD    e_maxalloc
+0eh    WORD    e_ss
+10h    WORD    e_sp
+12h    WORD    e_csum
+14h    WORD    e_ip
+16h    WORD    e_cs
+18h    WORD    e_lfarlc
+1ah    WORD    e_ovno
+1ch    WORD    e_res[4]
+24h    WORD    e_oemid
+26h    WORD    e_oeminfo
+29h    WORD    e_res2[10]
+3ch    DWORD   e_lfanew//  RVA     指向PE文件頭
} IMAGE_DOS_HEADER ENDS

需要關註的點是結構體的第一個和第二個元素。

e_magic: DOS頭的標記位,值為 4D5Ah 。ASCII為 ”MZ“ ,判斷一個文件是否為PE文件是會用。

e_lfanew: 這是一個RVA,代表了PE文件頭到基址的偏移量,我們可以用它來找到PE文件頭的位置。

我們用010editor打開一個exe文件:

技術分享圖片


PE文件頭

IMAGE_NT_HEADERS STRUCT 結構體

IMAGE_NT_HEADERS STRUCT
{
+0h       DWORD    Signature
+4h       IMAGE_FILE_HEADER    FileHeader
+18h      IMAGE_OPTIONAL_HEADER32   OptionalHeader
} IMAGE_NT_HEADERS ENDS

Signature 字段

在一個PE文件中Signature字段被設置為 4550h ,ASCII碼為 ”PE00“ 。如上圖所示。

IMAGE_FILE_HEADER 結構體

structIMAGE_FILE_HEADER
{
WORD Machine;//運行平臺
WORD NumberOfSections;//區塊表的個數
DWORD TimeDataStamp;//文件創建時間,是從1970年至今的秒數
DWORD PointerToSymbolicTable;//指向符號表的指針
DWORD NumberOfSymbols;//符號表的數目
WORD SizeOfOptionalHeader;//IMAGE_NT_HEADERS結構中OptionHeader成員的大小,對於win32平臺這個值通常是0x00e0
WORD Characteristics;//文件的屬性值
}

在010 Editor上查看一下:

技術分享圖片

IMAGE_OPTIONAL_HEADER 結構體

typedefstruct_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    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
// 數據目錄表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

重要的有:

AddressOfEntryPoint: 也就是上文提到的OEP,程序源入口點。

 

ImageBase: 默認加載基址。

 

SectionAlignment: 內存當中的塊對齊數,一般為0x1000。

 

FileAlignment:磁盤當中塊對齊數,一般為0x200。

 

SizeOfHeaders:所有頭部大小 也就是DOS頭 文件頭 以及區塊頭的總大小,文件主體相對文件其實的偏移。

 

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]:數據目錄表,保存了各種表的RVA及大小。

來看一下數據目錄的定義:



IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress    DWORD       ?   ; 數據的起始RVA
Size             DWORD       ?   ; 數據塊的長度
IMAGE_DATA_DIRECTORY ENDS
 

技術分享圖片





最後附上pe文件格式的腦圖

技術分享圖片

《逆向工程核心原理》學習筆記(六)