一、加载配置表
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中的一样。
绑定导入表的生效,有两个前提条件:
- 程序初始化时,导入的DLL都加载到了首选基址中
- 程序执行了绑定导入操作以后,导入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;