在 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 ~
进程保护效果:
进程保护(保护自身进程 SSDTProcess.exe):
取消进程保护(这里还是以 SSDTProcess.exe 为例):
下面的截图表示 SSDTProcess.exe 已经被取消了保护,
此时再到任务管理器中结束 SSDTProcess.exe 时,你可以发现是可以正常结束这个进程的 ~
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 中都没有声明,
所以需要在我们自己的代码中手动声明 ~
NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation ( __in SYSTEM_INFORMATION_CLASS SystemInformationClass, __out_bcount_opt(SystemInformationLength) PVOID SystemInformation, __in ULONG SystemInformationLength, __out_opt PULONG ReturnLength ); typedef NTSTATUS (* NTQUERYSYSTEMINFORMATION)( __in SYSTEM_INFORMATION_CLASS SystemInformationClass, __out_bcount_opt(SystemInformationLength) PVOID SystemInformation, __in ULONG SystemInformationLength, __out_opt PULONG ReturnLength );
完成了这些声明后,我们就可以来实现自己的 NtQuerySystemInformation Hook 函数了,
在这个 Hook 函数中,我们需要对我们感兴趣的进程进行隐藏 ~
然后这里需要注意的是,我是如何来实现对进程隐藏的,
首先我是判断这个进程的 ID 是否是需要隐藏的进程 ID,
这是通过 ValidateProcessNeedHide 函数来判断的 ~ 这个函数会在后面给出 ~
注意结合代码中的注释来看(虽然注释比较少 ~ 嘿嘿 ~ )
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: }