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

无法从C++内核驱动向Rust输出缓冲区接收数据

内核驱动输出缓冲区数据无法被Rust用户态接收问题

我在实现C++内核驱动与Rust用户态程序的DeviceIoControl通信时遇到问题:内核端已确认输出缓冲区被正确写入值123,但Rust用户态接收到的始终是0。

内核端C++代码

PVOID outputBuffer = Irp->UserBuffer;
LONG outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;

LONG tempVmFullAccessCount = g_VmFullAccessCount;
KdPrint(("Long size: %d\n", sizeof(LONG)));
LONG constValue = 123;
RtlCopyMemory(outputBuffer, &constValue, sizeof(LONG));
information = sizeof(LONG);
KdPrint(("Output buffer value: %d\n", *((PLONG)outputBuffer)));

Rust用户态第一种尝试代码

let mut bytes_returned: DWORD = 0;

let mut buffer: [u8; 4] = [0; 4];
let output_buffer = buffer.as_mut_ptr() as PVOID;

let result: BOOL = unsafe {
    DeviceIoControl(
        h_device,
        IOCTL_NOTIFY_USERMODE_APP,
        null_mut(),
        0,
        output_buffer,
        size_of::<i32>() as u32,
        &mut bytes_returned as *mut DWORD,
        null_mut(),
    )
};

if result != FALSE {
    println!("Bytes returned: {}", bytes_returned);
    println!("VM Full Access Counter: {}", vm_full_access_counter);
    println!("Size of i32: {}", size_of::<i32>());

    vm_full_access_counter = i32::from_ne_bytes(buffer);
    println!("VM Full Access Counter: {}", vm_full_access_counter);
} else {
    eprintln!("DeviceIoControl failed. Error: {}", unsafe { winapi::um::errhandlingapi::GetLastError() });
}

运行后output_buffer始终为[0,0,0,0],于是尝试直接传递变量而非u8缓冲区:

Rust用户态第二种尝试代码

let mut vm_full_access_counter: i32 = 100;
let mut bytes_returned: DWORD = 0;

let result: BOOL = unsafe {
    DeviceIoControl(
        h_device,
        IOCTL_NOTIFY_USERMODE_APP,
        null_mut(),
        0,
        &mut vm_full_access_counter as *mut i32 as PVOID,
        size_of::<i32>() as u32,
        &mut bytes_returned as *mut DWORD,
        null_mut(),
    )
};

if result != FALSE {
    println!("Bytes returned: {}", bytes_returned);
    println!("VM Full Access Counter: {}", vm_full_access_counter);
    println!("Size of i32: {}", size_of::<i32>());

    let raw_ptr = &mut vm_full_access_counter as *mut i32;
    let value = unsafe { *raw_ptr };
    println!("VM Full Access Counter: {}", value);
} else {
    eprintln!("DeviceIoControl failed. Error: {}", unsafe { winapi::um::errhandlingapi::GetLastError() });
}

结果:变量vm_full_access_counter调用后从初始值100变为0,依然无法正确获取内核写入的123。

已知关键信息

  • LONG和i32的大小均为4字节
  • 内核端打印确认outputBuffer已写入123
  • Rust端bytes_returned返回值为4
  • 已通过定时器+Arc&Mutex验证,排除延迟更新问题

排查方向与解决方法

  1. 检查IRP完成状态设置
    内核端必须正确设置IRP的完成状态和返回信息长度,否则用户态无法正确接收数据:

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = information; // 此处为sizeof(LONG)
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
  2. 验证IOCTL缓冲区类型匹配
    IOCTL定义的缓冲区类型需与内核代码使用的缓冲区对应:

    • METHOD_BUFFERED:内核应使用Irp->AssociatedIrp.SystemBuffer而非UserBuffer
    • METHOD_IN_DIRECT/METHOD_OUT_DIRECT:需处理MDL
    • 仅METHOD_NEITHER可直接使用UserBuffer
  3. 确认用户态权限与IOCTL定义一致性

    • 确保设备句柄h_device有效,打开设备时请求了GENERIC_READ | GENERIC_WRITE权限
    • 验证IOCTL_NOTIFY_USERMODE_APP的控制码定义(设备类型、功能码、传输类型)与内核端完全一致
  4. 显式字节序转换验证
    x86/x64平台为小端序,可尝试在Rust端用i32::from_le_bytes(buffer)替代from_ne_bytes,确认是否因隐式字节序问题导致数据错误。

  5. 内核端内存访问合法性检查
    内核端添加内存访问探测,避免非法内存写入导致数据未正确提交:

    __try {
        ProbeForWrite(outputBuffer, sizeof(LONG), sizeof(CHAR));
        RtlCopyMemory(outputBuffer, &constValue, sizeof(LONG));
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        Irp->IoStatus.Status = GetExceptionCode();
        return Irp->IoStatus.Status;
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:42:20