有些WINDOWS
程序为了提高加载速度,会直接把DLL中的函数地址写入到IAT表
例如上一篇文章里提到的,打印NOTEPAD
的IAT
表其中Name
为空(这里其实是函数地址)
但是这样会有两个问题:
(1)当DLL没有占住ImageBase
时IAT
中的地址就是错的
(2)当链接的DLL被修改后IAT
里写的地址也是错的
遇到这两种情形之一,加载时就必须修复IAT表
对于第二种情形,DLL是否被修改,是根据比较DLL的时间戳和绑定导入表中的记录的DLL时间戳来判断的,如果不一致,说明DLL被修改了
加载程序时,操作系统根据导入表中的时间戳来判断程序是否使用了绑定导入。当时间戳为0,表示不使用绑定导入表;当时间戳为0xFFFFFFFF,说明该程序使用绑定导入
对于使用绑定导入的程序,绑定导入表存储在最后一个节表后面(数据目录最后一部分)
绑定导入表结构:
xxxxxxxxxx
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD NumberOfModuleForwarderRefs;
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
字段说明:
IMAGE_BOUND_IMPORT_DESCRIPTOR
的偏移
依赖模块结构:
xxxxxxxxxx
typedef struct _IMAGE_BOUND_FORWARDER_REF {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
属性意思相同,其中Reserved
字段保留无意义
省略头部解析代码
数据目录第二个是导入表,其中的时间戳如果是0表示不存在绑定导入表
xxxxxxxxxx
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer +
RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));
if (pImportTable->TimeDateStamp == 0)
{
printf("该程序没有绑定导入\n");
return;
}
遍历所有绑定导入表
NumberOfModuleForwarderRefs
总大小xxxxxxxxxx
while (pBoundImportTable->TimeDateStamp ||
pBoundImportTable->OffsetModuleName ||
pBoundImportTable->NumberOfModuleForwarderRefs)
{
pBoundImportTable = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pBoundImportTable +
sizeof(IMAGE_BOUND_IMPORT_DESCRIPTOR) +
pBoundImportTable->NumberOfModuleForwarderRefs * sizeof(IMAGE_BOUND_FORWARDER_REF));
}
这里的OffsetModuleName
偏移不是RVA
也不是FOA
而是相对于第一个绑定导入表的偏移,因此需要在循环前复制一份第一个绑定导入表,在输出DLL名称以及依赖名称时使用
xxxxxxxxxx
PIMAGE_BOUND_IMPORT_DESCRIPTOR pFirstBoundImportTable = pBoundImportTable;
while(...)
{
printf("%s\n", (LPCSTR)((DWORD)pFirstBoundImportTable + pBoundImportTable->OffsetModuleName));
}
类似地,打印依赖时也需要使用这种计算方式
xxxxxxxxxx
PIMAGE_BOUND_FORWARDER_REF pBFR = (PIMAGE_BOUND_FORWARDER_REF)((DWORD)pBoundImportTable +
sizeof(IMAGE_BOUND_IMPORT_DESCRIPTOR));
for (int i = 0; i < pBoundImportTable->NumberOfModuleForwarderRefs; i++)
{
printf("\t%s\n", (LPCSTR)((DWORD)pFirstBoundImportTable + pBFR[i].OffsetModuleName));
printf("\tTimeDateStamp:%x\n", pBFR[i].TimeDateStamp);
}
整体代码
xxxxxxxxxx
VOID PrintBoundImportTable(LPVOID pFileBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer +
RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));
if (pImportTable->TimeDateStamp == 0)
{
printf("该程序没有绑定导入\n");
return;
}
PIMAGE_BOUND_IMPORT_DESCRIPTOR pBoundImportTable = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer +
RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[11].VirtualAddress));
PIMAGE_BOUND_IMPORT_DESCRIPTOR pFirstBoundImportTable = pBoundImportTable;
while (pBoundImportTable->TimeDateStamp ||
pBoundImportTable->OffsetModuleName ||
pBoundImportTable->NumberOfModuleForwarderRefs)
{
puts("-------------------------------------------");
printf("%s\n", (LPCSTR)((DWORD)pFirstBoundImportTable + pBoundImportTable->OffsetModuleName));
printf("TimeDateStamp:%x\n", pBoundImportTable->TimeDateStamp);
printf("NumberOfModuleForwarderRefs:%d\n", pBoundImportTable->NumberOfModuleForwarderRefs);
PIMAGE_BOUND_FORWARDER_REF pBFR = (PIMAGE_BOUND_FORWARDER_REF)((DWORD)pBoundImportTable +
sizeof(IMAGE_BOUND_IMPORT_DESCRIPTOR));
for (int i = 0; i < pBoundImportTable->NumberOfModuleForwarderRefs; i++)
{
printf("\t%s\n", (LPCSTR)((DWORD)pFirstBoundImportTable + pBFR[i].OffsetModuleName));
printf("\tTimeDateStamp:%x\n", pBFR[i].TimeDateStamp);
}
pBoundImportTable = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pBoundImportTable +
sizeof(IMAGE_BOUND_IMPORT_DESCRIPTOR) +
pBoundImportTable->NumberOfModuleForwarderRefs * sizeof(IMAGE_BOUND_FORWARDER_REF));
}
}
打印NOTEPAD
的截图