看流星社区

 找回密码
 注册账号
查看: 5626|回复: 1

Ring3与Ring0同步手段(转自迪奥思宇)

[复制链接]

该用户从未签到

发表于 2013-8-28 12:40:20 | 显示全部楼层 |阅读模式
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);

该用户从未签到

发表于 2013-9-23 10:08:41 | 显示全部楼层
好东西大家分享,下来试用
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-3-29 19:26

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表