PE文件结构的介绍并使用C++读取PE文件信息

14 篇文章 8 订阅

目录

PE头

Windows api 读取PE文件结构信息


PE文件结构分为五个部分:

  • DOS文件头

  • DOS加载模块

  • PE文件头

  • 区段表

  • 区段

PE头


windows环境中会直接跳过前两个部分直接从PE文件头开始,所以直接略过这两个部分。PE文件头大小为224字节左右,因为有个可选头所以实际长度不定。里面包含许多关于PE文件的整体信息,这些信息主要是描述PE文件的大概情况,但是更细节信息在区段表中。

如上图所示,PE文件头分为三个结构,第一个为签名字段,第二个为文件头字段,第三个为可选头字段。

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;//文件的属性值
}

image_optional_header字段


typedefstruct_IMAGE_OPTIONAL_HEADER
{
+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;

这里大多数选项都很重要,例如数据段代码段入口RVA,或者说内存中区块对齐大小与文件中区块对齐大小imagebase等等。下面给出几个建议熟悉的选项:

入口点EOP                #一般跟base of code值一样
基地址Imagebase      #一般为00400000
SectionAlignment      #一般为 0x1000
fileAligment                #一般为0x0200

转载自 ——Shanfenglan7

Windows api 读取PE文件结构信息

#include "stdafx.h"
#include <Windows.h>
 
extern void DirectoryString(DWORD dwIndex);
 
int _tmain(int argc, _TCHAR* argv[])
{
	//获取文件句柄
	HANDLE hFile = CreateFile(
		_T("D:\\PE.exe"),
		GENERIC_READ,
		0,
		NULL,
		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	//获取文件大小
	DWORD dwFileSize = GetFileSize(hFile, NULL);
	CHAR *pFileBuf = new CHAR[dwFileSize];
	//将文件读取到内存
	DWORD ReadSize = 0;
	ReadFile(hFile, pFileBuf, dwFileSize, &ReadSize, NULL);
 
	//判断是否为PE文件
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
	if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
	{
		//不是PE
		printf("不是PE文件\n");
		system("pause");
		return 0;
	}
 
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuf + pDosHeader->e_lfanew);
	if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
	{
		//不是PE文件
		printf("不是PE文件\n");
		system("pause");
		return 0;
	}
 
	//获取基本PE头信息
	//获取信息所用到的两个结构体指针	(这两个结构体都属于NT头)
	PIMAGE_FILE_HEADER		pFileHeader		= &(pNtHeader->FileHeader);
	PIMAGE_OPTIONAL_HEADER	pOptionalHeader	= &(pNtHeader->OptionalHeader);
	//输出PE头信息
	printf("================== 基 本 P E 头 信 息 ==================\n\n");
	printf("入 口 点:\t%08X\t", pOptionalHeader->AddressOfEntryPoint);
	printf("子 系 统:\t%04X\n", pOptionalHeader->Subsystem);
	printf("镜像基址:\t%08X\t", pOptionalHeader->ImageBase);
	printf("区段数目:\t%04X\n", pFileHeader->NumberOfSections);
	printf("镜像大小:\t%08X\t", pOptionalHeader->SizeOfImage);
	printf("日期时间标志:\t%08X\n", pFileHeader->TimeDateStamp);
	printf("代码基址:\t%08X\t", pOptionalHeader->BaseOfCode);
	printf("部首大小:\t%08X\n", pOptionalHeader->SizeOfHeaders);
	printf("数据基址:\t%08X\t", pOptionalHeader->BaseOfData);
	printf("特 征 值:\t%04X\n", pFileHeader->Characteristics);
	printf("块 对 齐:\t%08X\t", pOptionalHeader->SectionAlignment);
	printf("校 验 和:\t%08X\n", pOptionalHeader->CheckSum);
	printf("文件块对齐:\t%08X\t", pOptionalHeader->FileAlignment);
	printf("可选头部大小:\t%04X\n", pFileHeader->SizeOfOptionalHeader);
	printf("标 志 字:\t%04X\t\t", pOptionalHeader->Magic);
	printf("RVA数及大小:\t%08X\n\n", pOptionalHeader->NumberOfRvaAndSizes);
 
	printf("======================= 目 录 表 =======================\n");
	//获取目录表头指针
	PIMAGE_DATA_DIRECTORY pDataDirectory = pOptionalHeader->DataDirectory;
	printf("\t\t  RAV\t\t  大小\n");
	for (DWORD i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
	{
		DirectoryString(i);
		printf("%08X\t%08X\n",
			pDataDirectory[i].VirtualAddress, pDataDirectory[i].Size);
	}
 
	printf("======================= 区 段 表 =======================\n");
	//获取区段表头指针
	PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
	printf("名称      VOffset   VSize     ROffset   RSize     标志\n");
	//获取区段个数
	DWORD dwSectionNum = pFileHeader->NumberOfSections;
	//根据区段个数遍历区段信息
	for (DWORD i = 0; i < dwSectionNum; i++, pSectionHeader++)
	{
		for (DWORD j = 0; j < IMAGE_SIZEOF_SHORT_NAME; j++)
		{
			printf("%c", pSectionHeader->Name[j]);
		}
		printf("  %08X  %08X  %08X  %08X  %08X\n",
			pSectionHeader->VirtualAddress,
			pSectionHeader->Misc.VirtualSize,
			pSectionHeader->PointerToRawData,
			pSectionHeader->SizeOfRawData,
			pSectionHeader->Characteristics);
	}
	printf("\n");
 
	system("pause");
	return 0;
}
 
void DirectoryString(DWORD dwIndex)
{
	switch (dwIndex)
	{
	case 0:printf("输出表:\t\t");
		break;
	case 1:printf("输入表:\t\t");
		break;
	case 2:printf("资源:\t\t");
		break;
	case 3:printf("异常:\t\t");
		break;
	case 4:printf("安全:\t\t");
		break;
	case 5:printf("重定位:\t\t");
		break;
	case 6:printf("调试:\t\t");
		break;
	case 7:printf("版权:\t\t");
		break;
	case 8:printf("全局指针:\t");
		break;
	case 9:printf("TLS表:\t\t");
		break;
	case 10:printf("载入配置:\t");
		break;
	case 11:printf("输入范围:\t");
		break;
	case 12:printf("IAT:\t\t");
		break;
	case 13:printf("延迟输入:\t");
		break;
	case 14:printf("COM:\t\t");
		break;
	case 15:printf("保留:\t\t");
		break;
	}
}

上述代码原文

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值