30、驱动程序调用驱动程序
有两种方法,一种是以文件句柄的形式,另外一种是通过设备指针调用其它驱动程序。
1、以文件句柄形式调用
1)应用程序 调用 驱动A 调用 驱动B
这种方法类似于在应用程序中调用驱动程序。
在应用程序中用CreateFile,ReadFile,CloseHandle来操作相应文件,驱动中用ZwCreateFile,ZwReadFile,Irp结束操作。
要注意:
ZwCreateFile,如果是同步打开设备,则参数DesiredAccess,设为SYNCHRONIZE,参数CreateOptions设为FILE_SYNCHRONOUS_IO_ALERT或者是FILE_SYNCHRONOUS_IO_NONALERT。如果是异步打开设备,则参数DesiredAccess,不设为SYNCHRONIZE,且CreateOptions不能指定为上面两者中任何其一。
1 //DriverA
2 typedef struct _DEVICE_EXTENSION {
3 PDEVICE_OBJECT pDevice;
4 UNICODE_STRING ustrDeviceName; //设备名称
5 UNICODE_STRING ustrSymLinkName; //符号链接名
6
7 KDPC pollingDPC; // 存储DPC对象
8 KTIMER pollingTimer;// 存储计时器对象
9 PIRP currentPendingIRP;//记录当前挂起的IRP
10 } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
11
12 #pragma LOCKEDCODE
13 VOID OnTimerDpc( IN PKDPC pDpc,
14 IN PVOID pContext,
15 IN PVOID SysArg1,
16 IN PVOID SysArg2 )
17 {
18 PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pContext;
19 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
20
21 PIRP currentPendingIRP = pdx->currentPendingIRP;
22
23 KdPrint(("DriverA:complete the Driver A IRP_MJ_READ irp!\n"));
24
25 //设置完成状态为STATUS_CANCELLED
26 currentPendingIRP->IoStatus.Status = STATUS_SUCCESS;
27 currentPendingIRP->IoStatus.Information = 0; // bytes xfered
28 IoCompleteRequest( currentPendingIRP, IO_NO_INCREMENT );
29 }
30
31 /************************************************************************
32 * 函数名称:CreateDevice
33 * 功能描述:初始化设备对象
34 * 参数列表:
35 pDriverObject:从I/O管理器中传进来的驱动对象
36 * 返回 值:返回初始化状态
37 *************************************************************************/
38 #pragma INITCODE
39 NTSTATUS CreateDevice (
40 IN PDRIVER_OBJECT pDriverObject)
41 {
42 NTSTATUS status;
43 PDEVICE_OBJECT pDevObj;
44 PDEVICE_EXTENSION pDevExt;
45
46 //创建设备名称
47 UNICODE_STRING devName;
48 RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDeviceA");
49
50 //创建设备
51 status = IoCreateDevice( pDriverObject,
52 sizeof(DEVICE_EXTENSION),
53 &(UNICODE_STRING)devName,
54 FILE_DEVICE_UNKNOWN,
55 0, TRUE,
56 &pDevObj );
57 if (!NT_SUCCESS(status))
58 return status;
59
60 pDevObj->Flags |= DO_BUFFERED_IO;
61 pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
62 pDevExt->pDevice = pDevObj;
63 pDevExt->ustrDeviceName = devName;
64
65 KeInitializeTimer( &pDevExt->pollingTimer );
66
67 KeInitializeDpc( &pDevExt->pollingDPC,
68 OnTimerDpc,
69 (PVOID) pDevObj );
70
71 //创建符号链接
72 UNICODE_STRING symLinkName;
73 RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDKA");
74 pDevExt->ustrSymLinkName = symLinkName;
75 status = IoCreateSymbolicLink( &symLinkName,&devName );
76 if (!NT_SUCCESS(status))
77 {
78 IoDeleteDevice( pDevObj );
79 return status;
80 }
81 return STATUS_SUCCESS;
82 }
83 /************************************************************************
84 * 函数名称:HelloDDKRead
85 * 功能描述:对读IRP进行处理
86 * 参数列表:
87 pDevObj:功能设备对象
88 pIrp:从IO请求包
89 * 返回 值:返回状态
90 *************************************************************************/
91 #pragma PAGEDCODE
92 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
93 IN PIRP pIrp)
94 {
95 KdPrint(("DriverA:Enter A HelloDDKRead\n"));
96
97 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
98 pDevObj->DeviceExtension;
99
100 //将IRP设置为挂起
101 IoMarkIrpPending(pIrp);
102
103 //将挂起的IRP记录下来
104 pDevExt->currentPendingIRP = pIrp;
105
106 //定义3秒后将IRP_MJ_READ的IRP完成
107 ULONG ulMicroSecond = 3000000;
108
109 //将32位整数转化成64位整数
110 LARGE_INTEGER timeout = RtlConvertLongToLargeInteger(-10*ulMicroSecond);
111
112 KeSetTimer(
113 &pDevExt->pollingTimer,
114 timeout,
115 &pDevExt->pollingDPC );
116
117 KdPrint(("DriverA:Leave A HelloDDKRead\n"));
118
119 //返回pending状态
120 return STATUS_PENDING;
121 }
122
1 //DriverB
2 #pragma PAGEDCODE
3 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
4 IN PIRP pIrp)
5 {
6 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
7 NTSTATUS ntStatus = STATUS_SUCCESS;
8
9 UNICODE_STRING DeviceName;
10 RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );
11
12 //初始化objectAttributes
13 OBJECT_ATTRIBUTES objectAttributes;
14 InitializeObjectAttributes(&objectAttributes,
15 &DeviceName,
16 OBJ_CASE_INSENSITIVE,
17 NULL,
18 NULL );
19
20 HANDLE hDevice;
21 IO_STATUS_BLOCK status_block;
22 //同步打开设备
23 //设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
24 ntStatus = ZwCreateFile(&hDevice,
25 FILE_READ_ATTRIBUTES|SYNCHRONIZE,
26 &objectAttributes,
27 &status_block,
28 NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,
29 FILE_OPEN_IF,FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);
30
31 if (NT_SUCCESS(ntStatus))
32 {
33 ZwReadFile(hDevice,NULL,NULL,NULL,&status_block,NULL,0,NULL,NULL);
34 }
35
36 ZwClose(hDevice);
37
38 // 完成IRP
39 pIrp->IoStatus.Status = ntStatus;
40 pIrp->IoStatus.Information = 0; // bytes xfered
41 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
42 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
43 return ntStatus;
44 }
45
46 /************************************************************************
47 * 函数名称:HelloDDKDispatchRoutine
48 * 功能描述:对读IRP进行处理
49 * 参数列表:
50 pDevObj:功能设备对象
51 pIrp:从IO请求包
52 * 返回 值:返回状态
53 *************************************************************************/
54 #pragma PAGEDCODE
55 NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
56 IN PIRP pIrp)
57 {
58 KdPrint(("DriverB:Enter B HelloDDKDispatchRoutine\n"));
59 NTSTATUS ntStatus = STATUS_SUCCESS;
60 // 完成IRP
61 pIrp->IoStatus.Status = ntStatus;
62 pIrp->IoStatus.Information = 0; // bytes xfered
63 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
64 KdPrint(("DriverB:Leave B HelloDDKDispatchRoutine\n"));
65 return ntStatus;
66 }
67
68 #pragma PAGEDCODE
69 NTSTATUS HelloDDKCreate(IN PDEVICE_OBJECT pDevObj,
70 IN PIRP pIrp)
71 {
72 KdPrint(("DriverB:Enter B HelloDDKCreate\n"));
73 NTSTATUS ntStatus = STATUS_SUCCESS;
74 // 完成IRP
75 pIrp->IoStatus.Status = ntStatus;
76 pIrp->IoStatus.Information = 0; // bytes xfered
77 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
78
79 KdPrint(("DriverB:Leave B HelloDDKCreate\n"));
80
81 return ntStatus;
82 }
83
84 #pragma PAGEDCODE
85 NTSTATUS HelloDDKClose(IN PDEVICE_OBJECT pDevObj,
86 IN PIRP pIrp)
87 {
88 KdPrint(("DriverB:Enter B HelloDDKClose\n"));
89 NTSTATUS ntStatus = STATUS_SUCCESS;
90
91 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
92
93 // 完成IRP
94 pIrp->IoStatus.Status = ntStatus;
95 pIrp->IoStatus.Information = 0; // bytes xfered
96 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
97
98 KdPrint(("DriverB:Leave B HelloDDKClose\n"));
99
100 return ntStatus;
101 }
1 //main
2 #include <windows.h>
3 #include <stdio.h>
4
5 int main()
6 {
7
8 HANDLE hDevice =
9 CreateFile("\\\\.\\HelloDDKB",
10 GENERIC_READ | GENERIC_WRITE,
11 0, // share mode none
12 NULL, // no security
13 OPEN_EXISTING,
14 FILE_ATTRIBUTE_NORMAL,
15 NULL ); // no template
16
17 if (hDevice == INVALID_HANDLE_VALUE)
18 {
19 printf("Failed to obtain file handle to device "
20 "with Win32 error code: %d\n",
21 GetLastError() );
22 return 1;
23 }
24
25 DWORD dRet;
26 ReadFile(hDevice,NULL,0,&dRet,NULL);
27
28 CloseHandle(hDevice);
29
30 return 0;
31 }
同步 示例代码 P296
1 /************************************************************************
2 * 函数名称:DriverEntry
3 * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
4 * 参数列表:
5 pDriverObject:从I/O管理器中传进来的驱动对象
6 pRegistryPath:驱动程序在注册表的中的路径
7 * 返回 值:返回初始化驱动状态
8 *************************************************************************/
9 #pragma INITCODE
10 extern "C" NTSTATUS DriverEntry (
11 IN PDRIVER_OBJECT pDriverObject,
12 IN PUNICODE_STRING pRegistryPath )
13 {
14 NTSTATUS ntStatus;
15 KdPrint(("DriverB:Enter B DriverEntry\n"));
16
17 //注册其他驱动调用函数入口
18 pDriverObject->DriverUnload = HelloDDKUnload;
19 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKCreate;
20 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKClose;
21 pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
22 pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKRead;
23
24 //创建驱动设备对象
25 ntStatus = CreateDevice(pDriverObject);
26
27 KdPrint(("DriverB:Leave B DriverEntry\n"));
28 return ntStatus;
29 }
30
31 /************************************************************************
32 * 函数名称:CreateDevice
33 * 功能描述:初始化设备对象
34 * 参数列表:
35 pDriverObject:从I/O管理器中传进来的驱动对象
36 * 返回 值:返回初始化状态
37 *************************************************************************/
38 #pragma INITCODE
39 NTSTATUS CreateDevice (
40 IN PDRIVER_OBJECT pDriverObject)
41 {
42 NTSTATUS ntStatus;
43 PDEVICE_OBJECT pDevObj;
44 PDEVICE_EXTENSION pDevExt;
45
46 //创建设备名称
47 UNICODE_STRING devName;
48 RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevicB");
49
50 //创建设备
51 ntStatus = IoCreateDevice( pDriverObject,
52 sizeof(DEVICE_EXTENSION),
53 &(UNICODE_STRING)devName,
54 FILE_DEVICE_UNKNOWN,
55 0, TRUE,
56 &pDevObj );
57 if (!NT_SUCCESS(ntStatus))
58 return ntStatus;
59
60 pDevObj->Flags |= DO_BUFFERED_IO;
61 pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
62 pDevExt->pDevice = pDevObj;
63 pDevExt->ustrDeviceName = devName;
64
65 //创建符号链接
66 UNICODE_STRING symLinkName;
67 RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDKB");
68 pDevExt->ustrSymLinkName = symLinkName;
69 NTSTATUS status = IoCreateSymbolicLink( &symLinkName,&devName );
70 if (!NT_SUCCESS(status))
71 {
72 IoDeleteDevice( pDevObj );
73 return status;
74 }
75
76 return STATUS_SUCCESS;
77 }
78
79 /************************************************************************
80 * 函数名称:HelloDDKUnload
81 * 功能描述:负责驱动程序的卸载操作
82 * 参数列表:
83 pDriverObject:驱动对象
84 * 返回 值:返回状态
85 *************************************************************************/
86 #pragma PAGEDCODE
87 VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
88 {
89 PDEVICE_OBJECT pNextObj;
90 KdPrint(("DriverB:Enter B DriverUnload\n"));
91 pNextObj = pDriverObject->DeviceObject;
92
93 while (pNextObj != NULL)
94 {
95 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
96 pNextObj->DeviceExtension;
97
98 //删除符号链接
99 UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
100 IoDeleteSymbolicLink(&pLinkName);
101 pNextObj = pNextObj->NextDevice;
102 IoDeleteDevice( pDevExt->pDevice );
103 }
104 KdPrint(("DriverB:Enter B DriverUnload\n"));
105 }
106
107 VOID CompleteDriverA_Read(PVOID context,PIO_STATUS_BLOCK pStatus_block,ULONG)
108 {
109 KdPrint(("DriverB:The Driver A Read completed now!\n"));
110 KeSetEvent((PKEVENT)context,IO_NO_INCREMENT,FALSE);
111 }
112
113 #pragma PAGEDCODE
114 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
115 IN PIRP pIrp)
116 {
117 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
118 NTSTATUS ntStatus = STATUS_SUCCESS;
119
120 UNICODE_STRING DeviceName;
121 RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );
122
123 //初始化objectAttributes
124 OBJECT_ATTRIBUTES objectAttributes;
125 InitializeObjectAttributes(&objectAttributes,
126 &DeviceName,
127 OBJ_CASE_INSENSITIVE,
128 NULL,
129 NULL );
130
131 HANDLE hDevice;
132 IO_STATUS_BLOCK status_block;
133 //异步打开设备
134 //没有设定了FILE_SYNCHRONOUS_IO_NONALERT和FILE_SYNCHRONOUS_IO_ALERT为异步打开设备
135 ntStatus = ZwCreateFile(&hDevice,
136 FILE_READ_ATTRIBUTES,//没有设SYNCHRONIZE
137 &objectAttributes,
138 &status_block,
139 NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,
140 FILE_OPEN_IF,0,NULL,0);
141
142 KEVENT event;
143 //初始化事件,用于异步读
144 KeInitializeEvent(&event,SynchronizationEvent,FALSE);
145
146 LARGE_INTEGER offset = RtlConvertLongToLargeInteger(0);
147 if (NT_SUCCESS(ntStatus))
148 {
149 ntStatus = ZwReadFile(hDevice,NULL,CompleteDriverA_Read,&event,&status_block,NULL,0,&offset,NULL);
150 }
151
152 if (ntStatus==STATUS_PENDING)
153 {
154 KdPrint(("DriverB:ZwReadFile return STATUS_PENDING!\n"));
155 KdPrint(("DriverB:Waiting..."));
156 KeWaitForSingleObject(&event,Executive,KernelMode,FALSE,NULL);
157 }
158 ZwClose(hDevice);
159
160 ntStatus = STATUS_SUCCESS;
161 // 完成IRP
162 pIrp->IoStatus.Status = ntStatus;
163 pIrp->IoStatus.Information = 0; // bytes xfered
164 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
165 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
166 return ntStatus;
167 }
168
169 /************************************************************************
170 * 函数名称:HelloDDKDispatchRoutine
171 * 功能描述:对读IRP进行处理
172 * 参数列表:
173 pDevObj:功能设备对象
174 pIrp:从IO请求包
175 * 返回 值:返回状态
176 *************************************************************************/
177 #pragma PAGEDCODE
178 NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
179 IN PIRP pIrp)
180 {
181 KdPrint(("DriverB:Enter B HelloDDKDispatchRoutine\n"));
182 NTSTATUS ntStatus = STATUS_SUCCESS;
183 // 完成IRP
184 pIrp->IoStatus.Status = ntStatus;
185 pIrp->IoStatus.Information = 0; // bytes xfered
186 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
187 KdPrint(("DriverB:Leave B HelloDDKDispatchRoutine\n"));
188 return ntStatus;
189 }
190
191 #pragma PAGEDCODE
192 NTSTATUS HelloDDKCreate(IN PDEVICE_OBJECT pDevObj,
193 IN PIRP pIrp)
194 {
195 KdPrint(("DriverB:Enter B HelloDDKCreate\n"));
196 NTSTATUS ntStatus = STATUS_SUCCESS;
197 // 完成IRP
198 pIrp->IoStatus.Status = ntStatus;
199 pIrp->IoStatus.Information = 0; // bytes xfered
200 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
201
202 KdPrint(("DriverB:Leave B HelloDDKCreate\n"));
203
204 return ntStatus;
205 }
206
207 #pragma PAGEDCODE
208 NTSTATUS HelloDDKClose(IN PDEVICE_OBJECT pDevObj,
209 IN PIRP pIrp)
210 {
211 KdPrint(("DriverB:Enter B HelloDDKClose\n"));
212 NTSTATUS ntStatus = STATUS_SUCCESS;
213
214 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
215
216 // 完成IRP
217 pIrp->IoStatus.Status = ntStatus;
218 pIrp->IoStatus.Information = 0; // bytes xfered
219 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
220
221 KdPrint(("DriverB:Leave B HelloDDKClose\n"));
222
223 return ntStatus;
224 }
异步方式一 示例代码 P298
说明 MSDN中ApcRoutine是保留参数,应当设为NULL。本例中,直接可将Event参数设置即可,不需要再整一个函数,就可达到目的。由于本例只是说明问题,摘自别人的代码,也就没有改动。
1 #pragma PAGEDCODE
2 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
3 IN PIRP pIrp)
4 {
5 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
6 NTSTATUS ntStatus = STATUS_SUCCESS;
7
8 UNICODE_STRING DeviceName;
9 RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );
10
11 //初始化objectAttributes
12 OBJECT_ATTRIBUTES objectAttributes;
13 InitializeObjectAttributes(&objectAttributes,
14 &DeviceName,
15 OBJ_CASE_INSENSITIVE,
16 NULL,
17 NULL );
18
19 HANDLE hDevice;
20 IO_STATUS_BLOCK status_block;
21
22 //异步打开设备
23 ntStatus = ZwCreateFile(&hDevice,
24 FILE_READ_ATTRIBUTES,//没有设SYNCHRONIZE
25 &objectAttributes,
26 &status_block,
27 NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,
28 FILE_OPEN_IF,0,NULL,0);
29
30 LARGE_INTEGER offset = RtlConvertLongToLargeInteger(0);
31 if (NT_SUCCESS(ntStatus))
32 {
33 ntStatus = ZwReadFile(hDevice,NULL,NULL,NULL,&status_block,NULL,0,&offset,NULL);
34 }
35
36 if (ntStatus==STATUS_PENDING)
37 {
38 KdPrint(("DriverB:ZwReadFile return STATUS_PENDING!\n"));
39
40 PFILE_OBJECT FileObject;
41 ntStatus = ObReferenceObjectByHandle(hDevice, EVENT_MODIFY_STATE, *ExEventObjectType,
42 KernelMode, (PVOID*) &FileObject, NULL);
43 if (NT_SUCCESS(ntStatus))
44 {
45 KdPrint(("DriverB:Waiting..."));
46 KeWaitForSingleObject(&FileObject->Event,Executive,KernelMode,FALSE,NULL);
47 KdPrint(("DriverB:Driver A Read IRP completed now!\n"));
48 ObDereferenceObject(FileObject);
49 }
50 }
51 ZwClose(hDevice);
52
53 ntStatus = STATUS_SUCCESS;
54 // 完成IRP
55 pIrp->IoStatus.Status = ntStatus;
56 pIrp->IoStatus.Information = 0; // bytes xfered
57 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
58 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
59 return ntStatus;
60 }
异步方式二 示例代码 P298
说明:方式一通过将一个事件句柄传递给ZwReadFile,这个事件用来通知读取操作何时完成。方式二原理是:通过文件对象判断读取是否完毕;每打开一个设备,都会伴随存在一个关联的文件对象,利用ObReferenceObjectByHandle获取文件对象指针;当IRP_MJ_READ请求被结束后,文件对象的子域Event会被设备,因此可以用文件对象的子域进行判断。
2)通过符号链接打开设备
如何由符号链接得到设备名?
通过ZwOpenSymbolicLinkObject来得到符号链接的句柄,再通过ZwQuerySymbolicLinkObject来得到设备名。
1 #pragma PAGEDCODE
2 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
3 IN PIRP pIrp)
4 {
5 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
6 NTSTATUS ntStatus = STATUS_SUCCESS;
7
8 UNICODE_STRING DeviceSymbolicLinkName;
9 RtlInitUnicodeString( &DeviceSymbolicLinkName, L"\\??\\HelloDDKA" );
10
11 //初始化objectAttributes
12 OBJECT_ATTRIBUTES objectAttributes;
13 InitializeObjectAttributes(&objectAttributes,
14 &DeviceSymbolicLinkName,
15 OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
16 NULL,
17 NULL );
18
19 HANDLE hSymbolic;
20 //设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
21 ntStatus = ZwOpenSymbolicLinkObject(&hSymbolic,FILE_ALL_ACCESS,&objectAttributes);
22 #define UNICODE_SIZE 50
23 UNICODE_STRING LinkTarget;
24 LinkTarget.Buffer = (PWSTR)ExAllocatePool(PagedPool,UNICODE_SIZE);
25 LinkTarget.Length = 0;
26 LinkTarget.MaximumLength = UNICODE_SIZE;
27
28 ULONG unicode_length;
29 ntStatus = ZwQuerySymbolicLinkObject(hSymbolic,&LinkTarget,&unicode_length);
30
31 KdPrint(("DriverB:The device name is %wZ\n",&LinkTarget));
32
33 InitializeObjectAttributes(&objectAttributes,
34 &LinkTarget,
35 OBJ_CASE_INSENSITIVE,
36 NULL,
37 NULL );
38
39 HANDLE hDevice;
40 IO_STATUS_BLOCK status_block;
41 //设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
42 ntStatus = ZwCreateFile(&hDevice,
43 FILE_READ_ATTRIBUTES|SYNCHRONIZE,
44 &objectAttributes,
45 &status_block,
46 NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,
47 FILE_OPEN_IF,FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);
48
49 if (NT_SUCCESS(ntStatus))
50 {
51 ZwReadFile(hDevice,NULL,NULL,NULL,&status_block,NULL,0,NULL,NULL);
52 }
53
54 ZwClose(hDevice);
55 ZwClose(hSymbolic);
56 ExFreePool(LinkTarget.Buffer);
57
58 ntStatus = STATUS_SUCCESS;
59 // 完成IRP
60 pIrp->IoStatus.Status = ntStatus;
61 pIrp->IoStatus.Information = 0; // bytes xfered
62 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
63 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
64 return ntStatus;
65 }
示例代码 P302
2、通过设备指针调用其他驱动程序
调用驱动程序
ZwReadFile内核函数内部会创建IRP_MJ_READ类型的IRP,然后把这个IRP传递到相应的派遣函数中。这一小节中,我们是通过自己“手动”构造各个IRP,然后将IRP传递到相应的驱动程序的派遣函数中。
2、通过IoGetDeviceObjectPointer 来获得设备指针。
1)每个内核中的句柄都会和一个内核对象的指针联系起来。如ZwCreateFile内核函数可以通过设备名打开设备句柄,这个句柄和一个文件对象的指针关联。IoGetDeviceObjectPointer 可以通过设备名获得文件对象指针。
Windows内核会为每一个对象指针保存一个“引用计数”,对象被创建时引用计数为1。引用这个对象,引用计数加1。只有引用对象为0时,对象才能被真正删除。ObDereferenceObject 来使引用计数减一。
第一次调用IoGetDeviceObjectPointer 时,会根据设备名打开设备,计数为1,相当于对对象发送IRP_MJ_CREATE。使用完后调用ObDereferenceObject最终关闭设备时,相当于发送IRP_MJ_CLOSE。
2)共有如下几种方法,IoBuildSynchronousFsdRequest ,IoBuildAsynchronousFsdRequest ,IoBuildDeviceIoControlRequest ,IoAllocateIrp 来创建IRP。前3种都是通过IoAllocateIrp 实现的。创建完IRP后,还有构建IRP的I/O堆栈,每层I/O堆栈对应一个设备对象。最后通过IoCallDriver 调用相应的驱动。
总结一下,步骤为:
先得到设备的指针-》通过设备指针创建RIP-》构造I/O堆栈-》调用IoCallDriver 。
(1)IoBuildSynchronousFsdRequest
IoBuildSynchronousFsdRequest 与IoBuildAsynchronousFsdRequest 的区别就是是否提供同步对象。事件会和IRP请求相关联,当IRP请求结束时该事件被触发。
Intermediate or highest-level drivers can call IoBuildSynchronousFsdRequest to set up IRPs for requests sent to lower-level drivers, only if the caller is running in a nonarbitrary thread context and at IRQL = PASSIVE_LEVEL.
IoBuildSynchronousFsdRequest allocates and sets up an IRP that can be sent to a device driver to perform a synchronous read, write, flush, or shutdown operation. The IRP contains only enough information to get the operation started.
IoBuildSynchronousFsdRequest returns a pointer to the IRP, or NULL if an IRP cannot be allocated.
1 #pragma PAGEDCODE
2 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
3 IN PIRP pIrp)
4 {
5 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
6 NTSTATUS ntStatus = STATUS_SUCCESS;
7
8 UNICODE_STRING DeviceName;
9 RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );
10
11 PDEVICE_OBJECT DeviceObject = NULL;
12 PFILE_OBJECT FileObject = NULL;
13 //得到设备对象句柄,计数器加1
14 //如果是第一次调用IoGetDeviceObjectPointer,会打开设备,相当于调用ZwCreateFile
15 ntStatus = IoGetDeviceObjectPointer(&DeviceName,FILE_ALL_ACCESS,&FileObject,&DeviceObject);
16
17 KdPrint(("DriverB:FileObject:%x\n",FileObject));
18 KdPrint(("DriverB:DeviceObject:%x\n",DeviceObject));
19
20 if (!NT_SUCCESS(ntStatus))
21 {
22 KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x\n", ntStatus ));
23
24 ntStatus = STATUS_UNSUCCESSFUL;
25 // 完成IRP
26 pIrp->IoStatus.Status = ntStatus;
27 pIrp->IoStatus.Information = 0; // bytes xfered
28 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
29 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
30
31 return ntStatus;
32 }
33
34 KEVENT event;
35 KeInitializeEvent(&event,NotificationEvent,FALSE);
36 IO_STATUS_BLOCK status_block;
37 LARGE_INTEGER offsert = RtlConvertLongToLargeInteger(0);
38
39 //创建同步IRP
40 PIRP pNewIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
41 DeviceObject,
42 NULL,0,
43 &offsert,&event,&status_block);
44 KdPrint(("DriverB:pNewIrp:%x\n",pNewIrp));
45
46 PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp);
47 stack->FileObject = FileObject;
48
49 //调用DriverA,会一直调用到DriverA的派遣函数
50 NTSTATUS status = IoCallDriver(DeviceObject,pNewIrp);
51
52 if (status == STATUS_PENDING) {
53
54 //如果DriverA的派遣函数没有完成IRP,则等待IRP完成
55 status = KeWaitForSingleObject(
56 &event,
57 Executive,
58 KernelMode,
59 FALSE, // Not alertable
60 NULL);
61 status = status_block.Status;
62 }
63
64 //将引用计数减1,如果此时计数器减为0,
65 //则将关闭设备,相当于调用ZwClose
66 ObDereferenceObject( FileObject );
67
68
69 ntStatus = STATUS_SUCCESS;
70 // 完成IRP
71 pIrp->IoStatus.Status = ntStatus;
72 pIrp->IoStatus.Information = 0; // bytes xfered
73 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
74 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
75 return ntStatus;
76 }
示例代码 P306
另外,The IoGetCurrentIrpStackLocation routine returns a pointer to the caller’s stack location in the given IRP,IoGetNextIrpStackLocation returns a pointer to the next-lower-level driver's I/O stack location in the given IRP。
(2)IoBuildAsynchronousFsdRequest
可以通过IRP的 UserEvent子域来通知IRP请求的结束。调用IoBuildAsynchronousFsdRequest 创建IRP后,如果希望同步,即希望得到IRP被结束的通知,则需要设备UserEvent。当执行IoCompleteRequest时,OS会检查 IRP的UserEvent子域,如果非空,则它代表一个事件指针,IoCompleteRequest会设备这个事件。
1 #pragma PAGEDCODE
2 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
3 IN PIRP pIrp)
4 {
5 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
6 NTSTATUS ntStatus = STATUS_SUCCESS;
7
8 UNICODE_STRING DeviceName;
9 RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );
10
11 PDEVICE_OBJECT DeviceObject = NULL;
12 PFILE_OBJECT FileObject = NULL;
13 //得到设备对象指针
14 ntStatus = IoGetDeviceObjectPointer(&DeviceName,FILE_ALL_ACCESS,&FileObject,&DeviceObject);
15
16 KdPrint(("DriverB:FileObject:%x\n",FileObject));
17 KdPrint(("DriverB:DeviceObject:%x\n",DeviceObject));
18
19 if (!NT_SUCCESS(ntStatus))
20 {
21 KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x\n", ntStatus ));
22
23 ntStatus = STATUS_UNSUCCESSFUL;
24 // 完成IRP
25 pIrp->IoStatus.Status = ntStatus;
26 pIrp->IoStatus.Information = 0; // bytes xfered
27 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
28 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
29
30 return ntStatus;
31 }
32
33 KEVENT event;
34 KeInitializeEvent(&event,NotificationEvent,FALSE);
35 IO_STATUS_BLOCK status_block;
36 LARGE_INTEGER offsert = RtlConvertLongToLargeInteger(0);
37
38 //创建异步IRP
39 PIRP pNewIrp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ,
40 DeviceObject,
41 NULL,0,
42 &offsert,&status_block);
43 KdPrint(("pNewIrp->UserEvent :%x\n",pNewIrp->UserEvent));
44 //设置pNewIrp->UserEvent,这样在IRP完成后可以通知该事件
45 pNewIrp->UserEvent = &event;
46
47 KdPrint(("DriverB:pNewIrp:%x\n",pNewIrp));
48
49 PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp);
50 stack->FileObject = FileObject;
51
52 NTSTATUS status = IoCallDriver(DeviceObject,pNewIrp);
53
54 if (status == STATUS_PENDING) {
55 status = KeWaitForSingleObject(
56 &event,
57 Executive,
58 KernelMode,
59 FALSE, // Not alertable
60 NULL);
61 status = status_block.Status;
62 }
63
64 ZwClose(FileObject);
65
66 //关闭设备句柄
67 ObDereferenceObject( FileObject );
68
69 ntStatus = STATUS_SUCCESS;
70 // 完成IRP
71 pIrp->IoStatus.Status = ntStatus;
72 pIrp->IoStatus.Information = 0; // bytes xfered
73 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
74 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
75 return ntStatus;
76 }
示例代码 P309
(3)IoAllocateIrp
CreateFile,ZwCreateFile 等,都直接或间接的调用了IoAllocateIrp 。所以对设备的操作都会转化一个IRP,IRP会在驱动中被处理;IRP请求被结束,代表操作结束,IRP的完成状态就是操作的完成状态。所有的IRP最终都是由IoAllocateIrp 内核函数创建的。
整个Windows是个异步的框架,同步只是异步的一个特例;就像静止只是运行的一个特例一样。
IoAllocateIrp 创建IRP后,许多参数,如缓冲区等,用户自己填写。最终需要用IoFreeIrp删除IRP对象。
1 #pragma PAGEDCODE
2 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
3 IN PIRP pIrp)
4 {
5 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
6 NTSTATUS ntStatus = STATUS_SUCCESS;
7
8 UNICODE_STRING DeviceName;
9 RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );
10
11 PDEVICE_OBJECT DeviceObject = NULL;
12 PFILE_OBJECT FileObject = NULL;
13 //得到设备对象指针
14 ntStatus = IoGetDeviceObjectPointer(&DeviceName,FILE_ALL_ACCESS,&FileObject,&DeviceObject);
15
16 KdPrint(("DriverB:FileObject:%x\n",FileObject));
17 KdPrint(("DriverB:DeviceObject:%x\n",DeviceObject));
18
19 if (!NT_SUCCESS(ntStatus))
20 {
21 KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x\n", ntStatus ));
22 ntStatus = STATUS_UNSUCCESSFUL;
23 // 完成IRP
24 pIrp->IoStatus.Status = ntStatus;
25 pIrp->IoStatus.Information = 0; // bytes xfered
26 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
27 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
28
29 return ntStatus;
30 }
31
32 KEVENT event;
33 KeInitializeEvent(&event,NotificationEvent,FALSE);
34
35 PIRP pNewIrp = IoAllocateIrp(DeviceObject->StackSize,FALSE);
36 KdPrint(("pNewIrp->UserEvent :%x\n",pNewIrp->UserEvent));
37 pNewIrp->UserEvent = &event;
38
39 IO_STATUS_BLOCK status_block;
40 pNewIrp->UserIosb = &status_block;
41 pNewIrp->Tail.Overlay.Thread = PsGetCurrentThread();
42
43 //因为DriverA是BUFFER IO设备
44 pNewIrp->AssociatedIrp.SystemBuffer = NULL;
45
46 KdPrint(("DriverB:pNewIrp:%x\n",pNewIrp));
47
48 PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp);
49 stack->MajorFunction = IRP_MJ_READ;
50 stack->MinorFunction=IRP_MN_NORMAL;//0
51 stack->FileObject = FileObject;
52
53 //调用DriverA驱动
54 NTSTATUS status = IoCallDriver(DeviceObject,pNewIrp);
55
56 if (status == STATUS_PENDING) {
57 status = KeWaitForSingleObject(
58 &event,
59 Executive,
60 KernelMode,
61 FALSE, // Not alertable
62 NULL);
63 KdPrint(("STATUS_PENDING\n"));
64 }
65
66 ObDereferenceObject( FileObject );
67 IoFreeIrp(pNewIrp);
68
69 ntStatus = STATUS_SUCCESS;
70 // 完成IRP
71 pIrp->IoStatus.Status = ntStatus;
72 pIrp->IoStatus.Information = 0; // bytes xfered
73 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
74 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
75 return ntStatus;
76 }
示例代码 P314
3、其他方法获得设备指针
ObReferenceObjectByName 是一个未公开的函数。ObReferenceObjectByName通过名字得到指针,而操作类型可以是设备对象,内核对象,互斥事件等;而 IoGetDeviceObjectPointer 只能得到设备对象指针。同样,ObReferenceObjectByName也维护了一个设备对象的引用计数,使用完后,也需要调用 ObDereferenceObject来使引用计数减一。
1 #pragma PAGEDCODE
2 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
3 IN PIRP pIrp)
4 {
5 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
6 NTSTATUS ntStatus = STATUS_SUCCESS;
7
8 UNICODE_STRING DeviceName;
9 RtlInitUnicodeString( &DeviceName, L"\\??\\HelloDDKA" );
10
11 PDEVICE_OBJECT DeviceObject = NULL;
12 PFILE_OBJECT FileObject = NULL;
13 ntStatus = MyIoGetDeviceObjectPointer(&DeviceName,FILE_ALL_ACCESS,&FileObject,&DeviceObject);
14 // ntStatus = ObReferenceObjectByName(&DeviceName,OBJ_CASE_INSENSITIVE,NULL,FILE_ALL_ACCESS,IoDeviceObjectType,KernelMode,NULL,(PVOID*)&DeviceObject);
15
16 KdPrint(("ntStatus %x\n",ntStatus));
17 KdPrint(("DeviceObject %x\n",DeviceObject));
18 ObDereferenceObject( FileObject );
19 ntStatus = STATUS_SUCCESS;
20 // 完成IRP
21 pIrp->IoStatus.Status = ntStatus;
22 pIrp->IoStatus.Information = 0; // bytes xfered
23 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
24 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
25 return ntStatus;
26 }
示例代码 P316