DeviceIoControl 返回 ERROR_ACCESS_DENIED

Posted

技术标签:

【中文标题】DeviceIoControl 返回 ERROR_ACCESS_DENIED【英文标题】:DeviceIoControl returning ERROR_ACCESS_DENIED 【发布时间】:2020-05-02 16:20:18 【问题描述】:

我正在尝试与用于创建 TUN 接口 (WinTun) 的驱动程序进行交互,但为了从它们发送和接收数据,我需要注册一个环形缓冲区。我使用的代码看起来像这样(我省略了使用 SetupApi 创建设备的部分,因为这似乎有效):

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

#define REGISTER_RINGS_IOCTL CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)

#define BUF_CAPACITY 0x20000
#define BUF_TRAILING 0x10000

typedef struct 
    volatile ULONG Head;
    volatile ULONG Tail;
    volatile LONG Alertable;
    UCHAR Data[BUF_CAPACITY + BUF_TRAILING];
 TUN_RING;

typedef struct 
    ULONG Size;
    UCHAR Data;
 TUN_PACKET;

typedef struct 
    struct 
        ULONG RingSize;
        TUN_RING *Ring;
        HANDLE TailMoved;
     Send, Receive;
 TUN_REGISTER_RINGS;

int main() 

    HANDLE device = CreateFileW(
        L"\\\\?\\ROOT#NET#0006#cac88484-7515-4c03-82e6-71a87abac361",
        // ^^ This comes from the omitted code ^^
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );

    if(device == INVALID_HANDLE_VALUE) 
        printf("The device does not exist\n");
        return -1;
    

    TUN_RING Send, Receive = 0;

    TUN_REGISTER_RINGS params = 
        .Send.RingSize = sizeof(TUN_RING),
        .Send.Ring = &Send,
        .Send.TailMoved = CreateEventW(NULL, FALSE, FALSE, NULL),

        .Receive.RingSize = sizeof(TUN_RING),
        .Receive.Ring = &Receive,
        .Receive.TailMoved = CreateEventW(NULL, FALSE, FALSE, NULL),
    ;

    DWORD bytes;

    BOOL ret = DeviceIoControl(
        device,
        REGISTER_RINGS_IOCTL,
        &params,
        sizeof(TUN_REGISTER_RINGS),
        NULL,
        0,
        &bytes,
        NULL
    );

    if(ret == 0) 
        printf("Err: %d\n", GetLastError());
        return -2;
    

    return 0;

我的问题是此代码在 DeviceIoControl 处失败,错误 5 对应于 ERROR_ACCESS_DENIED。

我不知道为什么会发生这种情况,因为程序已经以管理员权限运行并且设备句柄已使用推荐的属性 (as you can see here) 打开。很抱歉缺少额外的信息,但我在 Windows 驱动程序方面没有太多经验,也不知道如何进一步调试。

我认为问题可能与 check in the source code of the driver 有关,因为它似乎在检查输入缓冲区之前停止(当我将垃圾放入输入缓冲区时它应该重新运行 INVALID_PARAMETER,但这不会发生)。

再次,如果我误解了某些内容或遗漏了一些重要的内容,我深表歉意,但我正在学习所有这些内容,提前致谢!

【问题讨论】:

从TunInitializeDispatchSecurityDescriptor 清楚可见它只允许FILE_ALL_ACCESS 用于LocalSystem 帐户。所以只有 LocalSystem 可以向设备发送一些 ioctl。如果您以管理员身份运行但不是 LocalSystem,则您必须得到 ERROR_ACCESS_DENIED @RbMm 是的!你说的对!我尝试将可执行文件作为 LocalSystem 运行,它终于成功了,我想它是为了确保与驱动程序对话的每个应用程序都是服务?无论如何,谢谢! @ErykSun - 如果我没弄错FILE_OPEN_FOR_BACKUP_INTENT(win32 的FILE_FLAG_BACKUP_SEMANTICS)仅在创建/打开文件操作中有效。但我们在 ioct 内部有问题,直接调用 SeAccessCheck here - 所以没有帮助 在调用 SeAccessCheck here 中根本没有使用来自 IrpFileObject,而只是调用线程的安全上下文。所以无论如何我们现在需要 LocalSystem 而 SeBackupPrivilege 和 SeRestorePrivilege 无济于事 @RbMm,抱歉打扰了,显然之前的CreateFileW 调用成功了。在 IOCTL 中进行另一次安全检查很奇怪。通常它取决于 I/O 管理器,基于授予句柄的访问权限和 IOCTL 所需的读/写访问权限。我想他们需要在IoCreateFileEx 中解决 I/O 管理器的权限检查问题,以便只允许 SYSTEM 使用 IOCTL,但是如果没有这个额外的安全描述符,有更简单的方法可以做到这一点。也许它们为代码提供了一种替换或扩展安全描述符的方法。 【参考方案1】:

找到了解决办法。正如@RbMm 所述,code that creates the security descriptor 仅允许访问 LocalSystem。这意味着它是唯一允许与司机交谈的帐户。

【讨论】:

以上是关于DeviceIoControl 返回 ERROR_ACCESS_DENIED的主要内容,如果未能解决你的问题,请参考以下文章

DeviceIoControl 完全不起作用,返回时 SystemBuffer 为空

SCSI INQUIRY 命令的 DeviceIoControl 返回错误 50

用于 DeviceIOControl 的 SetWindowsHookEx,要使用啥 hookid?

如何使用 DeviceIOControl() 从 CD 中获取“曲目信息”?

使用 DeviceIoControl 都有哪些好的策略?

带有输入无符号字符缓冲区 C++ 的 DeviceIoControl