我发现了类似的问题,但没有找到我正在寻找的答案。所以这里:
对于本机Win32 dll,是否有一个Win32 API来枚举其导出函数名称?
答案 0 :(得分:45)
dumpbin /exports
几乎是您想要的,但这是一个开发人员工具,而不是Win32 API。
DONT_RESOLVE_DLL_REFERENCES
的 LoadLibraryEx
受到严重警告,但恰好对这种特殊情况有用 - 它将DLL映射到内存中的繁重工作(但实际上并不需要或不需要使用库中的任何东西),这使得阅读标题变得微不足道:LoadLibraryEx
返回的模块句柄指向它。
#include <winnt.h>
HMODULE lib = LoadLibraryEx("library.dll", NULL, DONT_RESOLVE_DLL_REFERENCES);
assert(((PIMAGE_DOS_HEADER)lib)->e_magic == IMAGE_DOS_SIGNATURE);
PIMAGE_NT_HEADERS header = (PIMAGE_NT_HEADERS)((BYTE *)lib + ((PIMAGE_DOS_HEADER)lib)->e_lfanew);
assert(header->Signature == IMAGE_NT_SIGNATURE);
assert(header->OptionalHeader.NumberOfRvaAndSizes > 0);
PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY)((BYTE *)lib + header->
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
assert(exports->AddressOfNames != 0);
BYTE** names = (BYTE**)((int)lib + exports->AddressOfNames);
for (int i = 0; i < exports->NumberOfNames; i++)
printf("Export: %s\n", (BYTE *)lib + (int)names[i]);
完全未经测试,但我认为这或多或少是正确的。 (着名的遗言。)
答案 1 :(得分:8)
转到Microsoft研究并获取Detours Library。其中一个例子完全符合您的要求。整个库基本上使绕行/重新路由win32函数调用非常容易。它很酷的东西。
编辑另请注意,如果您只想查看导出表,则可以(至少在可视工作室中)设置项目属性以打印导出/导入表。我不记得确切的选项,但应该很容易google。
** Edit2:**选项是Project Properties-&gt; Linker-&gt; Debugging-&gt; Generate MapFile - &gt; Yes(/ MAP)
答案 2 :(得分:3)
虽然ephemient是正确的LoadLibraryEx
与DONT_RESOLVE_DLL_REFERENCES
可以简化这项任务,但你可以使它比他所展示的更简单。您可以使用SymEnumerateSymbols
为您列出符号,而不是自己查找和枚举DLL的导出目录。
虽然只比他的代码稍微简单一点(没有断言,他只有六行代码),这至少在理论上给了一些额外的灵活性,以防微软有朝一日决定改变可执行格式,和/或者改变HMODULE所指出的,所以他不再适用(因为大多数这些细节都没有正式记录)。
答案 3 :(得分:2)
试试这个:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void EnumExportedFunctions (char *, void (*callback)(char*));
int Rva2Offset (unsigned int);
typedef struct {
unsigned char Name[8];
unsigned int VirtualSize;
unsigned int VirtualAddress;
unsigned int SizeOfRawData;
unsigned int PointerToRawData;
unsigned int PointerToRelocations;
unsigned int PointerToLineNumbers;
unsigned short NumberOfRelocations;
unsigned short NumberOfLineNumbers;
unsigned int Characteristics;
} sectionHeader;
sectionHeader *sections;
unsigned int NumberOfSections = 0;
int Rva2Offset (unsigned int rva) {
int i = 0;
for (i = 0; i < NumberOfSections; i++) {
unsigned int x = sections[i].VirtualAddress + sections[i].SizeOfRawData;
if (x >= rva) {
return sections[i].PointerToRawData + (rva + sections[i].SizeOfRawData) - x;
}
}
return -1;
}
void EnumExportedFunctions (char *szFilename, void (*callback)(char*)) {
FILE *hFile = fopen (szFilename, "rb");
if (hFile != NULL) {
if (fgetc (hFile) == 'M' && fgetc (hFile) == 'Z') {
unsigned int e_lfanew = 0;
unsigned int NumberOfRvaAndSizes = 0;
unsigned int ExportVirtualAddress = 0;
unsigned int ExportSize = 0;
int i = 0;
fseek (hFile, 0x3C, SEEK_SET);
fread (&e_lfanew, 4, 1, hFile);
fseek (hFile, e_lfanew + 6, SEEK_SET);
fread (&NumberOfSections, 2, 1, hFile);
fseek (hFile, 108, SEEK_CUR);
fread (&NumberOfRvaAndSizes, 4, 1, hFile);
if (NumberOfRvaAndSizes == 16) {
fread (&ExportVirtualAddress, 4, 1, hFile);
fread (&ExportSize, 4, 1, hFile);
if (ExportVirtualAddress > 0 && ExportSize > 0) {
fseek (hFile, 120, SEEK_CUR);
if (NumberOfSections > 0) {
sections = (sectionHeader *) malloc (NumberOfSections * sizeof (sectionHeader));
for (i = 0; i < NumberOfSections; i++) {
fread (sections[i].Name, 8, 1, hFile);
fread (§ions[i].VirtualSize, 4, 1, hFile);
fread (§ions[i].VirtualAddress, 4, 1, hFile);
fread (§ions[i].SizeOfRawData, 4, 1, hFile);
fread (§ions[i].PointerToRawData, 4, 1, hFile);
fread (§ions[i].PointerToRelocations, 4, 1, hFile);
fread (§ions[i].PointerToLineNumbers, 4, 1, hFile);
fread (§ions[i].NumberOfRelocations, 2, 1, hFile);
fread (§ions[i].NumberOfLineNumbers, 2, 1, hFile);
fread (§ions[i].Characteristics, 4, 1, hFile);
}
unsigned int NumberOfNames = 0;
unsigned int AddressOfNames = 0;
int offset = Rva2Offset (ExportVirtualAddress);
fseek (hFile, offset + 24, SEEK_SET);
fread (&NumberOfNames, 4, 1, hFile);
fseek (hFile, 4, SEEK_CUR);
fread (&AddressOfNames, 4, 1, hFile);
unsigned int namesOffset = Rva2Offset (AddressOfNames), pos = 0;
fseek (hFile, namesOffset, SEEK_SET);
for (i = 0; i < NumberOfNames; i++) {
unsigned int y = 0;
fread (&y, 4, 1, hFile);
pos = ftell (hFile);
fseek (hFile, Rva2Offset (y), SEEK_SET);
char c = fgetc (hFile);
int szNameLen = 0;
while (c != '\0') {
c = fgetc (hFile);
szNameLen++;
}
fseek (hFile, (-szNameLen)-1, SEEK_CUR);
char* szName = calloc (szNameLen + 1, 1);
fread (szName, szNameLen, 1, hFile);
callback (szName);
fseek (hFile, pos, SEEK_SET);
}
}
}
}
}
fclose (hFile);
}
}
示例:
void mycallback (char* szName) {
printf ("%s\n", szName);
}
int main () {
EnumExportedFunctions ("C:\\Windows\\System32\\user32.dll", mycallback);
return 0;
}
输出:
ActivateKeyboardLayout
AddClipboardFormatListener
AdjustWindowRect
AdjustWindowRectEx
AlignRects
AllowForegroundActivation
AllowSetForegroundWindow
AnimateWindow
AnyPopup
AppendMenuA
AppendMenuW
ArrangeIconicWindows
AttachThreadInput
BeginDeferWindowPos
BeginPaint
BlockInput
BringWindowToTop
BroadcastSystemMessage
BroadcastSystemMessageA
BroadcastSystemMessageExA
BroadcastSystemMessageExW
BroadcastSystemMessageW
BuildReasonArray
CalcMenuBar
.....etc
答案 4 :(得分:1)
如果您不想编写自己的代码而宁愿使用已经存在的DLL,我建议使用PE File Format DLL。附带源代码,以便您可以根据需要进行修改。没有GPL担心。
还有一个GUI应用程序,它显示了如何使用DLL。
答案 5 :(得分:0)
如果您只是想找到一种方法来查找DLL中导出的函数,可以使用Microsoft的dependency walker(depends.exe)。如果您确实需要以编程方式发现导出,这将无法帮助您。
答案 6 :(得分:0)
我可能是错的,并且我没有仔细检查过,但我相信在使用与您的流程不同的架构下构建的模块上使用ephemient的代码可能存在一些兼容性问题。 (再说一次,我现在可能完全不在我的屁股上)
在github上有一个名为dll2def的项目使用相同的技术(尽管它自己将文件加载到内存中),但似乎有一些检查来查找导出,具体取决于体系结构二进制。您最有可能感兴趣的代码位于this file。
答案 7 :(得分:0)
问这个问题已经 12 年了,但想指出所提出的解决方案的一些问题。
它们都没有考虑序数(没有名称字符串的导出)。 由于有序索引之间的潜在差距而变得复杂。序数有一个起始基数(IMAGE_EXPORT_DIRECTORY 的“基数”字段),但不能保证序数是连续的。
不想花时间编写代码,但一种方法是通过索引 0 迭代到 NumberOfFunctions。
然后在第二个(内部)循环中,将 0 中的索引与 NumberOfNames 匹配到 AddressOfNameOrdinals 数组中。
如果您将函数索引与 AddressOfNameOrdinals 数组索引相匹配,那就是您在 AddressOfNames 数组中的索引(您必须解析的偏移量)。如果你没有得到匹配(通过 NumberOfNames 索引),那么它是一个有序的导出。
如果 AddressOfFunctions 条目中的函数索引为 0,那么它只是一个序数间隙,您可以跳到下一个索引。
要获得实际的序数(用于打印为字符串),请将“Base”添加到 NumberOfFunctions 循环索引中。