pe格式从入门到图形化显示(七)-导出表



前言

通过分析和解析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)));
    }
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mrack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值