Windows内核2 IRP(I/O Request Package) IRP是什么 从概念上讲IRP类似于windows应用程序的消息。我们知道在windows中应用程序是由消息驱动的。
IRP的全名是I/O Request Package,即输入输出请求包,它是Windows内核中的一种非常重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求,操作系统将相应的I/O请求转换成相应的IRP ,不同的IRP会根据类型被分派到不同的派遣例程中进行处理。
与IRP相关的几个常用的事件
驱动层
功能解释
对应于应用层
IRP_MJ_CREATE
请求一个句柄
CreateFile
IRP_MJ_CLOSE
关闭句柄
CloseHandle
IRP_MJ_READ
从设备得到数据
ReadFile
IRP_MJ_WRITE
传送数据到设备
WriteFile
IRP_MJ_DEVICE_CONTROL
控制操作利用IOCTL宏
DeviceIoControl
这几个IRP事件放在MajorFunction里,MajorFuncton是驱动对象(PDRIVER_OBJECT DriverObject)的一个数组形式的成员
查看IRP_MJ_MAXIMUM_FUNCTION可以找到整个IRP事件序列
一般写驱动文件=> 打开 关闭 读取 写入 控制 分别对应
IRP_MJ_CREATE 打开 IRP_MJ_CLOSE 关闭 IRP_MJ_READ 读取 //可选 IRP_MJ_WRITE 写入//可选 IRP_MJ_DEVICE_CONTROL 控制(可以取代读写)
在 Windows 驱动程序开发中,IRP(I/O Request Packet,输入/输出请求数据包)是用于描述和传递 I/O 请求的重要数据结构。在处理 IRP 时,可以直接从 IRP 结构本身获取一些信息,而另一些信息则需要从 IRP 堆栈(IRP stack)中获取。以下是常见的从 IRP 和 IRP 堆栈中获取的信息:
直接从 IRP 中获取的信息:
IRP 类型(IRP Type) :指示 IRP 的类型,例如 IRP_MJ_READ、IRP_MJ_WRITE 等。
IRP 标志(IRP Flags) :包含了关于 IRP 状态和属性的标志信息,例如是否为同步或异步操作。
请求标识(RequestorMode) :指示 IRP 是由用户模式发起的还是内核模式发起的请求。
I/O 栈大小(StackCount) :IRP 堆栈中的堆栈大小,通常用于多层驱动栈的处理。
I/O 栈指针(Tail.Overlay.CurrentStackLocation) :指向当前处理的 IRP 堆栈位置的指针。
从 IRP 堆栈中获取的信息:
MajorFunction :指示当前 IRP 堆栈帧的主要功能码,例如 IRP_MJ_READ、IRP_MJ_WRITE 等。
Parameters :包含了 IRP 参数的结构体,具体取决于 IRP 的主要功能码。例如,IRP_MJ_READ 对应的是 READ
操作的参数,IRP_MJ_WRITE 对应的是 WRITE
操作的参数。
FileObject :表示与 IRP 关联的文件对象,如果适用的话。
设备对象(DeviceObject) :指示 IRP 正在传输的设备对象。
传输的数据(Buffer、MdlAddress 等) :如果 IRP 包含数据传输(如读写操作),可以从相应的字段获取数据的缓冲区地址或者 MDL(Memory Descriptor List)地址。
状态信息(IoStatus) :用于指示 I/O 操作的状态和结果,如成功或失败的状态码。
常见的内核API前缀及其功能
Zw
/ Nt
用途 :用户模式和内核模式下的系统服务调用。于Ring3 API同功能的函数
Io
Ke
Mm
Ps
Ob
Ex
用途 :执行器(executive)函数,提供通用的系统服务,如内存分配、同步等。
Rtl
用途 :运行时库函数,提供字符串操作、内存管理等功能。和Ring3的C库不一样,但是Ring3的API还是能用,Ring0的这套库更安全
Cm
Po
Hal
FsRtl
全新字符串格式 微软设计UNICODE_STRING
和ANSI_STRING
结构体,而不是直接使用原始的char*
和wchar_t*
指针,是为了实现更高的安全性、灵活性和一致性。
原因:
安全性
长度信息 :UNICODE_STRING
和ANSI_STRING
结构体包含了字符串的长度信息。这使得操作这些字符串时能够避免缓冲区溢出等安全问题。直接使用char*
或wchar_t*
可能无法可靠地知道字符串的长度,容易引发缓冲区溢出等问题。
1 2 3 4 5 typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING;
灵活性
动态分配和管理 :使用结构体可以更方便地管理字符串的动态分配和释放。可以预先分配一个更大的缓冲区并在需要时调整长度,而无需频繁分配和释放内存。
1 2 UNICODE_STRING ustr; RtlInitUnicodeString (&ustr, L"Example" );
一致性
一致的API设计
通过定义结构体,微软可以设计一组一致的API来操作字符串。例如,
函数可以统一处理
结构,而不需要关心底层的字符指针操作。
1 2 3 4 NTSTATUS RtlCopyUnicodeString ( PUNICODE_STRING DestinationString, PCUNICODE_STRING SourceString ) ;
跨平台支持
平台无关:结构体可以更容易地在不同平台和架构之间保持一致。直接使用char*
和wchar_t*
在不同平台可能有不同的实现和行为,使用结构体可以抽象出这些差异。
高效处理
优化和高效处理:通过结构体,操作系统可以更高效地处理字符串操作。例如,
函数可以直接在结构体上操作,而不需要逐字符转换。
1 2 3 4 5 NTSTATUS RtlUpcaseUnicodeString ( PUNICODE_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString ) ;
兼容性
兼容性 :UNICODE_STRING
和ANSI_STRING
提供了一个明确的机制来处理不同字符编码(宽字符和窄字符)。这对于支持国际化和不同语言环境非常重要。
注册IRP 展示了一个典型的驱动程序调度例程,它通过检查IRP的Major Function来处理不同的I/O请求:
当然以下代码只是注册了IRP,还不能执行。
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 #include <ntifs.h> NTSTATUS IRP_CALL (PDEVICE_OBJECT device, PIRP pirp) { UNREFERENCED_PARAMETER (device); UNREFERENCED_PARAMETER (pirp); KdPrint (("zdsoft:进入派遣函数" )); PIO_STACK_LOCATION irpStackL; irpStackL = IoGetCurrentIrpStackLocation (pirp); switch (irpStackL->MajorFunction) { case IRP_MJ_DEVICE_CONTROL: { KdPrint (("zdsoft:用户层调用了DeviceIoControl" )); break ; } case IRP_MJ_CREATE: { KdPrint (("zdsoft:用户层调用了CreateFile" )); break ; } case IRP_MJ_CLOSE: { KdPrint (("zdsoft:用户层调用了CloseHandle" )); break ; } } pirp->IoStatus.Status = STATUS_SUCCESS; pirp->IoStatus.Information = 4 ; IoCompleteRequest (pirp, IO_NO_INCREMENT); KdPrint (("zdsoft:离开派遣函数" )); return STATUS_SUCCESS; } void DriverUnload (PDRIVER_OBJECT DriverObject) { KdPrint (("zdsoft:进入卸载例程DriverObject=%p 行号=%d" , DriverObject, __LINE__)); } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { DriverObject->DriverUnload = DriverUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = IRP_CALL; DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRP_CALL; KdPrint (("zdsoft:进入DriverEntry入口点 DriverObject=%p 行号=%d\n" , DriverObject, __LINE__)); KdPrint (("zdsoft:RegistryPath=%ws\n" , RegistryPath->Buffer)); return 0 ; }
代码分析:
1 2 3 4 5 pirp->IoStatus.Status = STATUS_SUCCESS; pirp->IoStatus.Information = 4 ; IoCompleteRequest (pirp, IO_NO_INCREMENT);KdPrint (("zdsoft:离开派遣函数" ));return STATUS_SUCCESS;
这几行代码是在处理完I/O请求后,设置IRP的状态并完成该请求。
pirp->IoStatus.Status = STATUS_SUCCESS;
设置IRP的状态为成功。IoStatus.Status
是一个NTSTATUS
类型的字段,用于指示I/O操作的结果。将其设置为STATUS_SUCCESS
表示操作已成功完成。
pirp->IoStatus.Information = 4;
设置IRP的附加信息。IoStatus.Information
字段用于传递与I/O操作相关的额外信息。在这个例子中,设置为4
,这通常是与特定操作相关的值,例如返回给DeviceIoControl
的lpBytesReturned
参数。lpBytesReturned
表示实际传输的字节数,设置这个字段可以告诉调用方操作的结果细节。
IoCompleteRequest(pirp, IO_NO_INCREMENT);
完成IRP请求。IoCompleteRequest
函数通知I/O管理器该IRP已经处理完毕,可以继续处理下一个I/O操作。
第二个参数IO_NO_INCREMENT
用于控制完成IRP时的优先级提升。IO_NO_INCREMENT
表示不提升线程的优先级。其他可能的值有IO_DISK_INCREMENT
和IO_CD_ROM_INCREMENT
,这些值会在完成I/O操作时暂时提升线程优先级,以便更快地处理后续操作。
KdPrint(("zdsoft:离开派遣函数"));
在大多数情况下,使用KdPrint
和DbgPrint
没有太大区别。Kd是一个宏定义,在Debug模式下,会自动替换为DbgPrint,在不是Debug的情况下,无作用
return STATUS_SUCCESS;
返回STATUS_SUCCESS
,表示调度例程成功执行。这个返回值是给操作系统使用的,表示该I/O请求已成功处理。
NTSTATUS
是Windows操作系统中用来表示函数返回状态的类型。它是一个32位的整数,用于标识函数调用的结果,包含了操作成功、错误代码、警告等不同的状态信息。
我们发现无论是加载或是卸载都没有触发IRP事件,这是因为在用户层(也就是R3环 )没有调用CreateFile打开设备也没有绑定符号。这个放在后面讲,现在只讲如何注册IRP。
实现交互 创建设备 IoCreateDevice
是一个内核模式 API,用于在驱动程序中创建设备对象。设备对象表示驱动程序控制的逻辑或物理设备。创建设备对象后,驱动程序可以处理 I/O 请求,并将这些请求传递给设备对象。
1 2 3 4 5 6 7 8 9 NTSTATUS IoCreateDevice ( PDRIVER_OBJECT DriverObject, ULONG DeviceExtensionSize, PUNICODE_STRING DeviceName, DEVICE_TYPE DeviceType, ULONG DeviceCharacteristics, BOOLEAN Exclusive, PDEVICE_OBJECT *DeviceObject ) ;
参数解释
DriverObject :指向驱动程序对象的指针,由操作系统在调用 DriverEntry
时提供。
DeviceExtensionSize :指定设备扩展的大小。设备扩展是驱动程序分配的一块内存,用于存储设备相关的上下文信息。
DeviceName :指向设备名称的指针。如果设备名不需要,则此参数可以为 NULL。填了NULL,系统会随机命名 手动命名要满足 \Device\MyDevice ( L”\Device\MyDevice” )
DeviceType :设备类型,可以是标准设备类型之一,例如 FILE_DEVICE_DISK
、FILE_DEVICE_KEYBOARD
等。
也可以填不知道
1 #define FILE_DEVICE_UNKNOWN 0x00000022
DeviceCharacteristics :设备特征标志,可以是 0 或由系统定义的一组特征标志的 OR 值。例如 FILE_DEVICE_SECURE_OPEN
。
Exclusive :指示设备是否为独占设备。TRUE 表示独占,FALSE 表示非独占。独占的话,一次只能一个人操作。
DeviceObject :指向 PDEVICE_OBJECT 类型的指针,用于返回新创建的设备对象。
创建设备的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 NTSTATUS status; PDEVICE_OBJECT DeviceObject = NULL ; UNICODE_STRING DeviceName; RtlInitUnicodeString (&DeviceName, L"\\Device\\MyDevice" );status = IoCreateDevice ( DriverObject, 0 , &DeviceName, FILE_DEVICE_UNKNOWN, 0 , FALSE, &DeviceObject ); if (!NT_SUCCESS (status)){ KdPrint (("Failed to create device: %X\n" , status)); return status; } KdPrint (("Device created successfully\n" ));
运行一下发现确实安装设备成功了
但是再安装,再启动就失败了。这是为什么?
之前提到过,驱动是不会自己释放资源的,设备名字已经有了,就不能再有重复的了。所以我们需要删除设备对象,所以我们在Unload函数卸载掉这个设备对象
1 2 3 4 5 6 7 8 9 10 void DriverUnload (PDRIVER_OBJECT DriverObject) { KdPrint (("zdsoft:进入卸载例程DriverObject=%p 行号=%d" , DriverObject, __LINE__)); if (DriverObject->DeviceObject != NULL ) { IoDeleteDevice (DriverObject->DeviceObject); } }
驱动设备与符号链接 在Windows驱动程序开发中,驱动设备与符号链接之间的关系是关键概念。符号链接(symbolic link)用于将用户模式下的设备名称与内核模式下的设备对象联系起来,使应用程序能够通过简单的设备名称访问设备对象。 也就是说,如果这个设备允许三环使用,那么就是用的是符号链接 ,不允许三环使用,就不要用符号链接
驱动设备不是驱动对象,而是驱动对象创建的,初学者经常把这两个在概念上搞混。那么符号链接又是什么?为什么会有符号链接?这个要从Windows下的设备命名说起,Windows下的设备命名是这样的:“\Device\设备名”,这个有点怪前面Device是固定的-常量,后面设备名是自己起的-变量。如果没有给设备起名字,I/O管理器也会随机分配一段数字为设备名,例如:“\Device\000001”
(以前有叫狗娃,狗蛋的,现在文明些了有叫果果,糖糖)”。但是这样的名字上不了台面,只能在家里用,家里就相当于内核驱动层,等长大一些了要上学了就要有个学名,学校的同学老师就相当于用户应用层,如果哪个同学叫你狗娃你肯定会不高兴了,所以我们在写驱动的时候最好创建一个符号链接,这是一个好习惯。
创建符号链接的示例:
1 2 3 4 5 6 7 8 9 UNICODE_STRING SymbolicLinkName; RtlInitUnicodeString (&SymbolicLinkName, L"\\DosDevices\\MySymbolicLink" );status = IoCreateSymbolicLink (&SymbolicLinkName, &DeviceName); if (!NT_SUCCESS (status)){ IoDeleteDevice (DeviceObject); KdPrint (("Failed to create symbolic link: %X\n" , status)); return status; }
然后Unload函数在卸载的时候,也要删除符号链接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 VOID DriverUnload (PDRIVER_OBJECT DriverObject) { UNICODE_STRING SymbolicLinkName; RtlInitUnicodeString (&SymbolicLinkName, L"\\DosDevices\\MySymbolicLink" ); IoDeleteSymbolicLink (&SymbolicLinkName); if (DriverObject->DeviceObject != NULL ) { IoDeleteDevice (DriverObject->DeviceObject); } KdPrint (("Driver Unloaded\n" )); }
总代码:
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 #include "header.h" VOID DriverUnload (PDRIVER_OBJECT DriverObject) { UNICODE_STRING SymbolicLinkName; RtlInitUnicodeString (&SymbolicLinkName, L"\\DosDevices\\MySymbolicLink" ); IoDeleteSymbolicLink (&SymbolicLinkName); if (DriverObject->DeviceObject != NULL ) { IoDeleteDevice (DriverObject->DeviceObject); } KdPrint (("Driver Unloaded\n" )); } extern "C" NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER (RegistryPath); NTSTATUS status; PDEVICE_OBJECT DeviceObject = NULL ; UNICODE_STRING DeviceName; RtlInitUnicodeString (&DeviceName, L"\\Device\\MyDevice" ); status = IoCreateDevice ( DriverObject, 0 , &DeviceName, FILE_DEVICE_UNKNOWN, 0 , FALSE, &DeviceObject ); if (!NT_SUCCESS (status)) { KdPrint (("Failed to create device: %X\n" , status)); return status; } UNICODE_STRING SymbolicLinkName; RtlInitUnicodeString (&SymbolicLinkName, L"\\DosDevices\\MySymbolicLink" ); status = IoCreateSymbolicLink (&SymbolicLinkName, &DeviceName); if (!NT_SUCCESS (status)) { IoDeleteDevice (DeviceObject); KdPrint (("Failed to create symbolic link: %X\n" , status)); return status; } DriverObject->DriverUnload = DriverUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = IRP_CALL; DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRP_CALL; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IRP_CALL; KdPrint (("Driver Loaded\n" )); return STATUS_SUCCESS; } NTSTATUS IRP_CALL (PDEVICE_OBJECT DeviceObject, PIRP Irp) { UNREFERENCED_PARAMETER (DeviceObject); PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp); switch (irpStack->MajorFunction) { case IRP_MJ_CREATE: KdPrint (("IRP_MJ_CREATE\n" )); break ; case IRP_MJ_CLOSE: KdPrint (("IRP_MJ_CLOSE\n" )); break ; case IRP_MJ_DEVICE_CONTROL: KdPrint (("IRP_MJ_DEVICE_CONTROL\n" )); break ; default : KdPrint (("Unknown IRP Major Function\n" )); break ; } Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0 ; IoCompleteRequest (Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; }
安装启动成功以后:
用WinObj查看GLOBAL就会显示出我们自己创建的符号链接,还可以看到和哪个设备绑定
所以我们平时在Ring3操作的C盘,D盘,其实就是箭号指向的符号链接
实现Ring0与Ring3的交互 如何在Visual Studio编译出能在XP上跑的代码?
看这个博客即可
VS2019怎样编译出可以在WinXP上运行的exe?_vs2019 xp-CSDN博客
我们在3环写程序:
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 #include <iostream> #include <windows.h> using namespace std;int main () { HANDLE hFile = CreateFileA ( "\\\\.\\MySymbolicLink" , GENERIC_READ | GENERIC_WRITE, 0 , NULL , OPEN_EXISTING, 0 , NULL ); char szBuffer[20 ]; DWORD dwBytes; DWORD flag; flag=ReadFile (hFile, szBuffer, sizeof (szBuffer), &dwBytes, NULL ); if (!flag) { cout << "Read error!" << endl; } flag=WriteFile (hFile, szBuffer, sizeof (szBuffer), &dwBytes, NULL ); if (!flag) { cout << "Write error!" << endl; } CloseHandle (hFile); system ("pasue" ); return 0 ; }
发现触发了驱动的回调函数,这就实现了Ring3与Ring0的交互
换成32位Win7 折腾了一下,发现64位驱动只能在64位系统下使用,32位驱动只能在32位系统下使用
XP系统还是太老了,于是决定换Win7系统,然后写一个MFC程序
Load和Unload的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void CMFCRing3Dlg::OnBnClickedLoad () { hDeviceFile = CreateFile ( "\\\\.\\MySymbolicLink" , GENERIC_READ | GENERIC_WRITE, 0 , NULL , OPEN_EXISTING, 0 , NULL ); return ; } void CMFCRing3Dlg::OnBnClickedUnload () { CloseHandle (hDeviceFile); return ; }
接下来是驱动的代码
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 #include "header.h" VOID DriverUnload (PDRIVER_OBJECT DriverObject) { UNICODE_STRING SymbolicLinkName; RtlInitUnicodeString (&SymbolicLinkName, L"\\DosDevices\\KR_2024_07_11_Device_SymbolLink" ); NTSTATUS status = IoDeleteSymbolicLink (&SymbolicLinkName); if (!NT_SUCCESS (status)) { KdPrint (("Failed to delete symbolic link! Status: 0x%X\n" , status)); } PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; while (deviceObject != NULL ) { PDEVICE_OBJECT nextDeviceObject = deviceObject->NextDevice; IoDeleteDevice (deviceObject); deviceObject = nextDeviceObject; } KdPrint (("Driver Unloaded\n" )); } NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) { UNREFERENCED_PARAMETER (RegisterPath); KdPrint (("qidong!" )); NTSTATUS status; PDEVICE_OBJECT DeviceObject = NULL ; UNICODE_STRING DeviceName; RtlInitUnicodeString (&DeviceName, L"\\Device\\KR_2024_07_11_Device" ); status = IoCreateDevice ( DriverObject, 0 , &DeviceName, FILE_DEVICE_UNKNOWN, 0 , FALSE, &DeviceObject ); if (!NT_SUCCESS (status)) { KdPrint (("Failed to create device! Status: 0x%X\n" , status)); return status; } UNICODE_STRING SymbolicLinkName; RtlInitUnicodeString (&SymbolicLinkName, L"\\DosDevices\\KR_2024_07_11_Device_SymbolLink" ); status = IoCreateSymbolicLink (&SymbolicLinkName, &DeviceName); if (!NT_SUCCESS (status)) { IoDeleteDevice (DeviceObject); KdPrint (("Failed to create symbolic link! Status: 0x%X\n" , status)); return status; } DriverObject->DriverUnload = DriverUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = IRP_CALL; DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRP_CALL; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IRP_CALL; DriverObject->MajorFunction[IRP_MJ_READ] = IRP_CALL; DriverObject->MajorFunction[IRP_MJ_WRITE] = IRP_CALL; KdPrint (("Driver Loaded\n" )); return STATUS_SUCCESS; } NTSTATUS IRP_CALL (PDEVICE_OBJECT DeviceObject, PIRP Irp) { UNREFERENCED_PARAMETER (DeviceObject); PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp); switch (irpStack->MajorFunction) { case IRP_MJ_CREATE: KdPrint (("IRP_MJ_CREATE\n" )); break ; case IRP_MJ_CLOSE: KdPrint (("IRP_MJ_CLOSE\n" )); break ; case IRP_MJ_DEVICE_CONTROL: KdPrint (("IRP_MJ_DEVICE_CONTROL\n" )); break ; case IRP_MJ_READ: KdPrint (("IRP_MJ_READ\n" )); break ; case IRP_MJ_WRITE: KdPrint (("IRP_MJ_WRITE\n" )); break ; default : KdPrint (("Unknown IRP Major Function\n" )); break ; } Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0 ; IoCompleteRequest (Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; }
效果如图: