深入理解PE结构中的导入表1_按照函数名称导入

当exe或者dll中引入了外部PE机构的函数的时候,怎么知道引入了哪些dll?怎么知道引入的这些dll中的哪些函数?怎么知道引入的这些函数的地址?

         因此在PE的数据目录表中就有这一张表:导入表,解决了上述3个问题。

        导入表在数据目录表的第一项中,在导出表的下边:

         lpOptionHeader1->DataDirectory[1].RVA,

   lpOptionHeader1->DataDirectory[1].SIZE 导入表的大小是说明性信息,不依赖这个值,可以随便修改。

 0x00002204 <====>(0x1004)FOA

 导入表的结构体为:

           

           OriginalFirstThunk指向的结构体 _IMAGE_THUNK_DATA,这是一个联合体(4字节)。

           这个联合体如果最高位为1, _IMAGE_THUNK_DATA里面的值剩余的31位(PE64是63位)就是一个序号

          这个联合体如果最高位为0, 那么这个数组里面的值就是一个RVA(指向函数名称) :

           这个 RVA ------>  typedef struct _IMAGE_IMPORT_BY_NAME {
                                              WORD Hint;//导入函数索引,这个属性其实没用,因为如果按照函数名导出,用下边的名字,如果按照序号导出,用的是IMAGE_THUNK_DATA32 里的值(后31位或后63位)作为序号
                                               CHAR Name[1];  //函数名称第一个字符
                                        } IMAGE_IMPORT_BY_NAME,

20个字节的0,表示导入表结束

 

 

 

INT数组中存的值,最高位如果为1,那么这个数组里的值就是一个序号,如果最高位为0,那么这个数组里面的值就是一个RVA(指向函数地址)

通过实验,我们现在发现在加载前IAT和INT 中存的值是一样的,但是为什么要存2份一样的表,并且存在不同的地方呢?

        加载前是一样的,看不出来。

        加载后:

 加载后,我们做个假设,因为前边说的是2个表一样的,我们这里去掉一个,把INT表去掉,那么把使用GetProcAddress获取函数地址之后,

       IAT里面存的只有地址,那么如何找函数地址对应的函数名字、序号呢?

因为IAT表里面存完地址之后,就不指向函数名称和序号,我们只是自己明白有这种一一对应的关系,但是如何查找呢:

     IAT表里面的地址,可以通过FirstThunk找到。

    但是函数名称、序号这个怎么找?INT表去掉了,这个路径行不通;通过IAT表呢,IAT表里面这个时候是函数地址了,不是RVA了,这个路径也行不通。

那么是不是可以在GetProcAddress获取地址的函数先保存这个函数名称、序号表的地址呢?

     可以,但是你要明白函数名称、序号表实际上他不是图中那样连续的保存函数序号和名字的,上图是为了理解才画在一起的。

      因此,如果要保存,那么还得分配一块和_IMAGE_THUNK_DATA数组一样的空间,分别指向函数的地址或序号。这不你自己保存函数名称、序号表的地址空间又成INT表了嘛,所以这种自己想的先保存函数名称、序号地址的方案还不如原来设计的INT这种方案呢。

这个时候,问题就比较明朗了,2个一样的表,是为了地址和函数、序号的对应关系。

     当IAT表中变成函数地址之后,可以通过FirstThunk找到对应的函数地址,通过OriginalFirstThunk找到函数对应的序号或者名称。

     如果去掉IAT表,那么函数地址又没法存了。

这就是为什么要存2份一样的表,并且存在不同的地方。

实现打印导入表代码:

void PrintfImportDirectory(void* FileBuffer) //FileBuffer是把磁盘文件读入内存后,保存到内存的地址
{
	unsigned int* NTOffset = (unsigned int*)((char*)FileBuffer + 0x3c);

	PE_ntHeader* lpNtHeader=(PE_ntHeader*)((unsigned char*)FileBuffer + *NTOffset);

	if(lpNtHeader->SizeofOptionalHeader == 0xF0)
	{
		//lpOptionHeade
		PE_OptionalHeader64* lpOptionHeader1= (PE_OptionalHeader64*)((char*)FileBuffer + *NTOffset + 24);
		unsigned int ImportTableOffset =  RvaToFoa(FileBuffer,lpOptionHeader1->DataDirectory[1].RVA);
		printf("ImportTableOffset:%x\n",ImportTableOffset);
		Image_ImportTable* lpImportTable  = (Image_ImportTable*)((char*)FileBuffer + ImportTableOffset);
		//INT表
		while(lpImportTable->FirstThunk!=0 && lpImportTable->Name!=0)
		{
			printf("***************INT:****%x-->%x***********************************\n",lpImportTable->INTName.OriginalFirstThunk,RvaToFoa(FileBuffer,lpImportTable->INTName.OriginalFirstThunk));
			printf("导入的dll名字:%s\n",(unsigned char*)FileBuffer+RvaToFoa(FileBuffer,lpImportTable->Name));
			
			unsigned long long* INTTableValue=(unsigned long long*)((unsigned char*)FileBuffer + RvaToFoa(FileBuffer,lpImportTable->INTName.OriginalFirstThunk));
			
			while(*INTTableValue!=0)
			{
				
				if(*INTTableValue & 0x8000000000000000)
				{
					//最高位为1是,那么取出最高位剩下的就是函数序号
					printf("按照序号导入:%x\n",*INTTableValue & 0x7FFFFFFFFFFFFFFF);

				}else{
					//最高位为0,那么这个值就是一个RVA,指向函数的名字
					Image_ImportByName* lpImageImportByName=(Image_ImportByName*)((unsigned char*)FileBuffer + RvaToFoa(FileBuffer,*INTTableValue));
					printf("按照名字导入Hint/Name:%d-%s\n",lpImageImportByName->Hint,lpImageImportByName->Name);
					//printf("导入函数名称为:%08x\n",*INTTableValue);
				}

				INTTableValue++;
			}

			printf("***************IAT:****%x-->%x***********************************\n",lpImportTable->FirstThunk,RvaToFoa(FileBuffer,lpImportTable->FirstThunk));
			//unsigned long long* IATTableValue=(unsigned long long*)((unsigned char*)FileBuffer + RvaToFoa(FileBuffer,lpImportTable->FirstThunk));
			unsigned long long* IATTableValue=(unsigned long long*)((char*)FileBuffer + RvaToFoa(FileBuffer,lpImportTable->FirstThunk));
			if(lpImportTable->TimeDateStamp == 0)
			{
				//未绑定,加载之前和INT表指向的内容一样
				while(*IATTableValue!=0)
				{
				
					if(*IATTableValue & 0x8000000000000000)
					{
					//最高位为1是,那么取出最高位剩下的就是函数序号
						printf("按照序号导入:%x\n",*INTTableValue & 0x7FFFFFFFFFFFFFFF);

					}else{
					//最高位为0,那么这个值就是一个RVA,指向函数的名字
						Image_ImportByName* lpImageImportByName=(Image_ImportByName*)((unsigned char*)FileBuffer + RvaToFoa(FileBuffer,*INTTableValue));
						printf("按照名字导入Hint/Name:%d-%s\n",lpImageImportByName->Hint,lpImageImportByName->Name);
						//printf("导入函数名称为:%08x\n",*INTTableValue);
					}

					IATTableValue++;
				}

			}else if(lpImportTable->TimeDateStamp == 0xFFFFFFFF)
			{
				//已绑定,
				while(*IATTableValue!=0)
				{
					printf("%016llx\n",*IATTableValue);
					IATTableValue++;
				}
			}
		
			lpImportTable++;
		}
	}else if(lpNtHeader->SizeofOptionalHeader == 0xE0)
	{

		PE_OptionalHeader32* lpOptionHeader1= (PE_OptionalHeader32*)((char*)FileBuffer + *NTOffset + 24);
	
		unsigned int ImportTableOffset =  RvaToFoa(FileBuffer,lpOptionHeader1->DataDirectory[1].RVA);
		printf("ImportTableOffset:%x\n",ImportTableOffset);
		Image_ImportTable* lpImportTable  = (Image_ImportTable*)((char*)FileBuffer + ImportTableOffset);
		
		while(lpImportTable->FirstThunk!=0 && lpImportTable->Name!=0)
		{
			printf("导入的dll名字:%s\n",(unsigned char*)FileBuffer+RvaToFoa(FileBuffer,lpImportTable->Name));

			unsigned int* INTTableValue=(unsigned int*)((unsigned char*)FileBuffer + RvaToFoa(FileBuffer,lpImportTable->INTName.OriginalFirstThunk));
			
			while(*INTTableValue!=0)
			{
				//Image_ImportByName* lpImageImportByName=(Image_ImportByName*)((unsigned char*)FileBuffer + RvaToFoa(FileBuffer,INTTableValue->INT.Ordinal));
				if(*INTTableValue & 0x80000000)
				{
					//最高位为1是,那么取出最高位剩下的就是函数序号
					printf("按照序号导入为:%x\n",*INTTableValue & 0x7FFFFFFF);

				}else{
					//最高位为0,那么这个值就是一个RVA,指向函数的名字
					Image_ImportByName* lpImageImportByName=(Image_ImportByName*)((unsigned char*)FileBuffer + RvaToFoa(FileBuffer,*INTTableValue));
					printf("按照名字导入Hint/Name:%d-%s\n",lpImageImportByName->Hint,lpImageImportByName->Name);
				}

				INTTableValue++;
			}


			lpImportTable++;
		}

	}

}

  

posted @ 2023-08-10 13:31  一日学一日功  阅读(165)  评论(0)    收藏  举报