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