一、導入表注入的原理
注入是把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所有課後作業的核心代碼都在這了!