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
Run Code Online (Sandbox Code Playgroud)
此字段似乎未使用且始终设置为 0。
DWORD TimeDateStamp
Run Code Online (Sandbox Code Playgroud)
指示此文件创建时间的时间/日期戳。
WORD MajorVersion
WORD MinorVersion
Run Code Online (Sandbox Code Playgroud)
这些字段似乎未使用并设置为 0。
DWORD Name
Run Code Online (Sandbox Code Playgroud)
带有此 DLL 名称的 ASCIIZ 字符串的 RVA。
DWORD Base
Run Code Online (Sandbox Code Playgroud)
导出函数的起始序号。例如,如果文件导出序号值为 10、11 和 12 的函数,则此字段包含 10。要获取函数的导出序号,您需要将此值添加到 AddressOfNameOrdinals 数组的相应元素。
DWORD NumberOfFunctions
Run Code Online (Sandbox Code Playgroud)
AddressOfFunctions 数组中的元素数。这个值也是这个模块导出的函数数。理论上,此值可能与 NumberOfNames 字段(下一个)不同,但实际上它们始终相同。
DWORD NumberOfNames
Run Code Online (Sandbox Code Playgroud)
AddressOfNames 数组中的元素数。该值似乎总是与 NumberOfFunctions 字段相同,导出函数的数量也是如此。
PDWORD *AddressOfFunctions
Run Code Online (Sandbox Code Playgroud)
该字段是一个 RVA,指向一个函数地址数组。函数地址是此模块中每个导出函数的入口点 (RVA)。
PDWORD *AddressOfNames
Run Code Online (Sandbox Code Playgroud)
该字段是一个 RVA,指向一个字符串指针数组。字符串是此模块中导出函数的名称。
PWORD *AddressOfNameOrdinals
Run Code Online (Sandbox Code Playgroud)
该字段是一个 RVA,指向一个 WORD 数组。WORD 是该模块中所有导出函数的导出序号。但是,不要忘记添加在 Base 字段中指定的起始序号。
导出表的布局有些奇怪(见图 4 和表 10)。正如我之前提到的,导出函数的要求是名称、地址和导出序号。您可能认为 PE 格式的设计者会将所有这三个项目放入一个结构中,然后拥有这些结构的数组。相反,导出条目的每个组件都是数组中的一个元素。共有三个这样的数组(AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals),它们彼此平行。要查找有关第四个函数的所有信息,您需要查找每个数组中的第四个元素。

图 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...
Run Code Online (Sandbox Code Playgroud)
顺便说一下,如果您转储 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
Run Code Online (Sandbox Code Playgroud)
//
// 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;
Run Code Online (Sandbox Code Playgroud)
编辑:在 PE 格式中,导出表没有区别,函数地址只是 64 位地址的 RVA。
来源: http : //msdn.microsoft.com/en-us/library/ms809762.aspx
| 归档时间: |
|
| 查看次数: |
3899 次 |
| 最近记录: |