Я думаю, что это новое ограничение безопасности в последней версии Windows 10.
Я искал строку KBD
(в формате UTF-16) - она существует только в двух драйверах в версии 1809, hidclass.sys и kbdhid.sys и не существует в версии 1709.
В hidclass.sys они изменили функцию HidpRegisterDeviceInterface
.Перед этим выпуском он вызывал IoRegisterDeviceInterface
с GUID_DEVINTERFACE_HID
и указателем ReferenceString , установленным на 0. Но в новой версии, в зависимости от результата GetHidClassCollection
, он передает KBD
как ReferenceString указатель.
Внутри kbdhid.sys они изменились KbdHid_Create
, и вот проверка для KBD
строка для возврата ошибок (отказ в доступе или нарушение совместного доступа).
Чтобы более точно понять, почему, необходимы дополнительные исследования.Некоторая беда:
![enter image description here](https://i.stack.imgur.com/xNq6w.png)
Для справки, HidpRegisterDeviceInterface from1709 build
![enter image description here](https://i.stack.imgur.com/TdznU.png)
здесь ReferenceString == 0 всегда ( xor r8d, r8d ), и естьнет проверки cmp word [rbp + a],6
на данных коллекции классов
Однако, KbdHid_Create
в 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
из Irp текущего местоположения стека.FileObject
не предоставлено или имеет пустое имя, разрешить открытие;в противном случае открытие завершается неудачно.
До 1809 года оно всегда завершалось с ошибкой STATUS_ACCESS_DENIED
(0xc0000022
), но начиная с 1809 проверяется имя, и если оно равно KBD
(чувствительно к регистру)другая ошибка - STATUS_SHARING_VIOLATION
возвращается.Однако имя всегда начинается с символа \
, поэтому оно никогда не будет совпадать с KBD
.Это может быть \KBD
, поэтому, чтобы исправить эту проверку, необходимо изменить следующую строку на:
UNICODE_STRING KBD = RTL_CONSTANT_STRING(L"\\KBD");
и выполнить сравнение с этой строкой.Итак, по замыслу у нас должна была быть ошибка STATUS_SHARING_VIOLATION
при попытке открыть устройство клавиатуры с именем *\KBD
, но из-за ошибки реализации мы фактически получили STATUS_ACCESS_DENIED
здесь
![enter image description here](https://i.stack.imgur.com/J5u0C.png)
Еще одно изменение было в HidpRegisterDeviceInterface
- перед вызовом IoRegisterDeviceInterface
на устройстве оно запрашивает результат GetHidClassCollection
и, если какое-то поле WORD
(2 байта) в структуреравно 6, добавляет суффикс KBD
( ReferenceString ).Я предполагаю (но я не уверен), что 6 может быть ID использования для клавиатуры , и обоснование для этого префикса состоит в том, чтобы установить Эксклюзивный режим доступа
На самом деле, мыможет начинаться с 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");
}
}
В тесте 1809 года я действительно получил STATUS_SHARING_VIOLATION
, что также показывает другую ошибку в 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));