显示有关IMAGE_EXPORT_DIRECTORY

时间:2018-04-07 09:06:11

标签: winapi portable-executable

我想以下列形式打印有关IMAGE_EXPORT_DIRECTORY的信息:

<Name1>,<Ordinal1>,<FileAddress1>

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

  1. AddressOfFunctions - (导出地址表,每个元素都是RVA)
  2. AddressOfNames - (导出名称指针表,每个元素也是RVA - 有序吗?)
  3. AddressOfNameOrdinals(其中数组元素 - Base代表EAT中的序数)
  4. 但是如何访问这些表以便在一行中打印每个元素的信息?此外,当NumberOfNames&lt; NumberOfFunctions?

    我已经知道如何进入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));
    }
    

    如果有人可以解释这些数组是如何工作的,或者是如何访问它们的算法,那将非常感激。

1 个答案:

答案 0 :(得分:1)

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

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

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

    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_addr_table[export_ordinal_table[j]] == export_rva)
            {
                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

此外,请注意,对于每个导出的函数(均按名称导出,仅按序数导出),此函数的有效符号名称(用于解析导入查找表条目的wrt)为#OrdinalNumber,其中OrdinalNumber 1}}被替换为实际序数(请参阅PE/COFF 6.3.2中的Forwarder RVA的描述),因此 可以列出作为非导出函数的名称按名称出口。