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++;
	}
}

posted @ 2020-06-09 11:00  zpchcbd  阅读(532)  评论(0编辑  收藏  举报