Доброго времени суток Требуется с помощью драйвера поменять функциональность устройства пользовательского интерфейса (HID), то есть грубо говоря, сделать так, чтобы в Windows XP можно было использовать геймпад вместо мышки. Собственно с этой целью думал написать драйвер-фильтр, который перехватывал бы запросы на чтение отчетов и дескрипторов и заменял их на аналогичные для мыши. И вот тут появляются вопросы: 1) Будет ли вообще такая идея работать, или она изначально обречена на провал?) 2) Написал на примере из WDK простейший фильтр, который просто перехватывает запросы, выдает сообщения о перехвате и пересылает их ниже по стеку. В inf-файле прописал установку драйвера в качестве нижнего фильтра и решил протестить его на USB-мышке. После установки он действительно виден как нижний фильтр устройства, но при этом он удаляет из стека драйверов все остальные, кроме hidusb.sys. При попытке нажатия на кнопку этой мыши или ее движении, положение курсора на экране не меняется и через секунду без всяких BSOD'ов комп просто перезагружается. Собственно в чем может быть проблема и каким образом ее можно исправить?) Заранее спасибо)
Для анализа проблемы используйте windbg+vmware для отладки драйвера и определение причин перезагрузки. Каким образом вы устанавливаете драйвер-фильтр?
дамп указывает что источником проблем является строка hDevice = WdfIoQueueGetDevice (Queue); из функции перехвата запросов IRP_MJ_INTERNAL_DEVICE_CONTROL
Я думал что комп не может сам перезагружаться, просто бсод быстро проходит изза того что дамп не пишется. Кстати про дамп, в последнем посте ты его упоминаешь, можешь приаттачить? (мини конечно)
Да с BSOD'ом это я уже понял) А вот с дампом разобраться не очень получается, ибо сталкиваюсь впервые. Дамп, который у меня сейчас, весит порядка 90 метров. Если подскажете, как сделать его "мини", то с удовольствием приаттачу) И все-таки, если плясать от печки, сильно волнует вопрос с удаленными из стека устройства драйверами. Такое ощущение, что ставлю я его не так, как надо. Возможно inf-файл составил криво, хотя уже вроде все примеры из WDK по этому поводу прошерстил...
Что бы из дампа сделать минидамп надо этот дамп открыть в windbg и выполнить команду типа вот такой: .dump /m c:\minidump.dmp
Теперь откройте windbg -> open crash dump указав ваш dump (лучше полный). Затем подгрузите символы (а главное filtr.pdb (созданный при компиляции драйвера) положите в c:\symbols) file->symbol file path srv*c:\Symbols*http://msdl.microsoft.com/download/symbols reload Затем !analyze -v и вы увидите что ошибка в вашей функуии f78ae8e0 f6330072 79ebbc30 79d5dc90 86027ef8 filtr+0x4154 f78ae904 f6331432 79ebbc30 79d5dc90 86027ef8 wdf01000!FxIoQueueIoInternalDeviceControl::Invoke+0x30 Причем немного меня смутило 1: kd> u filtr+0x4154 filtr+0x4154: f77cb154 ?? ??? ^ Memory access error in 'u filtr+0x4154' Если все сделает правильно, то windbg, укажет точное место в вашем исходнике, где ошибка!
Я уже писал, на какое место указывает WinDbg, а именно на hDevice = WdfIoQueueGetDevice (Queue); из функции перехвата запросов IRP_MJ_INTERNAL_DEVICE_CONTROL. Мне кажется, что дело все-таки в неправильной установке драйвера, ибо удаления из стека драйвера класса явно быть не должно! Возможен какой-либо другой способ установки нижнего фильтра устройства? Или необходимы какие-то дополнительные записи в inf-файле, кроме Код (Text): [filtr.NT.HW] AddReg = filtr.HW.AddReg [filtr.HW.AddReg] HKR,,"LowerFilters", 0x00010000, "filtr"
Ну собственно вот что: случилось исключение и оно не было перехвачено (KERNEL_MODE_EXCEPTION_NOT_HANDLED_M), причем есть странный момент: Arg1: 80000003, The exception code that was not handled Arg2: 80501a3f, The address that the exception occurred at Arg3: f78ae4e0, Trap Frame Arg4: 00000000 Здесь сказано что случившееся исключение 80000003 - а это ни много ни мало STATUS_BREAKPOINT, т.е. исключение вызвано сработавшим брекпоинтом (я так полагаю). Т.е. такой код получишь если напишешь в коде что то вроде __asm { int 3 } или DebugBreak(); Ничего такого в своем драйвере не писал?
Да с этой проблемой разобрался. Что-то в этом роде какраз и написал, а именно PAGED_CODE(). На нем система и загибалась) Теперь появилась следующая проблема. Я то по глупости надеялся, что мой драйвер встанет где-то между HIDCLASS и HIDUSB, чтобы перехватывать запросы типа IOCTL_HID_GET_DEVICE_DESCRIPTOR и IOCTL_HID_READ_REPORT. А фильтр, судя по всему, встал после HIDUSB и перехватывает только IOCTL_INTERNAL_USB_SUBMIT_URB. Отсюда вопрос, возможно ли его "переставить" в нужное мне место в стеке, или надо вытаскивать данные, соответствующие дескрипторам и отчетам, получаемые в ответ на IOCTL_INTERNAL_USB_SUBMIT_URB? Или возможна какая-либо другая реализация для решения данной задачи?
Доброго времени суток! Написал фильтр, перехватывающий URB, посылаемые геймпаду. Привязал к соответствующим запросам процедуры, вызываемые по факту их выполнения нижележащим драйвером. Сначала идут запросы типа URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, из которых я получаю дескрипторы устройства, потом URB_FUNCTION_SELECT_CONFIGURATION, выбирающие конфигурацию устройства, потом URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE, возвращающий дескриптор отчета устройства и уже URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER собственно возвращающие сами отчеты геймпада. Собственно провожу анализ отчетов, вывожу в дебаггер сообщения о том, какие кнопки нажаты и т.д. В данном случае драйвер работает нормально. Но исходя из своей первоначальной цели, пытаюсь заставить систему видеть джойстик как мышку: добавляю соответствующий код, подменяющий дескрипторы и отчеты геймпада на оные для мыши, устанавливаю сей драйвер, модифицирую запись в реестре в каталоге ENUM->HID (меняю класс устройства (с HIDClass на Mouse) и соответствующие драйверы). Включаю устройство, без ошибок проходит весь этап считывания и подмены дескрипторов, а вместо первой же передачи отчета - BSOD: Код (Text): ******************************************************************************* * * * Bugcheck Analysis * * * ******************************************************************************* SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (7e) This is a very common bugcheck. Usually the exception address pinpoints the driver/function that caused the problem. Always note this address as well as the link date of the driver/image that contains this address. Arguments: Arg1: c0000005, The exception code that was not handled Arg2: 80586219, The address that the exception occurred at Arg3: f895e93c, Exception Record Address Arg4: f895e638, Context Record Address Debugging Details: ------------------ *** No owner thread found for resource 80551f60 *** No owner thread found for resource 80551f60 *** No owner thread found for resource 80551f60 *** No owner thread found for resource 80551f60 EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - <Unable to get error code text> FAULTING_IP: nt!PipCallDriverAddDevice+277 80586219 83780400 cmp dword ptr [eax+4],0 EXCEPTION_RECORD: f895e93c -- (.exr 0xfffffffff895e93c) ExceptionAddress: 80586219 (nt!PipCallDriverAddDevice+0x00000277) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 00000000 Parameter[1]: 00000004 Attempt to read from address 00000004 CONTEXT: f895e638 -- (.cxr 0xfffffffff895e638) eax=00000000 ebx=40000000 ecx=45980003 edx=45970002 esi=8265fa98 edi=80584746 eip=80586219 esp=f895ea04 ebp=f895eabc iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00210246 nt!PipCallDriverAddDevice+0x277: 80586219 83780400 cmp dword ptr [eax+4],0 ds:0023:00000004=???????? Resetting default scope PROCESS_NAME: System ERROR_CODE: (NTSTATUS) 0xc0000005 - <Unable to get error code text> EXCEPTION_PARAMETER1: 00000000 EXCEPTION_PARAMETER2: 00000004 READ_ADDRESS: 00000004 FOLLOWUP_IP: nt!PipCallDriverAddDevice+277 80586219 83780400 cmp dword ptr [eax+4],0 BUGCHECK_STR: 0x7E DEFAULT_BUCKET_ID: NULL_CLASS_PTR_DEREFERENCE LOCK_ADDRESS: 80551fe0 -- (!locks 80551fe0) Resource @ nt!IopDeviceTreeLock (0x80551fe0) Shared 1 owning threads Threads: 82a02020-01<*> 1 total locks, 1 locks currently held PNP_TRIAGE: Lock address : 0x80551fe0 Thread Count : 1 Thread address: 0x82a02020 Thread wait : 0x1c57a LAST_CONTROL_TRANSFER: from 804f7b9d to 80527bdc STACK_TEXT: f895eabc 805876c0 00000000 00000001 f895ed54 nt!PipCallDriverAddDevice+0x277 f895ed18 80587b76 825d4ca0 00000001 00000000 nt!PipProcessDevNodeTree+0x1a4 f895ed4c 804f58b1 00000003 80552040 8055b0fc nt!PiProcessReenumeration+0x60 f895ed74 80534c02 00000000 00000000 82a02020 nt!PipDeviceActionWorker+0x141 f895edac 805c6160 00000000 00000000 00000000 nt!ExpWorkerThread+0x100 f895eddc 80541dd2 80534b02 80000001 00000000 nt!PspSystemThreadStartup+0x34 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16 SYMBOL_STACK_INDEX: 0 SYMBOL_NAME: nt!PipCallDriverAddDevice+277 FOLLOWUP_NAME: MachineOwner MODULE_NAME: nt IMAGE_NAME: ntkrnlpa.exe DEBUG_FLR_IMAGE_TIMESTAMP: 4802516a STACK_COMMAND: .cxr 0xfffffffff895e638 ; kb FAILURE_BUCKET_ID: 0x7E_VRF_nt!PipCallDriverAddDevice+277 BUCKET_ID: 0x7E_VRF_nt!PipCallDriverAddDevice+277 Followup: MachineOwner --------- Как я понимаю, указывает он на то, что ошибка произошла где-то в ядре. Отсюда собственно вопрос: Это обусловлено какими-либо моими неправильными действиями в области установки драйвера или модификации записи реестра, или же такой способ просто не подходит для решения данной задачи? Заранее спасибо)
В какой версии WDK вы нашли Фильтер-драйвер на wdf framework? Судя со всего это у вам 7600.16385.0\src\usb\usbsamp\sys\ Прочитайте сначало usbsamp.htm. В фильтрирующих драйверах не вызывают функции получения дескрипторов и выборку интерфейса для устройства. EXCEPTION_CODE: (NTSTATUS) 0xc0000005 == Думаю понятно Вообщем vmware(VirtualBox) + WinDbg (by pipe/com port ) Не знаю почему, но в этом пример нету бряков... в файл private.h, Добавляете #if DBG # DBG_ALLOW_BREAKPOINT 1 #endif #DBG_ALLOW_BREAKPOINT # MyDbgBreakpoint() __asm {int 3} #else # MyDbgBreakpoint() //nothing #endif После чего раставляете MyDbgBreakpoint(). И в перет.
драйвер писал на основе примеров \WinDDK\7600.16385.1\src\hid\hidusbfx2 \WinDDK\7600.16385.1\src\input\moufiltr \WinDDK\7600.16385.1\src\general\toaster\kmdf\filter\ Я не говорил, что вызываю эти функции, а только перехватвыю запросы URB, которые посылает минидрайвер и присваиваю им callback-процедуры, в которых читаю содержимое буфера ответного URB-пакета, откуда и получаю соответствующие дескрипторы. Сам фильтрующий драйвер не является источником никаких запросов, а только посредником в их передаче по стеку! И еще раз, эта проблема возникает только при попытке "подмены устройства". В противном случае драйвер работает нормально. Честно говоря, не очень то понятно. И чем мне тут может помочь расстановка брейкпоинтов?
Вот и смотри в какой функции падает, а точнее после какой функции падает ... Вы уверены что устройство работает корректно? то и есть все дескрипторы и все такое?
Если речь идет про сам геймпад, то десрипторы, которые я извлекаю из буфера пакета URB полностью совпадают. Если имеются в виду дескрипторы, которыми я заменяю реальные, то они взяты из спецификации HID USB, поэтому с ними проблем по идее быть не должно. В случае без замены дескрипторов, геймпад системой и приложениями определяется нормально, без косяков.
Значит касяки в замене дескрипторов .... А дальше ... Ядро ? Не известно что происходит в соединение устройства и винды....
То есть удалить все PAGED_CODE? Дело в том, что у меня происходит точно такой же бсод "system_thread_exception_not_handled STATUS_BREAKPOINT ". Код моего FDO: Код (Text): #include "MKJoy.h" NTSTATUS DriverEntry (PDRIVER_OBJECT, PUNICODE_STRING); #ifdef ALLOC_PRAGMA #pragma alloc_text (INIT, DriverEntry) #pragma alloc_text (PAGE, MKJoy_AddDevice) #pragma alloc_text (PAGE, MKJoy_PnP) #pragma alloc_text (PAGE, MKJoy_Power) #pragma alloc_text (PAGE, MKJoy_Unload) #endif NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Initialize the entry points of the driver. --*/ { HID_MINIDRIVER_REGISTRATION reg; // // Fill in all the dispatch entry points with the pass through function // and the explicitly fill in the functions we are going to intercept // // DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MKJoy_DispatchPassThrough; DriverObject->MajorFunction [IRP_MJ_PNP] = MKJoy_PnP; DriverObject->MajorFunction [IRP_MJ_POWER] = MKJoy_Power; DriverObject->DriverExtension->AddDevice = MKJoy_AddDevice; RtlZeroMemory(®, sizeof(reg)); reg.Revision = HID_REVISION; reg.DriverObject = DriverObject; reg.RegistryPath = RegistryPath; reg.DeviceExtensionSize = 0; //sizeof(DEVICE_EXTENSION) + GetSizeofGenericExtension(); reg.DevicesArePolled = FALSE; return HidRegisterMinidriver(®); } NTSTATUS MKJoy_AddDevice( IN PDRIVER_OBJECT Driver, IN PDEVICE_OBJECT PDO ) { PAGED_CODE(); return STATUS_SUCCESS; } NTSTATUS MKJoy_Complete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Generic completion routine that allows the driver to send the irp down the stack, catch it on the way up, and do more processing at the original IRQL. --*/ { PKEVENT event; event = (PKEVENT) Context; // // We could switch on the major and minor functions of the IRP to perform // different functions, but we know that Context is an event that needs // to be set. // KeSetEvent(event, 0, FALSE); // // Allows the caller to use the IRP after it is completed // return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS MKJoy_DispatchPassThrough( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Passes a request on to the lower driver. Considerations: If you are creating another device object (to communicate with user mode via IOCTLs), then this function must act differently based on the intended device object. If the IRP is being sent to the solitary device object, then this function should just complete the IRP (becuase there is no more stack locations below it). If the IRP is being sent to the PnP built stack, then the IRP should be passed down the stack. These changes must also be propagated to all the other IRP_MJ dispatch functions (such as create, close, cleanup, etc.) as well! --*/ { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); // // Pass the IRP to the target // IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp); } NTSTATUS MKJoy_PnP( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch routine for plug and play irps Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the request packet. Return Value: Status is returned. --*/ { PHID_DEVICE_EXTENSION devExt; PIO_STACK_LOCATION irpStack; NTSTATUS status = STATUS_SUCCESS; KIRQL oldIrql; KEVENT event; PAGED_CODE(); devExt = (PHID_DEVICE_EXTENSION) DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: { // // The device is starting. // // We cannot touch the device (send it any non pnp irps) until a // start device has been passed down to the lower drivers. // IoCopyCurrentIrpStackLocationToNext(Irp); KeInitializeEvent(&event, NotificationEvent, FALSE ); IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) MKJoy_Complete, &event, TRUE, TRUE, TRUE); // No need for Cancel status = IoCallDriver(devExt->NextDeviceObject, Irp); if (STATUS_PENDING == status) { KeWaitForSingleObject( &event, Executive, // Waiting for reason of a driver KernelMode, // Waiting in kernel mode FALSE, // No allert NULL); // No timeout } Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; } default: // // Here the filter driver might modify the behavior of these IRPS // Please see PlugPlay documentation for use of these IRPs. // IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(devExt->NextDeviceObject, Irp); break; } return status; } NTSTATUS MKJoy_Power( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch routine for power irps Does nothing except record the state of the device. Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the request packet. Return Value: Status is returned. --*/ { PHID_DEVICE_EXTENSION devExt; PAGED_CODE(); devExt = (PHID_DEVICE_EXTENSION) DeviceObject->DeviceExtension; PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(devExt->NextDeviceObject, Irp); }