摘要:從原理上介紹了進程隱藏的幾種方法,如通過API攔截和修改系統活動進程列表等方法,并對各種技術進行了優缺點分析。
關鍵詞:進程;隱藏
中圖分類號:TP309文獻標識碼:A文章編號: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
進程隱藏,也就在用戶不知情的情況下,悄悄執行自己的代碼。這一直是病毒、木馬程序設計者不斷探求的重要技術,因為這些程序都是見不得光的,都需要較好的隱藏和保護自己。了解進程隱藏技術,是開發防病毒和木馬軟件的基礎,一般來講,一個程序如果采用進程隱藏技術隱藏自己,那大多情況下其一定是一個病毒或木馬等惡意程序。但有些情況下,進程隱藏也是某些類型程序所需要的功能,如某些安全控制程序,例如上網控制系統,其功能要求只能上單位局域網,不能上internet,這種程序需要常駐系統,不能停止和卸載,這則要求進程能有效保護和隱藏自己,以防止用戶惡意刪除和卸載。
進程隱藏是一個古老但一直成長的技術,一直以來隱藏和破解隱藏的斗爭都在進行。從原理上講任何隱藏進程因為其都不能從操作系統的進程調度鏈中刪除,所以說都不能達到真正的隱藏,但是采用多種隱藏和保護機制,的確可以最大限度的保護程序。
進程隱藏現在主要有以下幾種技術:
1) 利用CreateRemoteThread()函數和代碼注入技術在宿主進程,如explorer中運行自己的代碼。
2) 利用API攔截技術攔截NtQuerySystemInformation函數,過濾掉要隱藏的進程,因為windows任務管理器調用這個函數來獲得系統運行的進程列表,這樣在windows任務管理器中就隱藏了目標進程。
3) 把要隱藏的進程從系統活動進程列表(EPROCESS LIST_ENTRY)中摘除,這樣其他的查找進程的函數都不能獲取目標進程的信息了。
1 利用CreateRemoteThread進行進程隱藏
這種方法的主要原理是通過代碼注入技術把代碼注入到宿主進程中,然后通過調用CreateRemoteThread()函數在宿主進程中生成自己的線程,運行自己的代碼。可以看出這種方法的一個主要工作是代碼的注入,也就是怎樣才能把自己的代碼映射到宿主進程的空間中。
代碼注入技術分為動態代碼注入技術和靜態代碼注入技術,動態代碼注入技術就是在進程啟動后或在進程啟動時在進程的運行空間中注入代碼的技術,而靜態注入技術就是在PE格式的.exe文件中插入代碼。靜態注入技術是病毒感染文件的常用方法,在文獻[1]中有詳細的敘述。動態注入技術也分為直接代碼注入技術和以dll形式的注入技術,直接代碼注入技術是利用VirtualAllocEx和CreateRemoteThread兩個API來進行的函數級代碼注入技術,可以采用匯編的形式,這種方法在文獻[1]中的進程隱藏一章中有詳細的講解,也可以采用高級語言如c語言的形式,這種方法有興趣的可以參考文獻[2]。直接代碼注入技術對注入的代碼有很高的要求,要解決地址重定位等問題,而且注入代碼的大小也受到很大的限制,所以不適用于進程級代碼的注入。所以進程隱藏一般采用dll形式的動態代碼注入技術。
利用CreateRemoteThread進行dll形式的動態代碼注入技術在[3]中有較詳細的論述。要在其他的進程中注入dll,就要求我們能在那個進程中調用LoadLibrary() API,但我們沒有權限獲得其他進程的執行控制權,幸好微軟提供了函數CreateRemoteThread()可以在其他的進程中創建遠程線程,而恰好線程函數的原型:
DWORD WINAPI ThreadProc(LPVOID lpParameter);
和LoadLibrary()的原型:
HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);
HMODULE和DWORD都是雙字節,調用方式都是WINAPI,LPVOID和LPCTSTR都是雙字節指針,所以函數原型是一樣的。從而我們利用CreateRemoteThread()來欺騙操作系統,使其執行LoadLibrary() API,如下:
hThread = ::CreateRemoteThread(
hProcessForHooking, //要插入dll的進程句柄
NULL,
0,
pfnLoadLibrary,// LoadLibrary函數的地址
\"C:\\\\HookTool.dll\", //要注入的dll的全路徑
0,
NULL);
LoadLibrary函數的地址因為其所在的Kernel32.DLL的映射地址在所有進程中是確定的,所以其值可以通過調用GetProcAddress()獲得。
這種進程隱藏的方法要把進程執行的代碼封裝進dll中,這可能不能滿足某些程序設計者的要求,而且經測試,瑞星等殺毒軟件都禁止這種方法的代碼注入,所以這種隱藏技術很難成功。
2 利用API攔截技術進行進程隱藏
API攔截的主要目的是在其他應用程序調用API之前將其攔截,由攔截者先處理傳遞的參數數據,然后決定是否再調用原來的API。比如API
BOOL TextOutA( HDC hdc, int nXStart,int nYStart,LPCTSTR lpString, int cbString);
在其他程序調用這個API之前,攔截程序可以先捕獲這個調用,先對參數等進行處理,比如將cbString翻譯為中文等,然后再調用原來的TextOutA進行文本輸出,這樣輸出的文本就變成中文了。
因為任務管理器是調用ntdll.dll中的NtQuerySystemInformation()API來獲得進程列表的,所以只要我們先截獲這個調用,在我們的替換函數中先調用原來的NtQuerySystemInformation,從返回的進程信息鏈表中去除要隱藏的目標進程的信息,然后把改變的進程列表返回給任務管理器,那么目標進程則從任務管理器中隱藏。NtQuerySystemInformation函數的原形如下:
NTSTATUSNTAPI ZwQuerySystemInformation(
INULONGSystemInformationClass,
INPVOIDSystemInformation,
INULONGSystemInformationLength,
OUT PULONG ReturnLength
);
其中SystemInformation中返回的就是如下結構體構成的進程信息鏈表。
struct _SYSTEM_PROCESSES
{ULONGNextEntryDelta;
ULONGThreadCount;
......
UNICODE_STRING ProcessName;
KPRIORITYBasePriority;
ULONGProcessId;
......};
其中ProcessName指向的就是進程的可執行文件名,我們可以通過可執行文件名或進程ID ProcessId進行進程過濾。
從上面介紹可以看出,采用這種方法進行進程隱藏的關鍵是怎樣進行API攔截,API攔截可以在用戶層(ring3)進行,也可以在內核層(ring0)中進行。用戶層的API攔截技術可以參考文獻[4],這里詳細介紹了用戶層API攔截的各種技術。內核層的API攔截技術可以參考文獻[5] 監控Native API調用一章,它的原理是通過替換windows系統服務描述符表中的Native API處理例程的入口地址來截獲API。
這種技術可靠性好,容易實現,并且可以進行其他功能的隱藏,如注冊表項隱藏,文件隱藏等,還可以通過截獲進程控制API來防止殺死進程,所以可以更好的隱藏和保護進程,所以這種方法使用的較廣泛。
3 修改系統活動進程列表
Window系統內部維護了一個活動進程鏈表,其節點結構體如下:
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;
系統通過查找這個鏈表獲得系統中進程的列表,所以如果把要隱藏的進程從這個鏈表中去除,則可以從系統中隱藏此進程.查找可以通過進程ID UniqueProcessId進行。
這種方法也是既可以在內核中進行,也可以在應用層進行。
(下轉第744頁)
(上接第741頁)
在內核層采用這種方法隱藏進程比較簡單,這個鏈表的表頭存儲在系統變量PsActiveProcessHead中,但這個變量windows沒有輸出,但我們可以通過PsGetCurrentProcess()函數獲取當前進程的EPROCESS結構體指針,然后通過ActiveProcessLinks遍歷列表來查找要隱藏的進程。這里需要注意的是不同版本的windows系統的EPROCESS結構可能不同。
在用戶層采用這種方法的原理也很簡單,因為windows系統在物理內存中的位置是固定的,所以活動進程鏈表的首地址也是固定的,通過調用ZwOpenSection()函數讀取和修改物理內存,然后修改的鏈表的相應位置來去除目標進程。在用戶層采用這種方法隱藏進程可靠性較差,因為和windows系統的版本關系太密切,不同windows版本中的很多參數都不同,如鏈表頭的位置,window內存的映像等,所以需要修改程序的很多參數,而且很多殺毒軟件都禁止對物理內存的直接讀寫,所以很多情況下都不能達到對進程的隱藏。
4 總結
本文從原理上介紹了進程隱藏的幾種方法,其中比較可靠和常用的是利用API攔截技術和在內核層修改活動進程鏈表兩種方法。就像在本文開始所講到的一樣,隱藏都是相對的,只要進程還在系統中運行,就必須服從windows的調度,那么其在系統中就一定可以查找得到,所以一些系統工具,如IceSword等就可以查找到隱藏的進程,但我們可以采用多種技術的復合,如三線程進程保護機制,文件隱藏機制等,增加進程的防殺功能,可以從一定程度上保護進程。
參考文獻:
[1] 羅云彬.Windows環境下32位匯編語言程序設計[M].電子工業出版社,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攔截技術[J].電腦知識與技術 2008,3(9):
[5] Sven Schreiber.Undocumented Windows 2000 Secrets[M].
注:本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文