以编程方式从函数名获取序数 [英] Getting ordinal from function name programatically
问题描述
在C ++中获取导出的dll函数的序数最简单的方法是什么?
(寻找一种不会自我解析IAT的方法...)
What's the easiest way in C++ to get an ordinal of an exported dll function, given its name? (Looking for a way which doesn't invlove parsing the IATs myself...)
感谢,
Dan
Thanks, Dan
推荐答案
我不能想到任何可怕的简单方法做你想要的。你至少有几个选项,我可以看到:
I can't think of any terribly simple way to do what you want. You have at least a couple of options that I can see:
- 采取由Mark提供的路线,虽然它看起来有点kludgy
$ b 使用名称指针表(NPT)和导出序数表(EOT)查找导出序号。
$ b
我看到的第一个选项的主要问题是,你不知道要尝试多少个序数(在序数中可以有间隙,因此请注意 GetProcAddress
返回NULL表示结束将不工作)。它也有点低效,因为它需要反复做出一个很多的Win32调用,它基本上相当于导出地址表的线性搜索。
The main problem I see with the first option is that you don't know how many ordinals to try (there can be gaps in the ordinal numbers, so counting on GetProcAddress
returning NULL to signal the end won't work). It's also somewhat inefficient because it requires making a lot of Win32 calls repeatedly and it basically amounts to a linear search of the export address table. Pretty inelegant, indeed.
作为一种替代方法,您可以搜索NPT并将结果索引用于EOT以获得序数。这是一个更优雅的方法,因为它以最直接的方式到达序数(实际上是动态链接器用于将导出名称解析为其地址的相同方法)。另外,因为NPT是词法排序的,所以可以进行二分搜索,这显然优于另一方法的线性搜索。事实上,用这种方法实现的 GetProcOrdinal
的单次调用应该比只是一次调用 GetProcAddress code>。也许更重要的是,这种方法不依赖于任何未知数(即序数)。这个方法的缺点是它不像其他方法那么简单。
As an alternative, you can search the NPT and use the resultant index into the EOT to obtain an ordinal. This is a more elegant approach because it arrives at the ordinal in the most direct way possible (it is actually the same method the dynamic linker uses to resolve export names to their addresses). Also, because the NPT is lexically sorted, it's possible to do a binary search which is obviously preferable to the other method's linear search. In fact, a single call to GetProcOrdinal
implemented with this method should be slightly faster than just one call to GetProcAddress
. Perhaps more importantly, this method doesn't depend on any unknowns (i.e. number of ordinals). The disadvantage to this method is that it's not as simple as the other method.
您可以使用Debug帮助库帮助避免做一些PE文件的解析图像(这是我最初做的),但事实证明,解析PE图像的所需部分不是那么困难。我认为避免对Debug帮助库的依赖是值得为解析PE镜像头所需的最少的额外工作。
You could use the Debug Help Library to help avoid doing some of the parsing of the PE file image (this is what I did initially), but it turns out that parsing the required parts of the PE image is not that difficult. I think avoiding the dependency on the Debug Help Library is worth the minimal extra effort required to parse the PE image headers.
下面是一个示例实现C:
Getting down to business, here is an example implementation in C:
#include <stdio.h>
#include "windows.h"
/// Efficiently searches a module's name pointer table (NPT) for the named
/// procedure.
///
/// @param[in] npt Address of the NPT to search.
///
/// @param[in] size Number of entries in the NPT.
///
/// @param[in] base Base address of the module containing the NPT. This is
/// used to resolve addresses in the NPT (which are relative
/// to the module's base address).
///
/// @param[in] proc String containing the name of the procedure to search
/// for.
///
/// @return Returns the index into the NPT of the entry matching the named
/// procedure. If no such matching entry exists, the function returns
/// -1.
///
DWORD FindNptProc (PDWORD npt, DWORD size, PBYTE base, LPCSTR proc)
{
INT cmp;
DWORD max;
DWORD mid;
DWORD min;
min = 0;
max = size - 1;
while (min <= max) {
mid = (min + max) >> 1;
cmp = strcmp((LPCSTR)(npt[mid] + base), proc);
if (cmp < 0) {
min = mid + 1;
} else if (cmp > 0) {
max = mid - 1;
} else {
return mid;
}
}
return -1;
}
/// Gets a pointer to a module's export directory table (EDT).
///
/// @param[in] module Handle to the module (as returned by GetModuleHandle).
///
/// @return Returns a pointer to the module's EDT. If there is an error (e.g.
/// if the module handle is invalid or the module has no EDT) the
/// function will return NULL.
///
PIMAGE_EXPORT_DIRECTORY GetExportDirectoryTable (HMODULE module)
{
PBYTE base; // base address of module
PIMAGE_FILE_HEADER cfh; // COFF file header
PIMAGE_EXPORT_DIRECTORY edt; // export directory table (EDT)
DWORD rva; // relative virtual address of EDT
PIMAGE_DOS_HEADER mds; // MS-DOS stub
PIMAGE_OPTIONAL_HEADER oh; // so-called "optional" header
PDWORD sig; // PE signature
// Start at the base of the module. The MS-DOS stub begins there.
base = (PBYTE)module;
mds = (PIMAGE_DOS_HEADER)module;
// Get the PE signature and verify it.
sig = (DWORD *)(base + mds->e_lfanew);
if (IMAGE_NT_SIGNATURE != *sig) {
// Bad signature -- invalid image or module handle
return NULL;
}
// Get the COFF file header.
cfh = (PIMAGE_FILE_HEADER)(sig + 1);
// Get the "optional" header (it's not actually optional for executables).
oh = (PIMAGE_OPTIONAL_HEADER)(cfh + 1);
// Finally, get the export directory table.
if (IMAGE_DIRECTORY_ENTRY_EXPORT >= oh->NumberOfRvaAndSizes) {
// This image doesn't have an export directory table.
return NULL;
}
rva = oh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
edt = (PIMAGE_EXPORT_DIRECTORY)(base + rva);
return edt;
}
/// Gets the ordinal of an exported procedure.
///
/// @param[in] module Handle (as returned by GetModuleHandle) of the module
/// that exports the procedure.
///
/// @param[in] proc String containing the name of the procedure.
///
/// @return Returns the procedure's ordinal. If an ordinal for the procedure
/// could not be located (e.g. if the named procedure is not exported
/// by the specified module) then the function will return -1.
///
DWORD GetProcOrdinal (HMODULE module, LPCSTR proc)
{
PBYTE base; // module base address
PIMAGE_EXPORT_DIRECTORY edt; // export directory table (EDT)
PWORD eot; // export ordinal table (EOT)
DWORD i; // index into NPT and/or EOT
PDWORD npt; // name pointer table (NPT)
base = (PBYTE)module;
// Get the export directory table, from which we can find the name pointer
// table and export ordinal table.
edt = GetExportDirectoryTable(module);
// Get the name pointer table and search it for the named procedure.
npt = (DWORD *)(base + edt->AddressOfNames);
i = FindNptProc(npt, edt->NumberOfNames, base, proc);
if (-1 == i) {
// The procedure was not found in the module's name pointer table.
return -1;
}
// Get the export ordinal table.
eot = (WORD *)(base + edt->AddressOfNameOrdinals);
// Actual ordinal is ordinal from EOT plus "ordinal base" from EDT.
return eot[i] + edt->Base;
}
int main (int argc, char *argv [])
{
LPCSTR procName;
HMODULE module = NULL;
LPCSTR moduleName;
DWORD ordinal;
if (argc != 3) {
printf("A DLL name and procedure name must be specified\n");
return EXIT_FAILURE;
}
moduleName = argv[1];
procName = argv[2];
if (NULL == LoadLibrary(moduleName)) {
printf("Could not load library %s\n", moduleName);
return EXIT_FAILURE;
}
module = GetModuleHandle(moduleName);
if (NULL == module) {
printf("Couldn't get a handle to %s\n", moduleName);
return EXIT_FAILURE;
}
ordinal = GetProcOrdinal(module, procName);
if (-1 == ordinal) {
printf("Could not find ordinal for %s in %s\n", procName, moduleName);
} else {
printf("Found %s at ordinal %d\n", procName, ordinal);
}
return EXIT_SUCCESS;
}
GetProcOrdinal
有趣的位发生。代码希望是相当不言自明的;然而,为了完全理解它可能需要一点知识关于PE文件格式,我不打算进入这里(网络上有很多信息)。 FindNptProc
只是一个方便的函数,进行NPT的二分查找。 GetExportDirectoryTable
是另一个方便的函数,它解析PE头来定位导出目录表。
GetProcOrdinal
is where the interesting bits happen. The code is hopefully fairly self-explanatory; however, to fully understand it may require a bit of knowledge about the PE file format, which I'm not about to get into here (there's plenty of info on the web about it). FindNptProc
is simply a convenience function that does the binary search of the NPT. GetExportDirectoryTable
is another convenience function that parses the PE headers to locate the export directory table.
对于我来说在Visual Studio 2008和Windows XP(SP3)下,但是YMMV。我不是一个真正的Windows家伙*,所以这可能不是最干净的代码可移植性(根据不同版本的Windows)。像往常一样,此代码是按原样提供的,没有任何形式的保证;)
The code above compiles cleanly for me under Visual Studio 2008 and Windows XP (SP3), but YMMV. I'm not really a Windows guy*, so this might not be the cleanest code portability-wise (in terms of different versions of Windows). As usual, this code is provided "as is" with no warranty of any kind ;)
*是的,如果你想知道, / em>在写所有的Microsoft风格的Windows代码后仍然感觉很脏。
*Yes, in case you're wondering, I do still feel kind of dirty after writing all that Microsoft-style Windows code.
这篇关于以编程方式从函数名获取序数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!