5. 导出表
DEF文件如下所示:
EXPORTS
Inject @12
MyAdd @14
MySub @17
MyMul @11 NONAME
MyDiv @19
g_szInject @15
用调试器加载编译好的dll文件,查看导出信息:
用十六进制编辑器打开dll文件,定位到导出表,可以发现导出的字符串是按ASCII顺序排列的,方便系统折半查找:
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; //未使用
DWORD TimeDateStamp; //可忽略
WORD MajorVersion; //未使用
WORD MinorVersion; //未使用
DWORD Name; //指向文件名
DWORD Base; //导出项起始序号
DWORD NumberOfFunctions; //导出项个数,不一定是函数,全局变量等也可以
DWORD NumberOfNames; //带名称的导出项个数
DWORD AddressOfFunctions; //地址表的RVA
DWORD AddressOfNames; //名称表的RVA
DWORD AddressOfNameOrdinals; //名称序号表的RVA,索引值是2字节的word,代表在AddressOfFunctions中的索引
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
通过序号查询函数,例如GetProcAddress(hModule, 14)
- 序号14减去Base,得到3
- 去地址表中找到下标为3的项,得到00001014
- 00001014加上hModule,即为函数地址
通过名称查询函数,例如GetProcAddress(hModule, “MyAdd”)
- 定位到名称表,对比字符串,发现000020D9指向的是“MyAdd”
- 000020D9在名称表中下标为1,所以找到序号表下标为1的项,其值为0003
- 地址表中找到下标为3的项,结果为00001014
- 00001014加上hModule,即为函数地址
关于转发:
如果查询到的地址在导出表范围内(加载后的地址,即VA),则为转发,需要递归调用自己实现的GetProcAddress,如果不在导出表范围内,则可直接返回。