(精華)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);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章