PE頭詳細分析

HackLee發表於2021-11-02

PE頭詳細分析

0x00 前言

最近我在學習Linux PWN相關知識的時候,也是在看《程式設計師的自我修養(裝載->連結->庫)》這本書的時候,接觸到了可執行檔案格式,COFF、ELF、PE,所以也找了Bilibili上海東老師的《滴水逆向三期》視訊中的PE課程在學習,剛看完PE頭這節課。在此做個筆記也算是整理學習的結果 並且分享給大家,共同學習,如有錯誤歡迎指正。

0x01 PE檔案介紹

PE檔案是Windows上的可執行檔案,就是我們滑鼠雙擊就能執行的程式,當然也有雙擊不能執行的程式。其中.exe.dll.sys這些都是PE檔案,那麼讀者可能有個疑問,我雙擊.txt的檔案也是能直接開啟執行的啊?Emmmmm....這個其實他是用Notepad記事本載入並開啟的,而PE可執行檔案他是經過系統載入並執行的。

image-20211102102833590

PE檔案是分塊儲存的。在圖中我們可以看出資料被載入到記憶體後會不一樣,其中PE檔案被載入到記憶體中的時候(相當於一個拉伸的過程),把資料給拉長了。

不過塊中的資料還是一樣的,我們可以看到最開頭的快就是PE頭的資料,接下來是節表等其他塊的資料,這些我們會在後續文章中介紹。

image-20211102111812462

0x02 PE頭詳細分析

PE主要由3部分構造,(1)、DOS頭 (2)、NT頭(標準PE頭、可選PE頭)。

參考圖:(OpenRCE.org網站上的PE格式圖.pdf)

image-20211102112750039

DOS頭解析

DOS頭中的資料如下,其中帶*號的是比較重要的資料,DOS頭大小為:64位元組。

//--> DOS頭(_IMAGE_DOS_HEADER ) <--
struct _IMAGE_DOS_HEADER
{
	WORD  e_magic;   //*DOS頭魔數 Magic*
	WORD  e_cblp;    //[Bytes on last page]
	WORD  e_cp;      //[Pages in file]
	WORD  e_crlc;    //[Relocations]
	WORD  e_cparhdr; //[Size of header]
	WORD  e_minalloc;//[Minium memory]
	WORD  e_maxalloc;//[Maxium Memory]
	WORD  e_ss;      //[Inital SS value]
	WORD  e_sp;      //[Inital SP value]
	WORD  e_csum;    //[Checksum]
	WORD  e_ip;      //[Inital IP value]
	WORD  e_cs;      //[Inital CS value]
	WORD  e_lfarlc;  //[Table offset]
	WORD  e_ovno;    //[Overlay number]
	WORD  e_res[4];  //[Reserved words]
	WORD  e_oemid;   //[OEM id]
	WORD  e_oeminfo; //[OEM infomation]
	WORD  e_res2[10];//[Reserved words]
	DWORD e_lfanew;  //*NT頭地址*
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

image-20211102113315651

NT頭解析

NT頭主要由3部分構成,標記、標準PE頭、可選PE頭。其中NT頭魔數就是PE字串,可見上圖。

//--> NT頭(_IMAGE_NT_HEADERS) <--
struct _IMAGE_NT_HREADERS
{
	DWORD Signature;//*NT頭魔數
	_IMAGE_FILE_HEADER FileHeader;//標準PE頭
	_IMAGE_OPTIONAL_HEADER OptionalHeader;//可選PE頭
}IMAGE_NT_HREADERS,*PIMAGE_NT_HREADERS;

標準PE頭解析

標誌PE頭的固定大小是20位元組,其中我們可以看見裡面有區段數目的資料。

還有比較關注的點是時間戳,這個時間戳我們可以用文章https://www.cnblogs.com/17bdw/p/6412158.html中的方法轉換成檔案建立時間

最後特徵的資料也要關注下,因為可以用他來判斷PE檔案的許多特徵資訊,比如是否為DLL檔案、重定位資訊是否被移去、是否為系統檔案等等。

//--> 標準PE頭(_IMAGE_FILE_HEADER) <--
struct _IMAGE_FILE_HEADER
{
	WORD Machine;//*執行平臺
	WORD NumberOfSections;//*區段數目
	DWORD TimeDateStamp;//*時間戳
	DWORD PointerToSymbolTable;//[Pointer to COFF]
	DWORD NumberOfSymbols;//[COFF table size]
	WORD SizeOfOptionalHeader;//*可選PE頭大小
	WORD Characteristics;//*特徵
}IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;

HEX資料

image-20211102115557549

解析後

image-20211102114951403

image-20211102115137714

image-20211102114847003

可選PE頭解析

可選PE頭結構

//--> 可選PE頭(_IMAGE_OPTIONAL_HEADER) <--
struct _IMAGE_OPTIONAL_HEADER
{
	WORD Magic;//[可選PE頭魔數]
	BYTE MajorLinkerVersion;//[主連結器版本]
	BYTE MinorLinkerVersion;//[副連結器版本]
	DWORD SizeOfCode;//[程式碼段大小]
	DWORD SizeOfInitializedData;//[初始化資料大小]
	DWORD SizeOfUninitializedData;//[未初始化資料大小]
	DWORD AddressOfEntryPoint;//*[程式入口點]
	DWORD BaseOfCode;//*[程式碼段地址]
	DWORD BaseOfData;//*[資料段地址]
	DWORD ImageBase;// *[載入到記憶體的開始地址] -> 一般好像都是0x400000
	DWORD SectionAlignment;//*[記憶體頁對其大小]
	DWORD FileAlignment;//[檔案對其大小]
	WORD MajorOperatingSystemVersion;//*[作業系統的主版本號]
	WORD MinjorOperatingSystemVersion;//*[作業系統的次版本號]
	WORD MajorImageVersion;//[程式主版本號]
	WORD MinorImageVersion;//[程式次版本號]
	WORD MajorSubsystemVersion;//[子系統主版本號]
	WORD MinorSubsystemVersion;//[子系統次版本號]
	DWORD Win32VersionValue;//[預設保留]
	DWORD SizeOfImage;//*[載入到記憶體映像的大小]
	DWORD SizeOfHeaders;//[DOS頭 PE頭 節頭組合大小]
	DWORD CheckSum;//*[獲取載入到記憶體映像的hash]
	WORD Subsystem;//[執行此映像所需要的子系統名稱]
	WORD DllCharacteristics;//[DLL映像的特徵]
	DWORD SizeOfStackReserve;//*[獲取保留堆疊的大小]
	DWORD SizeOfStackCommit;//*[獲取要提交堆疊的大小]
	DWORD SizeOfHeapReserve;//*[獲取保留堆空間的大小]
	DWORD SizeOfHeapCommit;//*[獲取要提交的本地堆空間大小]
	DWORD LoaderFlags;//[之前保留的成員]
	DWORD NumberOfRvaAndSizes;//*[獲取 PEHeader 剩餘部分中資料目錄項的數目 |位置和大小]
	_IMAGE_DATA_DIRECTORY DataDirectory[16];//[指向資料目錄中的第一個 IMAGE_DATA_DIRECTORY 結構的指標。]
}IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER;

可以看出可選PE頭資料最多,不過這是好事對我們有用的資料也很多,比如下面標紅的都是平時在逆向或者脫殼、破解中比較有用的資訊。

image-20211102120641106

基址

首先第一個魔數0x010B我們就不說了,主要應該是為了區別32位和64位程式吧。

接著我們看程式基地址0x01000000,這個基地址的意思就是程式載入到記憶體時候PE檔案所在的位置,Windows他會為每個程式分配一個虛擬的4GB空間。

這裡有的讀者會問那為什麼基址非要那麼大,為什麼不是0呢?因為Windows他有一段記憶體是用來保護用的,平時我們在寫C++程式碼時候比如:我們引用了一個NULL(空指標)其指向的記憶體地址就是0的時候,程式就會崩潰就會報錯!,沒錯這就是Windows為了保護程式設計的。

我們也可以用Winhex開啟載入在記憶體中Notepad.exe的資料,可以發現其首地址就是程式基址。

image-20211102121506217

程式碼段地址

接著我們來看程式碼段的地址0x001000,也就是PE檔案在這地方開始存的都是程式程式碼,當然在底層被轉為了彙編程式碼。

image-20211102122000641

我們可以用radare2套件中的rasm2來將十六進位制轉換成彙編程式碼看看。

rasm2 -a x86 -b 32 -d "十六進位制"
#-a 代表平臺 x86架構平臺
#-b 位數     32位
#-d 解碼     解析成彙編

image-20211102122324910

資料段地址

接下來看資料段的地址0x009000,資料段主要存放資料,比如字串等資料,在下圖中能看到存放了Notepad字串。

image-20211102132149727

OEP程式入口點

OEP是可選PE頭結構體中第7個成員AddressOfEntryPoint的資料,顧名思義指的是程式開始執行的第一行程式碼位置。

由於程式被載入到記憶體,所以我們還需要加上基址才是真實的程式入口點。

即:基址+OEP = 0x01000000 + 0x739D = 0x0100739D

image-20211102132902638

我們可以用工具將其轉換成彙編,看看第一行彙編程式碼是什麼?

image-20211102133015445

我們也可以利用OD來載入程式,OD載入程式預設會自動載入到OEP處。所以可以來驗證下我們找的位置對不對。

image-20211102133152033

好了可選頭PE內容介紹到這裡就結束了,其中可選PE頭還有最有一個結構體資料_IMAGE_DATA_DIRECTORY,這個暫時就先不介紹了,留在後面介紹其他內容的時候在詳解。

0x03 PE頭解析工具編寫

知道了PE頭結構後,程式碼寫起來也是很方便,而且微軟有自帶的PE頭結構體,我們可以直接open()檔案後直接讀取內容到結構體解析即可。

/*******************************************************
*
* 學習滴水逆向 PE結構分析程式碼練習
*
* 海東老師 Bilibili:滴水逆向三期
*********************************************************/

//標頭檔案定義
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <Windows.h>
using namespace std;

//--------------------------PE結構----------------------------------
//--> DOS頭(_IMAGE_DOS_HEADER ) <--
struct _IMAGE_DOS_HEADER_2
{
	WORD  e_magic;   //*DOS頭魔數 Magic*
	WORD  e_cblp;    //[Bytes on last page]
	WORD  e_cp;      //[Pages in file]
	WORD  e_crlc;    //[Relocations]
	WORD  e_cparhdr; //[Size of header]
	WORD  e_minalloc;//[Minium memory]
	WORD  e_maxalloc;//[Maxium Memory]
	WORD  e_ss;      //[Inital SS value]
	WORD  e_sp;      //[Inital SP value]
	WORD  e_csum;    //[Checksum]
	WORD  e_ip;      //[Inital IP value]
	WORD  e_cs;      //[Inital CS value]
	WORD  e_lfarlc;  //[Table offset]
	WORD  e_ovno;    //[Overlay number]
	WORD  e_res[4];  //[Reserved words]
	WORD  e_oemid;   //[OEM id]
	WORD  e_oeminfo; //[OEM infomation]
	WORD  e_res2[10];//[Reserved words]
	DWORD e_lfanew;  //*PE檔案頭地址*
} IMAGE_DOS_HEADER_2, *PIMAGE_DOS_HEADER_2;
//--> NT頭(_IMAGE_NT_HEADERS) <--
struct _IMAGE_NT_HREADERS_2
{
	DWORD Signature;
	_IMAGE_FILE_HEADER FileHeader;
	_IMAGE_OPTIONAL_HEADER OptionalHeader;
}IMAGE_NT_HREADERS_2,*PIMAGE_NT_HREADERS_2;
//--> 標準PE頭(_IMAGE_FILE_HEADER) <--
struct _IMAGE_FILE_HEADER_2
{
	WORD Machine;
	WORD NumberOfSections;
	DWORD TimeDateStamp;
	DWORD PointerToSymbolTable;
	DWORD NumberOfSymbols;
	WORD SizeOfOptionalHeader;
	WORD Characteristics;
}IMAGE_FILE_HEADER_2,*PIMAGE_FILE_HEADER_2;
//--> 可選PE頭(_IMAGE_OPTIONAL_HEADER) <--
struct _IMAGE_OPTIONAL_HEADER_2
{
	WORD Magic;//[可選PE頭魔數]
	BYTE MajorLinkerVersion;//[主連結器版本]
	BYTE MinorLinkerVersion;//[副連結器版本]
	DWORD SizeOfCode;//[程式碼段大小]
	DWORD SizeOfInitializedData;//[初始化資料大小]
	DWORD SizeOfUninitializedData;//[未初始化資料大小]
	DWORD AddressOfEntryPoint;//*[程式入口點]
	DWORD BaseOfCode;//*[程式碼段地址]
	DWORD BaseOfData;//*[資料段地址]
	DWORD ImageBase;// *[載入到記憶體的開始地址] -> 一般好像都是0x400000
	DWORD SectionAlignment;//*[記憶體頁對其大小]
	DWORD FileAlignment;//[檔案對其大小]
	WORD MajorOperatingSystemVersion;//*[作業系統的主版本號]
	WORD MinjorOperatingSystemVersion;//*[作業系統的次版本號]
	WORD MajorImageVersion;//[程式主版本號]
	WORD MinorImageVersion;//[程式次版本號]
	WORD MajorSubsystemVersion;//[子系統主版本號]
	WORD MinorSubsystemVersion;//[子系統次版本號]
	DWORD Win32VersionValue;//[預設保留]
	DWORD SizeOfImage;//*[載入到記憶體映像的大小]
	DWORD SizeOfHeaders;//[DOS頭 PE頭 節頭組合大小]
	DWORD CheckSum;//*[獲取載入到記憶體映像的hash]
	WORD Subsystem;//[執行此映像所需要的子系統名稱]
	WORD DllCharacteristics;//[DLL映像的特徵]
	DWORD SizeOfStackReserve;//*[獲取保留堆疊的大小]
	DWORD SizeOfStackCommit;//*[獲取要提交堆疊的大小]
	DWORD SizeOfHeapReserve;//*[獲取保留堆空間的大小]
	DWORD SizeOfHeapCommit;//*[獲取要提交的本地堆空間大小]
	DWORD LoaderFlags;//[之前保留的成員]
	DWORD NumberOfRvaAndSizes;//*[獲取 PEHeader 剩餘部分中資料目錄項的數目 |位置和大小]
	_IMAGE_DATA_DIRECTORY DataDirectory[16];//[指向資料目錄中的第一個 IMAGE_DATA_DIRECTORY 結構的指標。]
}IMAGE_OPTIONAL_HEADER_2,*PIMAGE_OPTIONAL_HEADER_2;
//-----------------------------------------------------------------


int main(int args,char *argv[])
{
	if (args < 2)
	{
		printf("引數有誤,請按照如下格式呼叫本程式!\n");
		printf("PEAnysis.exe 程式名.exe\n");
		return 0;
	}
	//初始化可有顏色終端Handle
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//
	printf("===================PE Anysis-PE檔案分析程式工具!==========================\n\n");
	FILE* fp = fopen(argv[1], "rb");
	if (fp != NULL)
	{
		//讀取DOS頭
		fread(&IMAGE_DOS_HEADER_2, sizeof(IMAGE_DOS_HEADER_2), 1, fp);
		//跳轉到NT頭
		fseek(fp, IMAGE_DOS_HEADER_2.e_lfanew, 0);
		//讀取NT頭
		fread(&IMAGE_NT_HREADERS_2, sizeof(IMAGE_NT_HREADERS_2), 1, fp);

		printf("---------------PE頭資料----------------\n");
		//輸出DOS頭資訊
		cout << "--> DOS頭(_IMAGE_DOS_HEADER ) <--" << endl;
		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
		char szMagic[3] = { 0 };
		memcpy(szMagic, &IMAGE_DOS_HEADER_2.e_magic, 2);
		printf("*DOS頭魔數:0x%x|%s\n", IMAGE_DOS_HEADER_2.e_magic, szMagic);
		SetConsoleTextAttribute(handle, 0x07);
		printf("[Bytes on last page]:0x%x\n", IMAGE_DOS_HEADER_2.e_cblp);
		printf("[Pages in file]:0x%x\n", IMAGE_DOS_HEADER_2.e_cp);
		printf("[Relocations]:0x%x\n", IMAGE_DOS_HEADER_2.e_crlc);
		printf("[Size of header]:0x%x\n", IMAGE_DOS_HEADER_2.e_cparhdr);
		printf("[Minium memory]:0x%x\n", IMAGE_DOS_HEADER_2.e_minalloc);
		printf("[Maxium Memory]:0x%x\n", IMAGE_DOS_HEADER_2.e_maxalloc);
		printf("[Inital SS value]:0x%x\n", IMAGE_DOS_HEADER_2.e_ss);
		printf("[Inital SP value]:0x%x\n", IMAGE_DOS_HEADER_2.e_sp);
		printf("[Checksum]:0x%x\n", IMAGE_DOS_HEADER_2.e_csum);
		printf("[Inital IP value]:0x%x\n", IMAGE_DOS_HEADER_2.e_ip);
		printf("[Inital CS value]:0x%x\n", IMAGE_DOS_HEADER_2.e_cs);
		printf("[Table offset]:0x%x\n", IMAGE_DOS_HEADER_2.e_lfarlc);
		printf("[Overlay number]:0x%x\n", IMAGE_DOS_HEADER_2.e_ovno);
		printf("[Reserved words]:", IMAGE_DOS_HEADER_2.e_res);
		for (size_t i = 0; i < 4; i++)
		{
			printf("0x%x, ", IMAGE_DOS_HEADER_2.e_res[0]);
		}
		cout << endl;
		printf("[OEM id]:0x%x\n", IMAGE_DOS_HEADER_2.e_oemid);
		printf("[OEM infomation]:0x%x\n", IMAGE_DOS_HEADER_2.e_oeminfo);
		printf("[Reserved words]:", IMAGE_DOS_HEADER_2.e_res2);
		for (size_t i = 0; i < 10; i++)
		{
			printf("0x%x, ", IMAGE_DOS_HEADER_2.e_res2[0]);
		}
		cout << endl;
		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
		printf("*PE檔案頭地址:0x%x\n", IMAGE_DOS_HEADER_2.e_lfanew);
		SetConsoleTextAttribute(handle, 0x07);
		cout << "DOS頭大小:" << sizeof(IMAGE_DOS_HEADER_2) << endl;
		cout << endl;

		//輸出標準PE頭資訊
		cout << "--> 標準PE頭(_IMAGE_FILE_HEADER) <--" << endl;
		char szNTSignature[3] = { 0 };
		memcpy(szNTSignature, &IMAGE_NT_HREADERS_2.Signature, 2);
		printf("[NT頭標識]:%s\n", szNTSignature);
		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
		printf("*[執行平臺]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.Machine);
		printf("*[節數量]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections);
		printf("*[時間戳]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.TimeDateStamp);
		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
		struct  tm test_gmtime_s;
		errno_t err = gmtime_s(&test_gmtime_s, (time_t*)&IMAGE_NT_HREADERS_2.FileHeader.TimeDateStamp);
		printf("  檔案建立時間:%d年%d月%d日 %02d時:%02d分:%02d秒(周%d)\n", test_gmtime_s.tm_year + 1900, test_gmtime_s.tm_mon, test_gmtime_s.tm_mday,
			test_gmtime_s.tm_hour + 8, test_gmtime_s.tm_min, test_gmtime_s.tm_sec, test_gmtime_s.tm_wday);
		SetConsoleTextAttribute(handle, 0x07);
		printf("[Pointer to COFF]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.PointerToSymbolTable);
		printf("[COFF table size]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections);
		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
		printf("*[可選頭大小]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.SizeOfOptionalHeader);
		printf("*[特徵/特性]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.Characteristics);
		SetConsoleTextAttribute(handle, 0x07);
		cout << "標準PE頭大小:" << sizeof(IMAGE_NT_HREADERS_2.FileHeader) << endl;
		cout << endl;

		//輸出可選PE頭資訊
		cout << "--> 可選PE頭(_IMAGE_OPTIONAL_HEADER) <--" << endl;
		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
		printf("*[程式記憶體入口點]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.AddressOfEntryPoint + IMAGE_NT_HREADERS_2.OptionalHeader.ImageBase);
		printf("*[可選PE頭魔數]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.Magic);
		printf("*[主連結器版本]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MajorLinkerVersion);
		printf("*[副連結器版本]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MinorLinkerVersion);
		printf("*[程式碼段大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfCode);
		printf("*[初始化資料大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfInitializedData);
		printf("*[未初始化資料大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfUninitializedData);
		printf("*[程式碼段地址]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.BaseOfCode);
		printf("*[資料段地址]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.BaseOfData);
		printf("*[PE檔案基地址]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.ImageBase);
		printf("*[程式入口點]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.AddressOfEntryPoint);
		SetConsoleTextAttribute(handle, 0x07);
		printf("[記憶體對其大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SectionAlignment);
		printf("[檔案對其大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.FileAlignment);
		printf("[作業系統的主版本號]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MajorOperatingSystemVersion);
		printf("[作業系統的次版本號]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MinorOperatingSystemVersion);
		printf("[程式主版本號]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MajorImageVersion);
		printf("[程式次版本號]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MinorImageVersion);
		printf("[子系統主版本號]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MajorSubsystemVersion);
		printf("[子系統次版本號]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MinorSubsystemVersion);
		printf("[Win32版本值]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.Win32VersionValue);
		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
		printf("*[記憶體映像大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfImage);
		printf("*[DOS|PE|節頭大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfHeaders);
		printf("*[記憶體映像hash]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.CheckSum);
		SetConsoleTextAttribute(handle, 0x07);
		printf("[程式可以執行的系統]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.Subsystem);
		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
		printf("*[DLL映像的特徵]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.DllCharacteristics);
		printf("*[獲取保留堆疊的大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfStackReserve);
		printf("*[獲取要提交堆疊的大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfStackCommit);
		printf("*[獲取保留堆空間的大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfHeapReserve);
		printf("*[獲取要提交的本地堆空間大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfHeapCommit);
		SetConsoleTextAttribute(handle, 0x07);
		printf("[載入標誌(已廢棄)]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.LoaderFlags);
		printf("[獲取PEHeader剩餘部分資料,位置和大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.NumberOfRvaAndSizes);
		printf("[指向IMAGE_DATA_DIRECTORY結構指標]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.DataDirectory);
		cout << "可選PE頭大小:" << sizeof(IMAGE_NT_HREADERS_2.OptionalHeader) << endl;
		cout << endl;
		printf("---------------節表資料----------------\n");

		printf("===========================================================================\n\n");

	}
	else 
	{
		printf("檔案開啟失敗,請檢查是否被佔用!\n");
		return 0;
	}


	int x;
	cin >> x;

	return 0;
}

PE分析

最後歡迎大家加群:1145528880

Pwn菜雞學習小分隊群聊二維碼

相關文章