本节介绍“手动”构造各个IRP,然后将IRP传递到相应驱动程序的派遣函数里。
- 获得设备指针
每个内核中的句柄都会和一个内核对象的指针联系起来。ZwCreateFile内核函数可以通过设备名打开设备句柄,这个设备句柄和一个文件对象的指针关联。IoGetDeviceObjectPointer内核函数可以通过设备名获得文件对象指针,而不是获得设备句柄。当调用IoGetDeviceObjectPointer内核函数后,设备对象的引用计数就会加1。当用完这个设备对象后,应该调用ObDereferenceObject内核函数,使其引用计数减1。当第一次调用IoGetDeviceObjectPointer内核函数时,会根据设备名打开设备,这时文件对象指针的引用计数为1。此后如果再次调用IoGetDeviceObjectPointer打开设备,就不会真正的打开设备了,而只是将引用计数加1——只有在第一次真正打开设备时,系统才会创建一个IRP_MJ_CREATE类型的IRP,并将这个IRP传递到驱动程序的派遣函数中。
每次调用ObDereferenceObject内核函数都会将“引用计数”减1,如果减至0就会关闭设备。关闭设备时,系统会创建一个IRP_MJ_CLOSE类型的IRP。
- 创建IRP
利用内核函数IoBuildSynchronousFsdRequest和IoBuildAsynchronousFsdRequest两个内核函数创建IRP,它们分别用来创建同步类型和异步类型的IRP。这两个内核函数可以创建IRP_MJ_PNP、IRP_MJ_READ、IRP_MJ_WRITE、IRP_MJ_FLUSH_BUFFERS和IRP_MJ_SHUTDOWN类型的IRP。
可以通过IoBuildDeviceIoControlRequest内核函数创建IRP_MJ_INTERNAL_DEVICE_CONTROL和IRP_MJ_DEVICE_CONTROL两个类型的IRP,这个内核函数只能创建同步类型的IRP。
另外,还可以使用IoAllocateIrp内核函数,它可以创建任意类型的IRP。IoBuildSynchronousFsdRequest、IoBuildAsynchronousFsdRequest、IoBuildDeviceIoControlRequest这三个函数属于靠近上层的内核函数。而IoAllocateIrp是比较底层的内核函数,以上三个内核函数都是用过IoAllocateIrp实现的。
创建完IRP后,还要构造IRP的I/O堆栈,每层I/O堆栈对应一个设备对象。最后通过IoCallDriver内核函数调用相应的驱动。
手动创建IRP步骤如下:
1.先得到设备的指针。一种方法是用IoGetDeviceObjectPointer内核函数得到设备对象指针。另一种方法是用ZwCreate内核函数先得到设备句柄,然后调用ObReferenceObjectByPointer内核函数通过设备句柄得到设备对象指针。
2.手动创建IRP,有上述四个内核函数可选。
3.构造IRP的I/O堆栈。
4.调用IoCallDriver内核函数,其内部会调用设备对象的派遣函数。
- 用IoBuildSynchronousFsdRequest创建IRP
IoBuildSynchronousFsdRequest内核函数创建同步类型IRP,关键在于其第六个参数Event。在调用IoBuildSynchronousFsdRequest之前,需要准备一个事件。这个事件会和IRP请求进行关联,当IRP请求被结束时该事件被触发。
示例代码如下:
1 NTSTATUS HelloDDKRead_ManualIRP_SYN(PDEVICE_OBJECT pDevObj, PIRP pIrp){ 2 UNREFERENCED_PARAMETER(pDevObj); 3 DbgPrint("Enter HelloDDKRead_ManualIRP_SYN!\n"); 4 NTSTATUS status = STATUS_SUCCESS; 5 UNICODE_STRING DeviceName; 6 RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDDKDevice"); 7 PDEVICE_OBJECT pDeviceObject = NULL; 8 PFILE_OBJECT pFileObject = NULL; 9 //获得文件对象和设备对象的指针 10 status = IoGetDeviceObjectPointer(&DeviceName, FILE_ALL_ACCESS, &pFileObject, &pDeviceObject); 11 if (!NT_SUCCESS(status)){ 12 status = STATUS_UNSUCCESSFUL; 13 pIrp->IoStatus.Status = status; 14 pIrp->IoStatus.Information = 0; 15 IoCompleteRequest(pIrp, IO_NO_INCREMENT); 16 return status; 17 } 18 KEVENT event; 19 KeInitializeEvent(&event, NotificationEvent, FALSE); 20 IO_STATUS_BLOCK status_block; 21 LARGE_INTEGER offset = RtlConvertLongToLargeInteger(0); 22 PIRP pNewIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, 23 pDeviceObject, 24 NULL, 0, 25 &offset, &event, 26 &status_block); 27 28 PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp); 29 stack->FileObject = pFileObject; 30 status = IoCallDriver(pDeviceObject, pNewIrp); 31 if (status == STATUS_PENDING){ 32 status = KeWaitForSingleObject(&event, 33 Executive, 34 KernelMode, 35 FALSE, 36 NULL); 37 status = status_block.Status; 38 } 39 40 ObDereferenceObject(pFileObject); 41 status = STATUS_SUCCESS; 42 pIrp->IoStatus.Status = status; 43 pIrp->IoStatus.Information = 0; 44 IoCompleteRequest(pIrp, IO_NO_INCREMENT); 45 DbgPrint("Leave HelloDDKRead_ManualIRP_SYN!\n"); 46 return status; 47 }
运行结果如下:
- 用IoBuildAsynchronousFsdRequest创建IRP
比用IoBuildSynchronousFsdRequest创建IRP少一个事件参数。使用IoBuildAsynchronousFsdRequest内核函数创建IRP,当IRP请求被结束后,操作系统不会通过事件进行通知。不过可以通过IRP的UserEvent子域来通知IRP请求的结束。调用IoBuildAsynchronousFsdRequest内核函数创建IRP后,如果希望进行同步处理,即希望得到IRP被结束的通知,则只需要设置IRP的子域UserEvent。
示例代码如下:
1 NTSTATUS HelloDDKRead_ManualIRP_ASYN(PDEVICE_OBJECT pDevObj, PIRP pIrp){ 2 UNREFERENCED_PARAMETER(pDevObj); 3 DbgPrint("Enter HelloDDKRead_ManualIRP_ASYN!\n"); 4 NTSTATUS status = STATUS_SUCCESS; 5 UNICODE_STRING DeviceName; 6 RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDDKDevice"); 7 PDEVICE_OBJECT pDeviceObject = NULL; 8 PFILE_OBJECT pFileObject = NULL; 9 status = IoGetDeviceObjectPointer(&DeviceName, FILE_ALL_ACCESS, &pFileObject, &pDeviceObject); 10 if (!NT_SUCCESS(status)){ 11 status = STATUS_UNSUCCESSFUL; 12 pIrp->IoStatus.Status = status; 13 pIrp->IoStatus.Information = 0; 14 IoCompleteRequest(pIrp, IO_NO_INCREMENT); 15 return status; 16 } 17 KEVENT event; 18 KeInitializeEvent(&event, NotificationEvent, FALSE); 19 IO_STATUS_BLOCK status_block; 20 LARGE_INTEGER offset = RtlConvertLongToLargeInteger(0); 21 PIRP pNewIrp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ, 22 pDeviceObject, 23 NULL, 0, 24 &offset, &status_block); 25 pNewIrp->UserEvent = &event; 26 PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp); 27 stack->FileObject = pFileObject; 28 status = IoCallDriver(pDeviceObject, pNewIrp); 29 if (status == STATUS_PENDING){ 30 status = KeWaitForSingleObject(&event, 31 Executive, 32 KernelMode, 33 FALSE, 34 NULL); 35 status = status_block.Status; 36 } 37 ObDereferenceObject(pFileObject); 38 status = STATUS_SUCCESS; 39 pIrp->IoStatus.Status = status; 40 pIrp->IoStatus.Information = 0; 41 IoCompleteRequest(pIrp, IO_NO_INCREMENT); 42 DbgPrint("Leave HelloDDKRead_ManualIRP_ASYN!\n"); 43 return status; 44 }
运行结果如下:
- 用IoAllocateIrp创建IRP
所有对设备的操作都会转化为一个IRP,IRP会在驱动程序中被处理。IRP请求被结束,就代表着对设备操作的结束,IRP的完成状态就是操作的完成状态。而所有的IRP最终都是由IoAllocateIrp内核函数创建的。
示例代码:
1 NTSTATUS HelloDDKRead_ManualIRP(PDEVICE_OBJECT pDevObj, PIRP pIrp){ 2 UNREFERENCED_PARAMETER(pDevObj); 3 DbgPrint("Enter HelloDDKRead_ManualIRP!\n"); 4 NTSTATUS status = STATUS_SUCCESS; 5 UNICODE_STRING DeviceName; 6 RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDDKDevice"); 7 PDEVICE_OBJECT pDeviceObject = NULL; 8 PFILE_OBJECT pFileObject = NULL; 9 status = IoGetDeviceObjectPointer(&DeviceName, FILE_ALL_ACCESS, &pFileObject, &pDeviceObject); 10 if (!NT_SUCCESS(status)){ 11 status = STATUS_UNSUCCESSFUL; 12 pIrp->IoStatus.Status = status; 13 pIrp->IoStatus.Information = 0; 14 IoCompleteRequest(pIrp, IO_NO_INCREMENT); 15 return status; 16 } 17 18 KEVENT event; 19 KeInitializeEvent(&event, NotificationEvent, FALSE); 20 PIRP pNewIrp = IoAllocateIrp(pDeviceObject->StackSize, FALSE); 21 pNewIrp->UserEvent = &event; 22 IO_STATUS_BLOCK status_block; 23 pNewIrp->UserIosb = &status_block; 24 pNewIrp->Tail.Overlay.Thread = PsGetCurrentThread(); 25 pNewIrp->AssociatedIrp.SystemBuffer = NULL; 26 PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp); 27 stack->MajorFunction = IRP_MJ_READ; 28 stack->MinorFunction = IRP_MN_NORMAL; 29 stack->FileObject = pFileObject; 30 status = IoCallDriver(pDeviceObject, pNewIrp); 31 if (status == STATUS_PENDING){ 32 status = KeWaitForSingleObject(&event, 33 Executive, 34 KernelMode, 35 FALSE, NULL); 36 } 37 ObDereferenceObject(pFileObject); 38 status = STATUS_SUCCESS; 39 pIrp->IoStatus.Status = status; 40 pIrp->IoStatus.Information = 0; 41 IoCompleteRequest(pIrp, IO_NO_INCREMENT); 42 DbgPrint("Leave HelloDDKRead_ManualIRP!\n"); 43 return status; 44 }
运行结果如下:
我这样验证了,返回的DeviceObject的指针就是返回的FileObject的成员:
输出结果: