Народ, подскажите, как прицепиться к стеку USB/ВТН клавиатуры? На данный момент есть рабочий пример с клавой PS/2: Код (Text): NTSTATUS HookKeyboard(IN PDRIVER_OBJECT DriverObject) { PDEVICE_EXTENSION devExt; PDEVICE_OBJECT device; UNICODE_STRING devName; NTSTATUS status; PFILE_OBJECT use_fileObject; PDEVICE_OBJECT use_deviceObject; RtlInitUnicodeString(&devName, L"\\Device\\KeyboardClass0"); status = IoGetDeviceObjectPointer(&devName, FILE_READ_DATA, &use_fileObject, &use_deviceObject); if(!NT_SUCCESS(status)) { DbgPrint("Error get device object poiner: %d", status); return status; } status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, use_deviceObject->DeviceType, 0, FALSE, &device ); if (!NT_SUCCESS(status)) { DbgPrint("Error during device creation: %d", status); return (status); } RtlZeroMemory(device->DeviceExtension, sizeof(DEVICE_EXTENSION)); devExt = (PDEVICE_EXTENSION) device->DeviceExtension; devExt->TopOfStack = IoAttachDeviceToDeviceStack(device, use_deviceObject); IoRegisterShutdownNotification(device); if (devExt->TopOfStack == NULL) { DbgPrint("Impossible to attach device to stack drivers"); IoDeleteDevice(device); return STATUS_DEVICE_NOT_CONNECTED; } ASSERT(devExt->TopOfStack); device->Flags = use_deviceObject->Flags; //копируем сост. флагов у предыдущего ус-ва device->Flags &= ~DO_DEVICE_INITIALIZING; return status; } По идее простой заменой \\Device\\KeyboardClass0 на \\Device\\KeyboardClass1 должно все решаться, но IoGetDeviceObjectPointer(...) выдает ошибку, что странно. Кто сталкивался, подскажите, как решить?
Переписал под WDM. Так он даже запускаться перестал... Как правильно соединиться со стеком в AddDevice?
Ты жжошь, нет, ты - нереально жжошь! Слушай, я вот когда исходник глянул, так чуть не упал со стула. Это ты не "переписал под WDM", это ты херню очередную сморозил. Итак, по пунктам: 1. Ты понимаешь, что у тебя в AddDevice() твоей происходит? Эта функция нужна как раз для того, чтобы не заморачиваться с получением указателей целевых девайсов. Здесь не нужно делать это вручную, он уже пришёл тебе сам (!) во втором параметре в AddDevice(). Всё, что тебе нужно сделать, - это создать свой FiDO через IoCreateDevice() и приаттачить его к этому девайсу. Т.е. весь код, который у тебя получает указатель на \Device\KeyboardClassXxx можешь выкинуть фтопку. 2. Кроме этого, ты должен понять, что WDM-драйвера как правило, сами по себе не запускаются, т.е. не вручную. Этим занимается PnP-менеджер, он и только он решает, когда нужно запустить твой драйвера и нужно ли вообще это делать. Ты, как разработчик, можешь лишь косвенно указать ему, когда это следует делать, но не более. Ты должен сообщить PnP-менеджеру что есть твой драйвер, для чего он и какова его задача. Вот в данном случае твой драйвер это фильтр, клавиатурный фильтр, и ты должен корректно сообщить об этом системе. Для этого драйвер должен быть установлен, но обычной установки недостаточно, фильтр класса должен быть соответствующим образом прописан в реестре. 3. Если совсем кратко, то в твоём случае достаточно записать имя драйвера (имя службы) в ключ HKLM\System\CurrentControlSet\Control\Class\{4D36E96B-E325-11CE-BFC1-08002BE10318}. Значение UpperFilters этого ключа должно содержать имя твоего драйвера, например, если ты регистрируешь драйвер в системе под именем kbfiltr, то значение UpperFilters должно содержать как одну из строк ещё и слово kbfiltr. При этом если значение UpperFilters уже содержит какие-то строки, ты не имеешь права их удалять, ты можешь лишь дописать свою в конец списка. Но это я тебе говорю, чтобы ты смог быстро попробовать это всё на деле, реально в коммерческом продукте так делать нельзя, WDM-драйвера должны устанавливаться в систему через .inf-файлы (см. пример kbfiltr в WDK). Всё это приведёт к тому, что при каждом запуске системы: 3.1. Будет загружен драйвер клавиатуры (не имеет значения, через какой стек). 3.2. Будет загружен твой драйвер, потому как он прописан в верхних фильтрах для класса. 3.3. Будет вызвана функция AddDevice() твоего драйвера для каждого из девайсов класса Keyboard. 4. Функцию IoRegisterShutdownNotification() я вообще не понимаю, зачем ты сюда приплёл? Это не является обязательным для фильтра клавиатуры, по крайне мере в WDK'шном примере этого нет, а у тебя зачем? Ты уверен, что без него работать не будет? Думаю, будет. Убери лучше от греха. 5. Копирование стека перед IoSetCompletionRoutine() делается не вручную, а вызовом IoCopyCurrentIrpStackLocationToNext(). А зачем тебе там currentIrpStack и nextIrpStack - непонятно. 6. Не вижу у тебя в коде обработку IRP_MJ_PNP и IRP_MJ_POWER. Эти обработчики обязательны для драйверов(-фильтров) устройств. Как правильно это делать, см. опять же в примере kbfiltr в WDK. В обработчике IRP_MJ_POWER по большому счёту достаточно только этого: Код (Text): PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(devExt->TopOfStack, Irp); Обработка же IRP_MJ_PNP нужна по большей части для того, чтобы корректно отреагировать на некоторые события, связанные с целевым устройством. Например, ежу понятно, что если устройство удалили (или вынули физически из разъёма), то фильтр должен отцепиться от него, т.к. на этот нижележащий девайс уже никогда не придут запросы, а при повторном включении устройства будет создан уже другой объект девайса. 7. Какая-то особенная обработка IRP_MJ_INTERNAL_DEVICE_CONTROL, думаю, тебе не потребуется. Ты ведь не знаешь, что будешь там делать? Например, если тебе нужно не более чем знать, какие скан-коды получает приложение, то фильтрации IRP_MJ_READ будет достаточно, если что-то более продвинутое, тогда да, - см. в сторону всяких там IOCTL_INTERNAL_KEYBOARD_CONNECT, IOCTL_INTERNAL_I8042_HOOK_KEYBOARD и прочее. В любом случае я настоятельно рекомендую как следует изучить исходный код двух драйверов - kbdclass и kbfiltr (оба есть в WDK), первый даст понимание, как это всё работает, второй поможет понять, как это всё фильтровать.
8. По поводу выгрузки драйвера посмотри ещё как это сделано в kbfiltr. Счётчик запросов и прочее не нужно, всем этим заведует I/O-менеджер. В IRP_MJ_PNP тебе сообщат, когда можно отцепиться от нижележащего девайса. Выгружать WDM-драйвер вручную не рекомендуется.
Переписал так: Код (Text): NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { ULONG i; UNREFERENCED_PARAMETER (RegistryPath); for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = DispatchPassThrough; } DriverObject->DriverExtension->AddDevice = HookKeyboard; DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead; DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]= DispatchShutdown; DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower; DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnP; DriverObject->DriverUnload = DispatchUnload; return STATUS_SUCCESS; } //------------------------------------------------------------------------------ NTSTATUS HookKeyboard(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT device) { PDEVICE_EXTENSION devExt; NTSTATUS status; status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, device->DeviceType, 0, FALSE, &device ); if (!NT_SUCCESS(status)) { DbgPrint("Error during device creation: %d", status); return (status); } RtlZeroMemory(device->DeviceExtension, sizeof(DEVICE_EXTENSION)); devExt = (PDEVICE_EXTENSION) device->DeviceExtension; devExt->TopOfStack = IoAttachDeviceToDeviceStack(device, device); IoRegisterShutdownNotification(device); if (devExt->TopOfStack == NULL) { DbgPrint("Impossible to attach device to stack drivers"); IoDeleteDevice(device); return STATUS_DEVICE_NOT_CONNECTED; } ASSERT(devExt->TopOfStack); device->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE); device->Flags &= ~DO_DEVICE_INITIALIZING; return (status); } ну и добавил две функции для pnp: DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower; DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnP; С начинкой из примера DDK kbdfiltr. Добавил запись в ветку HKLM\System\CurrentControlSet\Control\Class\{4D36E96B-E325-11CE-BFC1-08002BE10318}поле UpperFilter и скопировал драйвер в C:\windows\system32\drivers\. После перезагрузки клавиатуру отстрелило наглухо. Но ведь по крайней мере к стеку то я должен был подключиться!?
Вот после этого Код (Text): devExt->TopOfStack = IoAttachDeviceToDeviceStack(device, device); Дальше можно не читать уже. Ты третий день хернёй занимаешься, между прочим. Значит так, чтобы две недели тебя здесь ни слышно ни видно было, читаешь MSDN и Уолтера Они, смотришь исходник ctrl2cap, kbdclass и kbfiltr, роешь гугол и пишешь код. Делаешь всё это сам, без подсказок, т.к. мы видим уже, что подсказки на пользу тебе не идут. Через две (ну максимум три) недели приходишь просветлённый. Впрочем, скорее всего уже не приходишь )
Согласен, глупость сморозил, исправился, клава заработала, но драйвер в стек не зашел. Ну да ладно, раз ты такой строгий, но справедливый, кинь хоть исходник Ctrl2Cap, а то все про него говорят, а исходника в сети невидно...