摘要:從原理上介紹了進(jìn)程隱藏的幾種方法,如通過(guò)API攔截和修改系統(tǒng)活動(dòng)進(jìn)程列表等方法,并對(duì)各種技術(shù)進(jìn)行了優(yōu)缺點(diǎn)分析。
關(guān)鍵詞:進(jìn)程;隱藏
中圖分類(lèi)號(hào):TP309文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2008)30-0740-02
Windows Process Hiding Method
SHI Yong-lin,PAN Jin,PANG Xiong-chang,XIE Qing-song
(Department one of Xi'an Communication Institute, Xi'an 710016,China)
Abstract: This paper gives a detail explanation of how to hide process on windows, it introduces three ways to achieve the goal.It also tell the strong and weak point of the different methods.
Key words: process;hiding
進(jìn)程隱藏,也就在用戶(hù)不知情的情況下,悄悄執(zhí)行自己的代碼。這一直是病毒、木馬程序設(shè)計(jì)者不斷探求的重要技術(shù),因?yàn)檫@些程序都是見(jiàn)不得光的,都需要較好的隱藏和保護(hù)自己。了解進(jìn)程隱藏技術(shù),是開(kāi)發(fā)防病毒和木馬軟件的基礎(chǔ),一般來(lái)講,一個(gè)程序如果采用進(jìn)程隱藏技術(shù)隱藏自己,那大多情況下其一定是一個(gè)病毒或木馬等惡意程序。但有些情況下,進(jìn)程隱藏也是某些類(lèi)型程序所需要的功能,如某些安全控制程序,例如上網(wǎng)控制系統(tǒng),其功能要求只能上單位局域網(wǎng),不能上internet,這種程序需要常駐系統(tǒng),不能停止和卸載,這則要求進(jìn)程能有效保護(hù)和隱藏自己,以防止用戶(hù)惡意刪除和卸載。
進(jìn)程隱藏是一個(gè)古老但一直成長(zhǎng)的技術(shù),一直以來(lái)隱藏和破解隱藏的斗爭(zhēng)都在進(jìn)行。從原理上講任何隱藏進(jìn)程因?yàn)槠涠疾荒軓牟僮飨到y(tǒng)的進(jìn)程調(diào)度鏈中刪除,所以說(shuō)都不能達(dá)到真正的隱藏,但是采用多種隱藏和保護(hù)機(jī)制,的確可以最大限度的保護(hù)程序。
進(jìn)程隱藏現(xiàn)在主要有以下幾種技術(shù):
1) 利用CreateRemoteThread()函數(shù)和代碼注入技術(shù)在宿主進(jìn)程,如explorer中運(yùn)行自己的代碼。
2) 利用API攔截技術(shù)攔截NtQuerySystemInformation函數(shù),過(guò)濾掉要隱藏的進(jìn)程,因?yàn)閣indows任務(wù)管理器調(diào)用這個(gè)函數(shù)來(lái)獲得系統(tǒng)運(yùn)行的進(jìn)程列表,這樣在windows任務(wù)管理器中就隱藏了目標(biāo)進(jìn)程。
3) 把要隱藏的進(jìn)程從系統(tǒng)活動(dòng)進(jìn)程列表(EPROCESS LIST_ENTRY)中摘除,這樣其他的查找進(jìn)程的函數(shù)都不能獲取目標(biāo)進(jìn)程的信息了。
1 利用CreateRemoteThread進(jìn)行進(jìn)程隱藏
這種方法的主要原理是通過(guò)代碼注入技術(shù)把代碼注入到宿主進(jìn)程中,然后通過(guò)調(diào)用CreateRemoteThread()函數(shù)在宿主進(jìn)程中生成自己的線(xiàn)程,運(yùn)行自己的代碼??梢钥闯鲞@種方法的一個(gè)主要工作是代碼的注入,也就是怎樣才能把自己的代碼映射到宿主進(jìn)程的空間中。
代碼注入技術(shù)分為動(dòng)態(tài)代碼注入技術(shù)和靜態(tài)代碼注入技術(shù),動(dòng)態(tài)代碼注入技術(shù)就是在進(jìn)程啟動(dòng)后或在進(jìn)程啟動(dòng)時(shí)在進(jìn)程的運(yùn)行空間中注入代碼的技術(shù),而靜態(tài)注入技術(shù)就是在PE格式的.exe文件中插入代碼。靜態(tài)注入技術(shù)是病毒感染文件的常用方法,在文獻(xiàn)[1]中有詳細(xì)的敘述。動(dòng)態(tài)注入技術(shù)也分為直接代碼注入技術(shù)和以dll形式的注入技術(shù),直接代碼注入技術(shù)是利用VirtualAllocEx和CreateRemoteThread兩個(gè)API來(lái)進(jìn)行的函數(shù)級(jí)代碼注入技術(shù),可以采用匯編的形式,這種方法在文獻(xiàn)[1]中的進(jìn)程隱藏一章中有詳細(xì)的講解,也可以采用高級(jí)語(yǔ)言如c語(yǔ)言的形式,這種方法有興趣的可以參考文獻(xiàn)[2]。直接代碼注入技術(shù)對(duì)注入的代碼有很高的要求,要解決地址重定位等問(wèn)題,而且注入代碼的大小也受到很大的限制,所以不適用于進(jìn)程級(jí)代碼的注入。所以進(jìn)程隱藏一般采用dll形式的動(dòng)態(tài)代碼注入技術(shù)。
利用CreateRemoteThread進(jìn)行dll形式的動(dòng)態(tài)代碼注入技術(shù)在[3]中有較詳細(xì)的論述。要在其他的進(jìn)程中注入dll,就要求我們能在那個(gè)進(jìn)程中調(diào)用LoadLibrary() API,但我們沒(méi)有權(quán)限獲得其他進(jìn)程的執(zhí)行控制權(quán),幸好微軟提供了函數(shù)CreateRemoteThread()可以在其他的進(jìn)程中創(chuàng)建遠(yuǎn)程線(xiàn)程,而恰好線(xiàn)程函數(shù)的原型:
DWORD WINAPI ThreadProc(LPVOID lpParameter);
和LoadLibrary()的原型:
HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);
HMODULE和DWORD都是雙字節(jié),調(diào)用方式都是WINAPI,LPVOID和LPCTSTR都是雙字節(jié)指針,所以函數(shù)原型是一樣的。從而我們利用CreateRemoteThread()來(lái)欺騙操作系統(tǒng),使其執(zhí)行LoadLibrary() API,如下:
hThread = ::CreateRemoteThread(
hProcessForHooking, //要插入dll的進(jìn)程句柄
NULL,
0,
pfnLoadLibrary,// LoadLibrary函數(shù)的地址
\"C:\\\\HookTool.dll\", //要注入的dll的全路徑
0,
NULL);
LoadLibrary函數(shù)的地址因?yàn)槠渌诘腒ernel32.DLL的映射地址在所有進(jìn)程中是確定的,所以其值可以通過(guò)調(diào)用GetProcAddress()獲得。
這種進(jìn)程隱藏的方法要把進(jìn)程執(zhí)行的代碼封裝進(jìn)dll中,這可能不能滿(mǎn)足某些程序設(shè)計(jì)者的要求,而且經(jīng)測(cè)試,瑞星等殺毒軟件都禁止這種方法的代碼注入,所以這種隱藏技術(shù)很難成功。
2 利用API攔截技術(shù)進(jìn)行進(jìn)程隱藏
API攔截的主要目的是在其他應(yīng)用程序調(diào)用API之前將其攔截,由攔截者先處理傳遞的參數(shù)數(shù)據(jù),然后決定是否再調(diào)用原來(lái)的API。比如API
BOOL TextOutA( HDC hdc, int nXStart,int nYStart,LPCTSTR lpString, int cbString);
在其他程序調(diào)用這個(gè)API之前,攔截程序可以先捕獲這個(gè)調(diào)用,先對(duì)參數(shù)等進(jìn)行處理,比如將cbString翻譯為中文等,然后再調(diào)用原來(lái)的TextOutA進(jìn)行文本輸出,這樣輸出的文本就變成中文了。
因?yàn)槿蝿?wù)管理器是調(diào)用ntdll.dll中的NtQuerySystemInformation()API來(lái)獲得進(jìn)程列表的,所以只要我們先截獲這個(gè)調(diào)用,在我們的替換函數(shù)中先調(diào)用原來(lái)的NtQuerySystemInformation,從返回的進(jìn)程信息鏈表中去除要隱藏的目標(biāo)進(jìn)程的信息,然后把改變的進(jìn)程列表返回給任務(wù)管理器,那么目標(biāo)進(jìn)程則從任務(wù)管理器中隱藏。NtQuerySystemInformation函數(shù)的原形如下:
NTSTATUSNTAPI ZwQuerySystemInformation(
INULONGSystemInformationClass,
INPVOIDSystemInformation,
INULONGSystemInformationLength,
OUT PULONG ReturnLength
);
其中SystemInformation中返回的就是如下結(jié)構(gòu)體構(gòu)成的進(jìn)程信息鏈表。
struct _SYSTEM_PROCESSES
{ULONGNextEntryDelta;
ULONGThreadCount;
......
UNICODE_STRING ProcessName;
KPRIORITYBasePriority;
ULONGProcessId;
......};
其中ProcessName指向的就是進(jìn)程的可執(zhí)行文件名,我們可以通過(guò)可執(zhí)行文件名或進(jìn)程ID ProcessId進(jìn)行進(jìn)程過(guò)濾。
從上面介紹可以看出,采用這種方法進(jìn)行進(jìn)程隱藏的關(guān)鍵是怎樣進(jìn)行API攔截,API攔截可以在用戶(hù)層(ring3)進(jìn)行,也可以在內(nèi)核層(ring0)中進(jìn)行。用戶(hù)層的API攔截技術(shù)可以參考文獻(xiàn)[4],這里詳細(xì)介紹了用戶(hù)層API攔截的各種技術(shù)。內(nèi)核層的API攔截技術(shù)可以參考文獻(xiàn)[5] 監(jiān)控Native API調(diào)用一章,它的原理是通過(guò)替換windows系統(tǒng)服務(wù)描述符表中的Native API處理例程的入口地址來(lái)截獲API。
這種技術(shù)可靠性好,容易實(shí)現(xiàn),并且可以進(jìn)行其他功能的隱藏,如注冊(cè)表項(xiàng)隱藏,文件隱藏等,還可以通過(guò)截獲進(jìn)程控制API來(lái)防止殺死進(jìn)程,所以可以更好的隱藏和保護(hù)進(jìn)程,所以這種方法使用的較廣泛。
3 修改系統(tǒng)活動(dòng)進(jìn)程列表
Window系統(tǒng)內(nèi)部維護(hù)了一個(gè)活動(dòng)進(jìn)程鏈表,其節(jié)點(diǎn)結(jié)構(gòu)體如下:
typedef struct _EPROCESS
{
/*000*/ KPROCESSPcb;
/*06C*/ NTSTATUSExitStatus;
/*070*/ KEVENTLockEvent;
/*080*/ DWORDLockCount;
/*084*/ DWORDd084;
/*088*/ LARGE_INTEGERCreateTime;
/*090*/ LARGE_INTEGERExitTime;
/*098*/ PVOIDLockOwner;
/*09C*/ DWORDUniqueProcessId;
/*0A0*/ LIST_ENTRYActiveProcessLinks;
......
}EPROCESS;
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY;
系統(tǒng)通過(guò)查找這個(gè)鏈表獲得系統(tǒng)中進(jìn)程的列表,所以如果把要隱藏的進(jìn)程從這個(gè)鏈表中去除,則可以從系統(tǒng)中隱藏此進(jìn)程.查找可以通過(guò)進(jìn)程ID UniqueProcessId進(jìn)行。
這種方法也是既可以在內(nèi)核中進(jìn)行,也可以在應(yīng)用層進(jìn)行。
(下轉(zhuǎn)第744頁(yè))
(上接第741頁(yè))
在內(nèi)核層采用這種方法隱藏進(jìn)程比較簡(jiǎn)單,這個(gè)鏈表的表頭存儲(chǔ)在系統(tǒng)變量PsActiveProcessHead中,但這個(gè)變量windows沒(méi)有輸出,但我們可以通過(guò)PsGetCurrentProcess()函數(shù)獲取當(dāng)前進(jìn)程的EPROCESS結(jié)構(gòu)體指針,然后通過(guò)ActiveProcessLinks遍歷列表來(lái)查找要隱藏的進(jìn)程。這里需要注意的是不同版本的windows系統(tǒng)的EPROCESS結(jié)構(gòu)可能不同。
在用戶(hù)層采用這種方法的原理也很簡(jiǎn)單,因?yàn)閣indows系統(tǒng)在物理內(nèi)存中的位置是固定的,所以活動(dòng)進(jìn)程鏈表的首地址也是固定的,通過(guò)調(diào)用ZwOpenSection()函數(shù)讀取和修改物理內(nèi)存,然后修改的鏈表的相應(yīng)位置來(lái)去除目標(biāo)進(jìn)程。在用戶(hù)層采用這種方法隱藏進(jìn)程可靠性較差,因?yàn)楹蛍indows系統(tǒng)的版本關(guān)系太密切,不同windows版本中的很多參數(shù)都不同,如鏈表頭的位置,window內(nèi)存的映像等,所以需要修改程序的很多參數(shù),而且很多殺毒軟件都禁止對(duì)物理內(nèi)存的直接讀寫(xiě),所以很多情況下都不能達(dá)到對(duì)進(jìn)程的隱藏。
4 總結(jié)
本文從原理上介紹了進(jìn)程隱藏的幾種方法,其中比較可靠和常用的是利用API攔截技術(shù)和在內(nèi)核層修改活動(dòng)進(jìn)程鏈表兩種方法。就像在本文開(kāi)始所講到的一樣,隱藏都是相對(duì)的,只要進(jìn)程還在系統(tǒng)中運(yùn)行,就必須服從windows的調(diào)度,那么其在系統(tǒng)中就一定可以查找得到,所以一些系統(tǒng)工具,如IceSword等就可以查找到隱藏的進(jìn)程,但我們可以采用多種技術(shù)的復(fù)合,如三線(xiàn)程進(jìn)程保護(hù)機(jī)制,文件隱藏機(jī)制等,增加進(jìn)程的防殺功能,可以從一定程度上保護(hù)進(jìn)程。
參考文獻(xiàn):
[1] 羅云彬.Windows環(huán)境下32位匯編語(yǔ)言程序設(shè)計(jì)[M].電子工業(yè)出版社,200,10.
[2] Ciro Sisman Pereira.Portable Executable (P.E.) Code Injection: Injecting an Entire C[DB/OL].www.codeproject.com.
[3] Jeffrey Ritcher.Load Your 32-bit DLL into Another Process's Address Space Using INJLIB[J].MSJ May 1994.
[4] 史永林.Windows API攔截技術(shù)[J].電腦知識(shí)與技術(shù) 2008,3(9):
[5] Sven Schreiber.Undocumented Windows 2000 Secrets[M].
注:本文中所涉及到的圖表、注解、公式等內(nèi)容請(qǐng)以PDF格式閱讀原文