標籤:pe 無模組 注入 dll
對windows安全比較熟悉的同學對模組注入應該都比較瞭解,很多病毒、木馬、外掛都會用到,無模組注入應用得則比較少。
無模組注入的好處是DLL注入進去後,確實已經不以模組的形式存在了,用任何進程模組查看工具,都找不到注入進去的DLL。因為它已經變為一塊純堆記憶體,跟EXE主模組裡申請的堆沒有任何差別。
這裡講的一種無模組注入的方法,能夠讓DLL自身實現這樣的功能,無需外部注入工具協助處理。當然如果進程內自行載入這樣的DLL後,也是以無模組DLL形式存在。
注入完成後,進程內找不到注入的模組存在,用Dbgview每5秒可以看到"No Module Thread"訊息列印出來,表示無模組DLL內進程的活動
之前這個方法有在看雪發表過,不過沒有詳細講,有的同學不是很明白,這裡把源碼貼出來,加上注釋,應該會比較清楚了。
LoadPE.CPP
"No Module Thread"//coded by 燕十二#include "stdafx.h"#include <stdio.h>//將DLL檔案讀到記憶體BOOL LoadDll2Mem(PVOID &pAllocMem,DWORD &dwMemSize,char* strFileName) { HANDLE hFile = CreateFileA(strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { return FALSE; } PVOID pFileBuff = NULL;int nFileSize = GetFileSize(hFile, NULL); if (nFileSize == 0){return FALSE;}else{pFileBuff = VirtualAlloc(NULL,nFileSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);}DWORD dwReadSize = 0;if (!ReadFile(hFile, pFileBuff, nFileSize, &dwReadSize, NULL)) { return FALSE; }PVOID pBase = pFileBuff;//判斷是否是PEPIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)pFileBuff;if (IMAGE_DOS_SIGNATURE != pIDH->e_magic) { return FALSE; } PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)((ULONG)pFileBuff + pIDH->e_lfanew);if (IMAGE_NT_SIGNATURE != pINH->Signature) { return FALSE; } dwMemSize = nFileSize;pAllocMem = pFileBuff; return TRUE; } //將DLL檔案按section資訊構建記憶體,重新對齊填充各section,並自行計算填充匯入表BOOL PELoader(char *lpStaticPEBuff, PVOID& pExecuMem){ long lPESignOffset = *(long *)(lpStaticPEBuff + 0x3c); IMAGE_NT_HEADERS *pINH = (IMAGE_NT_HEADERS *)(lpStaticPEBuff + lPESignOffset); long lImageSize = pINH->OptionalHeader.SizeOfImage; char *lpDynPEBuff = (char *)VirtualAlloc(NULL,lImageSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE); if(lpDynPEBuff == NULL) { return FALSE; } memset(lpDynPEBuff, 0, lImageSize); long lSectionNum = pINH->FileHeader.NumberOfSections; IMAGE_SECTION_HEADER *pISH = (IMAGE_SECTION_HEADER *)((char *)pINH + sizeof(IMAGE_NT_HEADERS)); memcpy(lpDynPEBuff, lpStaticPEBuff, pISH->VirtualAddress); long lFileAlignMask = pINH->OptionalHeader.FileAlignment - 1; long lSectionAlignMask = pINH->OptionalHeader.SectionAlignment - 1; for(int nIndex = 0; nIndex < lSectionNum; nIndex++, pISH++) { memcpy(lpDynPEBuff + pISH->VirtualAddress, lpStaticPEBuff + pISH->PointerToRawData, pISH->SizeOfRawData); } if(pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size > 0) { IMAGE_IMPORT_DESCRIPTOR *pIID = (IMAGE_IMPORT_DESCRIPTOR *)(lpDynPEBuff + pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); for(; pIID->Name != NULL; pIID++) { IMAGE_THUNK_DATA *pITD = (IMAGE_THUNK_DATA *)(lpDynPEBuff + pIID->FirstThunk); char* pLoadName = lpDynPEBuff + pIID->Name; HINSTANCE hInstance = LoadLibrary(pLoadName); if(hInstance == NULL) {VirtualFree(lpDynPEBuff,lImageSize,MEM_DECOMMIT); return FALSE; } for(; pITD->u1.Ordinal != 0; pITD++) { FARPROC fpFun; if(pITD->u1.Ordinal & IMAGE_ORDINAL_FLAG32) { fpFun = GetProcAddress(hInstance, (LPCSTR)(pITD->u1.Ordinal & 0x0000ffff)); } else { IMAGE_IMPORT_BY_NAME * pIIBN = (IMAGE_IMPORT_BY_NAME *)(lpDynPEBuff + pITD->u1.Ordinal); fpFun = GetProcAddress(hInstance, (LPCSTR)pIIBN->Name); } if(fpFun == NULL) { delete lpDynPEBuff; return false; } pITD->u1.Ordinal = (long)fpFun; } } } if(pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size > 0) { IMAGE_BASE_RELOCATION *pIBR = (IMAGE_BASE_RELOCATION *)(lpDynPEBuff + pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); long lDifference = (long)lpDynPEBuff - pINH->OptionalHeader.ImageBase; for(; pIBR->VirtualAddress != 0; ) { char *lpMemPage = lpDynPEBuff + pIBR->VirtualAddress; long lCount = (pIBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) >> 1; short int *pRelocationItem = (short int *)((char *)pIBR + sizeof(IMAGE_BASE_RELOCATION)); for(int nIndex = 0; nIndex < lCount; nIndex++) { int nOffset = pRelocationItem[nIndex] & 0x0fff; int nType = pRelocationItem[nIndex] >> 12; if(nType == 3) { *(long *)(lpMemPage + nOffset) += lDifference; } else if(nType == 0) { } } pIBR = (IMAGE_BASE_RELOCATION *)(pRelocationItem + lCount); } } pExecuMem = lpDynPEBuff; return true;} typedef BOOL (WINAPI *DLL_MAIN)( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved);//通過PE資訊擷取EP作為入口函數,並運行,這裡把dwReaseon設定為NO_MODULE_MARK,保留參數設定為模組的路徑//相當於再次以(hModule,NO_MODULE_MARK,pModuleName)為參數,調用一次原始的DllMain函數bool CallDllMain(PVOID pExecMem,DWORD dwReaseon,char* pModuleName){PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)((ULONG)pExecMem + ((PIMAGE_DOS_HEADER)pExecMem)->e_lfanew);DWORD dwEP = pINH->OptionalHeader.AddressOfEntryPoint;DLL_MAIN lpDllMain = (DLL_MAIN)((DWORD)pExecMem + dwEP);lpDllMain((HMODULE)pExecMem,dwReaseon,pModuleName);return TRUE;}BOOL LaunchDll(char *strName,DWORD dwReason){PVOID pRelocMem = NULL;PVOID pExecuMem = NULL;DWORD dwMemSize = 0;if (LoadDll2Mem(pRelocMem,dwMemSize,strName)){PELoader((char *)pRelocMem,pExecuMem);CallDllMain(pExecuMem,dwReason,strName);ZeroMemory(pRelocMem,dwMemSize);VirtualFree(pRelocMem,dwMemSize,MEM_DECOMMIT);}return TRUE;}
TestDll.cpp
// TestDll.cpp : Defines the entry point for the DLL application.//Coded by 燕十二#include "stdafx.h"#include "LoadPE.h"//防止無模組DLL多次注入BOOL IsMutexExist(char* pstrMutex){BOOL bRet = FALSE;HANDLE hMutex = NULL; hMutex = CreateMutexA(NULL, TRUE, pstrMutex);if ( hMutex ){if ( GetLastError() == ERROR_ALREADY_EXISTS )bRet = TRUE;ReleaseMutex(hMutex);CloseHandle(hMutex);}else{bRet = TRUE;}return bRet;}//調用LoadPE.cpp裡的函數,自行處理PE載入,把DLL在新申請的記憶體載入起來,並執行入口函數void LaunchNoModule(){LaunchDll(dllModuleName,NO_MODULE_MARK);}unsigned int __stdcall NoModuleThread(void* lpParameter){while (TRUE){Sleep(5000);OutputDebugString("No Module Thread");}return TRUE;}//開啟一個線程,調用實際的DLL功能代碼void NoModuleEntryCall(HMODULE hModule, DWORD ul_reason_for_call, char* pstrModuleName){char szMutexName[MAX_PATH];wsprintf(szMutexName,"yanshier2013nomoduleinject%d",GetCurrentProcessId());g_hMutex = CreateMutex(NULL, TRUE, szMutexName);char szLog[MAX_PATH] = {0};wsprintf(szLog,"NoModuleEntryCall Module Start:%p",hModule);OutputDebugString(szLog);//下面為正常Dll功能代碼CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)NoModuleThread,NULL,NULL,NULL);}BOOL ChooseSub(HMODULE hModule, DWORD ul_reason_for_call, char* pstrModuleName){BOOL bRet = FALSE;GetModuleFileNameA(NULL, exeModuleName, MAX_PATH);if ( ul_reason_for_call == NO_MODULE_MARK )strcpy(dllModuleName, pstrModuleName);elseGetModuleFileName(hModule, dllModuleName, MAX_PATH);if ( ul_reason_for_call == NO_MODULE_MARK ){NoModuleEntryCall(hModule, DLL_PROCESS_ATTACH, 0);bRet = TRUE;}else{LaunchNoModule();bRet = FALSE;}return bRet;}//ul_reason_for_call等於NO_MODULE_MARK時,DllMain已經是在新申請的記憶體中運行//DllMain傳回值是個很關鍵的地方,可能平常寫DLLMain的時候不會注意到,都是直接返回TRUE,沒有去關注FALSE的情況//ul_reason_for_call等於DLL_PROCESS_ATTACH時,DllMain返回FALSE會使DLL自行卸載//利用這一點再結合PE自行載入,就可以實現無模組注入了//當然,這樣的DLL,如果由程式內部載入,也是以無模組的形式存在的BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){BOOL bRet = FALSE;if ( ul_reason_for_call == DLL_PROCESS_ATTACH || ul_reason_for_call == NO_MODULE_MARK ){char szMutexName[MAX_PATH];wsprintf(szMutexName,"yanshier2013nomoduleinject%d",GetCurrentProcessId());if ( IsMutexExist(szMutexName))return FALSE;bRet = ChooseSub(hModule, ul_reason_for_call, (char *)lpReserved);}else{if ( ul_reason_for_call == DLL_PROCESS_DETACH){ReleaseMutex(g_hMutex);CloseHandle(g_hMutex);bRet = TRUE;}}return bRet;}
源碼下載見附件
本文出自 “燕十二” 部落格,請務必保留此出處http://chenjava.blog.51cto.com/374566/1574442
源碼解析一種無模組注入進程方法