代码之家  ›  专栏  ›  技术社区  ›  Matteo Italia

自Windows 10 1809以来,USB HID设备上的CreateFile失败,访问被拒绝(5)

  •  9
  • Matteo Italia  · 技术社区  · 6 年前

    自从最新的windows1809更新后,我们再也不能打开一个usbhid键盘一样的设备使用 CreateFile

    #include <windows.h>
    #include <setupapi.h>
    #include <stdio.h>
    #include <hidsdi.h>
    
    void bad(const char *msg) {
        DWORD w = GetLastError();
        fprintf(stderr, "bad: %s, GetLastError() == 0x%08x\n", msg, (unsigned)w);
    }
    
    int main(void) {
        int i;
        GUID hidGuid;
        HDEVINFO deviceInfoList;
        const size_t DEVICE_DETAILS_SIZE = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + MAX_PATH;
        SP_DEVICE_INTERFACE_DETAIL_DATA *deviceDetails = alloca(DEVICE_DETAILS_SIZE);
        deviceDetails->cbSize = sizeof(*deviceDetails);
    
        HidD_GetHidGuid(&hidGuid);
        deviceInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL,
                                             DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
        if(deviceInfoList == INVALID_HANDLE_VALUE) {
            bad("SetupDiGetClassDevs");
            return 1;
        }
    
        for (i = 0; ; ++i) {
            SP_DEVICE_INTERFACE_DATA deviceInfo;
            DWORD size = DEVICE_DETAILS_SIZE;
            HIDD_ATTRIBUTES deviceAttributes;
            HANDLE hDev = INVALID_HANDLE_VALUE;
    
            fprintf(stderr, "Trying device %d\n", i);
            deviceInfo.cbSize = sizeof(deviceInfo);
            if (!SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i,
                                             &deviceInfo)) {
                if (GetLastError() == ERROR_NO_MORE_ITEMS) {
                    break;
                } else {
                    bad("SetupDiEnumDeviceInterfaces");
                    continue;
                }
            }
    
            if(!SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo,
                                            deviceDetails, size, &size, NULL)) {
                bad("SetupDiGetDeviceInterfaceDetail");
                continue;
            }
    
            fprintf(stderr, "Opening device %s\n", deviceDetails->DevicePath);
            hDev = CreateFile(deviceDetails->DevicePath, 0,
                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                              OPEN_EXISTING, 0, NULL);
            if(hDev == INVALID_HANDLE_VALUE) {
                bad("CreateFile");
                continue;
            }
    
            deviceAttributes.Size = sizeof(deviceAttributes);
            if(HidD_GetAttributes(hDev, &deviceAttributes)) {
                fprintf(stderr, "VID = %04x PID = %04x\n", (unsigned)deviceAttributes.VendorID, (unsigned)deviceAttributes.ProductID);
            } else {
                bad("HidD_GetAttributes");
            }
            CloseHandle(hDev);
        }
    
        SetupDiDestroyDeviceInfoList(deviceInfoList);
        return 0;
    }
    

    它枚举所有HID设备,尝试使用 创建文件 在提供的路径上 SetupDiGetDeviceInterfaceDetail HidD_GetAttributes

    此代码在以前的Windows版本上运行没有问题(在windows7、windows1709和1803上测试过,从中提取的原始代码一直从XP开始工作),但是最新的更新(1809)全部 创建文件 GetLastError() ==5)。以管理员身份运行程序没有任何效果。

    比较更新前后的输出,我注意到现在无法打开的设备有一个尾随 \kbd

    \\?\hid#vid_24d6&pid_8000&mi_00#7&294a3305&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
    

    现在是

    \\?\hid#vid_24d6&pid_8000&mi_00#7&294a3305&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
    

    在最新的Windows10版本中是否存在bug/新的安全限制?这个代码是不是一直都是错误的,它以前是偶然的?这个能修好吗?


    更新

    作为一种绝望的尝试,我们试图移除 \kbd公司 从返回的字符串。。。和 现在起作用了!所以,现在我们有了一个解决方法,但是如果这是一个bug,我们会很感兴趣的 设置DiGetDeviceInterfaceDetail ,如果这是有意的,并且这种解决方法实际上是正确的。

    2 回复  |  直到 6 年前
        1
  •  5
  •   andrew    6 年前

    修复程序出现在今天(2019年3月1日)发布的windows更新中。

    https://support.microsoft.com/en-us/help/4482887/windows-10-update-kb4482887

        2
  •  13
  •   Matteo Italia    6 年前

    我在找绳子 KBD (UTF-16格式)-在1809版本中,它只存在于两个驱动程序中, hidclass.sys ,在1709版本中不存在。

    hidclass.sys 他们改变了 HidpRegisterDeviceInterface 功能。在这次发布之前 IoRegisterDeviceInterface GUID_DEVINTERFACE_HID 以及 引用字符串 指针设置为0。但在新版本中,取决于 GetHidClassCollection ,它通过了 作为 引用字符串

    内部 kbdhid.sys公司 他们变了 KbdHid_Create KBD公司

    为了更确切地了解原因,还需要进行更多的研究。一些灾难:

    enter image description here enter image description here enter image description here


    作为参考,hidpresisterDeviceInterface来自1709 build

    enter image description here

    引用字符串 ),而且没有支票 cmp word [rbp + a],6 论类集合数据


    然而, 创建KbdHid 1809年有一个窃听器。代码是:

    NTSTATUS KbdHid_Create(PDEVICE_OBJECT DeviceObject, PIRP Irp)
    {
      //...
    
        PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
    
        if (PFILE_OBJECT FileObject = IrpSp->FileObject)
        {
            PCUNICODE_STRING FileName = &FileObject->FileName;
    
            if (FileName->Length)
            {
            #if ver == 1809
                UNICODE_STRING KBD = RTL_CONSTANT_STRING(L"KBD"); // !! bug !!
    
                NTSTATUS status = RtlEqualUnicodeString(FileName, &KBD, FALSE)
                    ? STATUS_SHARING_VIOLATION : STATUS_ACCESS_DENIED;
            #else
                NTSTATUS status = STATUS_ACCESS_DENIED;
            #endif
    
                // log
    
                Irp->IoStatus.Status = status;
                IofCompleteRequest(Irp, IO_NO_INCREMENT);
                return status;
            }
        }
        // ...
    }
    

    PFILE_OBJECT FileObject 当前堆栈位置。不是吗 FileObject

    在1809年以前,它总是错误地失败 STATUS_ACCESS_DENIED 0xc0000022 ),但从1809年开始,检查名称,如果它等于 KBD公司 STATUS_SHARING_VIOLATION 返回。但是,名称总是以 \ 符号,所以永远不会匹配 KBD公司 . 它可以 \KBD ,因此,要修复此检查,需要将以下行更改为:

    UNICODE_STRING KBD = RTL_CONSTANT_STRING(L"\\KBD");
    

    并与这个字符串进行比较。所以,按设计我们应该有一个 尝试通过打开键盘设备时出错 *\KBD 名称,但由于实现错误,我们实际上 拒绝访问状态

    enter image description here

    另一个变化发生了 IoRegisterDeviceInterface 在设备上查询 GetHidClassCollection 结果,如果有的话 WORD 结构中的(2字节)字段等于6,加上 后缀( 引用字符串 Usage ID for keyboard ,这个前缀的基本原理是设置独占访问模式


    实际上,我们可以在没有 \ 如果我们使用相关设备打开 OBJECT_ATTRIBUTES . 所以,为了测试,我们可以这样做:如果接口名以 ,首先打开不带此后缀的文件(因此相对设备名为空),此打开必须正常工作;然后,我们可以尝试使用名称相对打开文件 KBD公司 状态共享违反 1809年和 在这里将没有以前的版本 \KBD公司 后缀):

    void TestOpen(PWSTR pszDeviceInterface)
    {
        HANDLE hFile;
    
        if (PWSTR c = wcsrchr(pszDeviceInterface, '\\'))
        {
            static const UNICODE_STRING KBD = RTL_CONSTANT_STRING(L"KBD");
    
            if (!wcscmp(c + 1, KBD.Buffer))
            {
                *c = 0;
    
                OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, const_cast<PUNICODE_STRING>(&KBD) };
    
                oa.RootDirectory = CreateFileW(pszDeviceInterface, 0, 
                    FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
    
                if (oa.RootDirectory != INVALID_HANDLE_VALUE)
                {
                    IO_STATUS_BLOCK iosb;
    
                    // will be STATUS_SHARING_VIOLATION (c0000043)
                    NTSTATUS status = NtOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb, 
                        FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT);
    
                    CloseHandle(oa.RootDirectory);
    
                    if (0 <= status)
                    {
                        PrintAttr(hFile);
                        CloseHandle(hFile);
                    }
                }
    
                return ;
            }
        }
    
        hFile = CreateFileW(pszDeviceInterface, 0, 
             FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
    
        if (hFile != INVALID_HANDLE_VALUE)
        {
            PrintAttr(hFile);
            CloseHandle(hFile);
        }
    }
    void PrintAttr(HANDLE hFile)
    {
        HIDD_ATTRIBUTES deviceAttributes = { sizeof(deviceAttributes) };
    
        if(HidD_GetAttributes(hFile, &deviceAttributes)) {
            printf("VID = %04x PID = %04x\r\n", 
                (ULONG)deviceAttributes.VendorID, (ULONG)deviceAttributes.ProductID);
        } else {
            bad(L"HidD_GetAttributes");
        }
    }
    

    在1809年的一次测试中 状态共享违反 kbdhid.KbdHid_Create -如果我们检查一下 FileName ,我们需要检查 RelatedFileObject -是不是0。


    CM_Get_Device_Interface_List 而不是SetupAPI:

    volatile UCHAR guz = 0;
    
    CONFIGRET EnumInterfaces(PGUID InterfaceClassGuid)
    {
        CONFIGRET err;
    
        PVOID stack = alloca(guz);
        ULONG BufferLen = 0, NeedLen = 256;
    
        union {
            PVOID buf;
            PWSTR pszDeviceInterface;
        };
    
        for(;;) 
        {
            if (BufferLen < NeedLen)
            {
                BufferLen = RtlPointerToOffset(buf = alloca((NeedLen - BufferLen) * sizeof(WCHAR)), stack) / sizeof(WCHAR);
            }
    
            switch (err = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 
                0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
            {
            case CR_BUFFER_SMALL:
                if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, InterfaceClassGuid, 
                    0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
                {
            default:
                return err;
                }
                continue;
    
            case CR_SUCCESS:
    
                while (*pszDeviceInterface)
                {
                    TestOpen(pszDeviceInterface);
    
                    pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
                }
                return 0;
            }
        }
    }
    
    EnumInterfaces(const_cast<PGUID>(&GUID_DEVINTERFACE_HID));
    
        3
  •  0
  •   Walter Schrabmair    6 年前

    你可以在 Delphi-Praxis in German

        if not HidD_GetAttributes(HidFileHandle, FAttributes) then
      raise EControllerError.CreateRes(@RsEDeviceCannotBeIdentified);
    

        HidD_GetAttributes(HidFileHandle, FAttributes);