You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

内核驱动IRP_MJ_WRITE请求中Irp缓冲区成员异常原因咨询

内核驱动IRP_MJ_WRITE请求中SystemBuffer与MdlAddress为NULL的原因分析

我在编写内核模式驱动时,通过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

原因分析

  1. 设备默认处于Neither缓冲模式:调用IoCreateDevice时,你未设置DeviceObject->Flags中的DO_BUFFERED_IO或DO_DIRECT_IO标志。默认情况下,设备处于Neither模式,此时I/O管理器不会自动分配SystemBuffer或创建MdlAddress,而是直接将用户态缓冲区的虚拟地址传递到Irp->UserBuffer,驱动需自行处理内存安全校验(如你实现的MmProbeAndLockPages)。

  2. UserBuffer的角色误解:在Neither模式下,UserBuffer并非严格意义上的“输出缓冲区”。对于IRP_MJ_WRITE请求,用户态WriteFile传入的是待写入的数据源缓冲区,内核中UserBuffer直接指向该用户态地址,驱动需要手动完成内存的锁定与映射才能安全访问。

  3. 标志设置时机错误:若你尝试设置过DO_BUFFERED_IO但未生效,大概率是设置时机不对——必须在IoCreateDevice调用完成后、设备进入可用状态前设置标志,且不能在设备已被打开后修改。正确设置方式如下:

// 创建设备后立即添加
DriverObject->DeviceObject->Flags |= DO_BUFFERED_IO; // 或 DO_DIRECT_IO

内容的提问来源于stack exchange,提问作者forlayo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 18:57:52