解析Windows PE文件结构
推荐PE文件结构详解
Linux下C++实现
实现步骤
/*
lpszFileFullPath:文件路径
lpVersionBuf:获取的文件版本信息
lpVerBufLen:长度
*/
STATUS Version::GetFileVersion(const CHAR* lpszFileFullPath, LPSTR lpVersionBuf, PDWORD lpVerBufLen)
{
//获取文件句柄
STATUS status;
//指向文件缓存区指针
PVOID pFileBuf = nullptr;
//读取文件,获取文件句柄
Version::ReadFile(pFileBuf, lpszFileFullPath);
if(!pFileBuf){
strcpy(status.errorMsg, "pFileBuf is nullptr");
status.success = 0;
return status;
}
//判断是否为PE文件
//获取DOS头
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
if (!pDosHeader || pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
if(!pDosHeader)
strcpy(status.errorMsg, "pDosHeader is nullptr");
else
strcpy(status.errorMsg, "pDosHeader->e_magic != IMAGE_DOS_SIGNATURE");
status.success = 0;
return status;
}
//获取Nt头
//e_lfanew,这里是指pe的偏移量,用于找到pe头的位置
PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((size_t)pFileBuf + pDosHeader->e_lfanew);
if (!pNtHeader || pNtHeader->Signature != IMAGE_NT_SIGNATURE)
{
if(!pNtHeader)
strcpy(status.errorMsg, "pNtHeader is nullptr");
else
strcpy(status.errorMsg, "pNtHeader->e_magic != IMAGE_NT_SIGNATURE");
status.success = 0;
return status;
}
//获取可选头信息
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = &(pNtHeader->OptionalHeader);
if(!pOptionalHeader){
strcpy(status.errorMsg, "pOptionalHeader is nullptr");
status.success = 0;
return status;
}
//获取15种目录表头指针
//返回pDataDirectory是一个目录表的数组,有15种目录表,在这里获取的是IMAGE_DIRECTORY_ENTRY_RESOURCE资源目录表指针
PIMAGE_DATA_DIRECTORY pDataDirectory = pOptionalHeader->DataDirectory;
//需求根据相对虚拟地址VirtualAddress进行计算节,得到真实地址
PIMAGE_RESOURCE_DIRECTORY pResourceTable = (PIMAGE_RESOURCE_DIRECTORY)GetPtrFromRVA(pDataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress, pNtHeader, pFileBuf);
if(!pResourceTable){
strcpy(status.errorMsg, "pResourceTable is nullptr");
status.success = 0;
return status;
}
//根据获取的资源表指针进行三级循环遍历,获取需要的信息
bool flag = GetVersioninfo(status, pResourceTable, pNtHeader, pFileBuf, lpVersionBuf, lpVerBufLen);
if(!flag){
strcpy(status.errorMsg, "prase resource directory fail");
status.success = flag;
return status;
}
status.success = flag;
return status;
}
获取文件缓存区指针
size_t Version::ReadFile(PVOID& pFileBuffer, const CHAR* lpszFile)
{
FILE* pFile = nullptr;
size_t FileSize = 0;
size_t n = 0;
//打开文件
pFile = fopen(lpszFile, "rb");
if (pFile == nullptr)
{
printf("文件打开失败");
return 0;
}
//计算文件长度
fseek(pFile, 0, SEEK_END);
FileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
//分配缓冲区
pFileBuffer = new CHAR[FileSize];
if (pFileBuffer == nullptr)
{
printf("缓冲区分配失败");
fclose(pFile);
return 0;
}
//初始化缓冲区
memset(pFileBuffer, 0, FileSize);
n = fread(pFileBuffer, FileSize, 1, pFile);
if (n == 0)
{
fclose(pFile);
free(pFileBuffer);
pFileBuffer = nullptr;
return 0;
}
//关闭文件
fclose(pFile);
return n;
}
地址转换
PIMAGE_SECTION_HEADER Version::GetEnclosingSectionHeader(size_t rva, PIMAGE_NT_HEADERS32 pNTHeader) {
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
unsigned i;
for (i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++) {
DWORD size = section->Misc.VirtualSize;
if (0 == size)
size = section->SizeOfRawData;
// 检查RVA是否在此节内
if ((rva >= section->VirtualAddress) &&
(rva < (section->VirtualAddress + size)))
return section;
}
return 0;
}
PVOID Version::GetPtrFromRVA(size_t rva, PIMAGE_NT_HEADERS32 pNTHeader, PVOID imageBase) {
PIMAGE_SECTION_HEADER pSectionHdr;
int delta;
pSectionHdr = GetEnclosingSectionHeader(rva, pNTHeader);
if (!pSectionHdr)
return 0;
delta = (int)(pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData);
return (PVOID)((size_t)imageBase + rva - delta);
}
遍历资源目录,获取需要信息
资源表结构详解可以参考资源表
bool Version::GetVersioninfo(STATUS& status, PIMAGE_RESOURCE_DIRECTORY& pResourceTable, PIMAGE_NT_HEADERS32 pNtHeader, PVOID pFileBuf, PSTR szMsg, PDWORD dwMsgLen){
VS_VERSIONINFO* pVersionInfo = nullptr;
VS_FIXEDFILEINFO* pFixedFileInfo = nullptr;
//pResourceTable + 1 表示资源表指针后面紧跟着是对应的资源目录指针
PIMAGE_RESOURCE_DIRECTORY_ENTRY pResourceEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResourceTable + 1);
if(!pResourceEntry){
strcpy(status.errorMsg, "1,pResourceEntry is nullprt");
status.success = 0;
return false;
}
//解析第一层
//pResourceTable->NumberOfIdEntries + pResourceTable->NumberOfNamedEntries本目录中目录项的总和
size_t dwTypeCount = pResourceTable->NumberOfIdEntries + pResourceTable->NumberOfNamedEntries;
for (size_t i = 0; i < dwTypeCount; i++) {
PIMAGE_RESOURCE_DIRECTORY_ENTRY pDirEntry = &pResourceEntry[i];
if(!pDirEntry){
strcpy(status.errorMsg, "1, pDirEntry is nullptr");
status.success = 0;
return false;
}
//第二层
//id==16对应版本信息子目录
//pDirEntry->DataIsDirectory为1,表示还存在目录
if (pDirEntry->Id == 16 && pDirEntry->DataIsDirectory) {
// 获取版本信息的子目录
// pDirEntry->OffsetToDirectory偏移量,相对于最开始的资源表指针pResourceTable
//(size_t)pResourceTable转换地址最好使用较大的类型,否则发生截断,就会导致地址读取错误
PIMAGE_RESOURCE_DIRECTORY pVersionInfoDir = (PIMAGE_RESOURCE_DIRECTORY)((size_t)pResourceTable + pDirEntry->OffsetToDirectory);
if(!pVersionInfoDir){
strcpy(status.errorMsg, "2, pVersionInfoDir is nullptr");
status.success = 0;
return false;
}
size_t dwVersionCount = pVersionInfoDir->NumberOfIdEntries + pVersionInfoDir->NumberOfNamedEntries;
for (size_t j = 0; j < dwVersionCount; j++) {
PIMAGE_RESOURCE_DIRECTORY_ENTRY pVersionEntry = &((PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pVersionInfoDir + 1))[j];
if(!pVersionEntry){
strcpy(status.errorMsg, "2, pVersionEntry is nullptr");
status.success = 0;
return false;
}
//同上,解析第三层
if (pVersionEntry->DataIsDirectory) {
PIMAGE_RESOURCE_DIRECTORY pVersionDataDir = (PIMAGE_RESOURCE_DIRECTORY)((size_t)pResourceTable + pVersionEntry->OffsetToDirectory);
if(!pVersionDataDir){
strcpy(status.errorMsg, "3, pVersionDataDir is nullptr");
status.success = 0;
return false;
}
size_t dwDataCount = pVersionDataDir->NumberOfIdEntries + pVersionDataDir->NumberOfNamedEntries;
for (size_t k = 0; k < dwDataCount; k++) {
PIMAGE_RESOURCE_DIRECTORY_ENTRY pActualVersionEntry = &((PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pVersionDataDir + 1))[k];
if(!pActualVersionEntry){
strcpy(status.errorMsg, "3, pActualVersionEntry is nullptr");
status.success = 0;
return false;
}
// 获取版本信息的最终数据条目
//pActualVersionEntry->DataIsDirectory为0
if (!pActualVersionEntry->DataIsDirectory) {
// 转换RVA为文件偏移量
PIMAGE_RESOURCE_DATA_ENTRY pDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)((size_t)pResourceTable + pActualVersionEntry->OffsetToData);
// 获取VS_VERSIONINFO结构
if (pDataEntry != nullptr)
//pDataEntry->OffsetToData是虚拟地址,使用GetPtrFromRVA进行转换
//VS_VERSIONINFO信息结构
pVersionInfo = (VS_VERSIONINFO*)GetPtrFromRVA(pDataEntry->OffsetToData, pNtHeader, pFileBuf);
else
continue;
// 打印版本信息
pFixedFileInfo = &pVersionInfo->Value;
sprintf(szMsg, "%d,%d,%d,%d",
(int)pFixedFileInfo->dwProductVersionMS >> 16,
(int)pFixedFileInfo->dwProductVersionMS & 0xFFFF,
(int)pFixedFileInfo->dwProductVersionLS >> 16,
(int)pFixedFileInfo->dwProductVersionLS & 0xFFFF);
*dwMsgLen = strlen(szMsg);
}
}
}
}
}
}
return true;
}
涉及的结构体与类型定义
#pragma once
typedef unsigned short WORD;
typedef unsigned short WCHAR;
typedef unsigned int DWORD;
typedef unsigned char BYTE;
typedef void* PVOID;
typedef char CHAR;
typedef unsigned long long int64__;
typedef int64__ ULONG_PTR;
typedef long long LONG_PTR, *PLONG_PTR;
typedef CHAR *LPSTR, *PSTR;
typedef DWORD *PDWORD;
typedef int LONG;
#define IMAGE_SIZEOF_SHORT_NAME 8
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
#define IMAGE_DOS_SIGNATURE 0x5A4D
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
//pe head struct
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;
//option header
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
int64__ ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
int64__ SizeOfStackReserve;
int64__ SizeOfStackCommit;
int64__ SizeOfHeapReserve;
int64__ SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
typedef struct _IMAGE_OPTIONAL_HEADER {
// Standard fields.
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
// NT additional fields.
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
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;
//NT header
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
#ifdef _WIN64
typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER;
typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER;
#else
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER;
typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER;
#endif
typedef struct tagVS_FIXEDFILEINFO
{
DWORD dwSignature; /* e.g. 0xfeef04bd */
DWORD dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
DWORD dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
DWORD dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
DWORD dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
DWORD dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
DWORD dwFileFlagsMask; /* = 0x3F for version "0.42" */
DWORD dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
DWORD dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
DWORD dwFileType; /* e.g. VFT_DRIVER */
DWORD dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
DWORD dwFileDateMS; /* e.g. 0 */
DWORD dwFileDateLS; /* e.g. 0 */
} VS_FIXEDFILEINFO;
typedef struct {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey[16];
WORD Padding1;
VS_FIXEDFILEINFO Value;
WORD Padding2;
WORD Children;
} VS_VERSIONINFO;
typedef struct {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey;
WORD Padding;
WORD Value;
} String;
typedef struct {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey;
WORD Padding;
String Children;
} StringTable;
typedef struct {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey;
WORD Padding;
StringTable Children;
} StringFileInfo;
//dos header
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
//section
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;
#define FIELD_OFFSET(type, field) ((LONG)(LONG_PTR)&(((type *)0)->field))
#define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER) \
((ULONG_PTR)(ntheader) + \
FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + \
((ntheader))->FileHeader.SizeOfOptionalHeader \
))
//1 resource directory
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
WORD NumberOfNamedEntries;
WORD NumberOfIdEntries;
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
//2
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
union {
struct {
DWORD NameOffset:31;
DWORD NameIsString:1;
} DUMMYSTRUCTNAME;
DWORD Name;
WORD Id;
} DUMMYUNIONNAME;
union {
DWORD OffsetToData;
struct {
DWORD OffsetToDirectory:31;
DWORD DataIsDirectory:1;
} DUMMYSTRUCTNAME2;
} DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
//3
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
DWORD OffsetToData;
DWORD Size;
DWORD CodePage;
DWORD Reserved;
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
struct STATUS
{
STATUS(){
memset(errorMsg, 0, sizeof errorMsg);
success = -1;
}
CHAR errorMsg[128]; //错误信息
DWORD success; //状态码,0 失败,1 成功
};
注意点
要主要实现中不同系统下对基本类型字节长度的情况,上述代码使用与32位与64位下进行数据读取,64位下使用要将32结构体都换成64位,
其中差别主要是int64__与DWORD之间字节长度的不同,这部分建议查看官方文档,再根据自己的需求进行改进。