c++ - 导出的 DLL 函数未按词法排序?

标签 c++ windows portable-executable

嗯,今天我遇到了一个奇怪的事情。不久前,我编写了自己的 GetProcAddress 版本,以从远程进程获取函数地址。显然,我花了很多时间阅读 PE 架构,以找出解决这个问题的最佳方法。

根据 PECOFF v8 规范(我认为这是最新的官方规范),有以下关于导出名称指针表的符号:

The export name pointer table is an array of addresses (RVAs) into the export name table. The pointers are 32 bits each and are relative to the image base. The pointers are ordered lexically to allow binary searches.

所以我在编写我的 GetProcAddress 版本时考虑到了这一点。显然,使用二进制搜索而不是线性搜索来遍历导出表会大大提高效率...KERNEL32.dll(1300 多个导出函数)。

直到今天我遇到了一个奇怪的问题,这才有效了一段时间。看起来 Kernel32 中的一些导出函数实际上并没有按词法排序,这使我的二进制搜索失败了。以下是使用我将在下面发布的函数的导出 Dll 转储的摘录:

Ordinal: 810    Name: K32QueryWorkingSetEx
Ordinal: 811    Name: LCIDToLocaleName
Ordinal: 812    Name: LCMapStringA
Ordinal: 813    Name: LCMapStringEx
Ordinal: 814    Name: LCMapStringW
Ordinal: 815    Name: LZClose
Ordinal: 816    Name: LZCloseFile
Ordinal: 817    Name: LZCopy
Ordinal: 818    Name: LZCreateFileW
Ordinal: 819    Name: LZDone
Ordinal: 820    Name: LZInit
Ordinal: 821    Name: LZOpenFileA
Ordinal: 822    Name: LZOpenFileW
Ordinal: 823    Name: LZRead
Ordinal: 824    Name: LZSeek
Ordinal: 825    Name: LZStart
Ordinal: 826    Name: LeaveCriticalSection
Ordinal: 827    Name: LeaveCriticalSectionWhenCallbackReturns
Ordinal: 828    Name: LoadAppInitDlls
Ordinal: 829    Name: LoadLibraryA
Ordinal: 830    Name: LoadLibraryExA

有人发现这里的问题吗?尽管文档声称导出表是按词法排序的,但 LZRead 列在 LeaveCriticalSection 之前。

在处理字符串时,我一直将词法排序等同于字母排序,我在这里是错的还是 Kernel32 的导出表存在一些奇怪的问题?

用于转储导出的函数:

void DumpExports(PBYTE pBase)
{
    freopen("B:\\PeDump.txt", "wb", stdout);
    IMAGE_DOS_HEADER *pDosHd = (IMAGE_DOS_HEADER*)pBase;
    IMAGE_NT_HEADERS *pNtHd = (IMAGE_NT_HEADERS*)(pBase + pDosHd->e_lfanew);
    IMAGE_DATA_DIRECTORY expDir = pNtHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    if (expDir.Size)
    {
        IMAGE_EXPORT_DIRECTORY *pExpDir = (IMAGE_EXPORT_DIRECTORY*)(pBase + expDir.VirtualAddress);
        WORD *pOrds = (WORD*)(pBase + pExpDir->AddressOfNameOrdinals);
        DWORD *pNames = (DWORD*)(pBase + pExpDir->AddressOfNames);

        for(unsigned long i = 0; i < pExpDir->NumberOfNames; i++, pOrds++, pNames++)
            printf("Ordinal: %d\tName: %s\n", *pOrds, (char*)(pBase + *pNames));
    }
    else
    {
        printf("No functions are exported from this image.\n");
    }
    fflush(stdout);
    freopen("CON", "w", stdout);
}

编辑:我是个白痴。当然,“Z”在“o”之前,现在是凌晨 3 点,我的大脑无法正常工作。非常抱歉。

编辑 编辑:好的,我没有完全疯掉。一半的问题显然是 C# 的 string.CompareTo 扩展没有进行词法比较。

例如

"LoadLibraryW".CompareTo("LZRead");

返回“-1”。这是我困惑的根源。

最佳答案

LZReadLeaveCriticalSection 之前使用 ascii 按字典顺序排列。不要使用不区分大小写,它看起来会起作用。


关于文档的有趣观察。

The pointers are 32 bits each ... are ordered lexically to allow binary searches.

很难理解为什么要对指针(而不是符号名称)进行二进制搜索,但就是这么说的。

关于c++ - 导出的 DLL 函数未按词法排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11107095/

相关文章:

python - windows 7 32位系统pytorch安装

x86 - 如何解决 Windows 上的 GHC 错误 "Unknown PEi386 section name ` .idata$ 4' "?

c++ - 使用不同偏移量加载的 VS 2010 和 VS 2005 生成的 Exe

c++ - 这样的代码怎么会返回这么乱?

c++ - 如何安装libusb?

c++ - 如何使用 Windows API 自动将 C++ 代码从 Windows 转换为 Linux

visual-studio-2010 - 从 DLL 中去除特定符号

C++20 使用概念来强制类上的接口(interface)

c++ - qemu-系统-i386 : Error loading uncompressed kernel without PVH ELF Note

c++ - 使用模板减少类(class)规模的合理方法?