栀子博客 栀子花驿站--栀子博客主要关注移动互联网、云计算、虚拟化、信息安全、软件体系架构、操作系统内核、C/C++、Windows、Linux等相关产品与技术
2008-10-8 15:44:43
(精华)Ring3与Ring0通信方法若干
本人在[ (原创)应用程序与驱动通信的若干方式 ]文章中阐述了,上下层通信的技术实现部分,但没有结合应用,下面的文章就具体应用给大家展示了使用方法,希望能够大家一些启发。 Ring3与Ring0同步是很有用的手段,在此做一个简要的整理,希望对开发 这方面程序 的朋友有帮助,好了,开始吧。 1 同步的策略 初写驱动 的朋友都知道,通过DeviceIoControl这个API 函数 , 可以将Ring3的数据及控制请求发送给Ring0.但这是单向的,由Ring3主动发起,Ring0被动接受。可以说这是单向轮询的。 询问(CTL_CODE) Ring3 ---------------------------------------------------------------- > Ring0 回答(通过OUT参数 ) Ring3 <---------------------------------------------------------------- Ring0 这种方式用于主动获取Ring0数据,又不要求效率,和具有实时性的开发情况。 但做监控类软件 却与此相反,是Ring0先监控到事件,具有主动权,发起询问通知Ring3。 监控到事件(通知) Ring0(监控)----------------------------------------------------------------> Ring3 在实时性要求不高的情况下可以采用Ring3定时询问Ring0,当然Ring0保存了事件的状态。 这里介绍的就是高实时性,大数据传输的同步解决办法 。 原理 :通过Ring3创建事件,并将该事件传递给Ring0,同时Ring3创建监控线程 ,等待Ring0发起事件,此为应用 层驱动层共享事件。同时Ring0在内核 分配非分页内存,通过DeviceIoControl 传递给Ring3,此为应用层驱动层共享内存。因在DeviceIoControl 中传送Buffer,涉及到内核数据拷贝,大数据量下使用效率很低,故用共享内存的方法。 2 具体实现 Ring3 CODE : HANDLE m_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); // 创建事件 // 发事件给ring 0 if (0 == DeviceIoControl(hFile, \ SET_EVENT, \ &m_hEvent, \ sizeof(HANDLE), \ NULL, \ 0,\ &uRetBytes, NULL)) { CloseHandle(hFile); CloseHandle(m_hEvent); return ; } // 获得ring 0 的共享内存 unsigned int add = 0; if (0 == DeviceIoControl(hFile, \ GET_SHARE_ADD, \ NULL, \ 0, \ &add, \ sizeof (unsigned int),\ &uRetBytes, NULL)) { CloseHandle(hFile); CloseHandle(m_hEvent); return ; } m_pShareMem =(PVOID)add; //映射的共享内存 当Ring0有事件产生时,Ring3的线程就可处理m_pShareMen了。 UINT WorkThread(PVOID param) { ............... while(!bExit) { WaitForSingleObject(g_hEvent,INFINITE); ...................... // 可以处理分析m_pShareMen 了 } } Ring0 CODE : PVOID g_pSysAdd = NULL; PMDL g_pMdl = NULL; // 与ring 3 的共享内存描述 NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString) { ...... g_pSysAdd = ExAllocatePool(NonPagedPool, SHARE_MLEN + IPHEDLEN); g_pMdl = IoAllocateMdl(g_pSysAdd, SHARE_MLEN + IPHEDLEN, FALSE, FALSE, NULL); MmBuildMdlForNonPagedPool(g_pMdl); ...... } // I/O控制派遣例程 NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp) { PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); // 取得I/O控制代码 ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; switch(uIoControlCode) { case SET_EVENT: // 处理Ring3的事件 if(g_pInBuffer == NULL||g_nInBuffSize < sizeof(HANDLE)) { status = STATUS_INVALID_BUFFER_SIZE; break; } hEvent = *(HANDLE *)g_pInBuffer; status = ObReferenceObjectByHandle(hEvent, SYNCHRONIZE, *ExEventObjectType, KernelMode, (PVOID *)&g_pEvent, &objHandleInfo ); if(!NT_SUCCESS(status)) { g_pEvent = NULL; } DbgPrint("g_pEvent: %x\n",g_pEvent); // 得到了ring 3 事件句柄 break; case GET_SHARE_ADD: // 传给ring 3 内存地址 UserAddr = MmMapLockedPages(g_pMdl, UserMode); *((PVOID *)(pIrp->AssociatedIrp.SystemBuffer)) = UserAddr; status = STATUS_SUCCESS; break; } .............. } RtlCopyMemory(g_pSysAdd,pSource,dwLen) // 将写数据到共享内存, KeSetEvent((PKEVENT)g_pEvent,0,false); // 此刻Ring3就可以接受数据了。 最后一步记得释放内存。 void DriverUnload(PDRIVER_OBJECT pDriverObj) { // 清除共享内存 IoFreeMdl(g_pMdl); ExFreePool(g_pSysAdd); // 删除设备对象 IoDeleteDevice(pDriverObj->DeviceObject); } 以上就是整个处理过程 1. 驱动 中IoCreateNotificationEvent,KeClearEvent 应用 中OpenEvent(SYNCHRONIZE, FALSE, EVENT_NAME) 这样,只能在应用中WaitForSingleObject,而不能SetEvent,ResetEvent 驱动中可以KeSetEvent,(而且必须紧接着KeClearEvent,因为在应用中不能修改核心态创建的对象的状态,只能在这个时候清除状态了),即只能由驱动通知应用,在某些应用只需要等待通知的场合足够了,如应用等待数据准备好的通知,这时可以在中断处理函数 中设置事件有信号 注意,OpenEvent第一个参数 不能为EVENT_ALL_ACCESS,因为应用没有这么大权限操作在系统 上下文创建的事件 2.在驱动中创建事件,并把时间句柄传给应用,应用不必OpenEvent。应用程序 可以随意set,reset事件,这样可以和驱动中的系统线程 进行同步 这种方法的前提条件是,event是在应用的进程 上下文(在系统上下文创建的话就不可以)创建,并传给应用的,比如可以在某个IOCtl分支中创建事件并传给应用。 解释:在使用EVENT的PROCESS context中创的HANDLE就在这个进程的句柄表里,经检验没有权限限制,可以由应用直接使用;而在system context中创建的HANDLE当然就在SYSTEM进程里啦,若单单传句柄值给应用,而句柄表里根本就没有对应的句柄,当然不成功了。 代码 如下 驱动中: void ppppp(PVOID event) { KeWaitForSingleObject((PKEVENT)event,Executive,UserMode,0,0); //......验证处 } ...... WCHAR wEventNameBuf[]=L"\\BaseNamedObjects\\SharedEvent"; UNICODE_STRING uEventName; PKEVENT pEvent; HANDLE hEvent,hThread; ...... case IOCTL_******: RtlInitUnicodeString(&uEventName,wEventNameBuf); pEvent = IoCreateNotificationEvent(&uEventName,&hEvent); KeResetEvent(pEvent); RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,&hEvent,4); PsCreateSystemThread(&hThread,THREAD_ALL_ACCESS,0,0,0,ppppp,pEvent); 应用中: if(!DeviceIoControl(hDevice,IOCTL_******,0,0,&Handle,4,&Bytes,0)) MessageBox("DeviceIo Error!"); esle{ wsprintf(str,"%x,%x,%x",hDevice,Bytes,Handle); MessageBox(str); if(!SetEvent((HANDLE)Handle)) ...... } 会看到,点击MessageBox OK后ppppp的确继续执行了。 3.在应用中创建事件,然后通过ioctl传给驱动,驱动中ObReferenceObjectByHandle来引用该事件对象。 这样应用和驱动中都可以检查和修改事件状态。 应用程序: HANDLE m_hCommEvent = CreateEvent(NULL, false, false, NULL); // download event object to device driver, // m_hCommDevice is the device object DeviceIoControl(m_hCommDevice, IO_REFERENCE_EVENT, (LPVOID) m_hCommEvent, 0, NULL, 0, dwReturn, NULL); 在需要的地方等待 while(true) { WaitForSingleObject(m_hCommEvent, INFINITE); // After this function, the event is set to // non signaled. Get information and deal with it. } 驱动程序: case IO_REFERENCE_EVENT: hEvent = (HANDLE) irpStack-> Parameters.DeviceIoControl.Type3InputBuffer; status = ObReferenceObjectByHandle(hEvent, GENERIC_ALL, NULL, KernelMode, &gpEventObject, &objHandleInfo); the gpEventObject is a PRKEVENT object, so we can use KeEventXXX and KeWaitForXXX to operate it. 当事件发生时,置信号 KeSetEvent(gpEventObject, 0, FALSE); 当不再需要事件对象时: case IO_DEREFERENCE_EVENT: if(gpEventObject) ObDereferenceObject(gpEventObject);
1 .无风
我按照你的写了个程序 不过应用程序事件没有被设置 是什么原因呢
2009-4-27 19:47:30
2 .无风
我对这个问题 比较弧度 希望您能给点意见 谢谢了 我的QQ271548725,,
2009-4-27 19:48:21
3 .zhizihua
这个代码与原理我已经实际使用过,没有问题,你需要注意的是通信是 需要将 上层的创建的 Event的句柄(handle)通过Io control 下发给驱动,驱动 通过 ObReferenceObjectByHandle 来获的相应的 Event Object,然后通过 KeSetEvent 来激活Event,只要你按这个操作应该是没有问题的
2009-7-29 15:44:51