PE头字段说明

一、NT头

1、PE标识

2、标准PE头

3、扩展PE头

二、标准PE头

ypedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;		*	//可以运行在什么样的CPU上   任意:0    Intel 386以及后续:14C   x64:8664  
    WORD    NumberOfSections;	*       //表示节的数量
    DWORD   TimeDateStamp;	*       //编译器填写的时间戳 与文件属性里面(创建时间、修改时间)无关
    DWORD   PointerToSymbolTable;       //调试相关
    DWORD   NumberOfSymbols;	        //调试相关
    WORD    SizeOfOptionalHeader;*      //扩展PE头的大小(32位PE文件:0xE0  64位PE文件:0xF0)
    WORD    Characteristics;	  *     //文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;


三、扩展PE头

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD    Magic;		             *  //PE32:10B     PE32+:20B
    BYTE    MajorLinkerVersion;		        //链接器版本号
    BYTE    MinorLinkerVersion;		        //链接器版本号
    DWORD   SizeOfCode;		             *  //所有代码节的总和   文件对齐后的大小  编译器填的  没用
    DWORD   SizeOfInitializedData;	     *  //包含所有已经初始化数据的节的总大小  文件对齐后的大小 编译器填的  没用
    DWORD   SizeOfUninitializedData;	     *  //包含未初始化数据的节的总大小 文件对齐后的大小 编译器填的  没用
    DWORD   AddressOfEntryPoint;	     *  //程序入口
    DWORD   BaseOfCode;		             *  //代码开始的基址,编译器填的   没用
    DWORD   BaseOfData;		             *  //数据开始的基址,编译器填的   没用
    DWORD   ImageBase;		             *  //内存镜像基址  
    DWORD   SectionAlignment;		     *  //内存对齐
    DWORD   FileAlignment;		     *  //文件对齐
    WORD    MajorOperatingSystemVersion;	//标识操作系统版本号 主版本号
    WORD    MinorOperatingSystemVersion;	//标识操作系统版本号 次版本号
    WORD    MajorImageVersion;		        //PE文件自身的版本号 
    WORD    MinorImageVersion;		        //PE文件自身的版本号
    WORD    MajorSubsystemVersion;	        //运行所需子系统版本号
    WORD    MinorSubsystemVersion;	        //运行所需子系统版本号
    DWORD   Win32VersionValue;	                //子系统版本的值,必须为0
    DWORD   SizeOfImage;		     *  //内存中整个PE文件的映射的尺寸,可比实际的值大,必须是SectionAlignment的整数倍
    DWORD   SizeOfHeaders;		     *  //所有头+节表按照文件对齐后的大小,否则加载会出错
    DWORD   CheckSum;		             *  //校验和,一些系统文件有要求.用来判断文件是否被修改.
    WORD    Subsystem;		                //子系统	驱动程序(1)  图形界面(2)  控制台、DLL(3)
    WORD    DllCharacteristics;		        //文件特性 不是针对DLL文件的
    DWORD   SizeOfStackReserve;	             *  //初始化时保留的栈大小 
    DWORD   SizeOfStackCommit;	             *  //初始化时实际提交的大小 
    DWORD   SizeOfHeapReserve;	             *  //初始化时保留的堆大小
    DWORD   SizeOfHeapCommit;	             *  //初始化时实践提交的大小 
    DWORD   LoaderFlags;		        //调试相关
    DWORD   NumberOfRvaAndSizes;	     *  //目录项数目
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

四、扩展知识

1、虚拟内存

每个exe程序都有自己独立的4G内存空间,一个exe文件由一堆PE文件组成

2、ImageBase

ImageBase是exe在内存中的起始位置

3、程序入口

AddressOfEntryPoint + ImageBase 才是程序真正的入口

五、PE头打印

1、打开文件,并判断是否打开成功

FILE	*pFile		=	NULL;		
DWORD	fileSize	=	0;
LPVOID	pFileBuffer	=	NULL;


pFile = fopen(lpszFile,"rb");	//打开文件
if(!pFile)
{
	printf("打开文件失败");
	return NULL;
}

2、读取文件大小

//读取文件大小
fseek(pFile,0,SEEK_END);		//将指针从开始的位置移动到末尾
fileSize = ftell(pFile);		//获取数据大小

3、申请内存,并判断是否申请成功

#include "stdlib.h"		
//分配缓冲区(申请内存)
pFileBuffer = malloc(fileSize);
if(!pFileBuffer)
{
	printf("分配空间失败");
	fclose(pFile);
	return NULL;
}

4、将文件读取到内存中

	fseek(pFile,0,SEEK_SET);	//将指针指向开始
	size_t n = fread(pFileBuffer,fileSize,1,pFile);	//将数据读取到缓冲区中
	if(!n)
	{
		printf("读取数据失败");
		free(pFileBuffer);		//释放内存
		fclose(pFile);			//关闭文件
		return NULL;
	}


5、关闭文件

fclose(pFile);	

6、判断是否有效MZ标识

	//定义几个变量
	PIMAGE_DOS_HEADER			pDosHeader		=	NULL;	//接收DOS头
	PIMAGE_NT_HEADERS			pNTHeader		=	NULL;
	PIMAGE_FILE_HEADER			pPEHeader		=	NULL;
	PIMAGE_OPTIONAL_HEADER32	pOptionHeader	=	NULL;

	if(*((PWORD)pFileBuffer)	!=	IMAGE_DOS_SIGNATURE)
	{
		printf("不是有效的MZ头");
		free(pFileBuffer);
		return;
	}

7、打印DOS头

	pDosHeader	=	(PIMAGE_DOS_HEADER)pFileBuffer;		//类型转换 
	printf("MZ标志:	%x\n",pDosHeader->e_magic);
	printf("PE偏移:	%x\n",pDosHeader->e_lfanew);

8、判断是否为PE标识

	if(*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew))	!=	IMAGE_NT_SIGNATURE)
	{
		printf("不是有效的PE标志\n");
		free(pFileBuffer);
		return;
	}

9、打印PE文件标识

	pNTHeader	=	(PIMAGE_NT_HEADERS)((DWORD)pFileBuffer	+	pDosHeader->e_lfanew);	 //将指针移动到PE头开始位置,并且类型转换
	printf("*************************NT头*************************\n");
	printf("PE标识:	%x\n",pNTHeader->Signature);

10、打印标准PE头


	pPEHeader	=	(PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
	printf("*************************标准PE头*************************\n");
	printf("节的数量:	%x\n",pPEHeader->NumberOfSections);
	printf("可选PE头的大小:	%x\n",pPEHeader->SizeOfOptionalHeader);

11、打印可选PE头

	printf("*************************可选PE头*************************\n");
	pOptionHeader	=	(PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader	+	IMAGE_SIZEOF_FILE_HEADER);	
	printf("程序入口地址:	%x\n",pOptionHeader->AddressOfEntryPoint);
	printf("文件在内存中的入口地址:	%x\n",pOptionHeader->ImageBase);
	printf("文件对齐:	%x\n",pOptionHeader->FileAlignment);
	printf("内存对齐:	%x\n",pOptionHeader->SectionAlignment);
	printf("PE头在文件对齐后大小:	%x\n",pOptionHeader->SizeOfHeaders);
	printf("PE文件在内存对齐后大小:	%x\n",pOptionHeader->SizeOfImage);

12、释放内存

free(pFileBuffer);	

13、完整代码

#include "stdafx.h"
#include <windows.h>
#include "stdlib.h"	
#include "stdio.h"	


//读取文件
LPVOID ReadPEFile(LPSTR lpszFile)
{
	FILE	*pFile		=	NULL;		
	DWORD	fileSize	=	0;
	LPVOID	pFileBuffer	=	NULL;

	
	pFile = fopen(lpszFile,"rb");	//打开文件
	if(!pFile)
	{
		printf("打开文件失败");
		return NULL;
	}

	//读取文件大小
	fseek(pFile,0,SEEK_END);		//将指针从开始的位置移动到末尾
	fileSize = ftell(pFile);		//获取数据大小

	//分配缓冲区(申请内存)
	pFileBuffer = malloc(fileSize);
	if(!pFileBuffer)
	{
		printf("分配空间失败");
		fclose(pFile);
		return NULL;
	}

	//将文件数据读取到缓冲区
	fseek(pFile,0,SEEK_SET);	//将指针指向开始
	size_t n = fread(pFileBuffer,fileSize,1,pFile);	//将数据读取到缓冲区中
	if(!n)
	{
		printf("读取数据失败");
		free(pFileBuffer);		//释放内存
		fclose(pFile);			//关闭文件
		return NULL;
	}

	//关闭文件
	fclose(pFile);				//关闭文件
	return pFileBuffer;		//返回缓冲区的首地址
}


//*****************************************************************************************************************************
VOID PrintNTHeaders()
{

	LPVOID					pFileBuffer		=	NULL;
	PIMAGE_DOS_HEADER			pDosHeader		=	NULL;
	PIMAGE_NT_HEADERS			pNTHeader		=	NULL;
	PIMAGE_FILE_HEADER			pPEHeader		=	NULL;
	PIMAGE_OPTIONAL_HEADER32	        pOptionHeader	        =	NULL;

	//读取文件
	LPSTR	FILEPATH	=	"c:/notepad.exe";
	pFileBuffer	=	ReadPEFile(FILEPATH);
	if(!pFileBuffer)
	{
		printf("文件读取失败");
		return;
	}

	//判断是否是有效的MZ标志
	if(*((PWORD)pFileBuffer)	!=	IMAGE_DOS_SIGNATURE)
	{
		printf("不是有效的MZ头");
		free(pFileBuffer);
		return;
	}

	pDosHeader	=	(PIMAGE_DOS_HEADER)pFileBuffer;
	//打印DOS头
	printf("MZ标志:	%x\n",pDosHeader->e_magic);
	printf("PE偏移:	%x\n",pDosHeader->e_lfanew);

	//判断 是否是PE标志
	if(*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew))	!=	IMAGE_NT_SIGNATURE)
	{
		printf("不是有效的PE标志\n");
		free(pFileBuffer);
		return;
	}

//***************************************************************************************************
	printf("*************************PE头*************************\n");
	pNTHeader	=	(PIMAGE_NT_HEADERS)((DWORD)pFileBuffer	+	pDosHeader->e_lfanew);
	printf("PE标识:	%x\n",pNTHeader->Signature);

//***************************************************************************************************
	printf("*************************标准PE头*************************\n");
	pPEHeader	=	(PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
	printf("节的数量:	%x\n",pPEHeader->NumberOfSections);
	printf("可选PE头的大小:	%x\n",pPEHeader->SizeOfOptionalHeader);

//***************************************************************************************************
	printf("*************************可选PE头*************************\n");
	pOptionHeader	=	(PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader	+	IMAGE_SIZEOF_FILE_HEADER);	
	printf("程序入口地址:	%x\n",pOptionHeader->AddressOfEntryPoint);
	printf("文件在内存中的入口地址:	%x\n",pOptionHeader->ImageBase);
	printf("文件对齐:	%x\n",pOptionHeader->FileAlignment);
	printf("内存对齐:	%x\n",pOptionHeader->SectionAlignment);
	printf("PE头在文件对齐后大小:	%x\n",pOptionHeader->SizeOfHeaders);
	printf("PE文件在内存对齐后大小:	%x\n",pOptionHeader->SizeOfImage);
	


}

int main(int argc, char* argv[])
{
	PrintNTHeaders();
	getchar();
	return 0;
}

posted @ 2021-12-06 17:11  lnterpreter  阅读(119)  评论(0编辑  收藏  举报