前言
通过分析和解析Windows PE格式,并使用qt进行图形化显示
一、什么是Windows PE格式中的导出表?
PE文件格式的导出表是PE文件中用于记录程序导出函数信息的数据结构。导出表位于数据目录表的第一项,索引值为0。导出表记录了当前文件对外开放的函数接口,使得其他程序可以通过这些接口调用该文件中的函数。
二、解析导出表并显示
1.导出表的结构
IMAGE_EXPORT_DIRECTORY结构体包含了导出表的特征、时间戳、版本号、导出函数数量、导出函数名称数量、导出函数地址表、导出函数名称表和导出函数序号表等信息。IMAGE_EXPORT_DIRECTORY的定义如下:
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
IMAGE_EXPORT_DIRECTORY中的字段包括:
Characteristics:导出表的特征值,用于描述导出表的特性。
TimeDateStamp:时间戳,表示导出表创建或最后修改的时间。
MajorVersion和MinorVersion:导出表的主次版本号,用于标识导出表的版本信息。
Name:导出表的名称,通常为一个字符串,用于描述导出表的功能或用途。
Base:导出表中函数的起始序号,用于定位导出函数在文件中的位置。
NumberOfFunctions:导出表中导出的函数数量。
NumberOfNames:以名称导出的函数数量。
AddressOfFunctions:导出函数地址表在文件中的虚拟地址。
AddressOfNames:导出函数名称表在文件中的虚拟地址。
AddressOfNameOrdinals:导出函数序号表在文件中的虚拟地址。
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)));
//解析扩展PE文件头
long optionHeaderIndex = fileHeaderIndex + sizeof(IMAGE_FILE_HEADER);
//记录扩展PE文件头索引
m_optionHeaderIndex = optionHeaderIndex;
//解析扩展PE文件头
parserOptionHeader(fileData.mid(optionHeaderIndex, m_fileHeader.SizeOfOptionalHeader));
//解析节表
long sectionHeaderIndex = optionHeaderIndex + m_fileHeader.SizeOfOptionalHeader;
//节表结构在文件中开始的偏移
m_sectionHeaderIndex = sectionHeaderIndex;
//解析节表
parserSectionHeader(fileData.mid(sectionHeaderIndex,
m_fileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)));
//解析数据目录
parserDataDirectory();
//解析导出表
parserExportTable();
return true;
}
void PEParser::parserExportTable()
{
DWORD address = 0;
if (m_x86Flag)
{
address = m_optionalHeader32.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
}
else
{
address = m_optionalHeader64.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
}
if (address == 0)
{
return;
}
//将虚拟地址转换为文件地址
RVA2FOAInfo info = RVA2FOA(address);
//获取导出表数据
IMAGE_EXPORT_DIRECTORY exportTable;
memcpy(&exportTable, m_fileData.data() + info.FOA, sizeof(IMAGE_EXPORT_DIRECTORY));
QList<DWORD> functionAddressList;
QList<WORD> functionOrdinalsList;
//获取导出函数序号表数据
info = RVA2FOA(exportTable.AddressOfNameOrdinals);
QByteArray ordinalsTempData = m_fileData.mid(info.FOA, exportTable.NumberOfFunctions * sizeof(WORD));
//获取导出函数地址表数据
info = RVA2FOA(exportTable.AddressOfFunctions);
QByteArray addrTempData = m_fileData.mid(info.FOA, exportTable.NumberOfFunctions * sizeof(DWORD));
//遍历导出表中导出函数的数据
for (int i = 0; i < exportTable.NumberOfFunctions; ++i)
{
DWORD addr = 0;
memcpy(&addr, addrTempData.data() + i * sizeof(DWORD), sizeof(DWORD));
functionAddressList.append(addr);
WORD ordinal = 0;
memcpy(&ordinal, ordinalsTempData.data() + i * sizeof(WORD), sizeof(WORD));
functionOrdinalsList.append(ordinal);
}
//获取导出函数名称表数据
info = RVA2FOA(exportTable.AddressOfNames);
QByteArray nameTempData = m_fileData.mid(info.FOA, exportTable.NumberOfFunctions * sizeof(DWORD));
//遍历导出表中以名称导出的数据
QList<ExportFunctionInfo> exportFunctionInfo;
for (int i = 0; i < exportTable.NumberOfNames; ++i)
{
DWORD nameRVA = 0;
memcpy(&nameRVA, nameTempData.data() + i * sizeof(DWORD), sizeof(DWORD));
DWORD nameFOA = RVA2FOA(nameRVA).FOA;
int index = m_fileData.indexOf('\0', nameFOA);
ExportFunctionInfo info;
info.name = m_fileData.mid(nameFOA, index - nameFOA);
info.address = functionAddressList[functionOrdinalsList[i]];
info.ordinal = functionOrdinalsList[i];
exportFunctionInfo.append(info);
}
//按序号排序
std::sort(exportFunctionInfo.begin(), exportFunctionInfo.end(), [=](const ExportFunctionInfo &info1, const ExportFunctionInfo &info2) -> bool
{
return info1.ordinal < info2.ordinal;
});
emit sendExportTable(exportFunctionInfo);
}
3.显示导出表
void MainWindow::showExportTable(const QList<ExportFunctionInfo> &exportTable)
{
ui->tableWidget_exportTable->clearContents();
ui->tableWidget_exportTable->setRowCount(0);
for (int i = 0; i < exportTable.size(); ++i)
{
ui->tableWidget_exportTable->insertRow(i);
ui->tableWidget_exportTable->setItem(i, 0, new QTableWidgetItem(exportTable[i].name));
ui->tableWidget_exportTable->setItem(i, 1, new QTableWidgetItem(QString::asprintf("%08lX", exportTable[i].address)));
ui->tableWidget_exportTable->setItem(i, 2, new QTableWidgetItem(QString::asprintf("%08lX", exportTable[i].ordinal)));
}
}