PE头详细分析

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菜鸡学习小分队群聊二维码

posted @ 2021-11-02 13:38  VxerLee昵称已被使用  阅读(529)  评论(0编辑  收藏  举报