Windows驱动开发学习笔记(四)—— 3环与0环通信(常规方式)
设备对象
描述:
- 我们在开发窗口程序的时候,消息被封装成一个结构体:MSG
- 在内核开发时,消息被封装成另外一个结构体:IRP(I/O Request Package)
- 在窗口程序中,能够接收消息的只能是窗口对象
- 在内核中,能够接收IRP消息的只能是设备对象
创建设备对象
//创建设备名称
UNICODE_STRING Devicename;
RtlInitUnicodeString(&Devicename,L"\\Device\\MyDevice");
//"\\Device\\MyDevice"这个名字不要随便改
//因为在这个树形结构中存储着所有内核对象
//若改变,将会把这个设备对象挂到其它树中
//创建设备对象
NTSTATUS status = IoCreateDevice(
pDriver, //当前设备所属的驱动对象(PDRIVER_OBJECT)
0,
&Devicename, //设备对象的名称
FILE_DEVICE_UNKNOWN, //设备对象类型
FILE_DEVICE_SECURE_OPEN,
FALSE,
&pDeviceObj //设备对象指针(PDEVICE_OBJECT)
);
if( STATUS_SUCCESS != status )
{
DbgPrint("创建设备失败! . \r\n");
return status;
}
设置数据交互方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
/*缓冲区方式读写(DO_BUFFERED_IO) :操作系统将应用程序提供缓冲区的数据复制到内核模式下的地址中。
*优点:方便;缺点:效率低
*适合数据量较小时使用
*
*直接方式读写(DO_DIRECT_IO) :操作系统会将用户模式下的缓冲区锁住。
*然后操作系统将这段缓冲区在内核模式地址再次映射一遍。
*这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理内存。
*优点:效率高;缺点:单独占用物理页面,无法再进行其它操作(例如文件读写)
*适合数据量较大时使用
*
*其他方式读写(默认为其他方式):
*在使用其他方式读写设备时,派遣函数直接读写应用程序提供的缓冲区地址。
*在驱动程序中,直接操作应用程序的缓冲区地址是很危险的。
*只有驱动程序与应用程序运行在相同线程上下文的情况下,才能使用这种方式。
*/
创建符号链接
/*
*特别说明:
*1. 设备名称的作用是给内核对象用的,如果要在Ring3访问,必须要有符号链接
* 其实就是一个别名,没有这个别名,在Ring3不可见。
*2. 内核模式下,符号链接是以“\??\”开头的,如C盘就是“\??\C:”
*3. 而在用户模式下,则是以“\\.\”开头的,如C盘就是“\\.\C:
*/
//Ring3用CreateFile打开设备时,用"\\\\.\\MyTestDriver"
#define SYMBOLICLINK_NAME L"\\??\\MyTestDriver"
//创建符号链接名称
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);
//创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &Devicename);
if( STATUS_SUCCESS != status )
{
DbgPrint("创建设备失败! . \r\n");
IoDeleteDevice(pDeviceObj);
return status;
}
IRP与派遣函数
描述:
- 当对窗口对象传递消息时,窗口对象会根据我们所传入的消息类型执行对应的回调函数
- 当调用CreateFile等函数时,操作系统会将它们封装成一个结构体,此时就可以传递给设备对象,设备对象根据IRP的类型调用对应的派遣函数
IRP的类型
描述:当应用层通过CreateFile,ReadFile,WriteFile,CloseHandle等函数对设备进行打开、读取、写入、关闭的时候,会使操作系统产生出IRP_MJ_CREATE,IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_CLOSE等不同的IRP
其它类型的IRP
派遣函数
派遣函数注册位置
ntdll!_DRIVER_OBJECT
+0x034 DriverUnload : Ptr32 void //卸载函数
+0x038 MajorFunction : [28] Ptr32 long //派遣函数
注册派遣函数
NTSTATUS DriverEntry( 。。。。)
{
//设置派遣函数
pDriverObject->MajorFunction[IRP_MJ_CREATE] // 派遣函数1;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] // 派遣函数2;
pDriverObject->MajorFunction[IRP_MJ_WRITE] // 派遣函数3;
pDriverObject->MajorFunction[IRP_MJ_READ] // 派遣函数4;
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] // 派遣函数5;
pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] // 派遣函数6;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] // 派遣函数7;
pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] // 派遣函数8;
pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] // 派遣函数9;
//IRP_MJ_MAXIMUM_FUNCTION 派遣函数的最大值
//设置卸载函数
pDriverObject->DriverUnload = 卸载函数;
}
派遣函数的格式
NTSTATUS MyDispatchFunction(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
//处理自己的业务...
//设置返回状态
pIrp->IoStatus.Status = STATUS_SUCCESS; //getlasterror()得到的就是这个值
pIrp->IoStatus.Information = 0; //返回给3环多少数据 没有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
实验
Ring 0代码
#include <ntddk.h>
#include <ntstatus.h>
#define DEVICE_NAME L"\\Device\\MyDevice"
// Ring3用CreateFile打开设备时,用"\\\\.\\MyTestDriver"
#define SYMBOLICLINK_NAME L"\\??\\MyTestDriver"
// 0-2047是保留的 2048~4095
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)
// 卸载函数
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UNICODE_STRING SymbolicLinkName = {0};
DbgPrint("驱动程序停止运行了 . \r\n");
// 删除符号链接 删除设备
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);
IoDeleteSymbolicLink(&SymbolicLinkName);
IoDeleteDevice(pDriver->DeviceObject);
}
// IRP_MJ_CREATE处理函数
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("DispatchCreate ... \n");
// 返回状态如果不设置 Ring3返回的是失败
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// IRP_MJ_CLOSE处理函数
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("DispatchClose ... \n");
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// IRP_MJ_DEVICE_CONTROL处理函数 用来处理与Ring3交互
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
PIO_STACK_LOCATION pIrpStack;
ULONG uIoControlCode;
PVOID pIoBuffer;
ULONG uInLength;
ULONG uOutLength;
ULONG uRead;
ULONG uWrite;
// 设置临时变量的值
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
// 获取控制码
uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
// 获取缓冲区地址(输入和输出的缓冲区都是一个)
pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
// Ring3 发送数据的长度
uInLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
// Ring0 发送数据的长度
uOutLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch(uIoControlCode)
{
case OPER1:
{
DbgPrint("IrpDeviceControlProc -> OPER1 ... \n");
pIrp->IoStatus.Information = 0;
status = STATUS_SUCCESS;
break;
}
case OPER2:
{
DbgPrint("IrpDeviceControlProc -> OPER2 接收字节数:%d \n", uInLength);
DbgPrint("IrpDeviceControlProc -> OPER2 输出字节数:%d \n", uOutLength);
// Read From Buffer
memcpy(&uRead, pIoBuffer, 4);
DbgPrint("IrpDeviceControlProc -> OPER2 ... %x \n", uRead);
// Write To Buffer
memcpy(&pIoBuffer, &uWrite, 4);
// Set Status
pIrp->IoStatus.Information = 2;
status = STATUS_SUCCESS;
break;
}
}
// 设置返回状态
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
// 入口函数 相当于Main函数
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
NTSTATUS status = 0;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL;
UNICODE_STRING Devicename;
UNICODE_STRING SymbolicLinkName;
DbgPrint("驱动程序开始运行了 . \r\n");
// 创建设备名称
RtlInitUnicodeString(&Devicename, DEVICE_NAME);
// 创建设备
status = IoCreateDevice(pDriver, 0, &Devicename,
FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj);
if(status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! \r\n");
return status;
}
//设置交互数据的方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
// 创建符号链接名称
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINK_NAME);
// 创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &Devicename);
if(status != STATUS_SUCCESS)
{
DbgPrint("创建符号链接失败! \r\n");
IoDeleteDevice(pDeviceObj);
return status;
}
// 设置分发函数和卸载函数
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpCreateProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpCloseProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceControlProc;
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
Ring 3代码
#include <stdio.h>
#include <windows.h>
#include <winioctl.h>
#define IN_BUFFER_MAXLENGTH 0x10 //输入缓存最大长度
#define OUT_BUFFER_MAXLENGTH 0x10 //输出缓存最大长度
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define SYMBOLICLINK_NAME "\\\\.\\MyTestDriver"
HANDLE g_hDevice; //驱动句柄
/***************************************************************************/
//打开驱动服务句柄
//打开三环链接名:\\\\.\\Driver
/***************************************************************************/
BOOL Open(PCHAR pLinkName)
{
TCHAR szBuffer[10] = {0};
//在3环获取驱动程序
g_hDevice = CreateFile(pLinkName, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
DWORD err = GetLastError();
sprintf(szBuffer, "%d\n", err);
if(g_hDevice != INVALID_HANDLE_VALUE)
return TRUE;
else
return FALSE;
}
/***************************************************************************/
//与驱动通信的函数
/***************************************************************************/
BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen)
{
DWORD dw;
//驱动句柄/操作码/输入缓冲区地址/输入缓冲区长度/输出缓冲区地址/输出缓冲区长度/返回长度/指向OVERLAPPED 此处为NULL
DeviceIoControl(g_hDevice, dwIoCode, InBuff, InBuffLen, OutBuff, OutBuffLen, &dw, NULL);
return TRUE;
}
int main()
{
DWORD dwInBuffer = 0x11223344;
TCHAR szOutBuffer[OUT_BUFFER_MAXLENGTH] = {0};
//1. 通过符号链接,打开设备
Open(SYMBOLICLINK_NAME);
//2. 测试通信
IoControl(OPER2, &dwInBuffer, IN_BUFFER_MAXLENGTH, szOutBuffer, OUT_BUFFER_MAXLENGTH);
printf("%s", szOutBuffer);
//3. 关闭设备
CloseHandle(g_hDevice);
getchar();
return 0;
}
第一步:运行Ring 0代码
第二步:运行Ring3代码