深入理解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++;
}
}
}

浙公网安备 33010602011771号