【问题标题】:Show info about IMAGE_EXPORT_DIRECTORY显示有关 IMAGE_EXPORT_DIRECTORY 的信息
【发布时间】:2019-09-19 20:36:58
【问题描述】:

我想按以下格式打印有关IMAGE_EXPORT_DIRECTORY 的信息:

<Name1>,<Ordinal1>,<FileAddress1>

我知道这个 IMAGE 由 3 个数组组成:

  1. AddressOfFunctions -(导出地址表,每个元素都是一个RVA)
  2. AddressOfNames -(导出名称指针表,每个元素也是一个 RVA - 有序?)
  3. AddressOfNameOrdinals(数组元素 - Base 表示 EAT 中的序数)

但是我如何访问这些表格以便在一行中打印每个元素的信息?另外,当 NumberOfNames

我已经知道如何访问 IMAGE_EXPORT_DIRECTORY

PIMAGE_DATA_DIRECTORY pFirstDir = &(pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
if (pFirstDir->Size > 0)
{
        PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE *)pDosHeader + ConvertRvaToOffset(pFirstDir->VirtualAddress, pNTHeaders));
}

如果有人能解释这些数组的实际工作原理或如何访问它们的算法,将不胜感激。

【问题讨论】:

  • NumberOfNames &lt; NumberOfFunctions 表示存在无名函数,仅按序号导出
  • @RbMm 我知道,我的意思是我怎么能访问它们。该字段变为 0 ?你有一些我可以参考的文件吗?
  • 你想访问什么?
  • 名称和序数(每个函数的 对)
  • 在什么问题访问它?你有指向名称 rva 数组的指针(AddressOfNames)和指向索引的指针(AddressOfNameOrdinals

标签: winapi portable-executable


【解决方案1】:

您可以使用以下伪 C 代码:

UINT32 *export_addr_table = (UINT32 *) MAP(pExportDir->AddressOfFunctions);
UINT32 *export_nameptr_table = (UINT32 *) MAP(pExportDir->AddressOfNames);
UINT16 *export_ordinal_table = (UINT16 *) MAP(pExportDir->AddressOfNameOrdinals);

for (SIZE_T i = 0; i < pExportDir->NumberOfFunctions; i++)
{
    UINT32 ordinal = pExportDir->Base + i;

    UINT32 export_rva = export_addr_table[i];

    if (is_forwarder_rva(export_rva))
    {
        // TODO: special care must be taken here - we cannot resolve directly to a VA unless target module is memory mapped
    }
    else
    {
        BOOL found_symname = FALSE;
        char symname[MAX_SYMNAME_LEN];

        // Loop through all exported names
        for (SIZE_T j = 0; j < pExportDir->NumberOfNames; j++) 
        {
            if (export_ordinal_table[j] == i)
            {
                UINT32 export_symname_rva = export_nameptr_table[j];
                const char *export_symname = (const char *) MAP(export_symname_rva);
                found_symname = TRUE;

                // Copy export_symname into symname (i.e. using strncat or similar)
            }
        }

        if (!found_symname)
        {
            snprintf(symname, MAX_SYMNAME_LEN, "#%"PRIu32, ordinal);
        }

        // Print symname, ordinal, address
    }
}

这里,我使用MAP() 表示将RVA 解析为可取消引用的虚拟地址的操作(类似于您使用ConvertRvaToOffset 所做的操作)。

说明:

导出的所有函数都存在于导出地址表中,但正如您已经指出的那样,导出名称指针表和导出序数表(它们是并行表)中只存在命名函数。

导出名称指针表中的条目按词法排序,以促进从导出函数的符号名称快速解析为其“无偏”序数(即导出地址表的索引)。因此,如果换一种方式,找到导出函数的导出名称(如果有的话),唯一的解决方案实际上是遍历 每个 导出的名称并尝试匹配 RVA。

但是,导出地址表中的某些条目表示所谓的“转发器 RVA”条目,它将导出重定向到另一个 DLL 模块中的符号(请参阅PE/COFF 6.3.2)。这些并不能真正以您想要的格式打印,所以我只是在上面的伪代码中为这种情况添加了一个// TODO

此外,请注意,对于每个导出的函数(都按名称导出和仅按序号导出),此函数的有效符号名称(用于解析导入查找表条目)是 #OrdinalNumber,其中 OrdinalNumber 是替换为实际的序号(请参阅 PE/COFF 6.3.2 中的 Forwarder RVA 描述),因此可能将其列为未按名称导出的导出函数的名称。

【讨论】:

  • 我真的非常感谢你!你是最棒的
猜你喜欢
  • 2017-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-27
  • 1970-01-01
  • 1970-01-01
  • 2021-08-03
相关资源
最近更新 更多