關於進程殺死問題的研究報告
[摘要]
進程在運行狀態下可以經由Windows任務管理器殺死,在Windows9X系統下,通過把程序設置為系統程序,可以使它的進程不在任務管理器中顯示出來,從而避免了被用戶直接終止;在WindowsNT/2000/XP操作系統下,系統中運行的進程無法在任務管理器中隱藏,本文論述的,就是在NT內核的操作系統下(WindowsNT/2000/XP/2003),如何防止進程被用戶殺死的問題。
[關鍵詞] 進程 保護 Windows服務 安全對象 Windows特權
1??????? 防止任務管理器終止進程的基本原理
1.1????? 任務管理器終止進程的原理
任務管理器並不是直接終止進程的,它是通過OpenProcess函數打開一個進程對象,然後調用TerminateProcess函數發送一個消息到目標進程,從而達到關閉目標進程的目的。
所以,要阻止任務管理器關閉某一個進程,我們可以考慮從三個關鍵環節下手: 即OpenProcess的時候;發送TerminateProcess消息的時候,還有就是通過進程本身的自我保護機制,以下概述之。
1.2????? OpenProcess
任務管理器通過OpenProcess來打開目標進程對象。假如讓任務管理器沒有權限來打開這個進程對象,那麼OpenProcess將失敗,並返回一個錯誤代碼ERROR_ACCESS_DENIED(5)。
一個實際的例子是,當用戶試圖通過任務管理器終止一個Windows服務程序的時候,一般來說,任務管理器將無法使用OpenProcess打開目標進程對象,關閉進程的操作就會被取消。
實現這種效果的方法有兩種,一種是把應用程序註冊成Windows服務,另一種是修改進程的訪問安全級別。但是,它們都有各自的缺陷。
1.3????? 自身的保護機制
進程還可以通過偵測自身的狀態來實現自我保護,達到讓用戶無法關閉的效果。
實現這種效果的方法有三種。
第一種是雙工系統,就是讓系統同時運行兩個進程,一個在前台活動一個后台休眠,當一個進程被終止,另一個就投入使用,並重啟被關掉的進程,置為后台休眠狀態;
第二種是多線程自我監視,譬如進程運行三個線程,一個主線程,一個監視線程,一個嵌入到其他進程內,三個線程互相監督,防止被用戶關閉;
第三種是利用鉤子技術,就是通過設置全局API鉤子,一旦發現有TerminateProcess從別的進程發送過來,立刻取消這個消息,從而保護進程不被關閉。
以上三種方法,都有運用的實例。
2??????? 使用訪問控制技術
2.1????? 把應用程序註冊為服務
在一般情況下,用戶是沒有辦法從任務管理器中終止一個服務程序的。因此,可以把應用程序做成Windows服務(Service),從而達到防止被非法關閉的目的。
註冊一個新的服務可以通過以下代碼完成,為了便于表述,這裡省略了所有的錯誤處理。
代碼清單1
// 打開服務數據庫
schSCManager = OpenSCManager(lpHostName, NULL, SC_MANAGER_ALL_ACCESS);
// 創建服務
schService = CreateService(schSCManager, "Client", "Client", SERVICE_ALL_ACCESS,??? ???????????????????? SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START,??????????????????????????????????????????? SERVICE_ERROR_IGNORE, "Client.exe", NULL, NULL, NULL, NULL, NULL);
注意,創建服務的進程必須用有系統管理員級權限。
服務可能被用戶停止,在這裡,我們可以在進程中創建一個線程,每隔一定時間輪訊服務數據庫,把服務的其定模式更改為自動執行,如果發現服務停止了,就重啟。實例如下:
代碼清單2
const int?? MAX_SIZE?? = 256;
const DWORD SLEEP_TIME = 500;
while(1) // process every 500 milliseconds to ensure service config
{
// 和本機的服務管理數據庫建立連接
// ** 沒有事先查詢服務數據庫是否被鎖定, 在整合的時候必須加上
// ** 父進程必須必須擁有系統管理員權限或者足夠的特權級別
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
CString strSvrName;
strSvrName.Format("Messenger");
schService = OpenService(schSCManager, strSvrName, SERVICE_ALL_ACCESS);
// 以下設置服務的啟動模式
ChangeServiceConfig(
??????? schService, ?// handle of service
??????? SERVICE_NO_CHANGE, // service type: no change
??????? SERVICE_AUTO_START,?? // change service start type
??????? SERVICE_NO_CHANGE,?? // error control: no change
??????? NULL,???????????????? // binary path: no change
??????? NULL,???????????????? // load order group: no change
??????? NULL,???????????????? // tag ID: no change
??????? NULL,???????????????? // dependencies: no change
??????? NULL,???????????????? // account name: no change
??????? NULL,???????????????? // password: no change
??????? NULL)???????????????? // display name: no change
// 以下偵測並維護服務的運行情況
SERVICE_STATUS stuSvrStatus;
QueryServiceStatus(schService, &stuSvrStatus))
if (SERVICE_START_PENDING != stuSvrStatus.dwCurrentState &&
??????? SERVICE_RUNNING?????? != stuSvrStatus.dwCurrentState)
// service has been shut down
{
??????? // 重啟服務
??????? if (!StartService(schService,0,NULL))????????????????????????
??????? {
??????????????? dwErr = GetLastError();
??????????????? if (ERROR_SERVICE_ALREADY_RUNNING == dwErr)
??????????????? {
??????????????????????? Sleep(SLEEP_TIME);
??????????????????????? continue;
??????????????? }
??????????????? printf("StartService failed! code = %d\n", dwErr);
??????????????? CloseServiceHandle(schService);
??????????????? CloseServiceHandle(schSCManager);
??????????????? return;
??????? }
}
?
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
Sleep(SLEEP_TIME);
} // End while
同樣,我省略了所有的錯誤檢測代碼。
小結:
使用服務的優勢在於,服務可以在用戶登陸之前就開始運行,並且不受用戶登出的影響。但是輪訊服務狀態和重啟動會消耗比較多的資源,同時,如果惡意用戶頻繁停止服務,輪訊將導致程序頻繁重啟,程序將總是在進行初始化,而無法正常工作。
在Windows2000下,系統管理員在未使用特定權限的情況下不能終止服務,但是在WindowsXP/2003下,系統管理員是可以直接從任務管理器里終止服務的。因此使用服務並不能完美的達到防止進程被殺死的目的。
2.2????? 使用安全對象
原理和缺陷:
當任意一個進程對一個進程對象進行訪問的時候,系統將檢查目標進程的安全設置,如果安全設置不允許主控進程對這個進程進行該類型的訪問,這個訪問的請求將被拒絕。把進程設置為安全對象,將阻止未授權用戶非法殺死進程。
當用戶獲得某些特權之後,對進程對象的訪問將繞過安全設置直接進行,因此,安全設置不能保證特權用戶通過行使特權來關閉進程。當然,想要讓任務管理器獲得系統特權,是比較困難的,普通人幾乎不可能做到。
另外,由於微軟出于自身保護的原因,少有見到對安全對象技術詳盡的介紹,因此,我對這個技術還有很多不理解的地方,對其運用還不成熟。
簡介:
進程對象屬于Windows內核對象,每一個內核對象都有自身的訪問安全標誌,即所謂的SecurityDescriptor(SD)。
SD包含了全部的內核對象訪問信息,包括組群,所有者等等,其中關於訪問安全控制的是Access_Control List(ACL)。SD中包含兩個ACL,一個是任意ACL(DACL),另一個是系統ACL,即SACL。DACL指定了哪些用戶以及那類用戶能夠對指定內核對象進行或不能進行那些類型的訪問,SACL指定了系統管理員可以監看對內核對象的哪些訪問,這個監看的過程叫做審核,它將在系統中產生一個日誌文件,紀錄下SACL中指定的訪問情況。
ACL中包含了一個ACL頭結構和一組Access Entry(AE),AE可以有多個,也可以一個都沒有。
AE由一個AE頭結構和AE內容體組成。頭聲明了AE的類型,內容體包含了一個委派人(Trustee)和一組操作掩碼(Access Mask_Code),根據頭結構的聲明,系統將允許或者拒絕委派人執行操作掩碼所描述的訪問操作。
SID結構用來表示Trustee,SID是security identifier的縮寫,可以用它來標誌一個用戶或者一個群組,在網絡編程中,它還能表示一個或者一個群組的登陸用戶。
上圖演示了DACL的作用過程。
對於SD的具體應用代碼如下:
代碼清單3
// 初始化SecDescriptor
InitializeSecurityDescriptor(pSecDescriptor, SECURITY_DESCRIPTOR_REVISION))
?
// 設置ACL
?
pSID pSidGroup;
pSidGroup = &sidGroup;
?
// 初始化ACL
if (!InitializeAcl(pSecConfig, 1000, ACL_REVISION))
?
// 往ACL中添加一個ACCESS_DENIED_ACE
AddAccessDeniedAce(pSecConfig, ACL_REVISION,
??????? SPECIFIC_RIGHTS_ALL|STANDARD_RIGHTS_ALL, pSidGroup)
{
??????? dwErr = GetLastError();
??????? printf("AddAccessDeniedAce failed! Code = %d\n", dwErr);
??????? return FALSE;
}
?
// 設置DACL
SetSecurityDescriptorDacl(pSecDescriptor, TRUE, pSecConfig, FALSE)
if (!IsValidAcl(pSecConfig))
{
??????? printf("Create ACL failed!\nNot a valid ACL\n");
??????? return FALSE;
}
?
// 設置訪問安全性設置
pSecAtt->bInheritHandle = FALSE;
pSecAtt->lpSecurityDescriptor = pSecDescriptor;
pSecAtt->nLength = sizeof(*pSecAtt);
小結:
使用安全對象保護技術,可以在正常情況下使進程自身擁有防止被任務管理器殺死的能力。假如用戶有提昇任務管理器特權的技術水平,估計對如此高手也是防不勝防的。因此,這個技術是最有使用價值的。
3??????? 其他技術
本節述的技術比較複雜,並沒有具體實現。
3.1????? 多線程自我保護
三線程:
這種技術在”中國黑客”病毒中見到過,原理是把創建一個線程,並把這個線程嵌入到系統中的重要線程中(比如WinLogon),然後各個線程互相監看,發現那個被關掉了就把它再重啟起來。這種技術使用的實際上是一種自我監測機制。
進程再生:
這種技術應用在美萍反黃軟件上,原理是採用進程自我複製的辦法來防止被殺死。被複製的包括程序代碼和進程內核對象。
3.2????? 使用鉤子(Hook)技術
利用Windows全局API鉤子,一旦發現有外部進程試圖用TerminateProcess函數來終止進程,即進行攔截,使該操作無法進行,而進程內部使用ExitProcess函數,仍然可以終止進程。
另外,對於像WinLogon之類的核心服務,Windows任務管理器會直接拒絕用戶關閉,這是任務管理器在通過ListControl初始化服務名的時候自動拒絕的,和該服務沒有關係,證明是我可以用VisualC++Debuger來關掉WinLogon,當然,結果是當場系統崩潰。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=79138