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


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

 

推薦閱讀:

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

 

一、知識點講解

1. 設備對象

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

 

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

  在內核中,能夠接收IRP消息的只能時設備對象。

    

 

2. 創建設備對象

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

 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發送給設備對象,由設備對象轉發給對應的派遣函數。

  

6、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

 

7、派遣函數在哪里注冊呢?

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

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

  

 

8. 注冊派遣函數

  如下圖,我們直接采取對數組賦值的形式來設置派遣函數。

  

 9. 派遣函數格式

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

  

 

 

二、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 }

 

三、推薦閱讀:

 

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM