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驱动学习(二)-- 驱动层&应用层通讯
相关文章
相关标签/搜索