如何在 C# 中读取(本机)DLL 的导出函数名称?

发布于 2024-09-29 12:35:54 字数 240 浏览 7 评论 0原文

我知道我可以阅读 PE 规范 以便编写执行此操作的代码。 然而,由于我没有太多时间,我希望你们中的一些人可能已经准备好发送这样的代码示例。

重要提示:32位和64位之间有什么区别吗?

谢谢您的宝贵时间!

I know I can read the PE specification in order to write a code that does this.
However, since I don't have a lot of time on my hands, I was hoping some of you might already have such a code sample ready to send.

Important note: Is there any difference between 32bit and 64bit?

Thank you for your time!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

昵称有卵用 2024-10-06 12:35:54

PE 文件导出

与导入函数相反的是导出函数以供 EXE 或其他 DLL 使用。 PE 文件在 .edata 部分中存储有关其导出函数的信息。通常,Microsoft 链接器生成的 PE EXE 文件不会导出任何内容,因此它们没有 .edata 部分。 Borland 的 TLINK32 总是从 EXE 中导出至少一个符号。大多数 DLL 都会导出函数并具有 .edata 部分。 .edata 节(也称为导出表)的主要组成部分是函数名称、入口点地址和导出序数值的表。在 NE 文件中,导出表的等效项是条目表、常驻名称表和非常驻名称表。这些表作为 NE 标头的一部分存储,而不是存储在不同的段或资源中。

.edata 节的开头是一个 IMAGE_EXPORT_DIRECTORY 结构(参见表 10)。该结构后面紧跟着该结构中的字段所指向的数据。

表 10. IMAGE_EXPORT_DIRECTORY 格式

DWORD Characteristics

该字段似乎未使用,并且始终设置为 0。

DWORD TimeDateStamp

指示此文件创建时间的时间/日期戳。

WORD MajorVersion
WORD MinorVersion

这些字段似乎未使用并设置为 0。

DWORD Name

带有此 DLL 名称的 ASCIIZ 字符串的 RVA。

DWORD Base

导出函数的起始序号。例如,如果文件导出序数值为 10、11 和 12 的函数,则此字段包含 10。要获取函数的导出序数,需要将此值添加到 AddressOfNameOrdinals 数组的相应元素中。

DWORD NumberOfFunctions

AddressOfFunctions 数组中的元素数量。该值也是该模块导出的函数的数量。理论上,该值可能与 NumberOfNames 字段(下一个)不同,但实际上它们始终相同。

DWORD NumberOfNames

AddressOfNames 数组中的元素数量。该值似乎始终与 NumberOfFunctions 字段相同,导出函数的数量也是如此。

PDWORD *AddressOfFunctions

该字段是一个 RVA,指向函数地址数组。函数地址是该模块中每个导出函数的入口点 (RVA)。

PDWORD *AddressOfNames

该字段是一个 RVA,指向一个字符串指针数组。字符串是该模块中导出函数的名称。

PWORD *AddressOfNameOrdinals

该字段是一个 RVA,指向一个 WORD 数组。 WORD 是该模块中所有导出函数的导出序号。但是,不要忘记添加在“基本”字段中指定的起始序数。

导出表的布局有些奇怪(参见图 4 和表 10)。正如我之前提到的,导出函数的要求是名称、地址和导出序号。您可能会认为 PE 格式的设计者会将所有这三个项目放入一个结构中,然后拥有这些结构的数组。相反,导出条目的每个组件都是数组中的一个元素。这些数组共有三个(AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals),并且它们都是彼此并行的。要查找有关第四个函数的所有信息,您需要查找每个数组中的第四个元素。

alt text

图 4. 导出表布局

表 11. 典型导出表EXE 文件

Name:            KERNEL32.dll
  Characteristics: 00000000
  TimeDateStamp:   2C4857D3
  Version:         0.00
  Ordinal base:    00000001
  # of functions:  0000021F
  # of Names:      0000021F

  Entry Pt  Ordn  Name
  00005090     1  AddAtomA
  00005100     2  AddAtomW
  00025540     3  AddConsoleAliasA
  00025500     4  AddConsoleAliasW
  00026AC0     5  AllocConsole
  00001000     6  BackupRead
  00001E90     7  BackupSeek
  00002100     8  BackupWrite
  0002520C     9  BaseAttachCompleteThunk
  00024C50    10  BasepDebugDump
  // Rest of table omitted...

顺便说一句,如果您转储 Windows NT 系统 DLL 的导出(例如,KERNEL32.DLL 和 USER32.DLL),您会注意到,在许多情况下,有两个函数仅相差一个名称末尾的字符,例如 CreateWindowExA 和 CreateWindowExW。这就是透明地实现 UNICODE 支持的方式。以 A 结尾的函数是 ASCII(或 ANSI)兼容函数,而以 W 结尾的函数是该函数的 UNICODE 版本。在您的代码中,您没有明确指定要调用哪个函数。相反,通过预处理器 #ifdefs 在 WINDOWS.H 中选择适当的函数。 Windows NT WINDOWS.H 的摘录显示了其工作原理的示例:
复制

#ifdef UNICODE
#define DefWindowProc  DefWindowProcW
#else
#define DefWindowProc  DefWindowProcA
#endif // !UNICODE

//
// Export Format
//

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

编辑:在PE格式导出表中没有区别,函数的地址只是64位地址的RVA。

来源: http://msdn.microsoft.com /en-us/library/ms809762.aspx

PE File Exports

The opposite of importing a function is exporting a function for use by EXEs or other DLLs. A PE file stores information about its exported functions in the .edata section. Generally, Microsoft linker-generated PE EXE files don't export anything, so they don't have an .edata section. Borland's TLINK32 always exports at least one symbol from an EXE. Most DLLs do export functions and have an .edata section. The primary components of an .edata section (aka the export table) are tables of function names, entry point addresses, and export ordinal values. In an NE file, the equivalents of an export table are the entry table, the resident names table, and the nonresident names table. These tables are stored as part of the NE header, rather than in distinct segments or resources.

At the start of an .edata section is an IMAGE_EXPORT_DIRECTORY structure (see Table 10). This structure is immediately followed by data pointed to by fields in the structure.

Table 10. IMAGE_EXPORT_DIRECTORY Format

DWORD Characteristics

This field appears to be unused and is always set to 0.

DWORD TimeDateStamp

The time/date stamp indicating when this file was created.

WORD MajorVersion
WORD MinorVersion

These fields appear to be unused and are set to 0.

DWORD Name

The RVA of an ASCIIZ string with the name of this DLL.

DWORD Base

The starting ordinal number for exported functions. For example, if the file exports functions with ordinal values of 10, 11, and 12, this field contains 10. To obtain the exported ordinal for a function, you need to add this value to the appropriate element of the AddressOfNameOrdinals array.

DWORD NumberOfFunctions

The number of elements in the AddressOfFunctions array. This value is also the number of functions exported by this module. Theoretically, this value could be different than the NumberOfNames field (next), but actually they're always the same.

DWORD NumberOfNames

The number of elements in the AddressOfNames array. This value seems always to be identical to the NumberOfFunctions field, and so is the number of exported functions.

PDWORD *AddressOfFunctions

This field is an RVA and points to an array of function addresses. The function addresses are the entry points (RVAs) for each exported function in this module.

PDWORD *AddressOfNames

This field is an RVA and points to an array of string pointers. The strings are the names of the exported functions in this module.

PWORD *AddressOfNameOrdinals

This field is an RVA and points to an array of WORDs. The WORDs are the export ordinals of all the exported functions in this module. However, don't forget to add in the starting ordinal number specified in the Base field.

The layout of the export table is somewhat odd (see Figure 4 and Table 10). As I mentioned earlier, the requirements for exporting a function are a name, an address, and an export ordinal. You'd think that the designers of the PE format would have put all three of these items into a structure, and then have an array of these structures. Instead, each component of an exported entry is an element in an array. There are three of these arrays (AddressOfFunctions, AddressOfNames, AddressOfNameOrdinals), and they are all parallel to one another. To find all the information about the fourth function, you need to look up the fourth element in each array.

alt text

Figure 4. Export table layout

Table 11. Typical Exports Table from an EXE File

Name:            KERNEL32.dll
  Characteristics: 00000000
  TimeDateStamp:   2C4857D3
  Version:         0.00
  Ordinal base:    00000001
  # of functions:  0000021F
  # of Names:      0000021F

  Entry Pt  Ordn  Name
  00005090     1  AddAtomA
  00005100     2  AddAtomW
  00025540     3  AddConsoleAliasA
  00025500     4  AddConsoleAliasW
  00026AC0     5  AllocConsole
  00001000     6  BackupRead
  00001E90     7  BackupSeek
  00002100     8  BackupWrite
  0002520C     9  BaseAttachCompleteThunk
  00024C50    10  BasepDebugDump
  // Rest of table omitted...

Incidentally, if you dump out the exports from the Windows NT system DLLs (for example, KERNEL32.DLL and USER32.DLL), you'll note that in many cases there are two functions that only differ by one character at the end of the name, for instance CreateWindowExA and CreateWindowExW. This is how UNICODE support is implemented transparently. The functions that end with A are the ASCII (or ANSI) compatible functions, while those ending in W are the UNICODE version of the function. In your code, you don't explicitly specify which function to call. Instead, the appropriate function is selected in WINDOWS.H, via preprocessor #ifdefs. This excerpt from the Windows NT WINDOWS.H shows an example of how this works:
Copy

#ifdef UNICODE
#define DefWindowProc  DefWindowProcW
#else
#define DefWindowProc  DefWindowProcA
#endif // !UNICODE

//
// Export Format
//

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

EDIT: In PE format exports table is not diffrent, address of functions is a RVA of 64bit address only.

Source: http://msdn.microsoft.com/en-us/library/ms809762.aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文