C++ Win32 API 枚举dll 导出函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1128150/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:55:37  来源:igfitidea点击:

Win32 API to enumerate dll export functions?

c++windowswinapidll

提问by user15071

I found similar questions but no answer to what I am looking for. So here goes:

我发现了类似的问题,但没有找到我正在寻找的答案。所以这里是:

For a native Win32 dll, is there a Win32 API to enumerate its export function names?

对于本机 Win32 dll,是否有 Win32 API 来枚举其导出函数名称?

回答by ephemient

dumpbin /exportsis pretty much what you want, but that's a developer tool, not a Win32 API.

dumpbin /exports几乎是您想要的,但这是一个开发人员工具,而不是 Win32 API。

LoadLibraryExwith DONT_RESOLVE_DLL_REFERENCESis heavily cautioned against, but happens to be useful for this particular case – it does the heavy lifting of mapping the DLL into memory (but you don't actually need or want to use anything from the library), which makes it trivial for you to read the header: the module handle returned by LoadLibraryExpoints right at it.

LoadLibraryExwithDONT_RESOLVE_DLL_REFERENCES被严重警告,但恰好对这种特殊情况很有用 - 它完成了将 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]);

Totally untested, but I think it's more or less correct. (Famous last words.)

完全未经测试,但我认为它或多或少是正确的。(著名遗言。)

回答by DeusAduro

Go over to Microsoft research and grab the Detours Library. One of its examples does exactly what you are asking. The whole library basically makes detouring/rerouting win32 function calls extremely easy. Its pretty cool stuff.

转到 Microsoft 研究并获取 Detours 库。它的一个例子完全符合你的要求。整个库基本上使绕行/重新路由 win32 函数调用变得非常容易。它很酷的东西。

Detours

少走弯路

Edit:Also note that if you just want to look at the export table, you can (at least in visual studios) set your project properties to print out the export/import tables. I can't remember the exact option but should be easy to google.

编辑:另请注意,如果您只想查看导出表,则可以(至少在 Visual Studio 中)设置项目属性以打印导出/导入表。我不记得确切的选项,但应该很容易谷歌。

**Edit2:**The option is Project Properties->Linker->Debugging->Generate MapFile ->Yes(/MAP)

**Edit2:**选项是Project Properties->Linker->Debugging->Generate MapFile ->Yes(/MAP)

回答by DeusAduro

try this:

尝试这个:

#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 (&sections[i].VirtualSize, 4, 1, hFile);
                            fread (&sections[i].VirtualAddress, 4, 1, hFile);
                            fread (&sections[i].SizeOfRawData, 4, 1, hFile);
                            fread (&sections[i].PointerToRawData, 4, 1, hFile);
                            fread (&sections[i].PointerToRelocations, 4, 1, hFile);
                            fread (&sections[i].PointerToLineNumbers, 4, 1, hFile);
                            fread (&sections[i].NumberOfRelocations, 2, 1, hFile);
                            fread (&sections[i].NumberOfLineNumbers, 2, 1, hFile);
                            fread (&sections[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 != '
void mycallback (char* szName) {
    printf ("%s\n", szName);
}

int main () {
    EnumExportedFunctions ("C:\Windows\System32\user32.dll", mycallback);
    return 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); } }

example:

例子:

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

output:

输出:

##代码##

回答by Jerry Coffin

While ephemient is correct that LoadLibraryExwith DONT_RESOLVE_DLL_REFERENCEScan simplify this task a great deal, you can make it even simpler than he shows. Instead of finding and enumerating the DLL's export directory yourself, you can use SymEnumerateSymbolsto list the symbols for you.

虽然ephemient是正确的,LoadLibraryExDONT_RESOLVE_DLL_REFERENCES可以简化这个任务很大,你可以把它更简单比他表示。您可以使用SymEnumerateSymbols来为您列出符号,而不是自己查找和枚举 DLL 的导出目录。

Although only marginally simpler than his code (without the asserts, his is only half a dozen lines of code) this at least theoretically gives a little extra flexibility in case Microsoft should someday decide to change the executable format a bit, and/or change exactly what the HMODULE points at, so his no longer works (since most of these details aren't officially documented anyway).

尽管只比他的代码稍微简单一点(没有断言,他只有六行代码)这至少在理论上提供了一点额外的灵活性,以防微软有一天决定稍微改变可执行格式,和/或完全改变HMODULE 指向什么,所以他不再起作用(因为这些细节中的大部分都没有正式记录)。

回答by Stephen Kellett

If you don't want to go to the trouble of writing your own code and would rather use a DLL that already exists for this purpose, I recommend PE File Format DLL. Comes with source code so that you can modify if you wish. No GPL to worry about.

如果您不想麻烦地编写自己的代码,而更愿意为此目的使用已经存在的 DLL,我建议您使用PE 文件格式 DLL。附带源代码,以便您可以根据需要进行修改。无需担心 GPL。

Also available is a GUI application that shows how to use the DLL.

还提供了一个 GUI 应用程序,它显示了如何使用 DLL。

回答by Charles Grunwald

I may be wrong, and I haven't double checked to be honest, but I believe there may be some compatibility issues with using ephemient's code on a module that is built under a different architecture than that of your process. (Again, I may be speaking completely out of my ass right now)

我可能是错的,老实说我没有仔细检查过,但我相信在与您的流程架构不同的架构下构建的模块上使用 ephemient 的代码可能存在一些兼容性问题。(再说一次,我现在可能完全是在说我的屁股)

There's a project on github, called dll2defthat uses the same technique (though it loads the file into memory on its own), but seems to have some checks in place to find the exports depending on the architecture of the binary. The code you'd most likely be interested in is in this file.

github 上有一个名为dll2def的项目,它使用相同的技术(尽管它自己将文件加载到内存中),但似乎有一些检查可以根据二进制文件的体系结构找到导出。您最有可能感兴趣的代码在这个文件中

回答by Ferruccio

If you're just looking for a way to find out what functions are exported in a DLL, you can use Microsoft's dependency walker(depends.exe). This wont help you if you actually need to discover the exports programmatically, though.

如果您只是在寻找一种方法来找出 DLL 中导出的函数,您可以使用 Microsoft 的依赖项walker(depends.exe)。但是,如果您确实需要以编程方式发现导出,这对您没有帮助。