文章目录
前言
通过分析和解析Windows PE格式,并使用qt进行图形化显示
一、什么是Windows PE格式可选头?
PE文件格式的可选头(Optional Header)是一个结构体,它包含了关于PE文件的额外信息,如文件的属性、内存布局、加载参数等。可选头的结构体有两个版本:IMAGE_OPTIONAL_HEADER32和IMAGE_OPTIONAL_HEADER64。它们的主要区别在于64位版本支持64位地址空间,而32位版本仅支持32位地址空间。
二、怎么区别32位版本和64位版本
要区分32位PE文件版本和64位PE文件版本,可以查看PE文件的可选头中的Magic字段。Magic字段是一个标识PE文件类型的幻数,它有两个值:
对于32位PE文件,Magic字段的值为0x010B。
对于64位PE文件,Magic字段的值为0x020B。
因此,只需检查PE文件的可选头中的Magic字段,就可以判断PE文件是32位版本还是64位版本。如果Magic字段的值为0x010B,则PE文件是32位版本;如果Magic字段的值为0x020B,则PE文件是64位版本。
三、解析可选头并显示
1.32位和64位区别
32位PE文件版本和64位PE文件版本的结构体主要区别在于它们的可选头结构体。32位PE文件使用IMAGE_OPTIONAL_HEADER32结构体,而64位PE文件使用IMAGE_OPTIONAL_HEADER64结构体。这两个结构体的主要区别在于它们的字段类型和大小。
以下是一些主要的区别:
1、对于32位PE文件,IMAGE_OPTIONAL_HEADER32结构体中的ImageBase字段是一个DWORD类型,表示PE文件默认装入的基地址而对于64位PE文件,IMAGE_OPTIONAL_HEADER64结构体中的ImageBase字段是一个ULONGLONG类型,表示PE文件默认装入的基地址。
2、对于32位PE文件,IMAGE_OPTIONAL_HEADER32结构体中的SizeOfStackReserve和SizeOfStackCommit字段是DWORD类型,表示线程的栈初始保留的虚拟内存大小和初始提交的虚拟内存大小。而对于64位PE文件,IMAGE_OPTIONAL_HEADER64结构体中的SizeOfStackReserve和SizeOfStackCommit字段是ULONGLONG类型,表示线程的栈初始保留的虚拟内存大小和初始提交的虚拟内存大小。
3、对于32位PE文件,IMAGE_OPTIONAL_HEADER32结构体中的SizeOfHeapReserve和SizeOfHeapCommit字段是DWORD类型,表示进程的堆保留的虚拟内存大小和初始提交的虚拟内存大小。而对于64位PE文件,IMAGE_OPTIONAL_HEADER64结构体中的SizeOfHeapReserve和SizeOfHeapCommit字段是ULONGLONG类型,表示进程的堆保留的虚拟内存大小和初始提交的虚拟内存大小。
2.32位可选头数据结构以及字段描述
Magic:指定PE文件的目标机器类型,如I386、AMD64、ARM等。
MajorLinkerVersion:表示PE文件的主要链接器版本号。
MinorLinkerVersion:表示PE文件的次要链接器版本号。
SizeOfCode:表示PE文件中代码段的大小。
SizeOfInitializedData:表示PE文件中已初始化数据段的大小。
SizeOfUninitializedData:表示PE文件中未初始化数据段的大小。
AddressOfEntryPoint:表示PE文件的入口点地址。
BaseOfCode:表示PE文件中代码段的基址。
BaseOfData:表示PE文件中数据段的基址。
ImageBase:表示PE文件在内存中的基址。
SectionAlignment:表示PE文件中节的对齐方式。
FileAlignment:表示PE文件中文件的对齐方式。
MajorOperatingSystemVersion:表示PE文件所需的主要操作系统版本号。
MinorOperatingSystemVersion:表示PE文件所需的次要操作系统版本号。
MajorImageVersion:表示PE文件的主要图像版本号。
MinorImageVersion:表示PE文件的次要图像版本号。
MajorSubsystemVersion:表示PE文件所需的主要子系统版本号。
MinorSubsystemVersion:表示PE文件所需的次要子系统版本号。
Win32VersionValue:表示PE文件的Win32版本值。
SizeOfImage:表示PE文件在内存中的大小。
SizeOfHeaders:表示PE文件中头部的大小。
CheckSum:表示PE文件的校验和。
Subsystem:表示PE文件所需的子系统类型,如GUI、CUI等。
DllCharacteristics:表示PE文件的DLL特性。
SizeOfStackReserve:表示PE文件的堆栈保留大小。
SizeOfStackCommit:表示PE文件的堆栈提交大小。
SizeOfHeapReserve:表示PE文件的堆保留大小。
SizeOfHeapCommit:表示PE文件的堆提交大小。
LoaderFlags:表示PE文件的加载器标志。
NumberOfRvaAndSizes:表示PE文件中数据目录的数量。
DataDirectory:表示PE文件中数据目录的数组。
struct IMAGE_OPTIONAL_HEADER32 {
WORD Magic;
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;
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];
};
3.64位可选头数据结构以及字段描述
Magic:指定PE文件的目标机器类型,如AMD64等。
MajorLinkerVersion:表示PE文件的主要链接器版本号。
MinorLinkerVersion:表示PE文件的次要链接器版本号。
SizeOfCode:表示PE文件中代码段的大小。
SizeOfInitializedData:表示PE文件中已初始化数据段的大小。
SizeOfUninitializedData:表示PE文件中未初始化数据段的大小。
AddressOfEntryPoint:表示PE文件的入口点地址。
BaseOfCode:表示PE文件中代码段的基址。
ImageBase:表示PE文件在内存中的基址。
SectionAlignment:表示PE文件中节的对齐方式。
FileAlignment:表示PE文件中文件的对齐方式。
MajorOperatingSystemVersion:表示PE文件所需的主要操作系统版本号。
MinorOperatingSystemVersion:表示PE文件所需的次要操作系统版本号。
MajorImageVersion:表示PE文件的主要图像版本号。
MinorImageVersion:表示PE文件的次要图像版本号。
MajorSubsystemVersion:表示PE文件所需的主要子系统版本号。
MinorSubsystemVersion:表示PE文件所需的次要子系统版本号。
Win32VersionValue:表示PE文件的Win32版本值。
SizeOfImage:表示PE文件在内存中的大小。
SizeOfHeaders:表示PE文件中头部的大小。
CheckSum:表示PE文件的校验和。
Subsystem:表示PE文件所需的子系统类型,如GUI、CUI等。
DllCharacteristics:表示PE文件的DLL特性。
SizeOfStackReserve:表示PE文件的堆栈保留大小。
SizeOfStackCommit:表示PE文件的堆栈提交大小。
SizeOfHeapReserve:表示PE文件的堆保留大小。
SizeOfHeapCommit:表示PE文件的堆提交大小。
LoaderFlags:表示PE文件的加载器标志。
NumberOfRvaAndSizes:表示PE文件中数据目录的数量。
DataDirectory:表示PE文件中数据目录的数组。
struct IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG 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;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
};
4.解析
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)));
//解析扩展PE文件头
long optionHeaderIndex = fileHeaderIndex + sizeof(IMAGE_FILE_HEADER);
//记录扩展PE文件头索引
m_optionHeaderIndex = optionHeaderIndex;
//解析扩展PE文件头
parserOptionHeader(fileData.mid(optionHeaderIndex, m_fileHeader.SizeOfOptionalHeader));
return true;
}
void PEParser::parserOptionHeader(const QByteArray &peHeaderData)
{
WORD magic = 0;
QByteArray magicData = peHeaderData.mid(0, 2);
memcpy(&magic, magicData.data(), magicData.size());
m_directories.clear();
if (magic == PEType::PE32 && peHeaderData.size() == sizeof(IMAGE_OPTIONAL_HEADER32))
{
const IMAGE_OPTIONAL_HEADER32 *peHeader = reinterpret_cast<const IMAGE_OPTIONAL_HEADER32 *>(peHeaderData.data());
emit sendX86PEOptionHeader(*peHeader);
m_optionalHeader32 = *peHeader;
m_fileAlignment = peHeader->FileAlignment;
m_imageBase = peHeader->ImageBase;
m_sectionAlignment = peHeader->SectionAlignment;
m_x86Flag = true;
}
else if (magic == PEType::PE64 && peHeaderData.size() == sizeof(IMAGE_OPTIONAL_HEADER64))
{
const IMAGE_OPTIONAL_HEADER64 *peHeader = reinterpret_cast<const IMAGE_OPTIONAL_HEADER64 *>(peHeaderData.data());
emit sendX64PEOptionHeader(*peHeader);
m_optionalHeader64 = *peHeader;
m_fileAlignment = peHeader->FileAlignment;
m_imageBase = peHeader->ImageBase;
m_sectionAlignment = peHeader->SectionAlignment;
m_x86Flag = false;
}
}
5.显示
void MainWindow::showX86OptionalHeader(const IMAGE_OPTIONAL_HEADER32 &header)
{
ui->lineEdit_sizeOfCode->setText(QString::asprintf("%08lX", header.SizeOfCode));
ui->lineEdit_sizeOfImage->setText(QString::asprintf("%08lX",header.SizeOfImage));
ui->lineEdit_baseOfCode->setText(QString::asprintf("%08lX", header.BaseOfCode));
ui->lineEdit_baseOfData->setText(QString::asprintf("%08lX", header.BaseOfData));
ui->lineEdit_entry->setText(QString::asprintf("%08lX", header.AddressOfEntryPoint));
ui->lineEdit_imageBase->setText(QString::asprintf("%08lX", header.ImageBase));
ui->lineEdit_sizeOfHead->setText(QString::asprintf("%08lX", header.SizeOfHeaders));
ui->lineEdit_sectionAlign->setText(QString::asprintf("%08lX", header.SectionAlignment));
ui->lineEdit_fileAlign->setText(QString::asprintf("%08lX", header.FileAlignment));
ui->lineEdit_checkSum->setText(QString::asprintf("%08lX", header.CheckSum));
ui->lineEdit_baseInfo->setText("PE 32 文件");
}
void MainWindow::showX64OptionalHeader(const IMAGE_OPTIONAL_HEADER64 &header)
{
ui->lineEdit_sizeOfCode->setText(QString::asprintf("%08lX", header.SizeOfCode));
ui->lineEdit_sizeOfImage->setText(QString::asprintf("%08lX",header.SizeOfImage));
ui->lineEdit_baseOfCode->setText(QString::asprintf("%08lX", header.BaseOfCode));
ui->lineEdit_entry->setText(QString::asprintf("%08lX", header.AddressOfEntryPoint));
ui->lineEdit_imageBase->setText(QString::asprintf("%08lX", header.ImageBase));
ui->lineEdit_sizeOfHead->setText(QString::asprintf("%08lX", header.SizeOfHeaders));
ui->lineEdit_sectionAlign->setText(QString::asprintf("%08lX", header.SectionAlignment));
ui->lineEdit_fileAlign->setText(QString::asprintf("%08lX", header.FileAlignment));
ui->lineEdit_checkSum->setText(QString::asprintf("%08lX", header.CheckSum));
ui->lineEdit_baseInfo->setText("PE 64 文件");
}