PE文件解析-加载配置表、绑定导入表、导入地址表与延迟导入表

一、加载配置表

1.位置与简介

    载入配置表早期是用于描述当PE文件头或PE可选头无法描述或者因为太大而无法描述的各种功能。
    后来以XP及以后的系统主要是为了存储SEH句柄,称为安全结构化异常处理程序列表,如果SEH异常处理没有经过注册,在载入配置表中没有句柄,这个异常处理就不会被执行。
    据微软官方说明,这个载入配置表的作用是为了防止“x86异常处理程序劫持”的漏洞。因为年代久远就无从考据了。

    PE文件头可选映像头中数据目录表的第11成员IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG]指向加载配置表。

2.数据结构

    加载配置表起始于IMAGE_LOAD_CONFIG_DIRECTORY,它的定义如下:

typedef struct _IMAGE_LOAD_CONFIG_DIRECTORY {
    DWORD   Characteristics;              //属性,当前没使用
    DWORD   TimeDateStamp;                //(GMT时间)
    WORD    MajorVersion;                 //主版本号
    WORD    MinorVersion;                 //子版本号
    DWORD   GlobalFlagsClear;             //启动时清除全局标志
    DWORD   GlobalFlagsSet;               //启动时设置全局标志
    DWORD   CriticalSectionDefaultTimeout;//程序关键部分默认超时值
    DWORD   DeCommitFreeBlockThreshold;   //返回系统前必须释放的内存,以字节为单位
    DWORD   DeCommitTotalFreeThreshold;   //总共释放的内存
    PVOID   LockPrefixTable;              //预加锁表
    DWORD   MaximumAllocationSize;        //最大配置体积
    DWORD   VirtualMemoryThreshold;       //最大虚拟内存尺寸
    DWORD   ProcessHeapFlags;             //进程堆栈标志
    DWORD   ProcessAffinityMask;          //进程内部掩码
    WORD    CSDVersion;                   //CSD版本
    WORD    Reserved1;                    //保留,必须为0
    PVOID   EditList;                     //保留
    DWORD   Reserved[ 1 ];
} IMAGE_LOAD_CONFIG_DIRECTORY, *PIMAGE_LOAD_CONFIG_DIRECTORY;

二、绑定导入表

1.位置与简介

    绑定导入表的作用是加快程序的启动速度,一个PE程序在启动时会去加载导入表中的dll文件,并将导入表的FirstThunk指向的数组填入函数的真实地址,这需要耗去时间,当绑定导入表有效时,即使是未运行状态,系统也会自动将导入表中的FirstThunk填入函数的真实地址,否则导入表的IAT在未载入内存时的值通常跟OriginFirstThunk中的一样。

绑定导入表的生效,有两个前提条件:

  1. 程序初始化时,导入的DLL都加载到了首选基址
  2. 程序执行了绑定导入操作以后,导入DLL中引用的符号位置一直没有变化

如果有任何一个条件没有满足,系统就会忽略绑定导入操作。

    PE文件头可选映像头中数据目录表的第12成员IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]指向绑定导入表。

2.数据结构

    绑定导入表起始于IMAGE_BOUND_IMPORT_DESCRIPTOR,它的定义如下:

typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
    DWORD   TimeDateStamp;               //时间戳
    WORD    OffsetModuleName;            //dll名称偏移地址,基址是IMAGE_BOUND_IMPORT_DESCRIPTOR的开端
    WORD    NumberOfModuleForwarderRefs; //后面IMAGE_BOUND_FORWARDER_REF结构的数量
// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
} IMAGE_BOUND_IMPORT_DESCRIPTOR,  *PIMAGE_BOUND_IMPORT_DESCRIPTOR;

    IMAGE_BOUND_FORWARDER_REF的结构体定义如下:

typedef struct _IMAGE_BOUND_FORWARDER_REF {
    DWORD   TimeDateStamp;
    WORD    OffsetModuleName;
    WORD    Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;

    这个结构体的内容跟上面的其实是差不多的,绑定导入表中有一个函数转发链机制,比如说KERNEL32.DLL里面的HeapAlloc函数会转发到NTDLL.DLL中的RtlAllocateHeap函数,_IMAGE_BOUND_FORWARDER_REF 结构体就是用于保存转发函数的,一般情况下NumberOfModuleForwarderRefs为0。

三、导入地址表

    PE文件头可选映像头中数据目录表的第13成员IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]指向导入地址表。导入地址表的内容其实就是导入表中的FirstThunk的内容。

四、延迟导入表

1.位置与简介

延迟加载表本质上跟绑定导入表的目的是一样的,都是为了加快程序加载文件的速度,只不过方法不一样。
延迟加载是指在调用某个DLL时才去加载,目的是为了避免在程序启动之初就加载了不必要的DLL而浪费了时间。微软建议在两种情况下使用延迟加载:

程序并非在启动时就会调用DLL里面的函数
程序未必会调用该DLL里面的函数

延迟加载表不是系统支持的一个特性,它是由编译器控制的。

PE文件头可选映像头中数据目录表的第14成员IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT]指向延迟导入表,它保存在PE文件中,通常在".didata"区段。

2.数据结构

延迟导入表的结构体如下:

typedef struct ImgDelayDescr {  
    DWORD        grAttrs;       //延迟导入结构的属性,0x1为新版本,0x0为老版本
    RVA          rvaDLLName;    //dll名字的RVA
    RVA          rvaHmod;       //dll句柄的RVA
    RVA          rvaIAT;        //IAT表的RVA
    RVA          rvaINT;        //INT表的RVA
    RVA          rvaBoundIAT;   //绑定导入表的RVA
    RVA          rvaUnloadIAT;  //原始IAT的可选拷贝的RVA
    DWORD        dwTimeStamp;   //延迟载入DLL的时间戳,通常为0
} ImgDelayDescr, * PImgDelayDescr;

猜你喜欢

转载自blog.csdn.net/zhyulo/article/details/85927913