4

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

从 PECOFF v8 规范(我认为它是最新的官方规范)中,有以下关于 的符号Export Name Pointer Table

导出名称指针表是导出名称表中的地址数组 (RVA)。每个指针都是 32 位,并且相对于图像库。指针按词法排序以允许二进制搜索。

所以我在编写我的 GetProcAddress 版本时考虑到了这一点。显然,使用二进制搜索而不是线性搜索,在 say...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”。这是我困惑的根源。

4

2 回答 2

5

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


关于文档的有趣观察。

每个指针都是 32 位...按词法排序以允许二进制搜索

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

于 2012-06-19T18:31:10.703 回答
3

大写的字符序号在小写的序号之前。

于 2012-06-19T18:31:01.740 回答