内核事件KEVENT(同步)

Posted 刘收获

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核事件KEVENT(同步)相关的知识,希望对你有一定的参考价值。

转载请您注明出处:http://www.cnblogs.com/lsh123/p/7358702.html

 

一.驱动程序与驱动程序的事件交互    IoCreateNotificationEvent ———> IoCreateNotificationEvent

  

  在内核驱动中可以通过给某个内核对象创建一个命名对象,然后在另一个驱动中通过名字来获取这个对象,然后操作它来实现两个驱动之间的内核对象的通讯,针对事件对象来说,要实现两个驱动交互事件对象,通过这样几步: 


1. 在驱动Server中调用IoCreateNotificationEvent或者IoCreateSynchronizationEvent来创建一个通知事件对象或者同步事件对象 
2. 在驱动Client中调用 IoCreateNotificationEvent或者IoCreateSynchronizationEvent获取已经有名字的内核对象的句柄 ,设置事件的激发状态
(3. 在驱动B中调用ObReferenceObjectByHandle根据上面两个函数返回的句柄来获取A中的事件对象,并操作它) 

 

源代码:

Server.c

#include <ntifs.h>
#define EVENT_NAME  L"\\\\BaseNamedObjects\\\\ServerKernelEvent"

void ThreadProcedure(PVOID ParameterData);
VOID DriverUnload(PDRIVER_OBJECT DriverObject);

PKEVENT __Event;
HANDLE __EventHandle;

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
	NTSTATUS Status = STATUS_SUCCESS;
	UNICODE_STRING  EventName;
	PDEVICE_OBJECT  DeviceObject = NULL;
	HANDLE   ThreadHandle = NULL;
	CLIENT_ID       ClientID = { 0 };



	DriverObject->DriverUnload = DriverUnload;
	

	RtlInitUnicodeString(&EventName, EVENT_NAME);
	DriverObject->DriverUnload = DriverUnload;


	__Event = IoCreateNotificationEvent(
		&EventName,                    //自定义事件名
		&__EventHandle);               //返回的事件句柄
	KeResetEvent(__Event);


	Status = PsCreateSystemThread(&ThreadHandle, 0, NULL, NtCurrentProcess(), &ClientID,
		(PKSTART_ROUTINE)ThreadProcedure,NULL);



	return Status;
}

void ThreadProcedure(PVOID ParameterData)
{
	NTSTATUS Status;
	KeWaitForSingleObject(__Event, Executive, KernelMode, FALSE, NULL);
	DbgPrint("ThreadProcedure() Exit\\r\\n");
	PsTerminateSystemThread(STATUS_SUCCESS);
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
	DbgPrint("DriverUnload()\\r\\n");
	if (__EventHandle != NULL)
	{
		KeClearEvent(__Event);

		ZwClose(__EventHandle);

		__EventHandle = NULL;
		__Event = NULL;
	}

}

 

Client.c

#include <ntifs.h>

#define EVENT_NAME  L"\\\\BaseNamedObjects\\\\ServerKernelEvent"
VOID DriverUnload(PDRIVER_OBJECT DriverObject);

PKEVENT __Event;
HANDLE __EventHandle;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
	NTSTATUS Status = STATUS_SUCCESS;
	UNICODE_STRING  EventName;
	PDEVICE_OBJECT  DeviceObject = NULL;
	DriverObject->DriverUnload = DriverUnload;
	RtlInitUnicodeString(&EventName, EVENT_NAME);
	__Event = IoCreateNotificationEvent(&EventName, &__EventHandle);    //获取已经有名字的内核对象的句柄
	KeSetEvent(__Event, IO_NO_INCREMENT, FALSE);                        //设置激发态
	return Status;
}


VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
	DbgPrint("DriverUnload()\\r\\n");
	if (__EventHandle != NULL)
	{
		KeClearEvent(__Event);
		ZwClose(__EventHandle);
		__EventHandle = NULL;
		__Event = NULL;
	}

}

  

二.驱动程序与应用程序的事件交互(驱动程序创建事件——>应用程序设置事件)IoCreateNotificationEvent ——> OpenEvent

  应用程序中创建的事件和在内核模式下创建的事件对象本质上是同一个东西。用户模式下,它用句柄代表,在内核模式下,它用KEVENT数据结构代表。
应用程序中,所有内核对象都不会被用户看到,用户看到的只是代表内核对象的对象句柄。

  __Event = IoCreateNotificationEvent(&EventName, &__EventHandle); //DriverEntry 进程回调通知


  EventHandle = OpenEvent(
    SYNCHRONIZE, //请求访问权限
    FALSE, // 不继承
    L"Global\\\\Ring0KernelEvent"); //事件对象名称

  1.驱动程序IoCreateNotificationEvent创建事件
  2.应用程序OpenEvent得到事件句柄

 

Ring3.cpp

 

// Ring3(设置).cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;

int main()
{
	
	HANDLE EventHandle = NULL;


	while (TRUE)
	{
		EventHandle = OpenEvent(
			SYNCHRONIZE,          //请求访问权限
			FALSE,                // 不继承
			L"Global\\\\Ring0KernelEvent"); //事件对象名称


		if (EventHandle == NULL)
		{
			continue;
		}

		break;
	}

	cout << "Ring3等待" << endl;
	while (TRUE)
	{

		int Index = WaitForSingleObject(EventHandle, 3000);

		Index = Index - WAIT_OBJECT_0;

		if (Index == WAIT_TIMEOUT)
		{

			//注意这里当驱动卸载并关闭事件时事件对象是不能够得到及时的销毁 因为应用层占用了该对象 
			//所以我们长时间等待不到授信 就关闭并重新打开
			if (EventHandle != NULL)
			{
				CloseHandle(EventHandle);
				EventHandle = NULL;
				EventHandle = OpenEvent(SYNCHRONIZE, FALSE, L"Global\\\\Ring0KernelEvent");

				if (EventHandle == NULL)
				{
					cout << "对象已经不存在" << endl;
					break;
				}
			}



			continue;
		}

		if (Index == 0)            //有信号状态
		{
			cout << "Ring0触发Ring3" << endl;
		}


		if (Index == WAIT_FAILED)
		{
			break;
		}

		Sleep(1);
	}


	cout << "Input AnyKey To Exit" << endl;

	getchar();
	if (EventHandle != NULL)
	{
		CloseHandle(EventHandle);
		EventHandle = NULL;

	}
	return 0;
}

  

Ring0.c

#include <ntifs.h>

#define EVENT_NAME  L"\\\\BaseNamedObjects\\\\Ring0KernelEvent"
VOID DriverUnload(PDRIVER_OBJECT DriverObject);

PKEVENT __Event;
HANDLE __EventHandle;

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
	NTSTATUS Status = STATUS_SUCCESS;
	PDEVICE_OBJECT  DeviceObject = NULL;	
	UNICODE_STRING  EventName;

	RtlInitUnicodeString(&EventName, EVENT_NAME);
	DriverObject->DriverUnload = DriverUnload;


	__Event = IoCreateNotificationEvent(&EventName, &__EventHandle);    //DriverEntry     进程回调通知

	return Status;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
	DbgPrint("DriverUnload()\\r\\n");

	if (__EventHandle != NULL)
	{
		KeClearEvent(__Event);

		ZwClose(__EventHandle);

		__EventHandle = NULL;
		__Event = NULL;
	}

}

  

 

三.应用程序与驱动程序的事件交互(应用程序创建事件——>驱动程序设置事件) DeviceIoControl ——> ObReferenceObjectByHandle

  要将用户模式下创建的事件传递给驱动程序,可以用DeviceIoControl API函数。DDK提供了内核函数将句柄转化为指针,该函数如下:
  NTSTATUS 
     ObReferenceObjectByHandle(
      IN HANDLE  Handle,
      IN ACCESS_MASK  DesiredAccess,
      IN POBJECT_TYPE  ObjectType  OPTIONAL,
      IN KPROCESSOR_MODE  AccessMode,
      OUT PVOID  *Object,
      OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
      );

  ObReferenceObjectByHandle函数在得到指针的同时,会为对象的指针维护一个计数。每次调用ObReferenceObjectByHandle函数时会使计数加1.因此为了计数平衡,在使用完 ObReferenceObjectByHandle函数后,需要调用如下函数:             
  VOID 
    ObDereferenceObject(
      IN PVOID  Object
      );
  ObDereferenceObject函数使计数减一。

  1.应用程序通过符号链接名CreateFile函数得到设备句柄

  HANDLE DeviceHandle = CreateFile(DeviceLinkName,
    GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL);
  2.应用程序CreateEvent创建事件

    //创建自动重置的,初始为未激发的事件对象
    EventHandle[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

  3.应用程序通过DeviceIoControl 函数将用户模式下创建的事件传递给驱动程序

    //调用DeviceIoControl把事件句柄传进内核
    IsOk = DeviceIoControl(DeviceHandle, CTL_EVENT,
      EventHandle,
      sizeof(HANDLE) * 2,
      NULL,
      0,
      &ReturnLength,
      NULL);

    //调用DeviceIoControl,通知驱动程序设置事件激发状态
    IsOk = DeviceIoControl(DeviceHandle, CTL_SET_EVENT,
      NULL,
      0,
      NULL,
      0,
      &ReturnLength,
      NULL);

  4.驱动程序通过ObReferenceObjectByHandle句柄转化为PKEVENT指针

    /把句柄转化为KEvent结构
    Status = ObReferenceObjectByHandle(
      (HANDLE)EventHandle[i], //Irp->AssociatedIrp.SystemBuffer 句柄
      SYNCHRONIZE, //权限
      *ExEventObjectType, //对象类型,对象类型
      KernelMode, //访问模式分KernelMode
      &__KernelEvent[i], //指向映射句柄对象的指针
      NULL);

Ring3.cpp

#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;

#define CTL_EVENT \\
	CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)

#define CTL_SET_EVENT \\
	CTL_CODE(FILE_DEVICE_UNKNOWN,0x831,METHOD_BUFFERED,FILE_ANY_ACCESS)


HANDLE SeOpenDeviceObject(WCHAR* DeviceLinkName);
DWORD WINAPI ThreadProcedure(LPVOID ParameterData);
int main()
{
	HANDLE DeviceHandle = SeOpenDeviceObject(L"\\\\\\\\.\\\\Ring0DeviceLinkName");
	if (DeviceHandle == NULL)
	{
		return 0;
	}

	ULONG i = 0;
	HANDLE EventHandle[3] = { 0 };
	for (i = 0; i < 3; i++)
	{
		//创建自动重置的,初始为未激发的事件对象
		EventHandle[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
	}
	BOOL IsOk = 0;
	DWORD ReturnLength = 0;
	//调用DeviceIoControl把事件句柄传进内核
	IsOk = DeviceIoControl(DeviceHandle, CTL_EVENT,
		EventHandle,
		sizeof(HANDLE) * 2,
		NULL,
		0,
		&ReturnLength,
		NULL);


	if (IsOk == FALSE)
	{
		goto Exit;
	}


	HANDLE  ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcedure, 
		(PVOID)EventHandle, 0, NULL);
	//调用DeviceIoControl,通知驱动程序设置事件激发状态
	IsOk = DeviceIoControl(DeviceHandle, CTL_SET_EVENT,
		NULL,
		0,
		NULL,
		0,
		&ReturnLength,
		NULL);

	if (IsOk == FALSE)
	{
		cout << "Send IoCode Error" << endl;
		SetEvent(EventHandle[2]);
		WaitForSingleObject(ThreadHandle, INFINITE);
		goto Exit;
	}


	WaitForSingleObject(ThreadHandle, INFINITE);


Exit:
	{
		for (i = 0; i < 3; i++)
		{
			if (EventHandle[i] != NULL)
			{
				CloseHandle(EventHandle[i]);
				EventHandle[i] = NULL;
			}
		}
		if (ThreadHandle != NULL)
		{
			CloseHandle(ThreadHandle);
			ThreadHandle = NULL;
		}
		if (DeviceHandle != NULL)
		{
			CloseHandle(DeviceHandle);
			DeviceHandle = NULL;
		}
	}

	printf("卸载驱动后 Input AnyKey To Exit\\r\\n");
	getchar();
	getchar();


	return 0;

}

DWORD WINAPI ThreadProcedure(LPVOID ParameterData)
{

	cout << "Ring3等待" << endl;
	//等待三个之中有激发状态的信号
	DWORD Index = WaitForMultipleObjects(3, (HANDLE*)ParameterData, FALSE, INFINITE);
	Index = Index - WAIT_OBJECT_0;
	if (Index == 2)  //0 1 2
	{
		printf("ThreadProcedure() Exit\\r\\n");
		return 0;
	}
	cout << "Ring0触发Ring3" << endl;
	cout << "输入任意键Ring3触发Ring0" << endl;

	getchar();
	getchar();

	SetEvent(((HANDLE*)ParameterData)[1]);  //Ring0中KeWaitForSingleObject响应
	printf("ThreadProcedure() Exit\\r\\n");
	return 0;
}


HANDLE SeOpenDeviceObject(WCHAR* DeviceLinkName)
{
	HANDLE DeviceHandle = CreateFile(DeviceLinkName,
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (DeviceHandle == INVALID_HANDLE_VALUE)
	{
		return NULL;
	}

	return DeviceHandle;

}

  

Ring0.c

#include <ntifs.h>

#define CTL_EVENT \\
	CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define CTL_SET_EVENT \\
	CTL_CODE(FILE_DEVICE_UNKNOWN,0x831,METHOD_BUFFERED,FILE_ANY_ACCESS)


#define DEVICE_OBJECT_NAME  L"\\\\Device\\\\Ring0DeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME    L"\\\\DosDevices\\\\Ring0DeviceLinkName"
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
NTSTATUS Ring3EventHandleToRing0KernelEvent(HANDLE* EventHandle, ULONG_PTR EventHandleCount);

PKEVENT  __KernelEvent[20] = { 0 };
ULONG_PTR __KernelEventCount = 0;

extern
POBJECT_TYPE* ExEventObjectType;

NTSTATUS DriverEntry(PDRIVER_OBJECT  DriverObject, PUNICODE_STRING  RegisterPath)
{
	UNREFERENCED_PARAMETER(RegisterPath);
	NTSTATUS Status = STATUS_SUCCESS;
	PDEVICE_OBJECT  DeviceObject = NULL;
	UNICODE_STRING  DeviceObjectName;
	UNICODE_STRING  DeviceLinkName;
	ULONG			i;
	DriverObject->DriverUnload = DriverUnload;

	//创建设备对象名称
	RtlInitUnicodeString(&DeviceObjectName, DEVICE_OBJECT_NAME);

	//创建设备对象
	Status = IoCreateDevice(DriverObject, NULL,
		&DeviceObjectName,
		FILE_DEVICE_UNKNOWN,
		0, FALSE,
		&DeviceObject);
	if (!NT_SUCCESS(Status))
	{
		return Status;
	}
	//创建设备连接名称
	RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);

	//将设备连接名称与设备名称关联 
	Status = IoCreateSymbolicLink(&DeviceLinkName, &DeviceObjectName);

	if (!NT_SUCCESS(Status))
	{
		IoDeleteDevice(DeviceObject);
		return Status;
	}
	//设计符合我们代码的派遣历程	
	for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
		DriverObject->MajorFunction[i] = PassThroughDispatch;   //函数指针
	}
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch;

	return STATUS_SUCCESS;
}



NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp)
{

	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	ULONG_PTR Information = 0;
	PVOID InputData = NULL;
	ULONG InputDataLength = 0;
	PVOID OutputData = NULL;
	ULONG OutputDataLength = 0;
	ULONG IoControlCode = 0;
	PEPROCESS EProcess = NULL;
	PIO_STACK_LOCATION  IoStackLocation = IoGetCurrentIrpStackLocation(Irp);  //Irp堆栈	
	IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
	InputData = Irp->AssociatedIrp.SystemBuffer;
	OutputData = Irp->AssociatedIrp.SystemBuffer;
	InputDataLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
	OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
	switch (IoControlCode)
	{
	case CTL_EVENT:
	{

		if (InputData != NULL&&InputDataLength == sizeof(HANDLE)*2)
		{

			Status = Ring3EventHandleToRing0KernelEvent((HANDLE*)InputData, InputDataLength / sizeof(HANDLE));

		}

	
		Information = 0;

		break;

	}

	case CTL_SET_EVENT:
	{

		DbgPrint("Ring0触发Ring3\\r\\n");
		KeSetEvent(__KernelEvent[0], IO_NO_INCREMENT, FALSE);  //Ring3层线程中WaitForMultipleObjects响应

		DbgPrint("Ring0等待\\r\\n");
		Status = KeWaitForSingleObject(__KernelEvent[1],
			Executive, KernelMode, FALSE, NULL);    //注意这里的最后一个参数NULL 是永久等待

		DbgPrint("Ring3触发Ring0\\r\\n");
	
		Information = 0;
		break;

	}

	default:
	{

		Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
		Irp->IoStatus.Information = 0;



		break;
	}
	}

	Irp->IoStatus.Status = Status;
	Irp->IoStatus.Information = Information;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return Status;
}


NTSTATUS Ring3EventHandleToRing0KernelEvent(HANDLE* EventHandle, ULONG_PTR EventHandleCount)
{
	NTSTATUS   Status = STATUS_SUCCESS;
	PULONG_PTR HandleArray = NULL;
	ULONG i = 0;
	
	if (EventHandle==NULL)
	{
		return STATUS_UNSUCCESSFUL;
	}
	__KernelEventCount = EventHandleCount;
	for (i = 0; i < EventHandleCount; i++)
	{
		//把句柄转化为KEvent结构
		Status = ObReferenceObjectByHandle(
			(HANDLE)EventHandle[i],           //Irp->AssociatedIrp.SystemBuffer   句柄
			SYNCHRONIZE,                      //权限
			*ExEventObjectType,               //对象类型,对象类型
			KernelMode,                       //访问模式分KernelMode
			&__KernelEvent[i],                //指向映射句柄对象的指针
			NULL);
		if (!NT_SUCCESS(Status))
		{
			break;
		}
	}

	if (Status != STATUS_SUCCESS)
	{
		for (i = 0; i < EventHandleCount; i++)
		{
			if (__KernelEvent[i] != NULL)
			{
				//递减计数
				ObDereferenceObject(__KernelEvent[i]);

				__KernelEvent[i] = NULL;
			}
		}
	}
	return Status;
}


NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp)
{
	Irp->IoStatus.Status = STATUS_SUCCESS;     //LastError()
	Irp->IoStatus.Information = 0;             //ReturnLength 
	IoCompleteRequest(Irp, IO_NO_INCREMENT);   //将Irp返回给Io管理器
	return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
	int i = 0;
	UNICODE_STRING  DeviceLinkName;
	PDEVICE_OBJECT	v1 = NULL;
	PDEVICE_OBJECT  DeleteDeviceObject = NULL;

	RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
	IoDeleteSymbolicLink(&DeviceLinkName);

	DeleteDeviceObject = DriverObject->DeviceObject;
	while (DeleteDeviceObject != NULL)
	{
		v1 = DeleteDeviceObject->NextDevice;
		IoDeleteDevice(DeleteDeviceObject);
		DeleteDeviceObject = v1;
	}
	for (i = 0; i < __KernelEventCount; i++)
	{
		if (__KernelEvent[i] != NULL)
		{
			ObDereferenceObject(__KernelEvent[i]);

			__KernelEvent[i] = NULL;
		}
	}
}

  

 

以上是关于内核事件KEVENT(同步)的主要内容,如果未能解决你的问题,请参考以下文章

Ring3创建事件Ring0设置事件

向Windows内核驱动传递用户层定义的事件Event,并响应内核层的通知

OpenHarmony-内核对象事件之源码详解

事件Event:带你体验鸿蒙轻内核中一对多多对多任务同步

UCOSIII事件标志组和同时等待多个内核对象

UCOSIII事件标志组和同时等待多个内核对象