前言
通常情况下驱动想要主动与应用层通讯,都是借助于内核同步对象(比如事件、信号量等)来实现,
但是使用内核同步对象对 IRQL
有限制,某些特殊环境下是 APC_LEVEL
级别,这就产生了一些问题。
内核同步对象
我们以内核同步对象中的 事件(Event)
为例,可以在MSDN文档中查看 ZwSetEvent
和 KeSetEvent
的相关说明,其中 ZwSetEvent
只能运行在 PASSIVE_LEVEL
级别下,而 KeSetEvent
则是说明如下
可以知道 KeSetEvent
适应的范围更广,由于 KeSetEvent
操作的不是句柄,把内核中的事件跟应用层
关联起来的方法,就是使用 IoCreateNotificationEvent
和 IoCreateSynchronizationEvent
来
创建跟应用层 相同名称
的事件,可以获取到对应的句柄和内核对象。
注意:先从应用层创建,再从内核层打开,两者具有相同权限。如果先从内核层创建,再从应用层打开,
应用层只能 WaitForSingleObject
而没有修改的权限。
1 2 3 4 5 6 7 8 9
| PKEVENT IoCreateNotificationEvent( _In_ PUNICODE_STRING EventName, _Out_ PHANDLE EventHandle );
PKEVENT IoCreateSynchronizationEvent( _In_ PUNICODE_STRING EventName, _Out_ PHANDLE EventHandle );
|
MiniFilter通讯端口
MiniFilter跟应用层通讯,可以在 APC_LEVEL
级别下进行,某些情况下可以选择此通讯方式。
本例子以WDK源码中 src -> filesys -> miniFilter -> minispy
工程作为基础。
创建驱动与应用层通讯的端口,需要安全描述符,如下为相关创建和释放的函数
1 2 3 4
| NTSTATUS FltBuildDefaultSecurityDescriptor( _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, _In_ ACCESS_MASK DesiredAccess );
|
1 2 3
| VOID FltFreeSecurityDescriptor( _In_ PSECURITY_DESCRIPTOR SecurityDescriptor );
|
创建好安全描述符后,就可以使用描述符创建和关闭通讯端口
1 2 3 4 5 6 7 8 9 10
| NTSTATUS FltCreateCommunicationPort( _In_ PFLT_FILTER Filter, _Out_ PFLT_PORT *ServerPort, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PVOID ServerPortCookie, _In_ PFLT_CONNECT_NOTIFY ConnectNotifyCallback, _In_ PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback, _In_opt_ PFLT_MESSAGE_NOTIFY MessageNotifyCallback, _In_ LONG MaxConnections );
|
1 2 3
| VOID FltCloseCommunicationPort( _In_ PFLT_PORT ServerPort );
|
1 2 3 4
| VOID FltCloseClientPort( _In_ PFLT_FILTER Filter, _Out_ PFLT_PORT *ClientPort );
|
当应用层连接驱动的通讯端口时,就会触发 ConnectNotifyCallback
回调,在这里我们要保存
ClientPort
信息,给驱动主动往应用层发送数据时用。当应用层关闭与驱动连接的通讯时,就会
触发 DisconnectNotifyCallback
回调,这里我们要使用 FltCloseClientPort
关闭 ClientPort
,
当应用层主动向驱动发送数据时,就会触发 MessageNotifyCallback
回调,我们在这里进行数据操作。
1 2 3 4
| PFLT_FILTER FilterHandle = NULL; PFLT_PORT FilterServerPort = NULL; PFLT_PORT FilterClientPort = NULL;
|
如下为连接驱动通讯端口时 ConnectNotifyCallback
回调函数的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| NTSTATUS ConnectCallback( IN PFLT_PORT ClientPort, IN PVOID ServerPortCookie, IN PVOID ConnectionContext, IN ULONG SizeOfContext, OUT PVOID *ConnectionPortCookie) { UNREFERENCED_PARAMETER(ServerPortCookie); UNREFERENCED_PARAMETER(ConnectionContext); UNREFERENCED_PARAMETER(SizeOfContext); UNREFERENCED_PARAMETER(ConnectionPortCookie); FilterClientPort = ClientPort; return STATUS_SUCCESS; }
|
如下为关闭与驱动连接的通讯时 DisconnectNotifyCallback
回调函数的处理
1 2 3 4 5
| VOID DisconnectCallback(IN PVOID ConnectionCookie) { UNREFERENCED_PARAMETER(ConnectionCookie); FltCloseClientPort(FilterHandle, &FilterClientPort); }
|
如下为主动向驱动发送数据时 MessageNotifyCallback
回调函数的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| NTSTATUS MessageCallback( IN PVOID PortCookie, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength, OUT PULONG ReturnOutputBufferLength) { UCHAR Command = 0; UNREFERENCED_PARAMETER(PortCookie); if ((InputBuffer == NULL) || (InputBufferLength != 1)) return STATUS_INVALID_PARAMETER; if ((OutputBuffer == NULL) || (OutputBufferLength != 1)) return STATUS_INVALID_PARAMETER; if ((ReturnOutputBufferLength == NULL)) return STATUS_INVALID_PARAMETER; try { Command = *((PUCHAR)InputBuffer); } except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } Command++; try { *((PUCHAR)OutputBuffer) = Command; } except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } *ReturnOutputBufferLength = 1; return STATUS_SUCCESS; }
|
如下为驱动入口函数注册通讯端口的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { NTSTATUS Status = STATUS_SUCCESS; PSECURITY_DESCRIPTOR pDescriptor = NULL; OBJECT_ATTRIBUTES Attributes = { 370030 }; UNICODE_STRING PortName = RTL_CONSTANT_STRING(L"MyCommPort"); UNREFERENCED_PARAMETER(RegistryPath); Status = FltRegisterFilter(DriverObject, &Registration, &FilterHandle); if (!NT_SUCCESS(Status)) return Status; Status = FltBuildDefaultSecurityDescriptor(&pDescriptor, FLT_PORT_ALL_ACCESS); if (!NT_SUCCESS(Status)) { FltUnregisterFilter(FilterHandle); return Status; } InitializeObjectAttributes(&Attributes, &PortName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, pDescriptor); Status = FltCreateCommunicationPort(FilterHandle, &FilterServerPort, &Attributes, NULL, ConnectCallback, DisconnectCallback, MessageCallback, 1); FltFreeSecurityDescriptor(pDescriptor); if (!NT_SUCCESS(Status)) { FltUnregisterFilter(FilterHandle); return Status; } Status = FltStartFiltering(FilterHandle); if (!NT_SUCCESS(Status)) { FltCloseCommunicationPort(FilterServerPort); FltUnregisterFilter(FilterHandle); return Status; } return STATUS_SUCCESS; }
|
应用层向驱动通讯通讯时,连接通讯端口和发送数据的函数定义如下
1 2 3 4 5 6 7 8
| HRESULT FilterConnectCommunicationPort( _In_ LPCWSTR lpPortName, _In_ DWORD dwOptions, _In_opt_ LPCVOID lpContext, _In_ WORD dwSizeOfContext, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, _Out_ HANDLE *hPort );
|
1 2 3 4 5 6 7 8
| HRESULT FilterSendMessage( _In_ HANDLE hPort, _In_opt_ LPVOID lpInBuffer, _In_ DWORD dwInBufferSize, _Out_ LPVOID lpOutBuffer, _In_ DWORD dwOutBufferSize, _Out_ LPDWORD lpBytesReturned );
|
实际使用的示例代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| BOOL CommunicationToDriverTest() { HANDLE Port = NULL; HRESULT Result = FilterConnectCommunicationPort( L"MyCommPort", 0, NULL, 0, NULL, &Port); if (Result != S_OK) return FALSE; DWORD dwRet = 0; UCHAR InBuffer = 55; UCHAR OutBuffer = 0; Result = FilterSendMessage( Port, &InBuffer, sizeof(UCHAR), &OutBuffer, sizeof(UCHAR), &dwRet); CloseHandle(Port); if (Result != S_OK) return FALSE; return TRUE; }
|
驱动向应用层通讯
驱动向应用层通讯的工作方式,与Windows的消息循环模式类似,需要先在应用层创建消息循环体,
然后驱动再发送给应用层。并且应用层在读取和回复消息时,需要增加一个消息头结构体,如下所示
1 2 3 4
| typedef struct _FILTER_MESSAGE_HEADER { ULONG ReplyLength; ULONGLONG MessageId; } FILTER_MESSAGE_HEADER, *PFILTER_MESSAGE_HEADER;
|
1 2 3 4
| typedef struct _FILTER_REPLY_HEADER { NTSTATUS Status; ULONGLONG MessageId; } FILTER_REPLY_HEADER, *PFILTER_REPLY_HEADER;
|
我们这里做如下示例定义
1 2 3 4
| typedef struct _MY_GET_MSG { FILTER_MESSAGE_HEADER Header; ULONG ProcessID; } MY_GET_MSG, *PMY_GET_MSG;
|
1 2 3 4
| typedef struct _MY_REPLAY_MSG { FILTER_REPLY_HEADER Header; ULONG ProcessID; } MY_REPLAY_MSG, *PMY_REPLAY_MSG;
|
如下为实际使用时的示例代码,需要注意的是,编译器有个 结构体自动对齐
的功能,以如上定义的
结构体为例,尽管我们通讯的 ProcessID
是 ULONG
类型,但是整个结构体会对齐到 ULONGLONG
大小,
如果我们使用整个结构体的大小,就会超出驱动中能接收的 sizeof(ULONG)
大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| BOOL GetDriverCommunicationTest() { HANDLE Port = NULL; HRESULT Result = FilterConnectCommunicationPort( L"MyCommPort", 0, NULL, 0, NULL, &Port); if (Result != S_OK) return FALSE; MY_GET_MSG stGetMsg = { 0 }; MY_REPLAY_MSG stReplayMsg = { 0 }; while (1) { memset(&stGetMsg, 0, sizeof(MY_GET_MSG)); Result = FilterGetMessage( Port, &stGetMsg.Header, sizeof(MY_GET_MSG), NULL); if (Result != S_OK) break; stReplayMsg.Header.Status = STATUS_SUCCESS; stReplayMsg.Header.MessageId = stGetMsg.Header.MessageId; stReplayMsg.ProcessID = stGetMsg.ProcessID + 1; Result = FilterReplyMessage( Port, &stReplayMsg.Header, sizeof(FILTER_REPLY_HEADER) + sizeof(ULONG)); if (Result != S_OK) break; } CloseHandle(Port); if (Result != S_OK) return FALSE; return TRUE; }
|