1.关于绑定导入
一般情况下,在程序加载前IAT表和INT表中的内容相同,都是程序引用的dll中的函数的函数名或序号;
加载完成后IAT表中将替换为函数的真正地址;
但在加载前IAT表中直接写绝对地址是可以实现的;
加载前在IAT表中保存绝对地址的优点:启动程序快;
在启动程序时需要:申请4gb内存空间、贴exe、贴dll、将IAT表修复为地址等等;
如果直接用绝对地址,则省去了修复IAT表的操作;
缺点:
dll重定位时,如果dll没能占据自身ImageBase处的地址,则需要修复绝对地址;
dll被修改时,dll被修改,IAT表中对应的函数地址可能被改,需要修复函数地址;
例如windows提供的一些程序就使用了这种方式,比如记事本;
这种方式称为“绑定导入“;
2.如何判断绑定导入
在导入表中结构中有个属性:TimeDateStamp;
该属性表示时间戳;
如果值为0则表示当前的dll的函数没有被绑定,在程序加载时会调用系统函数获取函数地址;
如果值为-1则表示当前的dll的函数已经绑定,而且绑定的时间存在另外一张表里;那张表就是绑定导入表;
3.绑定导入表
PE加载EXE相关的DLL时,首先会根据IMAGE_IMPORT_DESCRIPTOR结构中的TimeDateStamp来判断是否要重新计算IAT表中的地址。
TimeDateStamp == 0 未绑定
TimeDateStamp == -1 已绑定 真正的绑定时间为IMAGE_BOUND_IMPORT_DESCRIPTOR的TimeDateStamp
绑定导入表位于数据目录的第12项;
绑定导入表的结构:
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD NumberOfModuleForwarderRefs; // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
TimeDateStamp ->时间戳;用来判断是否和绑定的dll是同一个版本;也就是看时间戳和dll的pe头中的时间戳是否一样;
OffsetModuleName ->dll的名字;注意保存的既不是RVA也不是FOA;
dll的名字计算公式为:第一个DESCRIPTOR的值+OffsetModuleName;
NumberOfModuleForwarderRefs ->当前dll另外依赖的dll数量;因为dll也可能依赖dll;
绑定导入表结构后面紧跟的并不一定是下一个绑定导入表;
如果NumberOfModuleForwarderRefs为N则还有N个另外的结构;
该结构也是用来描述dll的;
结构如下:
typedef struct _IMAGE_BOUND_FORWARDER_REF {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
前两个属性和绑定导入表意义一样;
第三个属性Reserved为保留字段没鸟用
绑定导入表的结构图:
注意:
当IMAGE_BOUND_IMPORT_DESCRIPTOR结构中的TimeDateStamp与DLL文件标准PE头中的TimeDateStamp值不相符时,
或者DLL需要重新定位的时候,就会重新计算IAT中的值.
4.解析绑定导入表
有两个需要注意的地方:
1】OffsetModuleName的计算方式:第一个DESCRIPTOR的值+OffsetModuleName
2】绑定导入表并不在任何一个节中,而是在头中,因此RVA转FOA的方式可能会和在节中时有所不同;
# define _CRT_SECURE_NO_WARNINGS
# include "stdio.h"
# include "stdlib.h"
# include "windows.h"
# include "PE.h"
/*
1、定位绑定导入表,并打印出绑定导入表中的内容
*/
int PrintBoundImportTable(PVOID FileAddress)
{
int ret = 0;
//1、指向相关内容
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(FileAddress);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
//2、获取绑定导入表的地址
DWORD BoundImportDirectory_RVAAdd = pOptionalHeader->DataDirectory[11].VirtualAddress;
DWORD BoundImportDirectory_FOAAdd = 0;
// (1)、判断绑定导入表是否存在
if (BoundImportDirectory_RVAAdd == 0)
{
printf("BoundImportDirectory 不存在!\n");
return ret;
}
// (2)、获取绑定导入表的FOA地址
ret = RVA_TO_FOA(FileAddress, BoundImportDirectory_RVAAdd, &BoundImportDirectory_FOAAdd);
if (ret != 0)
{
printf("func RVA_TO_FOA() Error!\n");
return ret;
}
//3、指向绑定导入表
PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundImportDirectory = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)FileAddress + BoundImportDirectory_FOAAdd);
//4、获取绑定导入表的基址
DWORD BaseBoundImport = (DWORD)BoundImportDirectory;
//5、循环打印绑定导入表信息
while (BoundImportDirectory->OffsetModuleName && BoundImportDirectory->TimeDateStamp)
{
// 1)指向模块名
PCHAR pModuleName = (PCHAR)(BaseBoundImport + BoundImportDirectory->OffsetModuleName);
// 2)打印绑定导入表信息
printf("ModuleName :%s\n", pModuleName);
printf("TimeDateStamp :%08X\n", BoundImportDirectory->TimeDateStamp);
printf("NumberOfModuleForwarderRefs :%04X\n", BoundImportDirectory->NumberOfModuleForwarderRefs);
printf("================ Start =========================\n");
// 3)循环后续结构
for (DWORD i = 0; i < BoundImportDirectory->NumberOfModuleForwarderRefs; i++)
{
// 4)指向后续结构
PIMAGE_BOUND_FORWARDER_REF BoundImport_Ref = (PIMAGE_BOUND_FORWARDER_REF)&BoundImportDirectory[i + 1];//两个结构大小一样
// 5)指向模块名
pModuleName = (PCHAR)(BaseBoundImport + BoundImport_Ref->OffsetModuleName);
// 6)打印信息
printf("ModuleName-----------:%s\n", pModuleName);
printf("TimeDateStamp--------:%08X\n\n", BoundImport_Ref->TimeDateStamp);
}
printf("================ End =========================\n");
// 7)指向下一个结构
BoundImportDirectory = &BoundImportDirectory[BoundImportDirectory->NumberOfModuleForwarderRefs + 1];
}
return ret;
}
int main14()
{
int ret = 0;
PVOID FileAddress = NULL;
//1、将文件读入到内存
ret = MyReadFile(&FileAddress);
if (ret != 0)
{
if (FileAddress != NULL)
free(FileAddress);
return ret;
}
//2、打印绑定导入表的信息
ret = PrintBoundImportTable(FileAddress);
if (ret != 0)
{
if (FileAddress != NULL)
free(FileAddress);
return ret;
}
if (FileAddress != NULL)
free(FileAddress);
system("pause");
return ret;
}