分层驱动程序的概念
分层驱动概念主要是为了区分前面介绍的单层驱动程序。分层驱动是指两个或两个以
上的驱动程序,它们分别创建设备对象,并且形成一个由高到低的设备对象栈。
IRP请求
一-般会被传送到设备栈的最顶层的设备对象,顶层的设备对象可以选择直接结束IRP请求,也可以选择将IRP请求向下层的设备对象转发。如果是向下层设备对象转发IRP请求,当
IRP请求结束时,IRP会顺着设备栈的反方向原路返回。当得知下层驱动程序已经结束IRP
请求时,本层设备对象可以选择继续将IRP向上返回,或者选择重新将IRP再次传递给底
层设备驱动。
由此看来,分层驱动处理IRP要比单层驱动灵活得多。事实上,单层驱动也很少被用
到,程序员大部分需要编写的是分层驱动程序。
分层驱动程序使设计变得模块化。一个功能复杂的驱动程序可以划分为若干个小
驱动程序,每个小驱动程序对上层驱动程序提供一个接口,上层驱动程序只关心下层驱动
程序的接口,而不必关心底层驱动程序是如何实现的。
在Windows 2000后,微软提出了WDM ( Windows Driver Model)驱动程序模型
的概念。WDM驱动程序就属于分层驱动程序。最简单的WDM驱动程序分为两层,一层
是PDO (物理设备对象Physical Device Object),另一 层是FDO (功能设备对象Function
Device Object)。 FDO是挂载在PDO之上的,PDO实现了即插即用的功能,FDO完成逻
辑功能,而将一些硬件 相关的请求发往PDO。
设备堆栈与挂载
挂载指的是将高一层的设备对象挂载在低一层的设备对象上,从而形成一个设备栈。
IRP处理的顺序是位于设备栈中高层的设备先处理IRP,然后决定是否传给低一层的设备.
实现挂载的内核函数是loAttachDeviceToDeviceStack,其声明如下:
PDEVICE_OBJECT
IoAttachDeviCeToDeviceStack (
IN PDEVICE_OBJECT SourceDevice, //设备栈中的一个设备指针
IN PDEVICE_OEITECT TargetDevice //将TargetDevice 挂载在设备栈的栈顶
);
当驱动程序卸载的时候,驱动程序所创建的设备对象应该从设备栈中弹出。出栈的顺
序必须和入栈的顺序相反。从设备栈弹出的内核函数是IoDetachDevice,其函数声明如下:
VOID
IoDetachDevice (
IN OUT PDEVICE_OBJECT TargetDevice //如果删除的设备栈栈顶的设备C 那么函数运行完TargetDevice的值就是设备B
);
I/O堆栈
在Windows驱动模型中,还有一个概念叫做“I/O堆栈”,用IO_STACK_LOCATION
数据结构表示。它和设备堆栈紧密联合。IRP一般会由应用程序的ReadFile或WriteFile创
建,然后发送到设备堆栈的顶层。如果最上层的设备不处理IRP,就会将IRP转发到下一
层设备。每一-层设备堆栈都有可能处理IRP。
在IRP的数据结构中,存储着一个IO STACK LOCATION数组的指针。调用lAllocateIrp
内核函数创建IRP时,有一个StackSize 参数,该参数就是I0_STACK_LOCATION数组的
大小。
作用:IRP每穿越一次设备堆栈, 就会用I0_STACK_LOCATION记录下本次操作的某些属
性。
I/O堆栈和设备堆栈的关系如图12-2所示。
向下转发IRP
当顶层驱动的设备对象收到IRP请求并进入派遣函数后,有多种选择方式处理IRP。
(1)直接处理该IRP,即调用IoCompleteRequest内核函数。
(2)调用StartIO,操作系统会将IRP请求串行化。除了当前运行的IRP,其他的IRP请求进入IRP队列。
(3)选择让底层驱动完成IRP。
向下转发IRP涉及设备堆栈和I/O 堆栈。一一个设备堆栈对应着一个I/O 堆栈元素( IO_STACK_LOCATION)。 IRP内部有个指针指向当前正在使用的IO_STACK_LOCATION,可以使用内核宏loGetCurrentIrpStackLocation获得当前IO堆栈。
每次调用loCallDriver时,内核函数都会将IRP的当前指针下移,指向下一个IO_STACK_ LOCATION指针。
但有时候,当前设备堆栈不对IRP做任何处理。因此,当前设备就不需要对应I/O堆
栈。但是loCallDriver已经将当前I/O堆栈向下移动了一个单位,所以DDK提供了内核宏
IoSkipCurrentIrpStackLocation,它的作用就是将当前I/O堆栈又往回(上)移动一个单位。
这样loCallDriver和loSkipCurrentIrpStackI ocation对设备堆栈的移动就实现了平衡,
也就是没有改变。这时loCallDriver调用的低一层驱动所用到的IO堆栈,实际上和一层用到的是同一个。因此,当本层驱动不需要用I/O堆栈时,
挂载设备对象实例
下面的示例是将DriverB加载在DriverA上,并且在DriverB的派遣函数中不对当前
设备栈处理,直接将IRP转发到下层设备上,即DriverA上。
DriverA是实现读的普通驱动,DriverB 首先在DriverEntry上,用loGetDeviceObject
Pointer找到DriverA所创建的设备对象,并将自己的设备对象挂载在设备栈上,并用设备
扩展将底层驱动的设备对象记录下来。
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;
//Irp默认处理
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_WRITE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_READ] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLEANUP] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SET_INFORMATION] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SHUTDOWN] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = IrpDefaultProc;
//设置派遣函数和卸载函数
pDriver->DriverUnload = DriverUnload;
pDriver->MajorFunction[IRP_MJ_READ] = IrpReadProc;
//创建设备名称
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
//创建设备 让三环的API能够找到,才能实现通信
status = IoCreateDevice(pDriver,
sizeof(DEVICE_EXTENSION), //扩展设备大小
&DeviceName,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! status=%x\r\n", status);
return status;
}
//设置交互数据方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
//创建符号链接名称,就是给该设备在三环起个能用的别名
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINE_NAME);
//创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if (!NT_SUCCESS(status))
{
DbgPrint("创建符号链接失败!\r\n");
IoDeleteDevice(pDeviceObj);
return status;
}
PDEVICE_EXTENSION pDevExt = pDeviceObj->DeviceExtension;
pDevExt->pDevice = pDeviceObj;
pDevExt->ustrDeviceName = DeviceName;
pDevExt->ustrSymLinkName = SymbolicLinkName;
/*测试代码*/
UNICODE_STRING DevName;
RtlInitUnicodeString(&DevName, CALLED_DEVICE_NAME);
PDEVICE_OBJECT DeviceObject = NULL;
PFILE_OBJECT FileObject = NULL;
//第一步 得到设备对象指针,计数器加1
//如果是第一次调用IoGetDeviceObjectPointer,会打开设备,相当于调用ZwCreateFile
status = IoGetDeviceObjectPointer(&DevName, FILE_ALL_ACCESS, &FileObject, &DeviceObject);
DbgPrint("打开的设备对象指针是:%p\n", DeviceObject);
//附加设备到目标的设备栈
PDEVICE_OBJECT pTargetDevice = IoAttachDeviceToDeviceStack(pDeviceObj, DeviceObject);
DbgPrint("附加的设备对象指针是:%p\n", pTargetDevice);
//保存附加的目标设备指针
pDevExt->AttachedDeciveObjectPointer = pTargetDevice;
/*测试代码*/
//ObDereferenceObject函数对象引用计数-1, 该函数先是对传过去的内核对象-0x18 得到_OBJECT_HEADER结构地址,然后PointCount-1 再比较该值是否为0,如果为0 就删除该内核对象.
ObDereferenceObject(FileObject);
//解除附加可以在卸载函数中进行.
IoDetachDevice(pTargetDevice);
return status;
}
转发IRP实例
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
NTSTATUS status = STATUS_SUCCESS;
//得到设备扩展
PDEVICE_EXTENSION pDeviceExtension = pDeviceObject->DeviceExtension;
//略过当前I/O堆栈
IoSkipCurrentIrpStackLocation(pIrp);
//调用底层驱动
status = IoCallDriver(pDeviceExtension->AttachedDeciveObjectPointer,pIrp);
DbgPrint("设备栈顶层设备不处理此IRP 转发给下一层处理\n");
return status;
}
driver.h头文件
#pragma once
#include <ntddk.h>
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")
#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")
#define arraysize(p) (sizeof(p)/sizeof((p)[0]))
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName; //设备名称
UNICODE_STRING ustrSymLinkName; //符号链接名
PDEVICE_OBJECT AttachedDeciveObjectPointer;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
完整代码: 被附加驱动A
#include<ntddk.h>
#include<ntstatus.h>
#define DEVICE_NAME L"\\Device\\MyDevice"
#define SYMBOLICLINE_NAME L"\\??\\MyTestDriver" //ring3用CreateFile打开设备时,用"\\\\.\\MyTestDriver"//相当于起的别名
#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};
RtlInitUnicodeString(&SymbolicLinkName,SYMBOLICLINE_NAME);
IoDeleteDevice(pDriver->DeviceObject);
IoDeleteSymbolicLink(&SymbolicLinkName);
}
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;//getlasterror()得到的就是这个值
pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pdriver, PIRP pIrp)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS IrpReadProc(PDEVICE_OBJECT pdriver, PIRP pIrp)
{
DbgPrint("驱动A 处理IRP\n");
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
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;
//创建设备名称
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
//创建设备 让三环的API能够找到,才能实现通信
status = IoCreateDevice(pdriver, 0, &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! status=%x\r\n",status);
return status;
}
//设置交互数据方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
//创建符号链接名称,就是给该设备在三环起个能用的别名
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINE_NAME);
//创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if (!NT_SUCCESS(status))
{
DbgPrint("创建符号链接失败!\r\n");
IoDeleteDevice(pDeviceObj);
return status;
}
//设置派遣函数和卸载函数
pdriver->MajorFunction[IRP_MJ_CREATE] = IrpCreateProc;
pdriver->MajorFunction[IRP_MJ_CLOSE] = IrpCloseProc;
pdriver->MajorFunction[IRP_MJ_READ] = IrpReadProc;
pdriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
附加的驱动B
#include<ntddk.h>
#include<ntstatus.h>
#include "Driver.h"
#define DEVICE_NAME L"\\Device\\dale"
#define CALLED_DEVICE_NAME L"\\Device\\MyDevice"
#define SYMBOLICLINE_NAME L"\\??\\dale_symbol" //ring3用CreateFile打开设备时,用"\\\\.\\MyTestDriver"//相当于起的别名
#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 pDriverObject)
{
PDEVICE_OBJECT pNextObj;
KdPrint(("Enter DriverUnload\n"));
pNextObj = pDriverObject->DeviceObject;
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
pNextObj->DeviceExtension;
//删除符号链接
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName);
IoDetachDevice(pDevExt->AttachedDeciveObjectPointer);
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
DbgPrint("删除设备\n");
}
}
NTSTATUS IrpDefaultProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;//getlasterror()得到的就是这个值
pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
DbgPrint("设备栈顶层设备不处理此IRP 转发给下一层处理\n");
NTSTATUS status = STATUS_SUCCESS;
//得到设备扩展
PDEVICE_EXTENSION pDeviceExtension = pDeviceObject->DeviceExtension;
//略过当前I/O堆栈
IoSkipCurrentIrpStackLocation(pIrp);
//调用底层驱动
status = IoCallDriver(pDeviceExtension->AttachedDeciveObjectPointer, pIrp);
return status;
}
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;
//Irp默认处理
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_WRITE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_READ] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLEANUP] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SET_INFORMATION] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SHUTDOWN] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = IrpDefaultProc;
//设置派遣函数和卸载函数
pDriver->DriverUnload = DriverUnload;
pDriver->MajorFunction[IRP_MJ_READ] = IrpReadProc;
//创建设备名称
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
//创建设备 让三环的API能够找到,才能实现通信
status = IoCreateDevice(pDriver,
sizeof(DEVICE_EXTENSION), //扩展设备大小
&DeviceName,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! status=%x\r\n", status);
return status;
}
//设置交互数据方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
//创建符号链接名称,就是给该设备在三环起个能用的别名
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINE_NAME);
//创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if (!NT_SUCCESS(status))
{
DbgPrint("创建符号链接失败!\r\n");
IoDeleteDevice(pDeviceObj);
return status;
}
PDEVICE_EXTENSION pDevExt = pDeviceObj->DeviceExtension;
pDevExt->pDevice = pDeviceObj;
pDevExt->ustrDeviceName = DeviceName;
pDevExt->ustrSymLinkName = SymbolicLinkName;
/*测试代码*/
//获得设备指针
UNICODE_STRING TargetDeviceName;
RtlInitUnicodeString(&TargetDeviceName, CALLED_DEVICE_NAME);
PFILE_OBJECT fileObject;
PDEVICE_OBJECT pTargetDevice;
status = IoGetDeviceObjectPointer(&TargetDeviceName, FILE_ALL_ACCESS, &fileObject, &pTargetDevice);
if (!NT_SUCCESS(status))
{
IoDeleteSymbolicLink(&SymbolicLinkName);
IoDeleteDevice(pDeviceObj);
//文件对象引用计数-1
ObDereferenceObject(fileObject);
return status;
}
status = IoGetDeviceObjectPointer(&TargetDeviceName, FILE_ALL_ACCESS, &fileObject, &pTargetDevice);
PDEVICE_OBJECT attactedDevice = IoAttachDeviceToDeviceStack(pDeviceObj, pTargetDevice);
if (!attactedDevice)
{
IoDeleteSymbolicLink(&SymbolicLinkName);
IoDeleteDevice(pDeviceObj);
//文件对象引用计数-1
ObDereferenceObject(fileObject);
return STATUS_DEVICE_ALREADY_ATTACHED;
}
pDevExt->AttachedDeciveObjectPointer = attactedDevice;
DbgPrint("attactedDevice=%p!\r\n", attactedDevice);
DbgPrint("pTargetDevice=%p!\r\n", pTargetDevice);
//文件对象引用计数-1
pDevExt->pDevice->DeviceType = attactedDevice->DeviceType;
pDevExt->pDevice->Characteristics = attactedDevice->Characteristics;
pDevExt->pDevice->Flags &= ~DO_DEVICE_INITIALIZING;
pDevExt->pDevice->Flags |= (attactedDevice->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO));
ObDereferenceObject(fileObject);
/*测试代码*/
return STATUS_SUCCESS;
}
3环测试代码
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
int main()
{
HANDLE hDevice =
CreateFile(L"\\\\.\\MyTestDriver",
GENERIC_READ | GENERIC_WRITE,
0, // share mode none
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ); // no template
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("Failed to obtain file handle to device "
"with Win32 error code: %d\n",
GetLastError() );
return 1;
}
DWORD dRet;
ReadFile(hDevice,NULL,0,&dRet,NULL);
CloseHandle(hDevice);
return 0;
}