PE 文件被执行时,Windows 加载器将文件装入内存并将导入表(Export Table) 登记的动态链接库(一般是DLL 格式)文件一并装入地址空间,再根据DLL 文件中的函数导出信息对被执行文件的IAT 进行修正。
数据目录表的第一个成员指向导出表,是一个IMAGE_EXPORT_DIRECTORY(以后简称IED)结构:
IMAGE_EXPORT_DIRECTORY STRUCT【导出表,共40字节】
{
+00 h DWORD Characteristics ; 未使用,总是定义为0
+04 h DWORD TimeDateStamp ; 文件生成时间
+08 h WORD MajorVersion ; 未使用,总是定义为0
+0A h WORD MinorVersion ; 未使用,总是定义为0
+0C h DWORD Name ; 模块的真实名称
+10 h DWORD Base ; 基数,加上序数就是函数地址数组的索引值
+14 h DWORD NumberOfFunctions ; 导出函数的总数
+18 h DWORD NumberOfNames ; 以名称方式导出的函数的总数
+1C h DWORD AddressOfFunctions ; 指向输出函数地址的RVA
+20 h DWORD AddressOfNames ; 指向输出函数名字的RVA
+24 h DWORD AddressOfNameOrdinals ; 指向输出函数序号的RVA
};IMAGE_EXPORT_DIRECTORY ENDS
参数:
Name:
一个RVA 值,指向一个定义了模块名称的字符串。如即使Kernel32.dll 文件被改名为"Ker.dll",仍然可以从这个字符串中的值得知其在编译时的文件名是"Kernel32.dll"。
NumberOfFunctions:
文件中包含的导出函数的总数。
NumberOfNames:
被 定义函数名称的导出函数的总数,显然只有这个数量的函数既可以用函数名方式导出。也可以用序号方式导出,剩下 的NumberOfFunctions 减去NumberOfNames 数量的函数只能用序号方式导出。该字段的值只会小于或者等于 NumberOfFunctions 字段的值,如果这个值是0,表示所有的函数都是以序号方式导出的。
AddressOfFunctions:
一个RVA 值,指向包含全部导出函数入口地址的双字数组。数组中的每一项是一个RVA 值,数组的项数等于NumberOfFunctions 字段的值。
Base: 导出函数序号的起始值,将AddressOfFunctions 字段指向的入口地址表的索引号加上这个起始值就是对应函数的导出 序号。假如Base 字段的值为x,那么入口地址表指定的第1个导出函数的序号就是x;第2个导出函数的序号就是x+1。总之,一个导出函数的导出序号等 于Base 字段的值加上其在入口地址表中的位置索引值。
AddressOfNames 和 AddressOfNameOrdinals:
均 为RVA 值。前者指向函数名字符串地址表。这个地址表是一个双字数组,数组中的每一项指向一个函数名称字符串的RVA。数组的项数等于NumberOfNames 字段的值,所有有名称的导出函数的名称字符串都定义在这个表中;后者指向另一个word 类型的数组(注意不是双字数组)。数组项目与文件名地址表中的项目一一对应,项目值代表函数入口地址表的索引,这样函 数名称与函数入口地址关联起来。