木馬/后門(mén)程序在WINNT中進(jìn)程隱藏及查找的方法
在WIN9X中,只需要將進(jìn)程注冊為系統服務(wù)就能夠從進(jìn)程查看器中隱形,可是這一切在WINNT中卻完全不同,無(wú)論木馬從端口、啟動(dòng)文件上如何巧妙地隱藏自己,始終都不能欺騙WINNT的任務(wù)管理器,以至于很多的朋友問(wèn)我:在WINNT下難道木馬真的再也無(wú)法隱藏自己的進(jìn)程了?本文試圖通過(guò)探討WINNT中木馬的幾種常用隱藏進(jìn)程手段,給大家揭示木馬/后門(mén)程序在WINNT中進(jìn)程隱藏的方法和查找的途徑。
本文引用地址:http://dyxdggzs.com/article/148821.htm我們知道,在WINDOWS系統下,可執行文件主要是Exe和Com文件,這兩種文件在運行時(shí)都有一個(gè)共同點(diǎn),會(huì )生成一個(gè)獨立的進(jìn)程,查找特定進(jìn)程是我們發(fā)現木馬的主要方法之一(無(wú)論手動(dòng)還是防火墻),隨著(zhù)入侵檢測軟件的不斷發(fā)展,關(guān)聯(lián)進(jìn)程和SOCKET已經(jīng)成為流行的技術(shù)(例如著(zhù)名的FPort就能夠檢測出任何進(jìn)程打開(kāi)的TCP/UDP端口),假設一個(gè)木馬在運行時(shí)被檢測軟件同時(shí)查出端口和進(jìn)程,我們基本上認為這個(gè)木馬的隱藏已經(jīng)完全失敗(利用心理因素而非技術(shù)手段欺騙用戶(hù)的木馬不在我們的討論范圍之內)。在NT下正常情況用戶(hù)進(jìn)程對于系統管理員來(lái)說(shuō)都是可見(jiàn)的,要想做到木馬的進(jìn)程隱藏,有兩個(gè)辦法,第一是讓系統管理員看不見(jiàn)(或者視而不見(jiàn))你的進(jìn)程;第二是不使用進(jìn)程。
看不見(jiàn)進(jìn)程的方法就是進(jìn)行進(jìn)程欺騙,為了了解如何能使進(jìn)程看不見(jiàn),我們首先要了解怎樣能看得見(jiàn)進(jìn)程:在Windows中有多種方法能夠看到進(jìn)程的存在:PSAPI(Process Status API),PDH(Performance Data Helper),ToolHelp API,如果我們能夠欺騙用戶(hù)或入侵檢測軟件用來(lái)查看進(jìn)程的函數(例如截獲相應的API調用,替換返回的數據),我們就完全能實(shí)現進(jìn)程隱藏,但是一來(lái)我們并不知道用戶(hù)/入侵檢測軟件使用的是什么方法來(lái)查看進(jìn)程列表,二來(lái)如果我們有權限和技術(shù)實(shí)現這樣的欺騙,我們就一定能使用其它的方法更容易的實(shí)現進(jìn)程的隱藏。
第二種方法是不使用進(jìn)程,不使用進(jìn)程使用什么?為了弄明白這個(gè)問(wèn)題,我們必須要先了解Windows系統的另一種“可執行文件”----DLL,DLL是Dynamic Link Library(動(dòng)態(tài)鏈接庫)的縮寫(xiě),DLL文件是Windows的基礎,因為所有的API函數都是在DLL中實(shí)現的。DLL文件沒(méi)有程序邏輯,是由多個(gè)功能函數構成,它并不能獨立運行,一般都是由進(jìn)程加載并調用的。(你你你,你剛剛不是說(shuō)不用進(jìn)程了?)別急呀,聽(tīng)我慢慢道來(lái):因為DLL文件不能獨立運行,所以在進(jìn)程列表中并不會(huì )出現DLL,假設我們編寫(xiě)了一個(gè)木馬DLL,并且通過(guò)別的進(jìn)程來(lái)運行它,那么無(wú)論是入侵檢測軟件還是進(jìn)程列表中,都只會(huì )出現那個(gè)進(jìn)程而并不會(huì )出現木馬DLL,如果那個(gè)進(jìn)程是可信進(jìn)程,(例如資源管理器Explorer.exe,沒(méi)人會(huì )懷疑它是木馬吧?)那么我們編寫(xiě)的DLL作為那個(gè)進(jìn)程的一部分,也將成為被信賴(lài)的一員而為所欲為。
運行DLL文件最簡(jiǎn)單的方法是利用Rundll32.exe,Rundll/Rundll32是Windows自帶的動(dòng)態(tài)鏈接庫工具,可以用來(lái)在命令行下執行動(dòng)態(tài)鏈接庫中的某個(gè)函數,其中Rundll是16位而Rundll32是32位的(分別調用16位和32位的DLL文件),Rundll32的使用方法如下:
Rundll32.exe DllFileName FuncName
例如我們編寫(xiě)了一個(gè)MyDll.dll,這個(gè)動(dòng)態(tài)鏈接庫中定義了一個(gè)MyFunc的函數,那么,我們通過(guò)Rundll32.exe MyDll.dll MyFunc就可以執行MyFunc函數的功能。
如何運行DLL文件和木馬進(jìn)程的隱藏有什么關(guān)系么?當然有了,假設我們在MyFunc函數中實(shí)現了木馬的功能,那么我們不就可以通過(guò)Rundll32來(lái)運行這個(gè)木馬了么?在系統管理員看來(lái),進(jìn)程列表中增加的是Rundll32.exe而并不是木馬文件,這樣也算是木馬的一種簡(jiǎn)易欺騙和自我保護方法(至少你不能去把Rundll32.exe刪掉吧?)
使用Rundll32的方法進(jìn)行進(jìn)程隱藏是簡(jiǎn)易的,非常容易被識破。(雖然殺起來(lái)會(huì )麻煩一點(diǎn))比較高級的方法是使用特洛伊DLL,特洛伊DLL的工作原理是替換常用的DLL文件,將正常的調用轉發(fā)給原DLL,截獲并處理特定的消息。例如,我們知道WINDOWS的Socket 1.x的函數都是存放在wsock32.dll中的,那么我們自己寫(xiě)一個(gè)wsock32.dll文件,替換掉原先的wsock32.dll(將原先的DLL文件重命名為wsockold.dll)我們的wsock32.dll只做兩件事,一是如果遇到不認識的調用,就直接轉發(fā)給wsockold.dll(使用函數轉發(fā)器forward);二是遇到特殊的請求(事先約定的)就解碼并處理。這樣理論上只要木馬編寫(xiě)者通過(guò)SOCKET遠程輸入一定的暗號,就可以控制wsock32.dll(木馬DLL)做任何操作。特洛伊DLL技術(shù)是比較古老的技術(shù),因此微軟也對此做了相當的防范,在Win2K的system32目錄下有一個(gè)dllcache的目錄,這個(gè)目錄中存放著(zhù)大量的DLL文件(也包括一些重要的exe文件),這個(gè)是微軟用來(lái)保護DLL的法寶,一旦操作系統發(fā)現被保護的DLL文件被篡改(數字簽名技術(shù)),它就會(huì )自動(dòng)從dllcache中恢復這個(gè)文件。雖然說(shuō)先更改dllcache目錄中的備份再修改DLL文件本身可以繞過(guò)這個(gè)保護,但是可以想見(jiàn)的是微軟在未來(lái)必將更加小心地保護重要的DLL文件,同時(shí)特洛伊DLL方法本身有著(zhù)一些漏洞(例如修復安裝、安裝補丁、檢查數字簽名等方法都有可能導致特洛伊DLL失效),所以這個(gè)方法也不能算是DLL木馬的最優(yōu)選擇。
DLL木馬的最高境界是動(dòng)態(tài)嵌入技術(shù),動(dòng)態(tài)嵌入技術(shù)指的是將自己的代碼嵌入正在運行的進(jìn)程中的技術(shù)。理論上來(lái)說(shuō),在Windows中的每個(gè)進(jìn)程都有自己的私有內存空間,別的進(jìn)程是不允許對這個(gè)私有空間進(jìn)行操作的(私人領(lǐng)地、請勿入內),但是實(shí)際上,我們仍然可以利用種種方法進(jìn)入并操作進(jìn)程的私有內存。在多種動(dòng)態(tài)嵌入技術(shù)中(窗口Hook、掛接API、遠程線(xiàn)程),我最喜歡的是遠程線(xiàn)程技術(shù)(其實(shí)、其實(shí)我就會(huì )這一種……),下面就為大家介紹一下遠程線(xiàn)程技術(shù)。
遠程線(xiàn)程技術(shù)指的是通過(guò)在另一個(gè)運行的進(jìn)程中創(chuàng )建遠程線(xiàn)程的方法進(jìn)入那個(gè)線(xiàn)程的內存地址空間。我們知道,在進(jìn)程中,可以通過(guò)CreateThread函數創(chuàng )建線(xiàn)程,被創(chuàng )建的新線(xiàn)程與主線(xiàn)程(就是進(jìn)程創(chuàng )建時(shí)被同時(shí)自動(dòng)建立的那個(gè)線(xiàn)程)共享地址空間以及其他的資源。但是很少有人知道,通過(guò)CreateRemoteThread也同樣可以在另一個(gè)進(jìn)程內創(chuàng )建新線(xiàn)程,被創(chuàng )建的遠程線(xiàn)程同樣可以共享遠程進(jìn)程(注意:是遠程進(jìn)程!)的地址空間,所以,實(shí)際上,我們通過(guò)創(chuàng )建一個(gè)遠程線(xiàn)程,進(jìn)入了遠程進(jìn)程的內存地址空間,也就擁有了那個(gè)遠程進(jìn)程相當多的權限:例如啟動(dòng)一個(gè)DLL木馬(與進(jìn)入進(jìn)程內部相比,啟動(dòng)一個(gè)DLL木馬是小意思,實(shí)際上我們可以隨意篡改那個(gè)進(jìn)程的數據)
閑話(huà)少說(shuō),我們來(lái)看代碼:
首先,我們通過(guò)OpenProcess 來(lái)打開(kāi)我們試圖嵌入的進(jìn)程(如果不允許打開(kāi),那么嵌入就無(wú)法進(jìn)行了,這往往是由于權限不夠引起的,例如你試圖打開(kāi)一個(gè)受系統保護的進(jìn)程)
hRemoteProcess = OpenProcess( PROCESS_CREATE_THREAD | //允許遠程創(chuàng )建線(xiàn)程
PROCESS_VM_OPERATION | //允許遠程VM操作
PROCESS_VM_WRITE, //允許遠程VM寫(xiě)
FALSE, dwRemoteProcessId );
由于我們后面需要寫(xiě)入遠程進(jìn)程的內存地址空間并建立遠程線(xiàn)程,所以需要申請足夠的權限(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。
然后,我們可以建立LoadLibraryW這個(gè)線(xiàn)程來(lái)啟動(dòng)我們的DLL木馬,LoadLibraryW函數是在kernel32.dll中定義的,用來(lái)加載DLL文件,它只有一個(gè)參數,就是DLL文件的絕對路徑名pszLibFileName,(也就是木馬DLL的全路徑文件名),但是由于木馬DLL是在遠程進(jìn)程內調用的,所以我們首先還需要將這個(gè)文件名復制到遠程地址空間:(否則遠程線(xiàn)程讀不到這個(gè)參數)
//計算DLL路徑名需要的內存空間
int cb = (1 + lstrlenW(pszLibFileName)) * sizeof(WCHAR);
//使用VirtualAllocEx函數在遠程進(jìn)程的內存地址空間分配DLL文件名緩沖區
pszLibFileRemote = (PWSTR) VirtualAllocEx( hRemoteProcess, NULL, cb,
MEM_COMMIT, PAGE_READWRITE);
//使用WriteProcessMemory函數將DLL的路徑名復制到遠程進(jìn)程的內存空間
iReturnCode = WriteProcessMemory(hRemoteProcess,
pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL);
//計算LoadLibraryW的入口地址
PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT(Kernel32)), LoadLibraryW);
說(shuō)明一下,上面我們計算的其實(shí)是自己這個(gè)進(jìn)程內LoadLibraryW的入口地址,但是因為kernel.dll模塊在所有進(jìn)程內的地址都是相同的(屬于內核模塊),所以這個(gè)入口地址同樣適用于遠程進(jìn)程。
OK,萬(wàn)事俱備,我們通過(guò)建立遠程線(xiàn)程時(shí)的地址pfnStartAddr(實(shí)際上就是LoadLibraryW的入口地址)和傳遞的參數pszLibFileRemote(我們復制到遠程進(jìn)程內存空間的木馬DLL的全路徑文件名)在遠程進(jìn)程內啟動(dòng)我們的木馬DLL:
//啟動(dòng)遠程線(xiàn)程LoadLibraryW,通過(guò)遠程線(xiàn)程調用用戶(hù)的DLL文件
hRemoteThread = CreateRemoteThread(hRemoteProcess, //被嵌入的遠程進(jìn)程
NULL, 0,
pfnStartAddr, //LoadLibraryW的入口地址
pszLibFileRemote, //木馬DLL的全路徑文件名
0, NULL);
至此,遠程嵌入順利完成,為了試驗我們的DLL是不是已經(jīng)正常的在遠程線(xiàn)程運行,我編寫(xiě)了以下的測試DLL,這個(gè)DLL什么都不做,僅僅返回所在進(jìn)程的PID:
BOOL APIENTRY DllMain(HANDLE hModule, DWORD reason, LPVOID lpReserved)
{ char * szProcessId = (char *)malloc(10*sizeof(char));
switch (reason){
case DLL_PROCESS_ATTACH:{
//獲取并顯示當前進(jìn)程ID
_itoa(GetCurrentProcessId(), szProcessId, 10);
MessageBox(NULL,szProcessId,RemoteDLL,MB_OK);
}
default:
return TRUE;
}
}
當我使用RmtDll.exe程序將這個(gè)TestDLL.dll嵌入Explorer.exe進(jìn)程后(PID=1208),該測試DLL彈出了1208字樣的確認框,證明TestDLL.dll已經(jīng)在Explorer.exe進(jìn)程內正確地運行了。(木馬已經(jīng)成為Explorer.exe的一部分)
DLL木馬的查找:查找DLL木馬的基本思路是擴展進(jìn)程列表至內存模塊列表,內存模塊列表將顯示每個(gè)進(jìn)程目前加載/調用的所有DLL文件,通過(guò)這種方法,我們能發(fā)現異常的DLL文件(前提是你對所有進(jìn)程需要調用的模塊都很熟悉,天哪,這幾乎是沒(méi)有可能的事,要知道隨便哪個(gè)進(jìn)程都會(huì )調用十七八個(gè)DLL文件,而Windows更是由數以千計的DLL所組成的,誰(shuí)能知道哪個(gè)有用哪個(gè)沒(méi)用?)對此,我寫(xiě)了一個(gè)內存模塊查看軟件,在http://www.patching.net/shotgun/ps.zip可以下載,該軟件使用PSAPI,如果是NT4.0,需要PSAPI.dll的支持,所以我把PSAPI.dll也放在了壓縮包里。
進(jìn)一步想想,用遠程線(xiàn)程技術(shù)啟動(dòng)木馬DLL還是比較有跡可尋的,如果事先將一段代碼復制進(jìn)遠程進(jìn)程的內存空間,然后通過(guò)遠程線(xiàn)程起動(dòng)這段代碼,那么,即使遍歷進(jìn)程內存模塊也無(wú)濟于事;或者遠程線(xiàn)程切入某個(gè)原本就需要進(jìn)行SOCKET操作的進(jìn)程(如iExplorer.exe),對函數調用或數據進(jìn)行某些有針對的修改……這樣的木馬并不需要自己打開(kāi)端口,代碼也只是存在于內存中,可以說(shuō)如羚羊掛角,無(wú)跡可尋。
無(wú)論是使用特洛伊DLL還是使用遠程線(xiàn)程,都是讓木馬的核心代碼運行于別的進(jìn)程的內存空間,這樣不僅能很好地隱藏自己,也能更好的保護自己。
這個(gè)時(shí)候,我們可以說(shuō)已經(jīng)實(shí)現了一個(gè)真正意義上的木馬,它不僅欺騙、進(jìn)入你的計算機,甚至進(jìn)入了用戶(hù)進(jìn)程的內部,從某種意義上說(shuō),這種木馬已經(jīng)具備了病毒的很多特性,例如隱藏和寄生(和宿主同生共死),如果有一天,出現了具備所有病毒特性的木馬(不是指蠕蟲(chóng),而是傳統意義上的寄生病毒),我想我并不會(huì )感到奇怪,倒會(huì )疑問(wèn)這一天為什么這么遲才到來(lái)。
附錄:利用遠程線(xiàn)程技術(shù)嵌入進(jìn)程的模型源碼:
/////////////////////////////////////////////////////////////////////////////////////////////// //
// Remote DLL For Win2K by Shotgun //
// This Program can inject a DLL into Remote Process //
// //
// Released: [2001.4] //
// Author: [Shotgun] //
// Email: [Shotgun@Xici.Net] //
// Homepage: //
// [http://IT.Xici.Net] //
// [http://WWW.Patching.Net] //
// //
// USAGE: //
// RmtDLL.exe PID[|ProcessName] DLLFullPathName //
// Example: //
// RmtDLL.exe 1024 C:WINNTSystem32MyDLL.dll //
// RmtDLL.exe Explorer.exe C:MyDLL.dll //
// //
///////////////////////////////////////////////////////////////////////////////////////////////
#include
評論