Windows中0环与3环通信(常规方式)
Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html
推荐阅读:
一、知识点讲解
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 }
三、推荐阅读: