Mat*_*lia 12 c windows usb winapi windows-10
自从最新的Windows 10 1809更新以来,我们无法再打开我们使用的USB HID键盘式设备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;
}
Run Code Online (Sandbox Code Playgroud)
它枚举所有HID设备,试图CreateFile
通过提供的路径获取每个HID设备的供应商ID /产品ID SetupDiGetDeviceInterfaceDetail
,然后调用HidD_GetAttributes
.
此代码在以前的Windows版本上运行没有问题(在Windows 7,Windows 10 1709和1803上测试,以及从中提取此内容的原始代码,因为始终从XP开始),但使用最新更新(1809)所有键盘设备(包括我们的)无法打开,因为CreateFile
拒绝访问失败(GetLastError()
== 5).以管理员身份运行程序没有任何效果.
比较更新前后的输出,我注意到现在无法打开\kbd
的设备在设备路径中获得了一个尾随,即之前的内容
\\?\hid#vid_24d6&pid_8000&mi_00#7&294a3305&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
Run Code Online (Sandbox Code Playgroud)
现在是
\\?\hid#vid_24d6&pid_8000&mi_00#7&294a3305&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
Run Code Online (Sandbox Code Playgroud)
它是最新Windows 10版本中的错误/新安全限制吗?这段代码总是错的吗,它之前是偶然的吗?这可以修复吗?
更新
作为绝望的尝试,我们试图\kbd
从返回的字符串中删除... CreateFile
现在有效!所以,现在我们有一个解决方法,但是SetupDiGetDeviceInterfaceDetail
如果它是故意的并且如果这个解决方法实际上是正确的事情,那么理解它是否是一个错误将是有趣的.
RbM*_*bMm 12
我认为这是最新Windows 10版本中的新安全限制.
我查找了字符串KBD
(UTF-16格式) - 它仅存在于版本1809中的两个驱动程序,hidclass.sys和kbdhid.sys中,并且在版本1709中不存在.
在hidclass.sys中,他们改变了HidpRegisterDeviceInterface
函数.在此版本之前,它调用IoRegisterDeviceInterface
with GUID_DEVINTERFACE_HID
并将ReferenceString指针设置为0.但在新版本中,根据结果GetHidClassCollection
,它将KBD
作为ReferenceString指针传递.
在kbdhid.sys里面他们改变了KbdHid_Create
,这里是检查KBD
字符串返回错误(访问被拒绝或共享违规).
为了更准确地理解原因,需要进行更多的研究.一些挫折:
供参考,HidpRegisterDeviceInterface从1709构建
这里ReferenceString == 0总是(xor r8d,r8d),并且没有检查cmp word [rbp + a],6
类集合数据
但是,KbdHid_Create
在1809年包含一个bug.代码是:
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;
}
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
这个功能在这里尝试做什么?它寻找PFILE_OBJECT FileObject
从Irp当前堆栈位置传递的内容.它没有FileObject
提供或它有一个空名称,允许打开; 否则,开放失败.
在1809之前,它始终以error STATUS_ACCESS_DENIED
(0xc0000022
)失败,但是从1809开始,检查名称,如果它等于KBD
(区分大小写),STATUS_SHARING_VIOLATION
则返回另一个错误.但是,名称始终以\
符号开头,因此它永远不会匹配KBD
.它可以\KBD
,所以,要解决这个问题的检查,需要以下行改为:
UNICODE_STRING KBD = RTL_CONSTANT_STRING(L"\\KBD");
Run Code Online (Sandbox Code Playgroud)
并执行与此字符串的比较.因此,通过设计我们应该STATUS_SHARING_VIOLATION
在尝试通过*\KBD
名称打开键盘设备时出错,但由于实现错误,我们实际上在STATUS_ACCESS_DENIED
这里
另一个变化是HidpRegisterDeviceInterface
- 在调用IoRegisterDeviceInterface
设备之前它查询GetHidClassCollection
结果,如果结构中的某个WORD
(2字节)字段等于6,则添加KBD
后缀(ReferenceString).我猜(但我不确定)6可以是键盘的使用ID,这个前缀的基本原理是设置独占访问模式
实际上,\
如果我们使用相对设备open via ,我们可以在没有FileName的情况下开始OBJECT_ATTRIBUTES
.所以,只是为了测试,我们可以这样做:如果接口名称以#结尾\KBD
,首先打开没有这个后缀的文件(所以使用空的相对设备名称),这个打开必须正常工作; 然后,我们可以尝试使用名称的相对打开文件KBD
- 我们必须STATUS_SHARING_VIOLATION
在1809和STATUS_ACCESS_DENIED
之前的版本中获得(但在这里我们将没有\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");
}
}
Run Code Online (Sandbox Code Playgroud)
在1809年的测试中我实际上得到了STATUS_SHARING_VIOLATION
,这也显示了另一个错误kbdhid.KbdHid_Create
- 如果我们检查FileName
,我们需要检查RelatedFileObject
- 它是否为0.
此外,与bug无关,但作为建议:使用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));
Run Code Online (Sandbox Code Playgroud)
小智 5
此修复程序今天(2019年3月1日)发布了此Windows更新中。
https://support.microsoft.com/zh-cn/help/4482887/windows-10-update-kb4482887
归档时间: |
|
查看次数: |
2089 次 |
最近记录: |