PE 绑定导入表
什么是绑定导入
PE在加载前 INT表和IAT表都指向一个名称表
但是有些程序,如果你打印该程序的IAT表的时候会指向地址中发现里面是地址?
比如如下:我先正常打印发现IAT表中的地址RVA转换FOA都直接转换失败了,那么肯定是地址中的值肯定有问题
那么就不进行RVA转换到FOA,可以先尝试直接打印地址中的值进行观察:
原因:我们的PE程序在加载的时候, PE中导入表中的IAT表会根据INT表来进行填写函数地址。但是这就造成了一个问题,PE程序启动慢,因为每次启动都要给IAT表填写函数地址
如果我们事先先把IAT表中的所需要用到的DLL函数地址都填好的话,是不是能够节省程序启动的时间?
答案:是的,但是缺点是万一需要被用到的DLL的Imagebase并没有加载到符合的位置,这样就需要修复重定位表中的地址!
那么如何判断该程序是否进行了绑定导入的操作呢?
在文件中IAT表是否填写地址,依据的地方 就是 导入表中的 TimeDateStamp (时间戳)成员, 如果为0 则是这个DLL没有绑定, 如果为-1 则是这个DLL进行了绑定导入
如下图:这个是进行了绑定导入的操作,所以打印PIMPORT_DESCRIPTOR的TimeDateStamp为ffffffff,有符号也就是-1
什么是绑定导入表
怎么判断导入表中的 IAT表函数地址是否绑定 根据 TimeDataStamp进行判断. 0未绑定 -1绑定
真正的绑定时间存放在绑定导入表IMAGE_BOUND_IMPORT_DESCRIPTOR的TimeDateStamp的属性中
TimeDateStamp == 0 未绑定
TimeDateStamp == -1 已绑定
定位方法:
在PE可选头结构中的数据目录的第12项
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
DWORD TimeDateStamp; //真正的时间戳,
WORD OffsetModuleName; //DLL的名字. PE的文件名
WORD NumberOfModuleForwarderRefs; //依赖的另外的DLL有几个
// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
我们的一个DLL可能依赖其他的DLL, 所以导入表的最后一个成员是依赖的DLL有几个,如果有两个,那么紧跟着下面就是依赖的DLL的绑定导入表结构
关于依赖DLL的结构体如下:
typedef struct _IMAGE_BOUND_FORWARDER_REF {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD Reserved; // 保留 无作用
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
打印绑定导入表的代码:
void PrintBindImportTable(PVOID pFileBuffer){
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_BOUND_IMPORT_DESCRIPTOR pIMAGE_BOUND_IMPORT_DESCRIPTOR = NULL;
PIMAGE_BOUND_FORWARDER_REF pIMAGE_BOUND_FORWARDER_REF = NULL;
char ModuleName[20] = {0};
DWORD BOUNG_IMPORT_DESCRIPTOR_TEMP = NULL;
int i = 0;
DWORD RVA = 0;
DWORD FOA = 0;
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
RVA_TO_FOA(pFileBuffer, pOptionHeader->DataDirectory[11].VirtualAddress,&FOA);
//保存第一个DESCRIPTOR的地址 后面加OffsetModuleName来进行使用
BOUNG_IMPORT_DESCRIPTOR_TEMP = (DWORD)pFileBuffer+(DWORD)FOA;
//开始进行打印操作
pIMAGE_BOUND_IMPORT_DESCRIPTOR = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer+(DWORD)FOA);
while (*(PDWORD)pIMAGE_BOUND_IMPORT_DESCRIPTOR)
{
printf("\n");
strcpy(ModuleName, (PVOID)((DWORD)BOUNG_IMPORT_DESCRIPTOR_TEMP + (DWORD)pIMAGE_BOUND_IMPORT_DESCRIPTOR->OffsetModuleName));
printf("模块名称: %s \n",ModuleName);
printf("模块的时间戳为: %x \n", pIMAGE_BOUND_IMPORT_DESCRIPTOR->TimeDateStamp);
printf("当前模块引用的dll的数量为: %x\n",pIMAGE_BOUND_IMPORT_DESCRIPTOR->NumberOfModuleForwarderRefs);
for(i=0;i<pIMAGE_BOUND_IMPORT_DESCRIPTOR->NumberOfModuleForwarderRefs;i++){
pIMAGE_BOUND_IMPORT_DESCRIPTOR++;
pIMAGE_BOUND_FORWARDER_REF = (PIMAGE_BOUND_FORWARDER_REF)pIMAGE_BOUND_IMPORT_DESCRIPTOR;
strcpy(ModuleName, (PVOID)((DWORD)BOUNG_IMPORT_DESCRIPTOR_TEMP + (DWORD)pIMAGE_BOUND_FORWARDER_REF->OffsetModuleName));
printf("\t引用的模块名称: %s \n",ModuleName);
printf("\t引用的模块的时间戳: %x\n", pIMAGE_BOUND_FORWARDER_REF->TimeDateStamp);
}
pIMAGE_BOUND_IMPORT_DESCRIPTOR++;
}
}