数据结构
数据目录表的第一个成员指向的地址就是输出表,其数据结构如下
字段说明:
Name:RVA值,指向dll在编译时的名称的字符串。
Base:导出函数序号的起始值。即入口地址表中第一个函数的序号。
NumberOfFunctions:导出函数总数。
NumberOfNames:有函数名称的导出函数个数,此种导出函数既可以用函数名导出,又可以用序号方式导出。
AddressOfFunctions:RVA值,指向一个包含全部导出函数入口地址的DWORD数组,即入口地址表,此表各项按序号排列。
AddressOfNames:RVA值,指向函数名字符串表,此表在通过函数名称获取地址时要用到。表中每一项都是DWORD型的RVA,各指向一个函数名字符串,表中共有NumberOfNames
项。
AddressOfNameOrdinals:RVA值,指向函数名序号表,此表在通过函数名称获取地址时要用到,每项都是WORD。
获取函数地址
有两种方式:通过序号获取函数地址和通过函数名获取函数地址。
通过序号获取函数地址
原理
这种方式虽然较简单,没有用到AddressOfNames
和AddressOfNameOrdinals
这两个字段,但存在缺陷:如果PE文件经过编辑,修改了导出函数,那么入口地址表就会改变,导致第4步中索引错误,这是致命的。
通过名称获取函数地址
原理
实例
一个简单的libdll_generate.dll,只有一个导出函数MyAdd,现在来模拟一下Windows的PELoader是如何通过函数名称找到其入口地址的。
LordPE查看导出表的RVA为92000H
看下区段表
此RVA在.edata节中偏移量为0的位置,转FO为8EC00
用C32ASM跟进
图中标蓝即为IED结构体,共40B
看下Name字段
Base字段,起始序号为1,此种方法中Base字段无用。
其余同理,知
导出函数总数:1
有名称的导出函数数:1
函数名字符串表的RVA:
其指向的字符串表:
循环查找各表项指向的字符串,如果找到字符串与待查找的目标字符串匹配,则记下此字符串在函数名字符串表中的索引,此处为0。
函数名序号表:
在函数名序号表中取出下标为0的WORD数据,即为此函数的序号,此处是0。
入口地址表:
上一步得到其序号为0,所以在入口地址表中取出下标为0的DWORD数据,即为此函数的入口地址,此处为00001450H
。
用Depends验证一下,无误
总结
此种方式实际上可以总结为:根据函数名在函数名字符串表中查找,获得其索引值,然后在函数名序号表中相同的索引位置获得此函数的序号,最后在函数入口地址表中根据序号获得其入口地址。