導入表注入原理和C語言實現

一、導入表注入的原理

注入是把DLL加載到另一個進程的4GB地址空間中,實現方式有很多種,導入表注入是我學的第一種注入,是通過修改程序的導入表,把自己的DLL添加到導入表中,來實現這個目的。

導入表是連續存儲在節裏的,它後面還有其他數據,不能直接追加一個導入表,所以必須找一塊足夠大的內存,把原來的所有導入表移動過去,再追加我們DLL的導入表。新增節是比較簡單的做法,我的代碼中也採用新增節。

導入表注入是讓系統根據EXE的導入表,幫我們實現加載DLL,需要知道一點,DLL至少要有一個導出函數,系統纔會幫我們加載DLL

新增節存儲如下數據(順序隨意安排):
原來的所有導出表 IMAGE_IMPORT_DESCRIPTOR
新增DLL的導出表 IMAGE_IMPORT_DESCRIPTOR
導出表結束標記,sizeof(IMAGE_IMPORT_DESCRIPTOR) 個0
INT表
INT表結束標記
IMPORT_BY_NAME 結構,包括Hint和導出函數名所佔的字節數
IAT表
IAT表結束標記
模塊名

這些數據都寫入到內存中後,根據他們的地址,給其中某些結構的屬性賦值
導入表中的FirstThunk,OriginalFirstThunk分別存儲INT,IAT的RVA;
導入表的Name存儲模塊名的RVA;
INT,IAT的值是IMPORT_BY_NAME的RVA;

除此之外,完成了所有結構的屬性設置後,不要忘了修改數據目錄項,讓導入表指向新增節首字節。

二、注入代碼和運行結果

// 導入表注入demo,通過修改導入表,將 InjectDll.dll 添加到導入表
// DLL只有一個導出函數 ExportFunction,保證至少有一個導出函數DLL纔會被加載
// DLL的主函數在加載和分離時會彈窗
DWORD ImportTableInjectDemo(LPVOID pFileBuffer, LPVOID *pNewFileBuffer, DWORD dwFileSize)
{
	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 + \
		RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));

	// 計算新增節的大小
	// 新增節存儲的內容有:原來的所有導入表,新導入表,新INT, IAT, 模塊名,一個_IMAGE_IMPORT_BY_NAME
	// 上述容器按0爲結束標記的,也要包含結束標記
	DWORD dwNewSectionSize = 0;
	DWORD dwNumberOfDll = 0;
	while (pImportTable->OriginalFirstThunk || pImportTable->FirstThunk)
	{
		//printf("%s\n", (LPCSTR)(RvaToFoa(pFileBuffer, pImportTable->Name) + (DWORD)pFileBuffer));
		dwNumberOfDll++;
		pImportTable++;
	}
	dwNewSectionSize += (dwNumberOfDll + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR); // 原有的和新添加的導入表,以及結束標記
	dwNewSectionSize += 16; // 這裏包括一個INT,一個IAT和兩個結束標記
	dwNewSectionSize += strlen("InjectDll.dll") + 1; // 模塊名
	dwNewSectionSize += 2 + strlen("ExportFunction") + 1; // _IMAGE_IMPORT_BY_NAME,包括Hint和函數名
	DWORD dwNewFileSize = AddCodeSection(pFileBuffer, pNewFileBuffer, dwFileSize, dwNewSectionSize);
	
	pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;
	pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
	pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)*pNewFileBuffer + \
		RvaToFoa(*pNewFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));

	// 設置新增節屬性
	pSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0xC0000040; // 可讀寫,含已初始化數據

	// 定義指針指向新增節首字節
	LPVOID pNewSec = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData);
	LPVOID pInsert = pNewSec;	
	// 複製原有的導入表
	memcpy(pInsert, pImportTable, dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR));	
	// 設置新導入表的時間戳,ForwarderChain
	pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pInsert + dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR));	
	pImportTable->TimeDateStamp = 0; // 表示不使用綁定導入
	pImportTable->ForwarderChain = -1;
	// 設置導入表結束標記
	pInsert = (LPVOID)((DWORD)pImportTable + sizeof(IMAGE_IMPORT_DESCRIPTOR)); // 指向結束標記
	memset(pInsert, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR));
	// 指定INT表插入點
	pInsert = (LPVOID)((DWORD)pInsert + sizeof(IMAGE_IMPORT_DESCRIPTOR)); // 現在指向INT表
	PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)pInsert; // 定義指向INT表的指針
	// 設置INT結束標誌
	memset(pINT + 1, 0, sizeof(IMAGE_THUNK_DATA));
	// 設置 IMPORT_BY_NAME,INT表和IAT表共同指向這塊內存
	PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pINT + 8); // IMPORT_BY_NAME插入點
	pImportByName->Hint = 0; // 設置沒有用的導出序號
	strcpy((char*)(pImportByName->Name), "ExportFunction"); // 設置函數名
	// INT表的值是 IMPORT_BY_NAME 的RVA
	*((PDWORD)pINT) = FoaToRva(*pNewFileBuffer, (DWORD)pImportByName - (DWORD)*pNewFileBuffer); // 設置INT的值
	// 設置IAT表
	pInsert = (LPVOID)((DWORD)pImportByName + 2 + strlen("ExportFunction") + 1); // 指向IAT表	
	PIMAGE_THUNK_DATA  pIAT = (PIMAGE_THUNK_DATA)pInsert; // IAT插入點
	memcpy(pIAT, pINT, 8);
	// 分配模塊名的內存,並完成導入表剩餘屬性的賦值
	pInsert = (LPVOID)((DWORD)pInsert + 8);	
	strcpy((char *)pInsert, "InjectDll.dll");
	// 設置導入表屬性
	pImportTable->FirstThunk = FoaToRva(*pNewFileBuffer, (DWORD)pINT - (DWORD)*pNewFileBuffer);
	pImportTable->OriginalFirstThunk = FoaToRva(*pNewFileBuffer, (DWORD)pIAT - (DWORD)*pNewFileBuffer);
	pImportTable->Name = FoaToRva(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer);
	// 更新目錄項中導入表的位置
	pOptionHeader->DataDirectory[1].VirtualAddress = FoaToRva(*pNewFileBuffer, ((DWORD)pNewSec - (DWORD)*pNewFileBuffer));
	return dwNewFileSize;
}

給ipmsg注入dll,雙擊運行exe,先彈出對話框
在這裏插入圖片描述

然後才運行程序
在這裏插入圖片描述

程序退出時,再次彈出對話框
在這裏插入圖片描述

三、DLL的代碼

這是DLL的主函數,加載時彈一次窗,分離時也彈一次窗。

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		Init();
		break;
	case DLL_PROCESS_DETACH:
		Destroy();
		break;
	}
    return TRUE;
}

四、完整代碼

至此,PE模塊的課程已經結束,有很多PE的知識還沒涉及到,這些要在以後學習了相關知識後再瞭解。

PE所有課後作業的核心代碼都在這了!
在這裏插入圖片描述

https://blog.csdn.net/Kwansy/article/details/106234264

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