导出表位置在可选头成员:
函数1名字地址 (宽度4字节)RVA |
函数2名字地址 (宽度4字节)RVA |
函数3名字地址 (宽度4字节)RVA |
..... |
函数1入口地址 (宽度4字节)RVA |
函数2入口地址 (宽度4字节) RVA |
函数3入口地址 (宽度4字节)RVA |
..... |
函数1序号值 (宽度2字节) 序号值+Base;才是真正的导出序号 |
函数2序号值 (宽度2字节) |
函数3序号值 (宽度2字节) |
..... |
注意:函数序号这个表并不是该程序的所有导出序号都在里面,该表是给函数名字用来找函数地址索引用的.所以它的长度(列表数)与函数名称表的长度(列表数)是一样的,这也是为什么用真正的序号查找函数地址表的时候 不用管这个表的原因。
以上三个表不一定互相对应:
NumberOfNames;以函数名导出的函数个数,这个表有可能比函数地址表大(可以两个一样的名字指向一个地址),有可能比它小(因为有的是通过序号导出的,没有名字)
名字表和序号表的长度(列表数)是一样的
NumberOfFunctions; (就是一个值)所有导出函数的个数,决定表有多大,遍历时候的次数。
函数地址表 有时不一定代表真正导出函数的个数(39:49),dll导出函数序号被修改成乱序后,表的大小=序号最大值-序号最小值+1,这个表中没有对应的导出序号的地址,会被填充0.所以说这个表里面的地址是有0的
6-2+1=5 但实际上它只有4个,如果序号中间没有断档的话 这个数量是对的,有断档的话地址表里就会有0
用函数名字找函数地址:
1.先找函数名字表,把从函数名字地址(开始地址+RVA)里读出函数的名字,与要找的名字进行对比,(要考虑没有找到这个名字的处理方式,return -1 0是列表第一个行号 没找到不要返回0),直到找到后,获取当前表的行号
2.用该行号去序号表,读取对应行的序号值,把这个序号值当做函数地址的列表索引 ,去函数地址表取地址,就好了.
用导出序号(序号+BASE)找函数地址:
1.先用要找的序号-BASE,得到的结果 当做函数地址表的列表索引,直接去函数地址表里找就好了
倒序查找:
用函数地址查找函数名:
根据当前函数地址在表的索引值,遍历序数表,对比序数表地址对应的值,第几行(列表索引)里面装的是这个索引值(函数地址表的索引),然后用这个列表索引,去函数名称表,读取函数名称地址,再用函数名称地址获取到函数名 (注意以上很多地址都是RVA,不再强调)
批量遍历查询注意事项:
能在序数表中找到对应的函数地址表的列表索引 说明一定是函数名称导出的,如果函数地址表中的地址是0,则直接过掉,说明是个无效地址,再遍历表中的下一个地址.需要注意的是0占用了一个表索引号,在遍历下一个地址时 索引值要+1,不要忽略。不然后面的结果都会错误
如果在序数表中找不到对应的值,则代表不是函数名字导出的,可以通过
索引值+Base=该函数的真正导出序号.