PE结构(四) 绑定导入表

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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值