PE文件学习--导入表

写在前面的话:今年疫情在家,才开始在CSDN与各位大佬们分享交流技术,最近一个月返校之后,一直忙碌于学校课设和期末考试的备考(毕竟平时不怎么听课),所以博客几乎一个多月没有更新,最近只剩下一门考试了,今天小库又回来了。
之前有关PE文件的知识好像更新到了导出表,今日来看看导入表。
我们提到导入表,最常说的就是导入表的双桥结构
先来看一下导入表描述符结构体IMAGE_IMPORT_DESCRIPTOR

typedef struct _IMAGE_IMPORT_DESCRIPTOR
 {
    union 
    {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;

老规矩,其中各字段的简单介绍。。
@Characteristics:属性
@OriginalFirstThunk:指向的是一个数组,数组中每个结构定义了一个导入函数的信息,最后以一个内容为全0的结构最为结束。指向的数组中每一项为一个IMAGE_THUNK_DATA结构,这是一个结构体,32位下大小是双字(DWORD,64位下为ULONGLONG),最高位0时,表示一个数值RVA,为1时,表示导入符号是一个名称。这也是经常说的桥1。
@TimeDateStamp:时间戳,一般为0。如果该导入表项被绑定,那么绑定后的这个时间戳就被设置对应DLL文件的时间戳。
@ForwarderChain:链表的前一个结构,如果没有,则为-1.
@Name:指向链接库名字的指针’\0’结尾的Ansi字符串。
@FirstThunk:我们所说的桥2,也是一个IMAGE_THUNK_DATA结构,指向当前导入动态库引入的所有导入函数。
导入表中最重要的结构莫不是双桥结构,桥1和桥2最终指向了同一个地方,是一个PIMAGE_IMPORT_BY_NAME结构体,但是这是在桥2没有发生断裂的前提下。
桥1指向的是INT(Import Name Table)表,其中的每一个成员都是一个RVA,指向PIMAGE_IMPORT_BY_NAME,这个结构体中两个成员分别表示函数编号和函数名称的字符串。直到遇到内容全0,INT表遍历结束。
桥2指向的是IAT(Import Address Table)表,在PE文件装入内存之前,它里面的成员和INT中指向的是一样的,直到PE文件被加载进虚拟内存空间之后,会发生断裂,其中指向的是函数的VA。
有关桥1桥2,在恢复导入表钩子的代码中有使用举例。
值得注意的一点是,单桥结构无法执行绑定导入操作,因为当PE文件加载进内存,如果不存在桥1,则无法重新找到VA到底调用了哪个函数,自然无法实施绑定。
下面给一个我自己写的枚举导入表信息的关键代码:

BOOL EnumImportTableAsDataFileInternal32(PVOID ModuleBase, list<IMPORT_DATA>& DataList,list<IMPORT>& DataList2)
{
	DWORD Size = 0;
	PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = NULL;
	PIMAGE_THUNK_DATA32 pImportProcNameList = NULL;
	PIMAGE_THUNK_DATA32 pImportProcAddressList = NULL;
	PIMAGE_IMPORT_BY_NAME pINTItem = NULL;

	DWORD FOA = 0;

	BOOL IsBind = FALSE;
	int Index = 0;
	ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)PEGetMemberInModule((HMODULE)ModuleBase, &Size, IMAGE_DIRECTORY_ENTRY_IMPORT, FALSE, TRUE);

	// 时间戳
	if (ImportDescriptor->TimeDateStamp == 0)
	{
		IsBind = FALSE;
	}
	else
	{
		IsBind = TRUE;
	}

	while (TRUE)
	{
		IMPORT Data2 = { 0 };
		Index = 0;
		// 空项结尾
		if (ImportDescriptor->Characteristics == 0)
		{
			break;
		}

		GetFOA32((PBYTE)ModuleBase, ImportDescriptor->FirstThunk, &FOA);
		// IAT
		pImportProcAddressList = (PIMAGE_THUNK_DATA32)((PBYTE)ModuleBase + FOA);
		Data2.ImportProcAddress = (DWORD)pImportProcAddressList;
		Data2.FOAImportProcAddress = FOA;

		GetFOA32((PBYTE)ModuleBase, ImportDescriptor->OriginalFirstThunk, &FOA);
		// INT
		pImportProcNameList = (PIMAGE_THUNK_DATA32)((PBYTE)ModuleBase + FOA);
		Data2.ImportProcName = (DWORD)pImportProcNameList;
		Data2.FOAImportProcName = FOA;

		while (TRUE)
		{
			// 遍历到尽头了
			if (pImportProcNameList[Index].u1.Function == NULL)
			{
				
				break;
			}
			else
			{
				if (pImportProcNameList[Index].u1.Function & IMAGE_ORDINAL_FLAG)
				{
					// 索引导入
				}
				else
				{
					GetFOA32((PBYTE)ModuleBase, pImportProcNameList[Index].u1.AddressOfData, &FOA);
					pINTItem = (PIMAGE_IMPORT_BY_NAME)(FOA + (PBYTE)ModuleBase);
				}
				IMPORT_DATA Data = { 0 };
				// 序号,便于修正地址时查询
				WORD Hint = pINTItem->Hint;
				Data.Ordinals = Hint;



				// 名称
				PCHAR Name = (PCHAR)pINTItem->Name;
				Data.FunctionName = Name;


				// Dll名称
				GetFOA32((PBYTE)ModuleBase, ImportDescriptor->Name, &FOA);
				PCHAR DllName = (PCHAR)(FOA + (PBYTE)ModuleBase);
				Data.DllName = DllName;
				Data2.DllName = DllName;
				

				if (IsBind)
				{
					// 指向函数地址
					GetFOA32((PBYTE)ModuleBase, pImportProcAddressList[Index].u1.Function, &FOA);
				}
				DataList.push_back(Data);
				Index++;
			}
		}
		Data2.NumberOfFunction = Index;
		Data2.TimeDateStamp = ImportDescriptor->TimeDateStamp;
		BOOL IsOk = FALSE;
		for (list<IMPORT>::iterator i = DataList2.begin();i != DataList2.end();i++)
		{
			if (Data2.DllName == i->DllName)
			{
				IsOk = TRUE;
			}
		}
		if (IsOk == FALSE)
		{
			DataList2.push_back(Data2);
		}
		
		ImportDescriptor++;

		
	}

	return TRUE;
}

BOOL EnumImportTableAsDataFileInternal64(PVOID ModuleBase, list<IMPORT_DATA>& DataList, list<IMPORT>& DataList2)
{
	DWORD Size = 0;
	PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = NULL;
	PIMAGE_THUNK_DATA64 pImportProcNameList = NULL;
	PIMAGE_THUNK_DATA64 pImportProcAddressList = NULL;
	PIMAGE_IMPORT_BY_NAME pINTItem = NULL;
	IMPORT_DATA Data = { 0 };
	DWORD FOA = 0;

	BOOL IsBind = FALSE;
	int Index = 0;
	ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)PEGetMemberInModule((HMODULE)ModuleBase, &Size, IMAGE_DIRECTORY_ENTRY_IMPORT, TRUE, TRUE);

	// 时间戳
	if (ImportDescriptor->TimeDateStamp == 0)
	{
		IsBind = FALSE;
	}
	else
	{
		IsBind = TRUE;
	}

	while (TRUE)
	{
		IMPORT Data2 = { 0 };
		Index = 0;
		// 空项结尾
		if (ImportDescriptor->Characteristics == 0)
		{
			break;
		}

		GetFOA64((PBYTE)ModuleBase, ImportDescriptor->FirstThunk, &FOA);
		// IAT
		pImportProcAddressList = (PIMAGE_THUNK_DATA64)((PBYTE)ModuleBase + FOA);
		Data2.ImportProcAddress = (DWORD)pImportProcAddressList;
		Data2.FOAImportProcAddress = FOA;

		GetFOA64((PBYTE)ModuleBase, ImportDescriptor->OriginalFirstThunk, &FOA);
		// INT
		pImportProcNameList = (PIMAGE_THUNK_DATA64)((PBYTE)ModuleBase + FOA);
		Data2.ImportProcName = (DWORD)pImportProcNameList;
		Data2.FOAImportProcName = FOA;

		while (TRUE)
		{
			// 遍历到尽头了
			if (pImportProcNameList[Index].u1.Function == NULL)
			{
				
				break;
			}
			else
			{
				if (pImportProcNameList[Index].u1.Function & IMAGE_ORDINAL_FLAG)
				{
					// 索引导入
				}
				else
				{
					GetFOA64((PBYTE)ModuleBase, pImportProcNameList[Index].u1.AddressOfData, &FOA);
					pINTItem = (PIMAGE_IMPORT_BY_NAME)(FOA + (PBYTE)ModuleBase);
				}

				// 序号,便于修正地址时查询
				WORD Hint = pINTItem->Hint;
				Data.Ordinals = Hint;

				// 名称
				PCHAR Name = (PCHAR)pINTItem->Name;
				Data.FunctionName = Name;

				// Dll名称
				GetFOA64((PBYTE)ModuleBase, ImportDescriptor->Name, &FOA);
				PCHAR DllName = (PCHAR)(FOA + (PBYTE)ModuleBase);
				Data.DllName = DllName;
				Data2.DllName = DllName;

				if (IsBind)
				{
					// 指向函数地址
					GetFOA64((PBYTE)ModuleBase, pImportProcAddressList[Index].u1.Function, &FOA);
				}
				DataList.push_back(Data);
				Index++;
			}
		}
		Data2.NumberOfFunction = Index;
		Data2.TimeDateStamp = ImportDescriptor->TimeDateStamp;
		BOOL IsOk = FALSE;
		for (list<IMPORT>::iterator i = DataList2.begin();i != DataList2.end();i++)
		{
			if (Data2.DllName == i->DllName)
			{
				IsOk = TRUE;
			}
		}
		if (IsOk == FALSE)
		{
			DataList2.push_back(Data2);
		}
		
		ImportDescriptor++;
	}

	return TRUE;
}

“众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。”我回来啦。。。
参考书籍:《Windows PE权威指南》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章