写在前面的话:今年疫情在家,才开始在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权威指南》