嗯,今天我遇到了一个奇怪的事情。不久前,我编写了自己的 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”。这是我困惑的根源。
最佳答案
LZRead
在 LeaveCriticalSection
之前使用 ascii 按字典顺序排列。不要使用不区分大小写,它看起来会起作用。
关于文档的有趣观察。
The pointers are 32 bits each ... are ordered lexically to allow binary searches.
很难理解为什么要对指针(而不是符号名称)进行二进制搜索,但就是这么说的。
关于c++ - 导出的 DLL 函数未按词法排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11107095/