前言
通过分析和解析Windows PE格式,并使用qt进行图形化显示
一、什么是Windows PE格式文件头?
Windows PE格式文件头(Portable Executable file format header)是Windows操作系统中可执行文件的一部分,用于存储有关文件结构和属性的信息。它位于可执行文件的开头部分,包含了许多字段,用于描述文件的类型、机器体系结构、节表、入口点等。
二、解析文件头并显示
1.数据结构
IMAGE_FILE_HEADER是一个结构体,它位于PE文件的IMAGE_NT_HEADERS结构中,用于描述PE文件的基本属性和结构。那么IMAGE_NT_HEADERS又是一个什么结构呢?
typedef struct _IMAGE_NT_HEADERS
{
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
以下是_IMAGE_NT_HEADERS结构的主要字段及其含义:
Signature:用于标识文件的开头部分,通常为"PE"(即0x50 0x45)。
FileHeader:表示PE文件的文件头信息,这对于加载和执行PE文件非常重要。例如,FileHeader.Machine字段告诉操作系统PE文件的目标机器类型,以便正确地加载和执行该文件。
OptionalHeader:表示PE文件的可选头信息,这些信息对于加载和执行PE文件非常重要。例如,OptionalHeader.Magic字段告诉操作系统PE文件的目标机器类型,以便正确地加载和执行该文件。OptionalHeader.AddressOfEntryPoint字段表示PE文件的入口点地址,这有助于操作系统了解PE文件的入口点。OptionalHeader.Subsystem字段表示PE文件所需的子系统类型,如GUI、CUI等。
以下是IMAGE_FILE_HEADER结构的主要字段:
Machine:指定PE文件的目标机器类型,如I386、AMD64、ARM等。
NumberOfSections:表示PE文件中的节(Section)数量。
TimeDateStamp:表示PE文件的创建时间戳。
PointerToSymbolTable:指向符号表(Symbol Table)的指针。
NumberOfSymbols:表示符号表中的符号数量。
SizeOfOptionalHeader:表示PE文件的可选头(Optional Header)的大小。
Characteristics字段是一个位掩码(bitmask),用于表示PE文件的特性。以下是一些常见的Characteristics字段的值及其含义:
IMAGE_FILE_RELOCS_STRIPPED (0x0001):表示PE文件中的重定位表已被删除。
IMAGE_FILE_EXECUTABLE_IMAGE (0x0002):表示PE文件是一个可执行文件。
IMAGE_FILE_LINE_NUMS_STRIPPED (0x0004):表示PE文件中的行号信息已被删除。
IMAGE_FILE_LOCAL_SYMS_STRIPPED (0x0008):表示PE文件中的本地符号信息已被删除。
IMAGE_FILE_AGGRESIVE_WS_TRIM (0x0010):表示PE文件中的工作集大小已被优化。
IMAGE_FILE_LARGE_ADDRESS_AWARE (0x0020):表示PE文件支持大于2GB的地址空间。
IMAGE_FILE_BYTES_REVERSED_LO (0x0080):表示PE文件中的字节顺序已被反转(低位)。
IMAGE_FILE_32BIT_MACHINE (0x0100):表示PE文件是一个32位机器的可执行文件。
IMAGE_FILE_DEBUG_STRIPPED (0x0200):表示PE文件中的调试信息已被删除。
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP (0x0400):表示PE文件可以从交换文件中运行。
IMAGE_FILE_NET_RUN_FROM_SWAP (0x0800):表示PE文件可以从网络交换文件中运行。
IMAGE_FILE_SYSTEM (0x1000):表示PE文件是一个系统文件。
IMAGE_FILE_DLL (0x2000):表示PE文件是一个动态链接库(DLL)文件。
IMAGE_FILE_UP_SYSTEM_ONLY (0x4000):表示PE文件只能在单处理器系统上运行。
IMAGE_FILE_BYTES_REVERSED_HI (0x8000):表示PE文件中的字节顺序已被反转(高位)。
Characteristics字段的值可以通过位运算(如按位与、按位或等)进行组合,以表示PE文件的多种特性。例如,如果Characteristics字段的值为0x0002(即IMAGE_FILE_EXECUTABLE_IMAGE),则表示PE文件是一个可执行文件。
struct IMAGE_FILE_HEADER
{
enum FILECharacteristics
{
IMAGE_FILE_RELOCS_STRIPPED = 0x0001,
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002,
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004,
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0004,
IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010,
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020,
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080,
IMAGE_FILE_32BIT_MACHINE = 0x0100,
IMAGE_FILE_DEBUG_STRIPPED = 0x0200,
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400,
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800,
IMAGE_FILE_SYSTEM = 0x1000,
IMAGE_FILE_DLL = 0x2000,
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000,
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000
};
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
};
2.解析
bool PEParser::parserFileData(const QByteArray &fileData)
{
//判断是否是MZ开头的文件
if (fileData.left(2) != "MZ")
{
return false;
}
//解析DOS头
parserDOSHeader(fileData.left(sizeof(IMAGE_DOS_HEADER)));
//DOSStub数据
m_dosStubData = fileData.mid(sizeof(IMAGE_DOS_HEADER), m_dosHeader.e_lfanew - sizeof(IMAGE_DOS_HEADER));
long peAddress = m_dosHeader.e_lfanew;
if (fileData.mid(peAddress, 2) != "PE")
{
return false;
}
m_fileData = fileData;
//去除前4个字节的PE头标识
long fileHeaderIndex = peAddress + 4;
//记录文件头索引
m_fileHeaderIndex = fileHeaderIndex;
//解析标准PE文件头
paserFileHeader(fileData.mid(fileHeaderIndex, sizeof(IMAGE_FILE_HEADER)));
return true;
}
void PEParser::paserFileHeader(const QByteArray &peFileHeaderData)
{
const IMAGE_FILE_HEADER *fileHeader = reinterpret_cast<const IMAGE_FILE_HEADER *>(peFileHeaderData.data());
emit sendPEFileHeader(*fileHeader);
m_fileHeader = *fileHeader;
}
3.显示
void MainWindow::showPEFileHeader(const IMAGE_FILE_HEADER &header)
{
ui->lineEdit_optionalSize->setText(QString::asprintf("%08X", header.SizeOfOptionalHeader));
ui->lineEdit_timeDateStamp->setText(QString::asprintf("%08lX", header.TimeDateStamp));
ui->lineEdit_numberOfSections->setText(QString::asprintf("%08X", header.NumberOfSections));
ui->lineEdit_characteristics->setText(QString::asprintf("%08X", header.Characteristics));
}