文章目錄:
1. 引子 – Demo 實現效果:
2. 進程隱藏與進程保護概念:
3. SSDT Hook 框架搭建:
4. Ring0 實現進程隱藏:
5. Ring0 實現進程保護:
6. 隱藏進程列表和保護進程列表的維護:
7. 小結:
1. 引子 – Demo 實現效果:
上一篇《進程隱藏與進程保護(SSDT Hook 實現)(一)》呢把 SSDT 說得差不多了,
博文地址:
http://www.cnblogs.com/BoyXiao/archive/2011/09/03/2164574.html
不過呢,那也只是些理論的東西,看不到什么實物,估計說來說去把人說暈了后,也沒什么感覺,
而這一篇博文的話,給出點新意的,讓人頭腦清醒點的 ~ 所以先給個 Demo 出來吧 ~
(不好意思,本着不喝倒怎出得了好文章這個理由,所以今天又喝多了點,文章有疏忽之處還請見諒 ~
順便在這里跟朋友們分享一下哈,晚上於這個時間點,比如 2 點的時候啊,喝點小酒,
聽點曲子,你會精神振奮,頭腦更加清醒,思路也會很清晰,尤其是寫起程序來那是唰唰的來の ~ )
進程隱藏效果:
應用程序主界面:
隱藏進程 taskmgr.exe:
取消進程隱藏 taskmgr.exe:
進程保護效果:
進程保護(保護自身進程 SSDTProcess.exe):
取消進程保護(這里還是以 SSDTProcess.exe 為例):
下面的截圖表示 SSDTProcess.exe 已經被取消了保護,
此時再到任務管理器中結束 SSDTProcess.exe 時,你可以發現是可以正常結束這個進程的 ~
2. 進程隱藏與進程保護概念:
在 Ring3 下獲取到當前 Windows 操作系統下的所有的進程無外乎以下的幾種方法:
第一種:使用 ToolHelp 遍歷獲取到所有進程,關於這種方式的話,筆者以前寫過一篇博文的,
《列舉 Windows 所有進程(ToolHelp)》博文地址如下:
http://www.cnblogs.com/BoyXiao/archive/2011/02/27/1966383.html
第二種:使用 PSAPI 下的 EnumProcesses 獲取到所有進程的 PID,然后提升進程權限為 SE_DEBUG 權限,
再調用 OpenProcess 即可打開進程,從而獲取到進程的基本信息(可以查看 MSDN 的 PSAPI 專題)。
第三種:使用未公開的本地 API 即位於 Ntdll.dll 中的未文檔化的 API – NtQuerySystemInformation,
而 Windows 任務管理器就是通過這種方式來獲取到所有的進程信息的 ~
而事實上的是,ToolHelp 和 PSAPI 只不過是對 Ntdll.dll 中 NtQuerySystemInformation API 的一個封裝,
所以在 Ring3 下獲取系統中所有進程信息最終都會回到 Ndll.dll 中 NtQuerySystemInformation API 的調用上。
如果要實現在 Ring3 中對進程進行隱藏的話,只需要 Hook 掉 NtQuerySystemInformation API 即可。
而至於進程保護的話,我們需要考慮到兩種情況,第一種則是該進程自行終止,第二種情況則是該進程被其他進程給殺掉,
第一種情況基本上對於窗口應用程序來說,一般都是用戶點擊了右上角的 x 按鈕,然后產生 WM_CLOSE 消息,
最后由窗口過程退出進程,這種情況下,我們應該是需要允許退出的,也就是進程是可以正常退出的。
而第二種情況的話,就是進程被別的進程殺掉,比如在任務管理器中就可以殺掉絕大部分的應用程序進程,
而這里的進程保護就是要實現進程不能夠被任務管理器或者其他的進程管理工具殺掉。
在 Ring3 中,由一個進程結束其他進程,調用的 API 為 Kernel32.dll 中的 TerminateProcess,
如果追溯這個 TerminateProcess,可以發現,其調用了 Ntdll.dll 中的 NtTerminateProcess API,
然后再追溯下去就可以到 ntoskrnl.exe 中的 ZwTerminateProcess 和系統服務 NtTerminateProcess 了。
而這和我的上一篇博文中介紹 NtQuerySystemInformation 就是一致的了,
所以如果我們要實現進程保護,需要 Hook 的系統服務就是 NtTerminateProcess ~
3. SSDT Hook 框架搭建:
從上面的介紹中,我們可以知道,要想實現進程隱藏和進程保護,我們需要在 SSDT 中 Hook 兩個系統服務,
即 Ring0 下的 NtQuerySystemInformation 和 Ring0 下的 NtTerminateProcess,
既然要實現兩個 Hook 的話,我干嘛不將 SSDT Hook 寫成一個框架呢,
這樣的話,以后我無論是需要 Hook 哪個 SSDT 中的系統服務,我直接調用這個 SSDT 框架不就 OK 了,
免得再去重復造輪子不,所以這里就來簡單介紹一下這個 SSDT 框架 ~
當然這里談得 SSDT Hook 框架可不是大伙眼里的什么 .Net 框架啊,MVC 框架啊之類的,
沒那么復雜,算到底也就是一個 .cpp 和一個 .h 的文件而已,然后再在其中對外公開幾個 API 即 OK 了 ~
這里既是使用了 SSDT 的話,而在前一篇博文中也談到了在 ntoskrnl.exe 中導出了 KeServiceDescriptorTable,
但是內核中導出歸導出,它導出有個屁用啊,別個類型啊什么的都沒給你,看你怎么在你代碼中使用它 ~
所以我們首先要做的就是如何使用這個 ntoskrnl.exe 中導出的 KeServiceDescriptorTable 了,
不過好在還有 WRK(當然在反匯編,逆向工程里面那些牛的作用也是相當的給力)的幫助,
我們可以定義下面的代碼來完成在自己的代碼中使用 KeServiceDescriptorTable 這個任務:
1: //=====================================================================================//
2: //Name: KSYSTEM_SERVICE_TABLE 和 KSERVICE_TABLE_DESCRIPTOR //
4: //Descripion: 用來定義 SSDT 結構 //
6: //=====================================================================================//
7: typedef struct _KSYSTEM_SERVICE_TABLE
8: {
9: PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址
10: PULONG ServiceCounterTableBase; // 包含 SSDT 中每個服務被調用的次數
11: ULONG NumberOfService; // 服務函數的個數, NumberOfService * 4 就是整個地址表的大小
12: ULONG ParamTableBase; // SSPT(System Service Parameter Table)的基地址
13:
14: } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
15:
16:
17: typedef struct _KSERVICE_TABLE_DESCRIPTOR
18: {
19: KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服務函數
20: KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服務函數(GDI32.dll/User32.dll 的內核支持)
21: KSYSTEM_SERVICE_TABLE notUsed1;
22: KSYSTEM_SERVICE_TABLE notUsed2;
23:
24: } KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
25:
26:
27: //導出由 ntoskrnl.exe 所導出的 SSDT
28: extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
既然是個 SSDT Hook 框架的話,自然需要能夠安裝 Hook,當然也需要能夠解除 Hook,
而我們拿什么來解除 Hook 呢,經過前面的介紹,我們可以知道的是,
SSDT Hook 其實就是拿我們自己的 Hook 函數的地址去替換掉原來 SSDT 中保存的系統服務的地址,
如果 Hook 了某個 API,那就意味着在 SSDT 中指定索引處所保存的系統服務的地址被修改為了 Hook 函數的地址,
而如果要解除這個 API 的 Hook,自然就需要將原來系統中原有的系統服務的地址寫回 SSDT 指定索引處,
但是我們拿什么來保存 SSDT Hook 之前的系統服務的地址呢 ?
由於在 32 位機器上,一個入口地址可以用 32 位來表示,也就可以使用一個 ULONG 類型來保存,
而由於我們這個是 SSDT 框架,也就是能夠隨意的 Hook SSDT 中的任意系統服務,
自然為了能成功實現 Hook 的解除,就需要將 SSDT 中在 Hook 之前的每一個系統服務的地址保存下來,
根據上面的總結,這可以通過一個 ULONG 數組來保存就可以了 ~
然后再在 Hook 任意的系統服務之前,將 SSDT 中的所有系統服務的地址保存或者說是備份到 ULONG 數組中即可 ~
而后在解除 Hook 時,我們就可以從這個 ULONG 數組中取出原有系統服務的地址,
然后將地址寫入到 SSDT 中即可實現 Hook 解除 ~
1: //定義 SSDT(系統服務描述表) 中服務個數的最大數目
2: //這里定義為 1024 個,實際上在 XP SP3 是 0x0128 個
3: #define MAX_SYSTEM_SERVICE_NUMBER 1024
4:
5: //用來保存 SSDT 中所有的舊的服務函數的地址
6: ULONG oldSysServiceAddr[MAX_SYSTEM_SERVICE_NUMBER];
同時由於要實現安裝 Hook,解除 Hook,所以自然也要公開兩個 API,一個用來安裝 Hook,一個用來解除 Hook,
根據上面的這些呢,我們大致可以確定至少需要三個 API:
1: //備份 SSDT 中所有系統服務的地址
2: VOID BackupSysServicesTable();
3:
4: //安裝 Hook
5: NTSTATUS InstallSysServiceHook(ULONG oldService, ULONG newService);
6:
7: //解除 Hook
8: NTSTATUS UnInstallSysServiceHook(ULONG oldService);
然后還需要注意的是,SSDT 中保存的地址不是說你想寫就可以寫的,
SSDT 在內存中是具有只讀屬性保護的,如果你想修改 SSDT 中的內容,你必須先要解除只讀屬性,
也就是要賦予 SSDT 所在的這塊內存具有可寫屬性才行,不然回饋你的將是一個無情的藍屏(內存寫入錯誤) ~
你給了這塊內存可寫屬性后,你他媽的寫完后總的把可寫屬性去掉,把別個恢復到只讀屬性吧 ~
不然也太不厚道了,用完就不管了 ~ 所以還需要一個恢復只讀屬性的 API,
綜述,在 SSDT Hook 框架中又有了兩個 API:
1: //禁止寫入保護,也就是恢復到只讀
2: VOID DisableWriteProtect(ULONG oldAttr);
3:
4: //允許寫入保護,也就是設置為可寫
5: VOID EnableWriteProtect(PULONG pOldAttr);
然后呢下面就將上面的這些個 API 的實現代碼給貼出來,個人覺得自己的代碼風格還算比較好的,
應該還是看得下去吧(當然這只是我現在的觀點,說不准再過段時間回頭來看這些代碼就會感慨這代碼是給人看的嘛) ~
1: #include "SSDTHook.h"
2:
4: //=====================================================================================//
5: //Name: VOID DisableWriteProtect() //
6: // //
7: //Descripion: 用來去掉內存的可寫屬性,從而實現內存只讀 //
8: // //
9: //=====================================================================================//
10: VOID DisableWriteProtect(ULONG oldAttr)
11: {
12: _asm
13: {
14: mov eax, oldAttr
15: mov cr0, eax
16: sti;
17: }
18: }
19:
20:
21: //=====================================================================================//
22: //Name: VOID EnableWriteProtect() //
23: // //
24: //Descripion: 用來去掉內存的只讀保護,從而實現可以寫內存 //
25: // //
26: //=====================================================================================//
27: VOID EnableWriteProtect(PULONG pOldAttr)
28: {
29: ULONG uAttr;
30:
31: _asm
32: {
33: cli;
34: mov eax, cr0;
35: mov uAttr, eax;
36: and eax, 0FFFEFFFFh; // CR0 16 BIT = 0
37: mov cr0, eax;
38: };
39:
40: //保存原有的 CRO 屬性
41: *pOldAttr = uAttr;
42: }
43:
44:
45: //=====================================================================================//
46: //Name: VOID BackupSysServicesTable() //
47: // //
48: //Descripion: 備份 SSDT 中原有服務的地址,因為在解除 Hook 時需要還原 SSDT 中原有地址 //
49: // //
50: //=====================================================================================//
51: VOID BackupSysServicesTable()
52: {
53: ULONG i;
54:
55: for(i = 0; (i < KeServiceDescriptorTable->ntoskrnl.NumberOfService) && (i < MAX_SYSTEM_SERVICE_NUMBER); i++)
56: {
57: oldSysServiceAddr[i] = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[i];
58: //oldSysServiceAddr[i] = *(PULONG)((ULONG)KeServiceDescriptorTable->ntoskrnl.ServiceTableBase + 4 * i);
59:
60: KdPrint(("\Function Information { Number: 0x%04X , Address: %08X}", i, oldSysServiceAddr[i]));
61: }
62: }
63:
64:
65: //=====================================================================================//
66: //Name: NTSTATUS InstallSysServiceHook() //
67: // //
68: //Descripion: 實現 Hook 的安裝,主要是在 SSDT 中用 newService 來替換掉 oldService //
69: // //
70: //=====================================================================================//
71: NTSTATUS InstallSysServiceHook(ULONG oldService, ULONG newService)
72: {
73: ULONG uOldAttr = 0;
74:
75: EnableWriteProtect(&uOldAttr);
76:
77: SYSCALL_FUNCTION(oldService) = newService;
78: //KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[SYSCALL_INDEX(oldService)] = newService;
79:
80: DisableWriteProtect(uOldAttr);
81:
82: return STATUS_SUCCESS;
83: }
84:
85:
86: //=====================================================================================//
87: //Name: NTSTATUS UnInstallSysServiceHook() //
88: // //
89: //Descripion: 實現 Hook 的解除,主要是在 SSDT 中用備份下的服務地址來替換掉 oldService //
90: // //
91: //=====================================================================================//
92: NTSTATUS UnInstallSysServiceHook(ULONG oldService)
93: {
94: ULONG uOldAttr = 0;
95:
96: EnableWriteProtect(&uOldAttr);
97:
98: SYSCALL_FUNCTION(oldService) = oldSysServiceAddr[SYSCALL_INDEX(oldService)];
100:
101: DisableWriteProtect(uOldAttr);
102:
103: return STATUS_SUCCESS;
104: }
可以注意到上面有兩個很重要的宏,即 SYSCALL_FUNCTION 和 SYSCALL_INDEX 宏,
關於這兩個宏的具體作用,可以看注釋的 ~
1: //根據 Zw_ServiceFunction 獲取 Zw_ServiceFunction 在 SSDT 中所對應的服務的索引號
2: #define SYSCALL_INDEX(ServiceFunction) (*(PULONG)((PUCHAR)ServiceFunction + 1))
3:
4:
5: //根據 Zw_ServiceFunction 來獲得服務在 SSDT 中的索引號,
6: //然后再通過該索引號來獲取 Nt_ServiceFunction的地址
7: #define SYSCALL_FUNCTION(ServiceFunction)
8: KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[SYSCALL_INDEX(ServiceFunction)]
4. Ring0 實現進程隱藏:
有了 SSDT Hook 框架后,其實要實現進程的隱藏是很簡單的了,
根據前面的介紹,要想實現進程隱藏,你可以通過 Hook NtQuerySystemInformation 來實現,
所以剩下的任務就只需要在我們自己的 Hook 處理函數中來將進程隱藏掉就 OK 了 ~
由於 NtQuerySystemInformation 這個系統服務在 ntddk.h 中並沒有被聲明,
雖然這個系統服務在 ntoskrnl.exe 中被導出了,但是沒有它的聲明,我們仍然是無法使用的,
所以我們就需要手動的聲明一下這個函數 ~
還有需要注意的是,我們在前面知道,在 ntoskrnl.exe 中實質上是存在 ZwQuerySystemInformation
以及 NtQuerySystemInformation 這兩個 API 的,
而在 SSDT Hook 中我們是根據 ZwQuerySystemInformation
來推算出在 SSDT 中保存有 NtQuerySystemInformation 的地址所在的索引號的 ~
關於這個,你可以查看 SYSCALL_INDEX 這個宏來再次確認一下 ~
然后有了這個索引號,我們才可以進行對 NtQuerySystemInformation 系統服務的 Hook,
所以在聲明時,我們需要聲明兩個 API,當然如果這些 API 在 ntddk.h 中聲明了就不需要了,
但是由於 ZwQuerySystemInformation 和 NtQuerySystemInformation 在 ntddk.h 中都沒有聲明,
所以需要在我們自己的代碼中手動聲明 ~
1: NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation (
2: __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
3: __out_bcount_opt(SystemInformationLength) PVOID SystemInformation,
4: __in ULONG SystemInformationLength,
5: __out_opt PULONG ReturnLength
6: );
7:
8: typedef NTSTATUS (* NTQUERYSYSTEMINFORMATION)(
9: __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
10: __out_bcount_opt(SystemInformationLength) PVOID SystemInformation,
11: __in ULONG SystemInformationLength,
12: __out_opt PULONG ReturnLength
13: );
(暫停一下,肚子餓了 ~ 吃飯去 ~ )
(好,飯給吃了,酒也喝了,精神亢奮中,現在咱繼續哈 ~ 嘿嘿 ~)
完成了這些聲明后,我們就可以來實現自己的 NtQuerySystemInformation Hook 函數了,
在這個 Hook 函數中,我們需要對我們感興趣的進程進行隱藏 ~
然后這里需要注意的是,我是如何來實現對進程隱藏的,
首先我是判斷這個進程的 ID 是否是需要隱藏的進程 ID,
這是通過 ValidateProcessNeedHide 函數來判斷的 ~ 這個函數會在后面給出 ~
注意結合代碼中的注釋來看(雖然注釋比較少 ~ 嘿嘿 ~ )
1: NTSTATUS HookNtQuerySystemInformation (
2: __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
3: __out_bcount_opt(SystemInformationLength) PVOID SystemInformation,
4: __in ULONG SystemInformationLength,
5: __out_opt PULONG ReturnLength
6: );
1: //=====================================================================================//
2: //Name: NTSTATUS HookNtQuerySystemInformation() //
3: // //
4: //Descripion: 自定義的 NtQuerySystemInformation,用來實現 Hook Kernel API //
5: // //
6: //=====================================================================================//
7: NTSTATUS HookNtQuerySystemInformation (
8: __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
9: __out_bcount_opt(SystemInformationLength) PVOID SystemInformation,
10: __in ULONG SystemInformationLength,
11: __out_opt PULONG ReturnLength
12: )
13: {
14: NTSTATUS rtStatus;
15:
16: pOldNtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)
17: oldSysServiceAddr[SYSCALL_INDEX(ZwQuerySystemInformation)];
18:
19: rtStatus = pOldNtQuerySystemInformation(SystemInformationClass, SystemInformation,
20: SystemInformationLength, ReturnLength);
21: if(NT_SUCCESS(rtStatus))
22: {
23: if(SystemProcessInformation == SystemInformationClass)
24: {
25: PSYSTEM_PROCESS_INFORMATION pPrevProcessInfo = NULL;
26: PSYSTEM_PROCESS_INFORMATION pCurrProcessInfo =
27: (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
28:
29: while(pCurrProcessInfo != NULL)
30: {
31: //獲取當前遍歷的 SYSTEM_PROCESS_INFORMATION 節點的進程名稱和進程 ID
32: ULONG uPID = (ULONG)pCurrProcessInfo->UniqueProcessId;
33: UNICODE_STRING strTmpProcessName = pCurrProcessInfo->ImageName;
34:
35: //判斷當前遍歷的這個進程是否為需要隱藏的進程
36: if(ValidateProcessNeedHide(uPID) != -1)
37: {
38: if(pPrevProcessInfo)
39: {
40: if(pCurrProcessInfo->NextEntryOffset)
41: {
42: //將當前這個進程(即要隱藏的進程)從 SystemInformation 中摘除(更改鏈表偏移指針實現)
43: pPrevProcessInfo->NextEntryOffset += pCurrProcessInfo->NextEntryOffset;
44: }
45: else
46: {
47: //說明當前要隱藏的這個進程是進程鏈表中的最后一個
48: pPrevProcessInfo->NextEntryOffset = 0;
49: }
50: }
51: else
52: {
53: //第一個遍歷到得進程就是需要隱藏的進程
54: if(pCurrProcessInfo->NextEntryOffset)
55: {
56: (PCHAR)SystemInformation += pCurrProcessInfo->NextEntryOffset;
57: }
58: else
59: {
60: SystemInformation = NULL;
61: }
62: }
63: }
64:
65: //遍歷下一個 SYSTEM_PROCESS_INFORMATION 節點
66: pPrevProcessInfo = pCurrProcessInfo;
67:
68: //遍歷結束
69: if(pCurrProcessInfo->NextEntryOffset)
70: {
71: pCurrProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
72: (((PCHAR)pCurrProcessInfo) + pCurrProcessInfo->NextEntryOffset);
73: }
74: else
75: {
76: pCurrProcessInfo = NULL;
77: }
78: }
79: }
80: }
81: return rtStatus;
82: }
既然有了自己的 Hook NtQuerySystemInformation 了,自然我們就可以通過利用 SSDT 框架來實現 Hook 了,
這部分的代碼其實是最簡單的,因為我只需要在 DriverEntry 中 Hook 掉 NtQuerySystemInformation 即可,
這里需要注意的是,在執行 Hook 之前需要備份一次 SSDT,即在 DriverEntry 中最先需要備份 SSDT ~
當然為了保證系統的安全以及其他諸多方面,我們在 DriverUnload 中會將 Hook 解除掉 ~
從下面的代碼中,我們看到在安裝 Hook 和解除 Hook 時參數傳遞進去的是 ZwQuerySystemInformation,
這樣很有可能會讓很多朋友認為我們在 Ring0 下的 Hook 的是 ZwQuerySystemInformation,
如果你這樣認為的話,那就大錯特錯了,確實在 Google 上搜索出的一大堆關於 SSDT Hook 中,
很多文章都說是 Hook 的 ZwQuerySystemInformation,而事實上這是大錯特錯的,
我們這里傳入 ZwQuerySystemInformation ,是因為我們需要調用 SYS_INDEX(ZwQuerySystemInformation)
來獲得 NtQuerySystemInformation 在 SSDT 中的地址所在的索引號,
然后我們根據這個索引號來 Hook NtQuerySystemInformation,
認識到這一點是非常重要的,因為我一開始也認為是 Hook 的 ZwQuerySystemInformation,
從而導致藍屏了 n 次,在這里非常鄙視那些把文章從別處拷貝過來也不加驗證就亂發表的 ~ 害死人 ~ 當然也要怪自己懶 ~
5. Ring0 實現進程保護:
有了上面實現進程隱藏的基礎,要再來實現進程保護,其實也就是過過場子了 ~
進程保護呢,上面也說了,是要 Hook NtTermianteProcess 這個系統服務 ~
由於 ZwTerminateProcess 呢,在 ntddk.h 中已經聲明了,
所以在我們自己的代碼中就不需要聲明 ZwTermianteProcess 了,
而只需要聲明 NtTerminateProcess 以及 Hook 函數就 OK 了 ~
1: typedef NTSTATUS (* NTTERMINATEPROCESS)(
2: __in_opt HANDLE ProcessHandle,
3: __in NTSTATUS ExitStatus
4: );
5:
6: NTSTATUS HookNtTerminateProcess(
7: __in_opt HANDLE ProcessHandle,
8: __in NTSTATUS ExitStatus
9: );
10:
11: NTTERMINATEPROCESS pOldNtTerminateProcess;
至於安裝 Hook 以及卸載 Hook ,都可以根據進程隱藏中的代碼來完成,因為有了 SSDT Hook 框架,
這一切也就變得很簡單了,只要在 DriverEntry 中 InstallHook ,然后再在 DriverUnload 中 UnInstallHook 即 OK ~
下面我們重點來看一看我們自己的 Hook NtTerminateProcess 中是如何實現進程保護的 ~
進程保護呢其實也是比較簡單的,因為從上面一層的調用會傳遞一個進程句柄下來,
而后我們可以根據這個進程句柄來獲得進程的 EPROCESS 對象(進程位於執行體層得對象),
通過這個 EPROCESS 對象,我們就可以獲得這個請求被結束的進程的 PID,
我們再判斷這個 PID 是否是我們已經保護了的 PID,如果是的話,直接返回一個請求被拒絕即可,
而如果這個 PID 未被保護,自然我們就交給原來的 NtTerminateProcess 處理即可 ~
1: //=====================================================================================//
2: //Name: NTSTATUS HookNtTerminateProcess() //
3: // //
4: //Descripion: 自定義的 NtTerminateProcess,用來實現 Hook Kernel API //
5: // //
6: //=====================================================================================//
7: NTSTATUS HookNtTerminateProcess(
8: __in_opt HANDLE ProcessHandle,
9: __in NTSTATUS ExitStatus
10: )
11: {
12: ULONG uPID;
13: NTSTATUS rtStatus;
14: PCHAR pStrProcName;
15: PEPROCESS pEProcess;
16: ANSI_STRING strProcName;
17:
18: //通過進程句柄來獲得該進程所對應的 FileObject 對象,由於這里是進程對象,自然獲得的是 EPROCESS 對象
19: rtStatus = ObReferenceObjectByHandle(ProcessHandle,
20: FILE_READ_DATA, NULL, KernelMode, &pEProcess, NULL);
21: if(!NT_SUCCESS(rtStatus))
22: {
23: return rtStatus;
24: }
25:
26: //保存 SSDT 中原來的 NtTerminateProcess 地址
27: pOldNtTerminateProcess =
28: (NTTERMINATEPROCESS)oldSysServiceAddr[SYSCALL_INDEX(ZwTerminateProcess)];
29:
30: //通過該函數可以獲取到進程名稱和進程 ID,該函數在內核中實質是導出的(在 WRK 中可以看到)
31: //但是 ntddk.h 中並沒有到處,所以需要自己聲明才能使用
32: uPID = (ULONG)PsGetProcessId(pEProcess);
33: pStrProcName = (PCHAR)PsGetProcessImageFileName(pEProcess);
34:
35: //通過進程名來初始化一個 ASCII 字符串
36: RtlInitAnsiString(&strProcName, pStrProcName);
37:
38: if(ValidateProcessNeedProtect(uPID) != -1)
39: {
40: //確保調用者進程能夠結束(這里主要是指 taskmgr.exe)
41: if(uPID != (ULONG)PsGetProcessId(PsGetCurrentProcess()))
42: {
43: //如果該進程是所保護的的進程的話,則返回權限不夠的異常即可
44: return STATUS_ACCESS_DENIED;
45: }
46: }
47:
48: //對於非保護的進程可以直接調用原來 SSDT 中的 NtTerminateProcess 來結束進程
49: rtStatus = pOldNtTerminateProcess(ProcessHandle, ExitStatus);
50:
51: return rtStatus;
52: }
6. 隱藏進程列表和保護進程列表的維護:
由於需要隱藏的進程以及需要被保護的進程都是由應用程序傳遞進來的,
也就是說這個內核程序是需要和應用程序通信的,自然就需要創建一個 Device,
然后我就采用了簡單的 DeviceIoControl 來實現了內核程序和應用程序的通信,
對於需要隱藏的進程或者是需要保護的進程,其由應用程序通過 DeviceIoControl 來將這個進程的 PID 傳遞給內核程序,
然后在內核程序中呢,維護了兩個數組,一個數組用來存放需要隱藏的進程的 PID,
另外一個數組自然就是用來存放需要保護的進程的 PID,
1: ULONG g_PIDHideArray[MAX_PROCESS_ARRARY_LENGTH];
2: ULONG g_PIDProtectArray[MAX_PROCESS_ARRARY_LENGTH];
3:
4: ULONG g_currHideArrayLen = 0;
5: ULONG g_currProtectArrayLen = 0;
為了維護上面的這兩個數組呢,又衍生出了幾個 API,即實現對數組中的 PID 進行增刪查 ~
1: //驗證 uPID 所代表的進程是否存在於隱藏進程列表中,即判斷 uPID 這個進程是否需要隱藏
2: ULONG ValidateProcessNeedHide(ULONG uPID);
3:
4: //驗證 uPID 所代表的進程是否存在於保護進程列表中,即判斷 uPID 這個進程是否需要保護
5: ULONG ValidateProcessNeedProtect(ULONG uPID);
6:
7: //往隱藏進程列表中插入 uPID
8: ULONG InsertHideProcess(ULONG uPID);
9:
10: //從隱藏進程列表中移除 uPID
11: ULONG RemoveHideProcess(ULONG uPID);
12:
13: //往保護進程列表中插入 uPID
14: ULONG InsertProtectProcess(ULONG uPID);
15:
16: //從隱藏進程列表中移除 uPID
17: ULONG RemoveProtectProcess(ULONG uPID);
對於前面談及的 HookNtQuerySystemInformation 和 HookNtTerminateProcess 的話,
需要判斷一個進程是否是需要被保護或者需要被隱藏的進程就是通過上面的數組來完成的,
即判斷一個進程是否需要被隱藏時,只需要判斷這個進程在隱藏列表中是否存在即可,
而對於實現進程保護的話,道理也是一樣的 ~
上面說過,應用程序和內核程序的通信是通過 DeviceIoControl 來完成的,
下面我們就來看看 DeviceIoControl 的代碼:
1: //=====================================================================================//
2: //Name: NTSTATUS SSDT01DeviceIoControlDispatcher() //
3: // //
4: //Descripion: 分發函數 //
5: // //
6: //=====================================================================================//
7: NTSTATUS SSDT01DeviceIoControlDispatcher(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
8: {
9: NTSTATUS rtStatus;
10:
11: ULONG uPID;
12: ULONG uInLen;
13: ULONG uOutLen;
14: ULONG uCtrlCode;
15:
16: PCHAR pInBuffer;
17:
18: PIO_STACK_LOCATION pStack;
19:
20: uPID = 0;
21: rtStatus = STATUS_SUCCESS;
22: pStack = IoGetCurrentIrpStackLocation(pIrp);
23:
24: uInLen = pStack->Parameters.DeviceIoControl.InputBufferLength;
25: uOutLen = pStack->Parameters.DeviceIoControl.OutputBufferLength;
26: uCtrlCode = pStack->Parameters.DeviceIoControl.IoControlCode;
27:
28: //使用緩沖區方式與應用程序進行通信
29: pInBuffer = (PCHAR)pIrp->AssociatedIrp.SystemBuffer;
30:
31: if(uInLen >= 4)
32: {
33: //stdlib.h(atol = Array To LONG)
34: uPID = atol(pInBuffer);
35:
36: switch(uCtrlCode)
37: {
38: case IO_INSERT_PROTECT_PROCESS:
39: {
40: if(InsertProtectProcess(uPID) == FALSE)
41: {
42: rtStatus = STATUS_PROCESS_IS_TERMINATING;
43: }
44: break;
45: }
46: case IO_REMOVE_PROTECT_PROCESS:
47: {
48: if(RemoveProtectProcess(uPID) == FALSE)
49: {
50: rtStatus = STATUS_PROCESS_IS_TERMINATING;
51: }
52: break;
53: }
54: case IO_INSERT_HIDE_PROCESS:
55: {
56: if(InsertHideProcess(uPID) == FALSE)
57: {
58: rtStatus = STATUS_PROCESS_IS_TERMINATING;
59: }
60: break;
61: }
62: case IO_REMOVE_HIDE_PROCESS:
63: {
64: if(RemoveHideProcess(uPID) == FALSE)
65: {
66: rtStatus = STATUS_PROCESS_IS_TERMINATING;
67: }
68: break;
69: }
70: default:
71: {
72: rtStatus = STATUS_INVALID_VARIANT;
73: break;
74: }
75: }
76: }
77: else
78: {
79: rtStatus = STATUS_INVALID_PARAMETER;
80: }
81:
82: //輸出信息總是為空,即該驅動程序不返回輸出信息
83: pIrp->IoStatus.Status = rtStatus;
84: pIrp->IoStatus.Information = 0;
85: IoCompleteRequest(pIrp, IO_NO_INCREMENT);
86:
87: return rtStatus;
88: }
7. 小結:
這篇博文呢,是承接前一篇博文《進程隱藏與進程保護(SSDT Hook 實現)(一)》來的,
前面的博文主要介紹了 SSDT 是個什么東西,以及我們做內核 Hook 的一些基礎,
而這篇博文則完整的介紹了 SSDT Hook 的具體實現,其中涉及到了很多底層的知識的,
對於絕大部分的代碼呢,大伙是可以參考代碼來進行理解的,而后我會將內核部分的代碼先公開出來 ~
本來呢是打算將這個 SSDT Hook 做兩篇博文就給結束得了,
不過第二篇博文寫了這么長了,但是在應用程序中的實現都還沒有開始介紹,
而今晚真的又太晚了,再寫下去天就亮了,還說要盡量不熬夜的 ~ 唉 ~ 算了,權當周末給自己找個借口吧 ~
下一篇博文將介紹的是如何在應用程序中獲取到所有的進程啊,以及應用程序如何和內核程序設備進行通信之類的知識,
知識重點是放在 Ring3 了,其中不會涉及到很多 Ring0 的內容了 ~
開發工具以及環境搭建:
Visual Studio 2010 + VirtualDDK + WDK + VMware + Windows Service 2003 SP1,至於具體環境的搭建,
可以參考我的博文《驅動程序環境搭建(VS2010 + WDK + VirtualDDK + VMware)》,博文地址如下:
http://www.cnblogs.com/BoyXiao/archive/2011/07/31/2122755.html
和前一篇文章一樣,我將我近來遇到的一些疑問放到博文的最后面,看是否有看官遇到過,如果有遇到過的,
還請不吝賜教 ~ 在下感激不盡 ~
疑問 1:
在 .Net WinForm 中,我有一個定時器,然后又有一個按鈕,
那么對應的就有一個定時器 Tick 處理事件,按鈕也會有一個 Click 事件,
考慮在多核處理器上,有沒有這樣一種可能,按鈕的 Click 事件和 Tick 處理事件並發執行 ?
如果有的話,大伙一般都是如何做處理的 ?
疑問 2:
大家都有用過 SVN 的,當我們安裝好客戶端工具 TortoiseSVN 后,
每次啟動 PC ,均會有一個 TSVNCache.exe 的進程隨 PC 自動啟動,
請教一下大家這個進程是如何啟動的 ?
版權所有,歡迎轉載,但轉載請注明: 轉載自 Zachary.XiaoZhen - 夢想的天空