如何在 C# 中读取(本机)DLL 的导出函数名称?
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
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 格式
该字段似乎未使用,并且始终设置为 0。
指示此文件创建时间的时间/日期戳。
这些字段似乎未使用并设置为 0。
带有此 DLL 名称的 ASCIIZ 字符串的 RVA。
导出函数的起始序号。例如,如果文件导出序数值为 10、11 和 12 的函数,则此字段包含 10。要获取函数的导出序数,需要将此值添加到 AddressOfNameOrdinals 数组的相应元素中。
AddressOfFunctions 数组中的元素数量。该值也是该模块导出的函数的数量。理论上,该值可能与 NumberOfNames 字段(下一个)不同,但实际上它们始终相同。
AddressOfNames 数组中的元素数量。该值似乎始终与 NumberOfFunctions 字段相同,导出函数的数量也是如此。
该字段是一个 RVA,指向函数地址数组。函数地址是该模块中每个导出函数的入口点 (RVA)。
该字段是一个 RVA,指向一个字符串指针数组。字符串是该模块中导出函数的名称。
该字段是一个 RVA,指向一个 WORD 数组。 WORD 是该模块中所有导出函数的导出序号。但是,不要忘记添加在“基本”字段中指定的起始序数。
导出表的布局有些奇怪(参见图 4 和表 10)。正如我之前提到的,导出函数的要求是名称、地址和导出序号。您可能会认为 PE 格式的设计者会将所有这三个项目放入一个结构中,然后拥有这些结构的数组。相反,导出条目的每个组件都是数组中的一个元素。这些数组共有三个(AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals),并且它们都是彼此并行的。要查找有关第四个函数的所有信息,您需要查找每个数组中的第四个元素。
图 4. 导出表布局
表 11. 典型导出表EXE 文件
顺便说一句,如果您转储 Windows NT 系统 DLL 的导出(例如,KERNEL32.DLL 和 USER32.DLL),您会注意到,在许多情况下,有两个函数仅相差一个名称末尾的字符,例如 CreateWindowExA 和 CreateWindowExW。这就是透明地实现 UNICODE 支持的方式。以 A 结尾的函数是 ASCII(或 ANSI)兼容函数,而以 W 结尾的函数是该函数的 UNICODE 版本。在您的代码中,您没有明确指定要调用哪个函数。相反,通过预处理器 #ifdefs 在 WINDOWS.H 中选择适当的函数。 Windows NT WINDOWS.H 的摘录显示了其工作原理的示例:
复制
编辑:在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
This field appears to be unused and is always set to 0.
The time/date stamp indicating when this file was created.
These fields appear to be unused and are set to 0.
The RVA of an ASCIIZ string with the name of this DLL.
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.
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.
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.
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.
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.
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.
Figure 4. Export table layout
Table 11. Typical Exports Table from an EXE File
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
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