一、綁定導入表的作用
有些windows程序,如notepad,爲了提高加載速度,會直接把DLL中的函數地址寫入到IAT表,省去了加載時的計算。但是這樣會有兩個問題,第一,當DLL沒有佔住ImageBase時,IAT中的地址就是錯的;第二,當鏈接的DLL被修改了,那IAT裏寫的地址也是錯的。遇到這兩種情形之一,加載時就必須修復IAT了。
對於第二種情形,DLL是否被修改,是根據比較DLL的時間戳和綁定導入表中的記錄的DLL時間戳來判斷的,如果不一致,說明DLL被修改了。
加載程序時,操作系統根據導入表中的時間戳來判斷程序是否使用了綁定導入。當時間戳爲0,表示不使用綁定導入表;當時間戳爲0xFFFFFFFF,說明該程序使用綁定導入。
對於使用綁定導入的程序,綁定導入表存儲在最後一個節表後面,如圖示:
二、綁定導入表的結構
綁定導入表結構:
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD NumberOfModuleForwarderRefs;
// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
TimeDateStamp 是時間戳,用於和DLL中的時間戳比較,判斷DLL是否已經發生變化;
OffsetModuleName 是當前模塊名距離第一個 _IMAGE_BOUND_IMPORT_DESCRIPTOR 的偏移。
NumberOfModuleForwarderRefs 是該模塊依賴的模塊數量;
依賴模塊結構:
typedef struct _IMAGE_BOUND_FORWARDER_REF {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
除了第三個屬性保留,其他與 _IMAGE_BOUND_IMPORT_DESCRIPTOR 相同。
三、打印綁定導入表
// 打印綁定導入表
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);
// 判斷方式一
/*if (NULL == pOptionHeader->DataDirectory[11].VirtualAddress)
{
printf("該程序綁定導入表爲空\n");
return;
}*/
// 判斷方式二
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + \
RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));
if (pImportTable->TimeDateStamp == 0)
{
printf("該程序沒有綁定導入\n");
return;
}
PIMAGE_BOUND_IMPORT_DESCRIPTOR pBoundImportTable = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + \
RvaToFoa(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的綁定導入表運行結果