PE文件學習--導入表

寫在前面的話:今年疫情在家,纔開始在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權威指南》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章