前段时间一直在学习内核监控进程创建的知识,虽然成功监视,但一直在内核输出到 DebugView 中,不能通知我们的应用程序来显示指定内容,无论如何也不方便,所以赶在周末参考了 Windows 内核安全与驱动开发
中第五章 “应用与内核通讯” 制作了以下程序。程序主要使用了内核事件 KEVENT
实现同步,更多请参考 Windows 内核安全与驱动开发
,程序运行后的效果如下:
可以看到,程序可以成功运行在 Win10x64 环境下,下面我们分别仔细的讲解一下程序的细节。程序的主要工作流程如下图: 先来看一下 DriverEntry 入口函数。函数中先创建了进程创建后的回调监听函数,我们在这个函数里面实现对进程的监控。随后初始化了链表锁、链表头(用于存放已经创建的进程数据)及事件句柄。最后入口函数设置了各个通知函数:
1 | NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) |
接下来我们看一下创建进程会调用的回调函数中,我们设定若发现新进程创建,则将进程的信息作为链表一个节点插入到链表中,并设置全局的 g_Event
为有信号状态。这里一定要注意 KeSetEvent 函数的使用,如果第三个参数设置为 TRUE 的话,100% 会蓝屏,代码为 0x0000004A。
1 | VOID CreateProcessNotifyEx( |
此时若有新进程创建全局的 g_Event
会被设置为有信号状态,接下来就到我们处理应用层使用 DeviceIoControl 在驱动中的响应功能了。我们设置了一个无限循环,一直从链表中取数据,若取出的数据为 NULL,则等待 g_Event
,当 g_Event
为有信号状态时,证明有新进程创建了,那么 KeWaitForSigleObject 立即返回执行下一次循环,这次循环就可以取到链表中的节点信息了,取到以后直接拷贝给应用层提供的 Buffer 中。应用层接收并打印内容。代码如下:
1 | NTSTATUS DeviceControlCompleteRoutine(PDEVICE_OBJECT pDeviceObject, PIRP pIrp) |
上面是驱动层的实现,我们来看一下应用层的示例代码。通过 CreateFile 打开设备,并调用 DeviceIoControl 函数向驱动发送一个接收数据的请求。此时如果驱动链表中没有数据,那么会停在 KeWaitForSingleObject 函数,同时应用层也阻塞在 DeviceIoControl 函数上。一旦 g_Event
被设置为有信号状态,则 KeWaitForSingleObject 返回,拷贝数据给应用层提供的 Buffer,应用层接收数据打印。
1 | // TestCommunication.cpp : 定义控制台应用程序的入口点。 |
这里请注意应用层和驱动层使用了相同的 PROCESSINFO
结构体,该结构体包含了创建进程的各种信息,我们使用一个公共头文件保存他,所有代码请参考 github:https://github.com/nmgwddj/Learn-Windows-Drivers,其中 Communication 是驱动层的实现,TestCommunication 是应用层的实现。