NT式驱动和WDM式驱动程序

1.Windows驱动程序分为两类,一类是不支持即插即用功能的NT式的驱动程序;另一类是支持即插即用功能的WDM式的驱动程序。

 

2.NT式的驱动程序要导入的头文件时NTDDK.H,而WDM式的驱动要导入的头文件为WDM.H.

 

3.DriverEntry需要放在INIT标志的内存中。INIT标志指明该函数只是在加载的时候需要载入内存,而当驱动程序加载成功后,该函数可以从内存中卸载掉。

 

4.C++编写驱动需要注意:

#ifdef  __cplusplus

extern  “C”

{

#endif

#include <NTDDK.H>

#ifdef  __cplusplus

}

#endif

 

#pragma  INITCODE  extern  “C”  NTSTATUS   DriverEntry(IN  PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING  pRegistryPath){}

 

5.来看一个简单的NT式驱动

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
60
61
#pragma once


#ifdef __cplusplus

extern "C"

{

#endif

#include <NTDDK.h>

#ifdef __cplusplus

}

#endif



#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;    //符号链接名

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;



// 驱动函数声明

NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject);

VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject);

NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,

                                 IN PIRP pIrp);

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#include "Driver.h"



/************************************************************************

* 函数名称:DriverEntry

* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象

* 参数列表:

      pDriverObject:从I/O管理器中传进来的驱动对象

      pRegistryPath:驱动程序在注册表的中的路径

* 返回 值:返回初始化驱动状态

*************************************************************************/


#pragma INITCODE

extern "C" NTSTATUS DriverEntry (

            IN PDRIVER_OBJECT pDriverObject,

            IN PUNICODE_STRING pRegistryPath    )

{

    NTSTATUS status;

    KdPrint(("Enter DriverEntry\n"));



    //注册其他驱动调用函数入口

    pDriverObject->DriverUnload = HelloDDKUnload;

    pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;

    pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;

    pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;

    pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;

   

    //创建驱动设备对象

    status = CreateDevice(pDriverObject);



    KdPrint(("DriverEntry end\n"));

    return status;

}



/************************************************************************

* 函数名称:CreateDevice

* 功能描述:初始化设备对象

* 参数列表:

      pDriverObject:从I/O管理器中传进来的驱动对象

* 返回 值:返回初始化状态

*************************************************************************/


#pragma INITCODE   //指明此函数加载到INIT内存区域(即只在加载的时候需要载入内存,加载成功后可以从内存中卸载掉)

NTSTATUS CreateDevice (

        IN PDRIVER_OBJECT    pDriverObject)

{

    NTSTATUS status;

    PDEVICE_OBJECT pDevObj;

    PDEVICE_EXTENSION pDevExt;

   

    //创建设备名称

    UNICODE_STRING devName;

    RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");

   

    //创建设备

    status = IoCreateDevice( pDriverObject,

                        sizeof(DEVICE_EXTENSION),

                        &(UNICODE_STRING)devName,

                        FILE_DEVICE_UNKNOWN,//此种设备为独占设备

                        0, TRUE,

                        &pDevObj );

    if (!NT_SUCCESS(status))

        return status;



    pDevObj->Flags |= DO_BUFFERED_IO;

    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

    pDevExt->pDevice = pDevObj;

    pDevExt->ustrDeviceName = devName;

    //创建符号链接

    UNICODE_STRING symLinkName;

    RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");

    pDevExt->ustrSymLinkName = symLinkName;

    status = IoCreateSymbolicLink( &symLinkName,&devName );

    if (!NT_SUCCESS(status))

    {

        IoDeleteDevice( pDevObj );

        return status;

    }

    return STATUS_SUCCESS;

}



/************************************************************************

* 函数名称: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 );

    }

}



/************************************************************************

* 函数名称:HelloDDKDispatchRoutine

* 功能描述:对读IRP进行处理

* 参数列表:

      pDevObj:功能设备对象

      pIrp:从IO请求包

* 返回 值:返回状态

*************************************************************************/


#pragma PAGEDCODE

NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,

                                 IN PIRP pIrp)

{

    KdPrint(("Enter HelloDDKDispatchRoutine\n"));

    NTSTATUS status = STATUS_SUCCESS;

    // 完成IRP

    pIrp->IoStatus.Status = status;

    pIrp->IoStatus.Information = 0;    // bytes xfered

    IoCompleteRequest( pIrp, IO_NO_INCREMENT );

    KdPrint(("Leave HelloDDKDispatchRoutine\n"));

    return status;

}

驱动虽然有了设备名称,但是这种设备名只能在内核态可见,而对于应用程序时不可见的。因此,驱动需要暴露一个符号链接,该链接指向真正的设备名称。

编译好生成的sys文件,我们可以使用KmdManager.exe加载,加载->启动->停止->卸载。

image

6.再来看看WDM式驱动有什么不同。头文件就直接忽略了。

DriverEntry入口函数:

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
/************************************************************************
* 函数名称:DriverEntry
* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
      pRegistryPath:驱动程序在注册表的中的路径
* 返回 值:返回初始化驱动状态
*************************************************************************/

#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
                                IN PUNICODE_STRING pRegistryPath)
{
    KdPrint(("Enter DriverEntry\n"));

    pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
    pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
    pDriverObject->MajorFunction[IRP_MJ_CREATE] =
    pDriverObject->MajorFunction[IRP_MJ_READ] =
    pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
    pDriverObject->DriverUnload = HelloWDMUnload;

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

这里增加了一个设置AddDevice回调函数,此回调函数的作用是创建设备对象并由PNP(即插即用)管理器调用。并设置对IRP_MJ_PNP的IRP的回调函数。这都是NT和WDM驱动最大的不同点。

而且在WDM驱动中,大部分卸载工作都不是由DriverUnload来处理,而是放在对IRP_MN_REMOVE_DEVICE的IRP的处理函数中处理。

下面是AddDevice回调函数的处理。

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
/************************************************************************
* 函数名称: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;

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

其中PAGED_CODE是一个DDK提供的宏,只在check版中有效。当此例程所在的中断请求及超过APC_LEVEL时,会产生一个断言。

本例对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;
}

而对卸载例程的处理可以什么都不用做。

其他PNP的IRP如果不需要做处理,那么直接传递到底层驱动,并将底层驱动的结果返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/************************************************************************
* 函数名称:DefaultPnpHandler
* 功能描述:对PNP IRP进行缺省处理
* 参数列表:
      pdx:设备对象的扩展
      Irp:从IO请求包
* 返回 值:返回状态
*************************************************************************/

#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{
    PAGED_CODE();
    KdPrint(("Enter DefaultPnpHandler\n"));
    IoSkipCurrentIrpStackLocation(Irp);
    KdPrint(("Leave DefaultPnpHandler\n"));
    return IoCallDriver(pdx->NextStackDevice, Irp);
}

安装WDM式驱动需要一个inf文件。inf文件描述了WDM驱动程序的操作硬件设备的信息和驱动程序的一些信息。

可以直接右击这个inf文件进行安装即可。

本文链接:http://www.alonemonkey.com/driver-nt-wdm.html