导出表大多存在于dll中,其主要目的就是导出一些可供其他exe调用的函数。
导出的函数分两种,一种按名称导出,一种按序号导出。
导出表的大体结构如下:
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 1) 保留,恒为0x00000000
DWORD TimeDateStamp; // 2) 时间戳,导出表创建的时间(GMT时间)
WORD MajorVersion; // 3) 主版本号:导出表的主版本号
WORD MinorVersion; // 4) 子版本号:导出表的子版本号
DWORD Name; // 5) 指向模块名称的RVA,指向模块名(导出表所在模块的名称)的ASCII字符的RVA
DWORD Base; // 6) 导出表用于输出导出函数序号值的基数: 导出函数序号 = 函数入口地址数组下标索引值 + 基数
DWORD NumberOfFunctions; // 7) 导出函数入口地址表的成员个数
DWORD NumberOfNames; // 8) 导出函数名称表中的成员个数
DWORD AddressOfFunctions; // 9) 函数入口地址表的相对虚拟地址(RVA),每一个非0的项都对应一个被导出的函数名称或导出序号(序号+基数等于导出函数序号)
DWORD AddressOfNames; // 10) 函数名称表的相对虚拟地址(RVA),存储着指向导出函数名称的ASCII字符的RVA
DWORD AddressOfNameOrdinals; // 11) 存储着函数入口地址表的数组下标索引值(序号表),跟导出函数名称表的成员顺序对应
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
其中最重要的就是
Name:保存了dll的名称
AddressOfFunctions:保存了所有导出函数的地址RVA
AddressOfNames:保存了所有按名称导出的函数的名称RVA
AddressOfNameOrdinals:保存了所有按名称导出的函数的序号
当我们要在导出表中查找某一个函数时,我们要先遍历AddressOfNames查找函数名称,记录当下的索引0。之后将索引0作为AddressOfNameOrdinals的索引值查找值,获得索引1,再将索引1作为AddressOfFunctions的索引找到函数地址的RVA。