内核驱动IRP_MJ_WRITE请求中Irp缓冲区成员异常原因咨询
我在编写内核模式驱动时,通过DirectIO创建设备和符号链接,监听IRP_MJ_WRITE请求,用户态调用WriteFile发送缓冲区数据,数据接收正常,但发现Irp->AssociatedIrp.SystemBuffer和Irp->MdlAddress始终为NULL——无论是否设置IO_BUFFERED标志,只能通过Irp->UserBuffer手动创建MDL来访问数据。按理论UserBuffer应为输出缓冲区,特此咨询该现象的原因。
驱动初始化代码
... // routine for handling write requests DriverObject->MajorFunction[IRP_MJ_WRITE] = DriverWrite; // Create device status = IoCreateDevice(DriverObject, 0, &DEVICE_NAME, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DriverObject->DeviceObject); if (NT_SUCCESS(status)) { DbgPrint("Device %wZ created", DEVICE_NAME); } else { DbgPrint("Could not create device %wZ", DEVICE_NAME); } // Create symbolic link, which is user-visible status = IoCreateSymbolicLink(&DEVICE_SYMBOLIC_NAME, &DEVICE_NAME); if (NT_SUCCESS(status)) { DbgPrint("Symbolic link %wZ created", DEVICE_SYMBOLIC_NAME); } else { DbgPrint("Error creating symbolic link %wZ", DEVICE_SYMBOLIC_NAME); } DbgPrint("Driver loaded"); return STATUS_SUCCESS; ...
用户态WriteFile代码
[DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr CreateFile( string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("kernel32.dll", SetLastError = true)] static extern bool WriteFile( IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped); [DllImport("kernel32.dll", SetLastError = true)] static extern bool CloseHandle(IntPtr hObject); IntPtr handle = CreateFile("\\\\.\\SampleDeviceLink", 0x40000000, 0, IntPtr.Zero, 3, 0, IntPtr.Zero); if (handle.ToInt32() == -1) { Console.WriteLine("Error opening the file"); return; } string frase = "testing stuff"; byte[] data = Encoding.ASCII.GetBytes(frase); uint bytesWritten; bool success = WriteFile(handle, data, (uint)data.Length, out bytesWritten, IntPtr.Zero); if (!success) { Console.WriteLine("Error written the file"); } else { Console.WriteLine($"Written {bytesWritten}"); } CloseHandle(handle); Console.WriteLine("Press any key to close this.."); Console.ReadLine();
内核DriverWrite函数中Irp状态
Irp->AssociatedIrp.SystemBuffer -> NULL Irp->MdlAddress -> NULL
缓冲区大小获取代码
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); ULONG bufferSize = irpStack->Parameters.Write.Length;
手动通过UserBuffer访问数据的代码
MDL* mdl = IoAllocateMdl(Irp->UserBuffer, bufferSize, FALSE, FALSE, NULL); MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess); PVOID v_buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority); // Here v_buffer has what I sent from WriteFile
原因分析
设备默认处于Neither缓冲模式:调用
IoCreateDevice时,你未设置DeviceObject->Flags中的DO_BUFFERED_IO或DO_DIRECT_IO标志。默认情况下,设备处于Neither模式,此时I/O管理器不会自动分配SystemBuffer或创建MdlAddress,而是直接将用户态缓冲区的虚拟地址传递到Irp->UserBuffer,驱动需自行处理内存安全校验(如你实现的MmProbeAndLockPages)。UserBuffer的角色误解:在Neither模式下,
UserBuffer并非严格意义上的“输出缓冲区”。对于IRP_MJ_WRITE请求,用户态WriteFile传入的是待写入的数据源缓冲区,内核中UserBuffer直接指向该用户态地址,驱动需要手动完成内存的锁定与映射才能安全访问。标志设置时机错误:若你尝试设置过
DO_BUFFERED_IO但未生效,大概率是设置时机不对——必须在IoCreateDevice调用完成后、设备进入可用状态前设置标志,且不能在设备已被打开后修改。正确设置方式如下:
// 创建设备后立即添加 DriverObject->DeviceObject->Flags |= DO_BUFFERED_IO; // 或 DO_DIRECT_IO
内容的提问来源于stack exchange,提问作者forlayo

