Фильтры, несколько вопросов

Тема в разделе "WASM.NT.KERNEL", создана пользователем Mika0x65, 1 фев 2011.

  1. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Мое почтение всем.

    Пробую драйвера-фильтры, есть несколько вопросов.

    1. Насколько я понимаю, IoAttachDevice возвращает указатель DEVICE_OBJECT, который был самым верхним до вызова и стал вторым (lower device) относительно добавленного с помощью ф-ии устройства. Так ли это?

    2. Что надо передавать в IoDetachDevice? В примере в книге написано, что в нее надо передавать DEVICE_OBJECT, полученный из IoAttachDevice. Но у меня сомнения. Я сделал так, но после выгрузки драйвера, при нажатии кнопки на клавиатуре I/O manager все равно вызывает completion routine выгруженного драйвера (это видно в отладчике). Вероятно, в нее надо передавать DEVICE_OBJECT самого верхнего устройства, адрес которого находится в DRIVER_OBJECT::lol: eviceObject?

    3. Попробовал передать DRIVER_OBJECT::lol: eviceObject, получил IRQL_NO_LESS_OR_EQUAL. IRQL == 2. Откуда? Я не поднимал IRQL. Дамп:
    Код (Text):
    1. 0: kd> !analyze -v
    2. *******************************************************************************
    3. *                                                                             *
    4. *                        Bugcheck Analysis                                    *
    5. *                                                                             *
    6. *******************************************************************************
    7.  
    8. IRQL_NOT_LESS_OR_EQUAL (a)
    9. An attempt was made to access a pageable (or completely invalid) address at an
    10. interrupt request level (IRQL) that is too high.  This is usually
    11. caused by drivers using improper addresses.
    12. If a kernel debugger is available get the stack backtrace.
    13. Arguments:
    14. Arg1: 000000b0, memory referenced
    15. Arg2: 00000002, IRQL
    16. Arg3: 00000000, bitfield :
    17.     bit 0 : value 0 = read operation, 1 = write operation
    18.     bit 3 : value 0 = not an execute operation, 1 = execute operation (only on chips which support this level of status)
    19. Arg4: 804ef13c, address which referenced memory
    20.  
    21. Debugging Details:
    22. ------------------
    23.  
    24.  
    25. READ_ADDRESS:  000000b0
    26.  
    27. CURRENT_IRQL:  2
    28.  
    29. FAULTING_IP:
    30. nt!IoDetachDevice+2a
    31. 804ef13c 8b80b0000000    mov     eax,dword ptr [eax+0B0h]
    32.  
    33. DEFAULT_BUCKET_ID:  DRIVER_FAULT
    34.  
    35. BUGCHECK_STR:  0xA
    36.  
    37. PROCESS_NAME:  System
    38.  
    39. TRAP_FRAME:  f7a48cd0 -- (.trap 0xfffffffff7a48cd0)
    40. ErrCode = 00000000
    41. eax=00000000 ebx=00000000 ecx=80552582 edx=00000000 esi=864f81d0 edi=eeaf0b84
    42. eip=804ef13c esp=f7a48d44 ebp=f7a48d50 iopl=0         nv up ei pl zr na pe nc
    43. cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
    44. nt!IoDetachDevice+0x2a:
    45. 804ef13c 8b80b0000000    mov     eax,dword ptr [eax+0B0h] ds:0023:000000b0=????????
    46. Resetting default scope
    47.  
    48. LAST_CONTROL_TRANSFER:  from 804f8afd to 8052a5d8
    49.  
    50. STACK_TEXT:  
    51. f7a48884 804f8afd 00000003 f7a48be0 00000000 nt!RtlpBreakWithStatusInstruction
    52. f7a488d0 804f96e8 00000003 000000b0 804ef13c nt!KiBugCheckDebugBreak+0x19
    53. f7a48cb0 805436d0 0000000a 000000b0 00000002 nt!KeBugCheck2+0x574
    54. f7a48cb0 804ef13c 0000000a 000000b0 00000002 nt!KiTrap0E+0x238
    55. f7a48d50 f7ccb4b5 864f81d0 eeaf0b84 f7a48d7c nt!IoDetachDevice+0x2a
    56. f7a48d60 8058014d 8641f458 eeaf0b84 8056375c kbdfilter!Unload+0x29 [d:\src\c\kbdfilter\src\core\core.c @ 73]
    57. f7a48d7c 80537757 eeaf0b84 00000000 865c28b8 nt!IopLoadUnloadDriver+0x19
    58. f7a48dac 805ce794 eeaf0b84 00000000 00000000 nt!ExpWorkerThread+0xef
    59. f7a48ddc 805450ce 80537668 00000001 00000000 nt!PspSystemThreadStartup+0x34
    60. 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
    61.  
    62.  
    63. STACK_COMMAND:  kb
    64.  
    65. FOLLOWUP_IP:
    66. kbdfilter!Unload+29 [d:\src\c\kbdfilter\src\core\core.c @ 73]
    67. f7ccb4b5 ff7604          push    dword ptr [esi+4]
    68.  
    69. FAULTING_SOURCE_CODE:  
    70.     69: {
    71.     70:     DBG_TRACE("Unload", "Unloading");
    72.     71:
    73.     72:     IoDetachDevice(DriverObject ->DeviceObject);
    74. >   73:     IoDeleteDevice(DriverObject ->DeviceObject);
    75.     74: }
    76.     75:
    77.     76: NTSTATUS IrpMjRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
    78.     77: {
    79.     78:     IoCopyCurrentIrpStackLocationToNext(Irp);
    80.  
    81.  
    82. SYMBOL_STACK_INDEX:  5
    83.  
    84. SYMBOL_NAME:  kbdfilter!Unload+29
    85.  
    86. FOLLOWUP_NAME:  MachineOwner
    87.  
    88. MODULE_NAME: kbdfilter
    89.  
    90. IMAGE_NAME:  kbdfilter.sys
    91.  
    92. DEBUG_FLR_IMAGE_TIMESTAMP:  4d473c29
    93.  
    94. FAILURE_BUCKET_ID:  0xA_kbdfilter!Unload+29
    95.  
    96. BUCKET_ID:  0xA_kbdfilter!Unload+29
    97.  
    98. Followup: MachineOwner
    99. ---------
    Заранее благодарен за ответы.
     
  2. x64

    x64 New Member

    Публикаций:
    0
    Регистрация:
    29 июл 2008
    Сообщения:
    1.370
    Адрес:
    Россия
    Да, и этот указатель надо сохранить в device extension твоего девайса-фильтра.

    Всё верно, тот указатель, который сохранили в device extension.

    Неправильно.

    Судя по всему, у тебя legacy-фильтр, так просто его выгружать нельзя, в отличие от PnP-фильтров. Представь ситуацию: тебе пришёл сверху запрос, ты повесил на него completion routine и послал ниже. Затем вдруг ты решил выгрузить свой фильтр, но проблема в том, что запрос-то ещё не был завершён нижележащим драйвером. А когда тот вызовет IoCompleteRequest(), то будет вызвана твоя comlpetion routine, которой в памяти уже нет. Разумеется, так делать нельзя. Эту проблему можно решить, используя IoSetCompletionRoutineEx(), однако следует помнить, что если сверху твоего legacy-фильтра приаттачится другой фильтр, то твой фильтр автоматически станет невыгружаемым, понятно почему? Чтобы избежать всех этих проблем, тебе следует переписать клавиатурный фильтр на PnP.

    Системные функции могут повышать IRQL неявно. Вообще, падение с IRQL_NOT_LESS_OR_EQUAL не всегда указывает на некорректное повышение IRQL, чаще оно указывает лишь на то, что где-то произошло обращение к невалидной памяти. В твоём случае это естественно, т.к. ты передал не тот указатель.
     
  3. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Я сохранил в глобальной переменной. Судя по MSDN, это позволительно. У меня только одной устройство, путаницы не будет.

    Мне показалось странным, что в IoDetachDevice передается низлежащее устройство, а не то, которое мы отцепляем. Ну да ладно.

    В том-то и дело, что я вызвал IoSetCompletionRoutineEx, передав в нее DEVICE_OBJECT моего драйвера. Как я представляю, IoDetachDevice не вернется до тех пор, пока не выполнятся все успевшие зарегистрироваться completion routines отсоединяемого устройства. Или не так? И можно ли избежать этих проблем, не переписывая все на PnP? Не хотелось бы сильно отклоняться от книги.

    Видимо IoDetachDevice поднимает IRQL до DISPATCH_LEVEL, но не опускает его, если указатель указывает не на то устройство.
     
  4. x64

    x64 New Member

    Публикаций:
    0
    Регистрация:
    29 июл 2008
    Сообщения:
    1.370
    Адрес:
    Россия
    Не так, схема немного другая. Во-первых, IoDetachDevice() никогда ничего не ждёт, она быстренько делает своё дело и отваливает. Во-вторых, вызов unload routine совершенно не означает, что драйвер сейчас будет выгружен, это всего уведомление о том, что был запрос на выгрузку и необходимо подчистить за собой всё, что можно. В этот момент драйвер всего лишь помечен для выгрузки, но его образ в памяти будет жить до тех пор, пока не будет уничтожен его DRIVER_OBJECT, а тот, в свою очередь, не будет уничтожен до тех пор, пока не будут удалены всего его девайсы. В-третьих, функция IoDeleteDevice() не удаляет объект девайса немедленно, вместо этого она лишь отмечает его для удаления. Кроме того, функция IoDeleteDevice() гарантирует, что девайс не будет открыт после вызова этой функции (т.е. во всех последующих NtCreateFile() на \Device\KeyboardClass0 будет отказано). И, наконец, в-четвёртых, сама по себе по себе функция IoSetCompletionRoutineEx() не является панацеей, она гарантирует лишь то, что драйвер не будет выгружен во время выполнения твоей completion routine, а о том, чтобы драйвер не был выгружен к моменту завершения запроса, который прошёл через твой фильтр, ты должен позаботиться самостоятельно. Например, с помощью remove locks или собственной реализации подсчёта ссылок.

    Верно, IoDetachDevice() захватывает глобальный спинлок, поднимая таким образом IRQL до DISPATCH_LEVEL. Далее происходит обращение к некорректным данных в переданном девайсе и, как результат, падение.
     
  5. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    x64
    Спасибо за объяснение, но хочу уточнить такую деталь: удаляет ли IoDetachDevice мою completion routine из IO_STACK_LOCATION (я так понял, указатель именно там хранится)? Просто у меня сложилась примерно такая картина: поток raw input (кажется так называется) шлет IRP стеку драйверов клавиатуры. Вызов проходит через мою IRP_MJ_READ, я регистрирую completion routine. IRP дошел до PDO, в этот момент я вызываю выгрузку драйвера. Выгрузка драйвера начинается, вызывается Unload, которая делает IoDetachDevice и IoDeleteDevice. После этого начинают вызываться completione routine стека. И вот тут вопрос: была ли удалена моя completion routine или нет. Если нет, то весьма логично, что она будет вызвана и будет BSoD.

    Вообще, в примере в книге использовался счетчик, котоый в IRP_MJ_READ увеличивался, а в completion routine уменьшался. И выгрузка происходила только тогда, когда он был равен нулю. Я подумал, что IoSetCompletionRoutineEx будет достаточно и убрал его. Видимо, это не правильно.

    И еще вопрос: как выгружать (или не выгружать вообще) драйвер, если кто-то добавил устройство "поверх" моего? Надо это как-то проверить, вероятно?