有些WINDOWS程序为了提高加载速度,会直接把DLL中的函数地址写入到IAT表
例如上一篇文章里提到的,打印NOTEPAD的IAT表其中Name为空(这里其实是函数地址)

但是这样会有两个问题:
(1)当DLL没有占住ImageBase时IAT中的地址就是错的
(2)当链接的DLL被修改后IAT里写的地址也是错的
遇到这两种情形之一,加载时就必须修复IAT表
对于第二种情形,DLL是否被修改,是根据比较DLL的时间戳和绑定导入表中的记录的DLL时间戳来判断的,如果不一致,说明DLL被修改了
加载程序时,操作系统根据导入表中的时间戳来判断程序是否使用了绑定导入。当时间戳为0,表示不使用绑定导入表;当时间戳为0xFFFFFFFF,说明该程序使用绑定导入
对于使用绑定导入的程序,绑定导入表存储在最后一个节表后面(数据目录最后一部分)
绑定导入表结构:

xxxxxxxxxxtypedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR { DWORD TimeDateStamp; WORD OffsetModuleName; WORD NumberOfModuleForwarderRefs;} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;字段说明:
IMAGE_BOUND_IMPORT_DESCRIPTOR的偏移
依赖模块结构:
xxxxxxxxxxtypedef struct _IMAGE_BOUND_FORWARDER_REF { DWORD TimeDateStamp; WORD OffsetModuleName; WORD Reserved;} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;属性意思相同,其中Reserved字段保留无意义
省略头部解析代码
数据目录第二个是导入表,其中的时间戳如果是0表示不存在绑定导入表
xxxxxxxxxxPIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));if (pImportTable->TimeDateStamp == 0){ printf("该程序没有绑定导入\n"); return;}遍历所有绑定导入表
NumberOfModuleForwarderRefs总大小xxxxxxxxxxwhile (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名称以及依赖名称时使用
xxxxxxxxxxPIMAGE_BOUND_IMPORT_DESCRIPTOR pFirstBoundImportTable = pBoundImportTable;
while(...){ printf("%s\n", (LPCSTR)((DWORD)pFirstBoundImportTable + pBoundImportTable->OffsetModuleName));}类似地,打印依赖时也需要使用这种计算方式
xxxxxxxxxxPIMAGE_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);}
整体代码
xxxxxxxxxxVOID 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的截图
