《Windows驱动开发技术详解》之驱动程序的基本结构

  • 驱动对象

每个驱动程序会有唯一的驱动对象与之对应,并且这个驱动对象是在驱动加载的时候被内核中的对象管理程序所创建的。驱动对象用DRIVER_OBJECT数据结构表示,它作为驱动的一个实例被内核加载,并且内核对一个驱动只加载一个实例。确切的说,是由内核中的I/O管理器负责加载的。驱动程序需要在DriverEntry中初始化。

其结构如下:

 1 typedef struct _DRIVER_OBJECT {
 2     CSHORT Type;
 3     CSHORT Size;
 4 
 5     //
 6     // The following links all of the devices created by a single driver
 7     // together on a list, and the Flags word provides an extensible flag
 8     // location for driver objects.
 9     //
10 
11     PDEVICE_OBJECT DeviceObject;//每个驱动程序会有一个或多个设备对象,每个设备对象都有
12     //一个指针指向下一个设备对象,最后一个设备对象指向空。此处的DeviceObject指向驱动对象的第一个设备对象。
13     //通过DeviceObject,就可以遍历驱动对象里的所有设备对象。
14     ULONG Flags;
15 
16     //
17     // The following section describes where the driver is loaded.  The count
18     // field is used to count the number of times the driver has had its
19     // registered reinitialization routine invoked.
20     //
21 
22     PVOID DriverStart;
23     ULONG DriverSize;
24     PVOID DriverSection;
25     PDRIVER_EXTENSION DriverExtension;
26 
27     //
28     // The driver name field is used by the error log thread
29     // determine the name of the driver that an I/O request is/was bound.
30     //
31 
32     UNICODE_STRING DriverName;//驱动程序的名字,一般为\Driver\[驱动程序名称]
33 
34     //
35     // The following section is for registry support.  Thise is a pointer
36     // to the path to the hardware information in the registry
37     //
38 
39     PUNICODE_STRING HardwareDatabase;
40 
41     //
42     // The following section contains the optional pointer to an array of
43     // alternate entry points to a driver for "fast I/O" support.  Fast I/O
44     // is performed by invoking the driver routine directly with separate
45     // parameters, rather than using the standard IRP call mechanism.  Note
46     // that these functions may only be used for synchronous I/O, and when
47     // the file is cached.
48     //
49 
50     PFAST_IO_DISPATCH FastIoDispatch;
51 
52     //
53     // The following section describes the entry points to this particular
54     // driver.  Note that the major function dispatch table must be the last
55     // field in the object so that it remains extensible.
56     //
57 
58     PDRIVER_INITIALIZE DriverInit;
59     PDRIVER_STARTIO DriverStartIo;
60     PDRIVER_UNLOAD DriverUnload;
61     PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
62 
63 } DRIVER_OBJECT;

其结构如图所示:

  • 设备对象

如上所述,设备对象会有一个指针指向下一个设备对象,形成一个设备链,设备链上的第一个设备是由DRIVER_OBJECT结构体中指明的。

其定义如下:

 1 typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _DEVICE_OBJECT{
 2     CSHORT Type;
 3     USHORT Size;
 4     LONG ReferenceCount;
 5     struct _DRIVER_OBJECT *DriverObject;//指向驱动程序中的驱动对象。同属于一个驱动程序的设备
 6     //对象指向的是统一的驱动对象
 7     struct _DEVICE_OBJECT *NextDevice;//设备链中的下一个设备对象
 8     struct _DEVICE_OBJECT *AttachedDevice;//通常指向的是过滤驱动的设备对象
 9     struct _IRP *CurrentIrp;//使用StartIO例程的时候,指向正在处理的IRP包
10     PIO_TIMER Timer;
11     ULONG Flags;                                // See above:  DO_...
12     ULONG Characteristics;                      // See ntioapi:  FILE_...
13     __volatile PVPB Vpb;
14     PVOID DeviceExtension;//指向设备的扩展对象,被指向的这个结构体是由程序员自己定义的
15     DEVICE_TYPE DeviceType;//指明设备的类型,如果创建的是虚拟设备,应选择FILE_DEVICE_UNKNOWN
16     CCHAR StackSize;
17     union {
18         LIST_ENTRY ListEntry;
19         WAIT_CONTEXT_BLOCK Wcb;
20     } Queue;
21     ULONG AlignmentRequirement;
22     KDEVICE_QUEUE DeviceQueue;
23     KDPC Dpc;
24 
25     //
26     //  The following field is for exclusive use by the filesystem to keep
27     //  track of the number of Fsp threads currently using the device
28     //
29 
30     ULONG ActiveThreadCount;
31     PSECURITY_DESCRIPTOR SecurityDescriptor;
32     KEVENT DeviceLock;
33 
34     USHORT SectorSize;
35     USHORT Spare1;
36 
37     struct _DEVOBJ_EXTENSION  *DeviceObjectExtension;
38     PVOID  Reserved;
39 
40 } DEVICE_OBJECT;

设备对象结构如图所示:

  • NT式驱动的基本结构

DriverEntry对驱动程序进行初始化工作,它是由系统进程所调用的。驱动加载的时候,系统进程启动新的线程,调用执行体组件中的对象管理器,创建一个驱动对象。这个驱动对象是一个DRIVER_OBJECT的结构体。另外,系统进程调用执行体组件中的配置管理程序,查询此驱动程序对应的注册表中的项。

对象管理器传入一个没有被初始化的对象的指针pDriverObject,而这个指针所指向的结构体就在DriverEntry中被填入内容(初始化)

在DriverEntry中,一般设置卸载例程和IRP的派遣函数,这些都是对驱动对象的设置。

  • 创建设备对象
NTSTATUS IoCreateDevice(
  _In_     PDRIVER_OBJECT  DriverObject,
  _In_     ULONG           DeviceExtensionSize,
  _In_opt_ PUNICODE_STRING DeviceName,//设备名用UNICODE字符串指定,且字符串必须是“\Device\[设备名]”
  _In_     DEVICE_TYPE     DeviceType,
  _In_     ULONG           DeviceCharacteristics,
  _In_     BOOLEAN         Exclusive,
  _Out_    PDEVICE_OBJECT  *DeviceObject
);

设备名只能被内核模式下的其它驱动所识别。用户模式下的程序要识别设备只有两种方法,一种是通过符号链接找到设备,二是通过设备接口找到设备。例如,C盘,指的是名为“C:”的符号链接,其真正的设备对象是“\Device\HarddiskVolume1”。

创建符号链接的函数如下:

NTSTATUS IoCreateSymbolicLink(
  _In_ PUNICODE_STRING SymbolicLinkName,
  _In_ PUNICODE_STRING DeviceName
);

在内核模式下,符号链接是以“\??\”开头的,如C盘就是“\??\C:”,而用户模式下,是以“\\.\”开头的,如“\\.\C:”

  • DriverUnload

在驱动对象中设置DriverUnload例程,此例程在驱动被卸载的时候调用。它通常负责删除在DriverEntry中创建的设备对象,并且将设备对象所关联的符号链接删除。另外,它还进行一些资源的回收。

  •  WinObj

以管理员身份运行才可以看到驱动对象:

查看设备对象:

查看符号链接:

  • DeviceTree

查看驱动对象:

查看设备对象:

  • 设备的层次结构

驱动程序的垂直层次结构:

每层的设备对象由不同的驱动程序所创建,或者说每层的设备对应着不同的驱动程序。底层设备对象寻找上一层的设备对象是依靠底层设备对象的AttachedDevice来寻找,如果某一个设备的AttachedDevice为空,说明已经到了设备堆栈的顶部,如图所示:

而高层设备寻找低一层的设备对象,设备对象没有相关子域可以使用。解决的办法是通过程序员自定义设备扩展,在设备扩展记录低一层的设备对象。

驱动程序的水平层次结构:

 

以USB设备为例,首先在PCI总线上,会枚举到USB控制器设备,并加载PDO和FDO。这个FDO会枚举在这个控制器上的USB HUB设备,为之创建PDO,并加载相应的FDO。该FDO枚举所有插在USB HUB上的USB设备,为之创建PDO,并加载相应的FDO。我们可以利用DriverTree查看一个驱动设备的加载过程:

  • 实验:枚举设备对

代码如下:

但此时输出结果又乱码:

出现乱码是因为,Buffer的类型是PWCHAR,以%S格式输出这个字符串需要Buffer有0结尾来判断输出的长度,而在给UnicodeString类型的赋值时,其中的Buffer并没有传入这个0。

所以应改为如下代码:

这样就能正常输出了:

posted @ 2016-05-22 11:36  _No.47  阅读(5637)  评论(0编辑  收藏  举报