延迟加载导入表
延迟加载导入表(Delay Load Import Table)是PE中引入的专门用来描述与动态链接库延迟加载相关的数据,因为这些数据所起的作用和结构与导入表数据基本一致,所以称为延迟加载导入表。
延迟加载导入的概念:系统开始运行程序时被指定的延迟加载的 DLL是不被载入的,只有等到程序调用了该动态链接库的函数时,系统才将该链接库载入内存空间,并执行相关函数代码
typedef struct _IMAGE_DELAYLOAD_DESCRIPTOR {
union {
DWORD AllAttributes; // 属性,必须为0
struct {
DWORD RvaBased : 1; // Delay load version 2
DWORD ReservedAttributes : 31;
} DUMMYSTRUCTNAME;
} Attributes;
DWORD DllNameRVA; // 指向DLL名称的RVA
DWORD ModuleHandleRVA; // DLL模块句柄的RVA
DWORD ImportAddressTableRVA; // 延迟加载导入IAT的RVA
DWORD ImportNameTableRVA; // 延迟加载导入INT的RVA
DWORD BoundImportAddressTableRVA; // 绑定延迟加载导入表的RVA
DWORD UnloadInformationTableRVA; // 卸载延迟加载导入地址表的RVA
DWORD TimeDateStamp; // 此映像绑定到DLL的时间戳
} IMAGE_DELAYLOAD_DESCRIPTOR, *PIMAGE_DELAYLOAD_DESCRIPTOR;
typedef const IMAGE_DELAYLOAD_DESCRIPTOR *PCIMAGE_DELAYLOAD_DESCRIPTOR;
延迟导入表解析
#include<Windows.h>
#include<iostream>
using namespace std;
DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
for (int i = 0; i < f->NumberOfSections; i++)
{
// FOA = 数据的RVA + 区段的RVA - 区段的FOA
if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->Misc.VirtualSize))
{
return rva - p->VirtualAddress + p->PointerToRawData;
}
f++;
p++;
}
return 0;
}
void ImageNtHeader(_In_z_ const char* path)
{
// 获取文件对象
HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 获取文件大小
DWORD fSize = GetFileSize(hfile, NULL);
char* pBuff = new char[fSize];
DWORD dwReadSize = 0;
// 读文件
BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
if (bSuccess)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT];
//填充延迟导入表数据结构
PIMAGE_DELAYLOAD_DESCRIPTOR pDelayLoad = (PIMAGE_DELAYLOAD_DESCRIPTOR)(RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) + pBuff);
while (pDelayLoad->DllNameRVA)
{
char* szDllName = (char*)(RvaToFoa(pDelayLoad->DllNameRVA, pSectionHeader, pFileHeader) + pBuff);
cout << "DllName:" << szDllName << endl;
cout << "AllAttributes:" << pDelayLoad->Attributes.AllAttributes << endl;
cout << "RvaBased:" << pDelayLoad->Attributes.RvaBased << endl;
cout << "ModuleHandleRVA:" << pDelayLoad->ModuleHandleRVA << endl;
cout << "ImportAddressTableRVA:" << pDelayLoad->ImportAddressTableRVA << endl;
cout << "ImportNameTableRVA:" << pDelayLoad->ImportNameTableRVA << endl;
cout << "BoundImportAddressTableRVA:" << pDelayLoad->BoundImportAddressTableRVA << endl;
cout << "UnloadInformationTableRVA:" << pDelayLoad->UnloadInformationTableRVA << endl;
cout << "TimeDateStamp:" << pDelayLoad->TimeDateStamp << endl;
pDelayLoad++;
}
}
else (cout.write("打开文件失败", 20));
CloseHandle(hfile);
delete[] pBuff;
}
void main()
{
ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
TLS表
使用线程本地存储器(TLS)可以将数据与执行的特定线程联系起来。当使用_declspec(thread)声明的TLS 变量时,编译器将它们放入一个.tls 区块。当应用程序加载到内存中时,系统要寻找可执行文件中的.tls 区块,并且动态地分配一个足够大的内存块,以便存放所有的 TLS变量。系统也将一个指向已分配的内存的指针放到TLS数组里,这个数组由 FS:[2Ch]指向(在x86架构上)
IMAGE_TLS_DIRECTORY结构中的地址是虚拟地址,而不是 RVA。这样,如果PE 文件不是从基地址载入的,那么这些地址就会通过基址重定位来修正。而且,IMAGE_TLS_DIRECTORY 本身不在.tls 区块中,而在 .rdata 区块中。
在一个可执行文件中,线程局部存储(TLS)数据是由数据目录表中的 IMAGE_DIRECTORY_ENTRY_TLS条目指出的。如果数据是非零的,这个字段指向一个IMAGE_TLS_DIRECTORY结构,其定义如下
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData; // TLS模板的起始地址
DWORD EndAddressOfRawData; // TLS模板的结束地址
DWORD AddressOfIndex; // TLS索引的位置,运行库使用这个索引来定位线程局部数据
DWORD AddressOfCallBacks; // PIMAGE_TLS_CALLBACK 函数指针数组的地址
DWORD SizeOfZeroFill; // 后面跟0的个数
union {
DWORD Characteristics; // 保留,目前设为0
struct {
DWORD Reserved0 : 20;