突破 Windows NT 内核进程监视设置限制

简介: 文章作者:whitecell (sinister_at_whitecell.org)文章来源:安全焦点(www.xfocus.net)Author: PolyMetaEmail: PolyMeta@whitecell.orgHomepage:http://www.whitecell.org Date: 2007-06-10监视进程创建和销毁,最常用的手段就是用 PsSetCreateProcessNotifyRoutine() 设置一个CALLBACK函数来完成。
文章作者:whitecell (sinister_at_whitecell.org)
文章来源:安全焦点(www.xfocus.net)

Author: PolyMeta
Email: PolyMeta@whitecell.org
Homepage: http://www.whitecell.org
Date: 2007-06-10

监视进程创建和销毁,最常用的手段就是用 PsSetCreateProcessNotifyRoutine()
设置一个CALLBACK函数来完成。该函数的原形如下:

VOID
(*PCreate_PROCESS_NOTIFY_ROUTINE) (
IN HANDLE ParentId,
IN HANDLE ProcessId,
IN BOOLEAN Create
);
NTSTATUS
PsSetCreateProcessNotifyRoutine(
IN PCreate_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
IN BOOLEAN Remove
);

安全类软件,诸如防火墙,AV,包括系统自身也在用这种方法来监视进程。不要问我为啥,有
文档的东西,大家都喜欢用。问题出来了,你有没有想过自己的监控回调函数就一定能设置成
功吗?很不幸的告诉你,不一定。特别是在非正规软件充斥internet的今天。原因很简单:
PsSetCreateProcessNotifyRoutine 最多只能设置8个回调函数,这点很多玩内核的人都知道。
然而狼多肉少,大家都想通过进程创建监控对付自己脑海中的"敌人".于是乎,自古以来抢地盘
的战争又在这里爆发了。

最残忍的抢地盘手段:为了自己的回调函数能够正常设置成功,干脆把之前其它软件设置
的回调一并干掉,也不管是正规或者非正规软件,反正挡路者死!这下好,用户倒霉了,很多
软件包括防火墙咋不好使了呢?

作为正规软件或者有道德的非正规软件作者就开始郁闷了,既要让自己的软件生效,又要
不影响用户的正常使用。难题既然出来了,就得想出来解决办法。于是

NTSTATUS
PsSetCreateProcessNotifyRoutineMustSuccess(
IN PCreate_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
IN BOOLEAN Remove
);

这个函数就诞生,该函数被笔者封装到了一个lib里面供兄弟们写驱动的时候使用,用以代替原始
的 PsSetCreateProcessNotifyRoutine 函数,使用方法和它一模一样。该函数的特点就是可以使
通过它设置的进程监控回调函数,无论在8个蹲位是否已经被占满的情况下都可以设置成功,并且
可以和之前由其它软件设置的进程监控回调函数和平共存。

工作原理:

PsSetCreateProcessNotifyRoutineMustSuccess(NotifyRoutine)
||
||获得PspCreateProcessNotifyRoutine地址
//
GetPspCreateProcessNotifyRoutine()
||
||调用原始函数
//
PsSetCreateProcessNotifyRoutine(NotifyRoutine) 调用GetFastRefObject()
|| /---------->pCallBackRoutineBlock
设置成功 || 设置失败则直接操作 保存 | ||
//------------//----------->PspCreateProcessNotifyRoutine[1]-----> 或 ||成员Function
|| || | //
|| ||替换为 /---------->pOldNotifyRoutine
// || ^
over 调用 // 再调用 |
[新进程创建]----或---------------->AgentProcessMonitor----------------------------/
| ||
| ||先调用
| 调用 //
/-----------------> NotifyRoutine

也不知道画的流程清晰不,核心的工作原理就是真对PspCreateProcessNotifyRoutine
[PSP_MAX_Create_PROCESS_NOTIFY] 这个有8个蹲位的
(#define PSP_MAX_Create_PROCESS_NOTIFY 8 ) 数组进行操作而已。实质上
PsSetCreateProcessNotifyRoutine 这个函数也就是对 PspCreateProcessNotifyRoutine
数组进行操作,把用户设置的NotifyRoutine给存到这个数组里面,当有新进程创建的时候,
系统就会从这个数组中取得回调函数NotifyRoutine,并且调用之。这也就是为什么我们最
多只能设置8个回调函数的原因了。

既然用 PsSetCreateProcessNotifyRoutine 设置失败,那么我们只好自己操作
PspCreateProcessNotifyRoutine 这个数组了,为我们的CALLBACK求得一个容身之处。
之前我们调用了 GetPspCreateProcessNotifyRoutine,该函数的目的就是要找到
PspCreateProcessNotifyRoutine 数组的地址,方便我们之后的DIY,嘿嘿。

找到PspCreateProcessNotifyRoutine了,就可以开始DIY了。我们的LIB中用了第二
个坑,即 PspCreateProcessNotifyRoutine[1] 兄弟们如果要自己实现的话,当然可以随
便选择位置。这里需要注意的是2000和xp以后系统的区别,在2000的系统上,
PsSetCreateProcessNotifyRoutine 直接将 NotifyRoutine 回调函数的地址存到了
PspCreateProcessNotifyRoutine 数组的坑里,因此我们可以直接保存和替换先前别的软件
设置的回调函数。而xp之后进行了一些改进,出于安全性的考虑,
PspCreateProcessNotifyRoutine 不再直接存放回调函数的地址,而是存放了一个叫
FastRef 的结构,其大小也是一个DWORD,内容是一个 PEX_CALLBACK_ROUTINE_BLOCK 类型
的指针+该指针的引用记数,而真正的 NotifyRoutine 回调函数的地址被存放在了
PEX_CALLBACK_ROUTINE_BLOCK->Function 成员里。函数 GetFastRefObject 就是为了从
FastRef 结构里得到真正的 PEX_CALLBACK_ROUTINE_BLOCK 类型的指针 pCallBackRoutineBlock,
然后通过 pCallBackRoutineBlock->Function 就可以像 2000 上一样方便的保存和替换先前别
的软件设置的回调函数了。

现在我们可以方便的替换已有的回调函数了,为了兼容被替换掉的回调函数,与其和平
共处,我们需要在自己的回调函数中调用调用一下之前被我们替换掉的回调。我们要提供的
是一个新的库函数,要做到对调用者的透明,当然不能要求函数的使用者在自己的回调函数
中调用之前被替换掉的回调函数,所以我们在库的内部提供这么一个代理回调
AgentProcessMonitor,真正替换原始回调的是这个代理函数而不是用户设置的 NotifyRoutine。
接下来我们在 AgentProcessMonitor 中先调用新设置的 NotifyRoutine,再调用原始的被
替换掉的那个函数,就能实现和平共处了。

VOID
AgentProcessMonitor(
IN HANDLE ParentId,
IN HANDLE ProcessId,
IN BOOLEAN Create
)
{
if( pNewNotifyRoutine)
{
pNewNotifyRoutine(ParentId,ProcessId,Create);
}
if(MmIsAddressValid((PVOID)pOldNotifyRoutine))
{
if(memcmp((PVOID)pOldNotifyRoutine,pSigCode,0x40)==0)
{
pOldNotifyRoutine(ParentId,ProcessId,Create);
}
}
}

兄弟们可能奇怪了,MmIsAddressValid((PVOID)pOldNotifyRoutine) 和
memcmp((PVOID)pOldNotifyRoutine,pSigCode,0x40)==0 是干嘛的。因为我们用
AgentProcessMonitor 替换掉了原始的回调函数 pOldNotifyRoutine,这样会导致设置
pOldNotifyRoutine 该回调函数的驱动卸载这个回调的时候失败,但这时驱动已经被卸
载掉了,如果我们还继续调用 pOldNotifyRoutine,后果可想而之,所以我们再调用前
要判断一下。接下来如果又有新驱动被加载起来,并且占用了 pOldNotifyRoutine 指
向的地址,那么继续调用 pOldNotifyRoutine 也会死,所以我们还得判断一下这个地
址入口的若干字节是否为先前那个驱动的代码,然后才能调它。


该函数在xpsp2和2000sp4下测试通过。

感谢大牛dingkai与我讨论,给予我一些提醒。

进程监视库下载

WSS(Whitecell Security Systems),一个非营利性民间技术组织,致力于各种系统安全技术的研究。坚持传统的hacker精神,追求技术的精纯。
WSS 主页: http://www.whitecell.org/
WSS 论坛: http://www.whitecell.org/forums/ 
 
目录
相关文章
|
1月前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
69 4
|
1月前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
1月前
|
算法 调度
探索操作系统的心脏:内核与进程管理
【10月更文挑战第25天】在数字世界的复杂迷宫中,操作系统扮演着关键角色,如同人体中的心脏,维持着整个系统的生命力。本文将深入浅出地剖析操作系统的核心组件——内核,以及它如何通过进程管理来协调资源的分配和使用。我们将从内核的概念出发,探讨它在操作系统中的地位和作用,进而深入了解进程管理的机制,包括进程调度、状态转换和同步。此外,文章还将展示一些简单的代码示例,帮助读者更好地理解这些抽象概念。让我们一起跟随这篇文章,揭开操作系统神秘的面纱,理解它如何支撑起我们日常的数字生活。
|
2月前
|
Java 关系型数据库 MySQL
java控制Windows进程,服务管理器项目
本文介绍了如何使用Java的`Runtime`和`Process`类来控制Windows进程,包括执行命令、读取进程输出和错误流以及等待进程完成,并提供了一个简单的服务管理器项目示例。
42 1
|
3月前
|
存储 开发者 Windows
WINDOWS 环境变量设置方法
本文旨在帮助使用Windows电脑的开发者们为其设备配置环境变量,以更好地支持大模型应用的开发工作。文中详细介绍了三种配置方法:一是将环境变量设置为系统级变量;二是在命令行界面通过`SET`命令或`PowerShell`临时设置变量;三是借鉴MAC的方式,创建全局环境变量文件`.zshrc`进行配置。这些方法简单实用,便于根据实际需求选择适合的方式进行配置。
|
3月前
|
算法 调度 Python
探索操作系统的内核——一个简单的进程调度示例
【9月更文挑战第17天】在这篇文章中,我们将深入探讨操作系统的核心组件之一——进程调度。通过一个简化版的代码示例,我们将了解进程调度的基本概念、目的和实现方式。无论你是初学者还是有一定基础的学习者,这篇文章都将帮助你更好地理解操作系统中进程调度的原理和实践。
|
4月前
|
调度 虚拟化 容器
探索操作系统的心脏:内核与进程管理
【8月更文挑战第28天】在数字世界的复杂迷宫中,操作系统扮演着关键角色。它如同一座桥梁,连接硬件与软件,确保一切顺畅运行。本文将深入剖析操作系统的核心——内核和进程管理,揭示它们如何协同工作,保障系统的稳定与高效。通过简化的比喻,我们将一探究竟,了解操作系统背后的神秘面纱。
|
4月前
|
算法 安全 调度
揭秘操作系统的心脏:内核与进程管理
【8月更文挑战第27天】在数字世界的庞大机器中,操作系统扮演着至关重要的角色。本文将深入探讨操作系统的核心组件之一——内核,以及它是如何高效地管理和调度进程的。通过浅显易懂的语言和生动的比喻,我们将一起探索这一技术领域的奥秘,并了解其对整个计算生态的影响。无论你是技术新手还是资深爱好者,这篇文章都将为你打开一扇了解操作系统深层工作机制的大门。
|
4月前
|
安全 Windows
【Azure云服务 Cloud Service】Cloud Service的实例(VM)中的服务描述Software Protection 与 Windows Defender, 如何设置Windows Defender Antivirus服务
【Azure云服务 Cloud Service】Cloud Service的实例(VM)中的服务描述Software Protection 与 Windows Defender, 如何设置Windows Defender Antivirus服务
|
4月前
|
Java Windows
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?