NT驱动和WDM驱动的结构层次

1.驱动对象(DRIVER_OBJECT)

typedef struct _DRIVER_OBJECT
{
     SHORT Type;
     SHORT Size;
     PDEVICE_OBJECT DeviceObject;          //每个驱动会有一个或多个设备对象,此处指向第一个设备对象,通过它可以遍历驱动对象里的所有设备
     ULONG Flags;
     PVOID DriverStart;                  //从这两个属性可以得到驱动加载的区域
     ULONG DriverSize;
     PVOID DriverSection;
     PDRIVER_EXTENSION DriverExtension;
     UNICODE_STRING DriverName;                //驱动的名字
     PUNICODE_STRING HardwareDatabase;
     PFAST_IO_DISPATCH FastIoDispatch;
     LONG * DriverInit;
     PVOID DriverStartIo;
     PVOID DriverUnload;
     LONG * MajorFunction[28];                   //记录的是一个函数指针数组,每一个函数就是IRP的派遣函数
} DRIVER_OBJECT, *PDRIVER_OBJECT;

image

2.设备对象(DEVICE_OBJECT)

typedef struct _DEVICE_OBJECT {
  CSHORT                      Type;
  USHORT                      Size;
  LONG                        ReferenceCount;
  struct _DRIVER_OBJECT  *DriverObject;               //指向驱动中的驱动对象
  struct _DEVICE_OBJECT  *NextDevice;                //指向下一个设备对象,这里指的是属于同一驱动的若干设备的下一个
  struct _DEVICE_OBJECT  *AttachedDevice;          //如果有更高一层的驱动附加到这个驱动的时候,AttachedDevice指的就是更高一层的驱动
  struct _IRP  *CurrentIrp;
  PIO_TIMER                   Timer;
  ULONG                       Flags;
  ULONG                       Characteristics;           //设备的扩展对象,每个都会指定一个设备扩展对象记录自己定义的特殊结构体
  __volatile PVPB             Vpb;
  PVOID                       DeviceExtension;
  DEVICE_TYPE                 DeviceType;              //设备的类型
  CCHAR                       StackSize;     //多层驱动情况下,驱动与驱动之间会形成类似堆栈的结构,IRP会一次从最高层传递到最底层,StackSize描述的就是这个层数
  union {
    LIST_ENTRY         ListEntry;
    WAIT_CONTEXT_BLOCK Wcb;
  } Queue;
  ULONG                       AlignmentRequirement;
  KDEVICE_QUEUE               DeviceQueue;
  KDPC                        Dpc;
  ULONG                       ActiveThreadCount;
  PSECURITY_DESCRIPTOR        SecurityDescriptor;
  KEVENT                      DeviceLock;
  USHORT                      SectorSize;
  USHORT                      Spare1;
  struct _DEVOBJ_EXTENSION  *  DeviceObjectExtension;
  PVOID                       Reserved;
} DEVICE_OBJECT, *PDEVICE_OBJECT;

image

3.NT驱动 DriverUnload例程中遍历设备删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/************************************************************************
* 函数名称:HelloDDKUnload
* 功能描述:负责驱动程序的卸载操作
* 参数列表:
      pDriverObject:驱动对象
* 返回 值:返回状态
*************************************************************************/

#pragma PAGEDCODE
VOID HelloDDKUnload (IN 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);
        pNextObj = pNextObj->NextDevice;
        IoDeleteDevice( pDevExt->pDevice );
    }
}

4.用WinObj观察驱动对象和设备对象

image

5.用DeviceTree观察驱动对象和设备对象

image

6.在WDM模型中,完成一个设备的操作,至少有两个设备对象共同完成。其中,一个是物理设备对象(PDO),另一个是功能设备对象(FDO).

当一个FDO附加在PDO上的时候,PDO设备对象的子域AttachedDevice会记录FDO的位置。PDO被称作底层驱动或者下层驱动,而FDO被称作高层驱动或者上层驱动。

image

这是最简单的一种情况,事实要比这个更加复杂。在FDO和FDO之间还会存在过滤驱动,在FDO上面的过滤驱动被称作上层过滤驱动,在FDO下层的驱动被称为下层过滤驱动。

image

7.NT驱动是主要加载设备的,也就是驱动一旦加载就创建设备。而WDM驱动是被动加载设备的,操作系统必须加载PDO以后,调用驱动的AddDevice例程,AddDevice例程中负责创建FDO,并且附加到PDO之上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/************************************************************************
* 函数名称:HelloWDMAddDevice
* 功能描述:添加新设备
* 参数列表:
      DriverObject:从I/O管理器中传进来的驱动对象
      PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象
* 返回 值:返回添加新设备状态
*************************************************************************/

#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject)
{
    PAGED_CODE();
    KdPrint(("Enter HelloWDMAddDevice\n"));

    NTSTATUS status;
    PDEVICE_OBJECT fdo;
    UNICODE_STRING devName;
    RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");
    status = IoCreateDevice(
        DriverObject,
        sizeof(DEVICE_EXTENSION),
        &(UNICODE_STRING)devName,
        FILE_DEVICE_UNKNOWN,
        0,
        FALSE,
        &fdo);
    if( !NT_SUCCESS(status))
        return status;
    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
    pdx->fdo = fdo;
    pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");

    pdx->ustrDeviceName = devName;
    pdx->ustrSymLinkName = symLinkName;
    status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);

    if( !NT_SUCCESS(status))
    {
        IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
        status = IoCreateSymbolicLink(&symLinkName,&devName);
        if( !NT_SUCCESS(status))
        {
            return status;
        }
    }

    fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
    fdo->Flags &= ~DO_DEVICE_INITIALIZING;


    Dump(DriverObject);
    DumpDeviceStack(PhysicalDeviceObject);

    KdPrint(("Leave HelloWDMAddDevice\n"));
    return STATUS_SUCCESS;
}

设备对象PhysicalDeviceObject就是底层总线驱动创建的PDO设备对象。传进该参数的目的就是将FDO附在PDO之上。

AddDevice基本步骤:

1. AddDevice通过IoCreateDevice函数创建FDO,创建FDO的符号链接

2. 在驱动设备扩展保存刚才创建的FDO的地址。

3. 调用IoAttachDeviceToDeviceStack()将FDO附加到PDO上。

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
  _In_  PDEVICE_OBJECT SourceDevice,
  _In_  PDEVICE_OBJECT TargetDevice
);

SourceDevice:FDO附加在PDO上时,这个参数代表FDO。

TargetDevice:被附加的设备。如果在FDO与PDO之间存在过滤驱动,则FDO实际上是附加在过滤驱动上的,过滤驱动则附加在PDO上。

返回值:返回SourceDevice的下层设备。如果没有过滤驱动那就是PDO,如果有就是过滤驱动

 

8.WDM 对IRP_MN_REMOVE_DEVICE的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/************************************************************************
* 函数名称:HandleRemoveDevice
* 功能描述:对IRP_MN_REMOVE_DEVICE IRP进行处理
* 参数列表:
      fdo:功能设备对象
      Irp:从IO请求包
* 返回 值:返回状态
*************************************************************************/

#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{
    PAGED_CODE();
    KdPrint(("Enter HandleRemoveDevice\n"));

    Irp->IoStatus.Status = STATUS_SUCCESS;
    NTSTATUS status = DefaultPnpHandler(pdx, Irp);
    IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);

    //调用IoDetachDevice()把fdo从设备栈中脱开:
    if (pdx->NextStackDevice)
        IoDetachDevice(pdx->NextStackDevice);
   
    //删除fdo:
    IoDeleteDevice(pdx->fdo);
    KdPrint(("Leave HandleRemoveDevice\n"));
    return status;
}

本文链接:http://www.alonemonkey.com/driver-struct-layer.html