导出表的定义
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; // RVA from base of image DWORD AddressOfNames; // RVA from base of image DWORD AddressOfNameOrdinals; // RVA from base of image } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
Characteristics:现在没有用到,一般为0。
TimeDateStamp:导出表生成的时间戳,由连接器生成。
MajorVersion,MinorVersion:看名字是版本,实际貌似没有用,都是0。
Name:模块的名字。
Base:序号的基数,按序号导出函数的序号值从Base开始递增。
NumberOfFunctions:所有导出函数的数量。
NumberOfNames:按名字导出函数的数量。
AddressOfFunctions:一个RVA,指向一个DWORD数组,数组中的每一项是一个导出函数的RVA,顺序与导出序号相同。
AddressOfNames:一个RVA,依然指向一个DWORD数组,数组中的每一项仍然是一个RVA,指向一个表示函数名字。
AddressOfNameOrdinals:一个RVA,还是指向一个WORD数组,数组中的每一项与AddressOfNames中的每一项对应,表示该名字的函数在AddressOfFunctions中的序号。
反正我看图也是没看太明白。
我用个例子我来解释
节点分布
节点遍历信息
文件中的字节码
导出表在紧跟导入表信息的后面。
从节点信息我们可以知道,文件中的偏移是从800开始,内存的偏移是从2000开始。
文件中的偏移=文件中的地址 – RVA内存偏移 + FOA文件偏移(这里是800)
如name文件偏移=HEX:90 21 00 00=2190-2000+800=990偏移,以/0结束。ansi就是winresult.dll。
接着往下是nBase,类推。不在详述。就很好理解导出表的结构了。
导出表的应用
1,导出函数的覆盖
还是用回之前的那个例子。如果我们要吧导出函数AnimateClose跟AnimateOpen调换。只需要把函数的虚拟地址修改调换即可。
导出函数从00002168=2168-2000+800=968开始。数量为4个,
HEX对应
83 11 00 00
82 12 00 00
22 10 00 00
23 13 00 00
我们可以对这4个HEX中第二组跟第三组进行调换。就可对导出函数进行互换。
注意:用这种方法需要清楚所调用的函数的参数必须相同。
这种用途,可以在不知道EXE源码的情况下。对DLL进行修改。从而让EXE的功能造成变动。
2,函数地址内的指令硬编码
这个是在函数内的地址对函数的功能代码做修改,可以做到功能修改。替换原来函数的功能。
导出私有函数
就是在导出函数中没有公开。私有的函数。默认我们是调用不了的。
修改步骤为:把导出表整体结构搬迁到一个空闲空间中。
然后NumberOfFunctions; NumberOfNames;分别加1,
增加函数名TOPXY,修改因搬迁而变动的地址。
PE头数据目录因搬迁而变动的地址
具体的导出表修改后如下。可跟上面的对比,(这里未包含PE头数据目录的导出表的修改信息)