Windows中0環與3環通訊(常規方式)

Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.htmlhtml

 

推薦閱讀:windows

  1. Windows驅動學習(二)-- 驅動層&應用層通訊

 

1、知識點講解數組

1. 設備對象函數

  咱們在開發窗口程序的時候,消息被封裝成一個結構體:MSG,在內核開發時,消息被封裝成另一個結構體:IRP(I/O Request Package I/O請求包)。學習

 

  在窗口程序中,可以接收消息的只能是窗口對象(由窗口對象將消息分發給各個窗口過程)。spa

  在內核中,可以接收IRP消息的只能時設備對象。操作系統

    

 

2. 建立設備對象.net

  調用 IoCreateDevice API 來建立設備對象,其中須要傳入設備名稱(R3依據這個找到),須要初始化字符串。3d

 1 //建立設備名稱
 2 UNICODE_STRING Devicename;
 3 RtlInitUnicodeString(&Devicename,L"\\Device\\MyDevice");
 4 
 5 //建立設備
 6 IoCreateDevice(
 7 pDriver,                //當前設備所屬的驅動對象
 8 0,
 9 &Devicename,            //設備對象的名稱
10 FILE_DEVICE_UNKNOWN,
11 FILE_DEVICE_SECURE_OPEN,
12 FALSE,
13 &pDeviceObj            //設備對象指針
14 );

 

3. 設置數據交互方式指針

  pDeviceObj->Flags |= DO_BUFFERED_IO (注意: I= 表示按位或的含義)

  其存在三種讀寫方式:

  1)緩衝區讀寫方式(DO_BUFFERED_IO):操做系統將應用程序提供緩衝區的數據複製到內核模式下的地址中。

  2)  直接方式讀寫(DO_DIRECT_IO):操做系統會將用戶模式下的緩衝區鎖住。而後操做系統將這段緩衝區在內核模式地址再次映射一遍。這樣,用戶模式的緩衝區和內核模式的緩衝區指向的時同一區域的物理內存。缺點就是要單獨佔用物理頁面。

  3)其餘方式讀寫(不設置Flags):很危險,直接讀寫緩衝區地址,很容易出現藍屏而且極可能數據丟失(讀取過程當中掛起頁置換)。

 

4. 建立符號連接

  //建立符號連接名稱

  RtlInitUnicodeString(&SymbolicLinkName,L"\\??\\MyTestDriver");

  // 建立符號連接

  IoCreateSymbolicLink(&SymbolicLinkName,&Devicename);

 

  特別說明:

  1)設備名稱的做用就是給內核對象用的,若是要在R3訪問,必需要有符號連接。其實就是一個別名,沒有這個別名,在R3不可見

  2)在內核模式下,符號連接是以 '\??\'開頭的,若是C盤就是 "\??\C:"

  3)  在用戶模式下,則是以 '\\.\' 開頭的,若是是C盤就是 "\\.\C:"

 

5. IRP與派遣函數

  以下圖,在R3層面上,其由窗口對象負責將消息發送給對應的回調函數;而在內核層,將IRP發送給設備對象,由設備對象轉發給對應的派遣函數。

  

六、IRP的類型

  微軟文檔 :IRP structure

  1)  當應用層經過CreateFile,ReadFile,WriteFile,CloseHandle等函數打開、從設備讀取數據、向設備寫入數據、關閉設備的時候,會時操做系統產生出IRP_MJ_CREATE、IRP_MJ_READ、IRP_MJ_CLOSE等不一樣的IRP。

  2)其餘的IRP類型

    IRP_MJ_DEVICE_CONTROL    DeviceControl函數會產生此IRP

    IRP_MJ_POWER        在操做系統處理電源消息時,產生次IRP

    IRP_MJ_SHUTDOWN      關閉系統前會產生此IRP

 

七、派遣函數在哪裏註冊呢?

  其在 _DRIVER_OBJECT 函數最後一個數組中。其派遣函數的種類及其有限(由IRP消息類型限制),能夠看出一共有29種。

  關於 _DRIVER_OBJECT的介紹能夠查看以前這篇博客:內核空間與內核地址

  

 

8. 註冊派遣函數

  以下圖,咱們直接採起對數組賦值的形式來設置派遣函數。

  

 9. 派遣函數格式

  必定要設置其返回狀態 NTSTATUS,三環程序判斷API是否調用成功就是根據這個狀態,若是不設置,則可能會出錯。

  

 

 

2、3環與0環通信案例講解

  下面使用驅動編寫0環程序,C++編寫3環程序。3環程序負責向0環程序讀取內容。

驅動代碼

  1 #include <ntddk.h>
  2 
  3 // 定義設備名和符號名
  4 #define DEVICE_NAME L"\\Device\\MTReadDevice_asajs123akdas"
  5 #define SYM_LINK_NAME L"\\??\\MTRead_asdkasjkadsjldasss213k"
  6 
  7 
  8 // 設備建立函數
  9 NTSTATUS DeviceCreate(PDEVICE_OBJECT Device, PIRP pIrp)
 10 {
 11     __asm int 3
 12     pIrp->IoStatus.Status = STATUS_SUCCESS;
 13     pIrp->IoStatus.Information = 0;
 14     // I/O請求處理完畢
 15     IoCompleteRequest(pIrp, IO_NO_INCREMENT);
 16     DbgPrint("Create Device Success\n");
 17     return STATUS_SUCCESS;
 18 }
 19 
 20 // 設備讀操做函數
 21 NTSTATUS DeviceRead(PDEVICE_OBJECT Device, PIRP pIrp)
 22 {
 23     __asm int 3
 24     // 獲取指向IRP的堆棧的指針
 25     PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
 26     // 獲取堆棧長度
 27     ULONG length = stack->Parameters.Read.Length;
 28     pIrp->IoStatus.Status = STATUS_SUCCESS;
 29     pIrp->IoStatus.Information = length;
 30     // 將堆棧上的數據全設置爲0xAA
 31     memset(pIrp->AssociatedIrp.SystemBuffer, 0xBB, length);
 32     IoCompleteRequest(pIrp, IO_NO_INCREMENT);
 33     DbgPrint("Read Device Success\n");
 34     return STATUS_SUCCESS;
 35 }
 36 
 37 // 設備關閉函數
 38 NTSTATUS DeviceClose(PDEVICE_OBJECT Device, PIRP pIrp)
 39 {
 40     __asm int 3
 41     // 跟設備建立函數相同
 42     pIrp->IoStatus.Status = STATUS_SUCCESS;
 43     pIrp->IoStatus.Information = 0;
 44     IoCompleteRequest(pIrp, IO_NO_INCREMENT);
 45     DbgPrint("Close Device Success\n");
 46     return STATUS_SUCCESS;
 47 }
 48 
 49 // 驅動卸載函數
 50 NTSTATUS DriverUnload(PDRIVER_OBJECT Driver)
 51 {
 52     NTSTATUS status;
 53     __asm int 3 
 54     // 刪除符號和設備
 55     UNICODE_STRING SymLinkName;
 56     RtlInitUnicodeString(&SymLinkName, SYM_LINK_NAME);
 57 
 58     status = (IoDeleteSymbolicLink(&SymLinkName));
 59     DbgPrint("刪除狀態碼:%x", status);
 60 
 61     PDRIVER_OBJECT pFirstObj = Driver->DeviceObject;
 62     if (pFirstObj->DeviceType == FILE_DEVICE_COMPORT)
 63     {
 64         
 65     }
 66     
 67     IoDeleteDevice(Driver->DeviceObject);
 68     DbgPrint("This Driver Is Unloading...\n");
 69     return STATUS_SUCCESS;
 70 }
 71 
 72 // 驅動入口函數
 73 NTSTATUS DriverEntry(PDRIVER_OBJECT Driver, PUNICODE_STRING RegPath)
 74 {
 75     __asm int 3
 76     NTSTATUS status;
 77 
 78     UNICODE_STRING DeviceName;
 79     UNICODE_STRING SymLinkName;
 80     // 將設備名轉換爲Unicode字符串
 81     RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
 82     // 建立設備對象
 83     PDEVICE_OBJECT pDevice = NULL;
 84     Driver->DriverUnload = DriverUnload;
 85     status = IoCreateDevice(Driver, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevice);
 86     if (!NT_SUCCESS(status))
 87     {
 88         IoDeleteDevice(pDevice);
 89         DbgPrint("Create Device Faild!\n");
 90         return STATUS_UNSUCCESSFUL;
 91     }
 92     // 設置pDevice以緩衝區方式讀取
 93     pDevice->Flags = DO_BUFFERED_IO;
 94 
 95     // 將符號名轉換爲Unicode字符串
 96     RtlInitUnicodeString(&SymLinkName, SYM_LINK_NAME);
 97     // 將符號與設備關聯
 98     status = IoCreateSymbolicLink(&SymLinkName, &DeviceName);
 99     if (!NT_SUCCESS(status))
100     {
101         DbgPrint("Create SymLink Faild!\n");
102         IoDeleteDevice(pDevice);
103         return STATUS_UNSUCCESSFUL;
104     }
105 
106     DbgPrint("Initialize Success\n");
107 
108     // 註冊設備建立函數、設備讀函數、設備關閉函數、驅動卸載函數
109     Driver->MajorFunction[IRP_MJ_CREATE] = DeviceCreate;
110     Driver->MajorFunction[IRP_MJ_READ] = DeviceRead;
111     Driver->MajorFunction[IRP_MJ_CLOSE] = DeviceClose;
112 
113 
114     DbgPrint("Initialize Success\n");
115 
116     return STATUS_SUCCESS;
117 }

 

3環應用程序代碼

 1 // 從驅動中讀取數據.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
 2 //
 3 
 4 #include "pch.h"
 5 #include <stdio.h>
 6 #include <tchar.h>
 7 #include <Windows.h>
 8 
 9 int main()
10 {
11     // 由於驅動層已經註冊符號了,那麼在R3層其就能夠顯示出來,以"\\.\SymName" 名稱顯示出來
12     
13     // 打開設備句柄(根據註冊符號)
14     HANDLE hDevice = CreateFile(L"\\\\.\\MTRead_asdkasjkadsjldasss213k", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
15     if (hDevice == INVALID_HANDLE_VALUE) {
16         printf("Failed to Obtain Device Handle!");
17         getchar();
18         getchar();
19         getchar();
20         return -1;
21     }
22 
23     // 建立一個緩衝區進行讀寫
24     UCHAR buffer[10];
25     ULONG size;
26     BOOL result = ReadFile(hDevice, buffer, 10, &size, NULL);
27     if (result) {
28 
29         // 打印到控制檯
30         printf(" Read %d bytes :", size);
31         for (int i = 0; i < (int)size; i++) {
32             printf("%02x", buffer[i]);
33         }
34         printf("\n");
35     }
36 
37     // 關閉設備句柄
38     CloseHandle(hDevice);
39     getchar();
40     getchar();
41     getchar();
42     getchar();
43     return 0;
44 }

 

3、推薦閱讀:

 

  1. Windows驅動學習(二)-- 驅動層&應用層通訊
相關文章
相關標籤/搜索