PE文件结构(四)
参考
书:《加密与解密》
视频:小甲鱼 解密系列 视频
输出表
一般来说输出表存在于dll中。输出表提供了 文件中函数的名字跟这些函数的地址, PE装载器通过输出表来修改IAT。
IMAGE_OPTIONAL_HEADER中的 DataDirectory[0] 提供了输出表的RVA。输出表是以一个IMAGE_EXPORT_DIRECTORY结构 开始的。
IMAGE_EXPORT_DIRECTORY结构:
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; //未使用 DWORD TimeDateStamp; //文件生成的时间 WORD MajorVersion; //主版本号,一般为0 WORD MinorVersion; //次版本号,一般为0 DWORD Name; //指向dll名的RVA DWORD Base; // 基数,一般为 1 (就是从1数起) DWORD NumberOfFunctions; // AddressOfFunctions指向的数组的元素的个数 DWORD NumberOfNames; // AddressOfNames 指向的数组的元素的个数 DWORD AddressOfFunctions; // 函数地址数组ENT的RVA DWORD AddressOfNames; // 函数名字数组EAT的RVA DWORD AddressOfNameOrdinals; // 输出序列号数组的RVA,这个数组是以WORD为单位的,用来函数名字数组连接函数地址数组的 } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
输出表主要就是来给PE加载器修改IAT的,即查找函数的入口地址。PE加载器查找函数的地址有两种方法,从序号查找,从函数名查找。
1. 从序号查找函数入口地址
PE加载器通过INT知道了序号,直接用这个序号 去找 函数地址数组EAT就可以了。
2. 从函数名查找函数入口地址
通过函数名,查找函数名数组ENT,找到这个函数名在数组中的序号 n(从0数起),然后取输出序列号数组的第n个值(从0数起)。然后 以这个值为序号的的函数地址数组EAT的值就是这个函数的入口地址。
即 ENT[ 输出序列号数组[n] ]
实例分析:
例如找user32.dll 中AdjustWindowRect 这个函数。
先查看user32.dll的二进制文件,它的IMAGE_OPTIONAL_HEADER结构为:
图片1
从中可以知道:
Name: RVA 55C0h 文件偏移值:49C0h
Base: 1
NumberOfFunctions: 02DCh
NumberOfNmae:02DCh
AddressOfFunctions: RVA 3928h 文件偏移值:2D28h
AddressOfNames: RVA 4498h 文件偏移值:3898h
AddressOfNameOrdinals: RVA 5008h 文件偏移值:4408h
查看ENT数组所指向的字符串,可以知道AdjustWindowRect是第2个元素。所以,查看AddressOfNameOrdinals所指向的输出序列号数组的第2个元素,可以发现值为 1。
图片2
再查看EAT中序号为1的元素(即第2个元素)值为021140h,这个就是AdjustWindowRect函数的RVA。
图片3