DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS

Тема в разделе "WASM.NT.KERNEL", создана пользователем Crash, 12 сен 2007.

  1. Crash

    Crash New Member

    Публикаций:
    0
    Регистрация:
    23 авг 2004
    Сообщения:
    73
    Здравствуйте, народ!

    Проблема такая: написал драйвер, предназначенный для перехвата
    некоторых функций ядра. Все работает ок, но как только дело
    доходит до выгрузки, то появляется синий экран с ошибкой
    DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS.

    Я читал, что это из-за того, что некоторые остаточные Irp-запросы продолжают
    поступать после выгрузки. И рекомендовалось использовать
    RemoveLock'и. Я так и сделал:

    Код (Text):
    1. // Данная функция вызывается всякий раз, когда для нашего драйвера вызывается
    2. // Win32 API функция CreateFile
    3. NTSTATUS CreateDriver(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    4.     NTSTATUS status;
    5.  
    6.     if (bDriverLocked) {
    7.         status = STATUS_ACCESS_DENIED;
    8.     } else {
    9.         status = STATUS_SUCCESS;
    10.         bDriverLocked = TRUE;
    11.  
    12.         IoInitializeRemoveLock(&global_dev_ext->RemoveLock, 0, 0, 0);
    13.         status = IoAcquireRemoveLock(&global_dev_ext->RemoveLock, 0);
    14.         if (!NT_SUCCESS(status)) {
    15.             IoReleaseRemoveLock(&global_dev_ext->RemoveLock, 0);
    16.         }
    17.     }
    18.  
    19.     CompleteIrp(Irp, status);
    20.     return status;
    21. }
    22.  
    23. // Данная функция вызывается всякий раз, когда вызвана DeviceIoControl для нашего драйвера
    24. NTSTATUS DriverIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    25.     NTSTATUS status = STATUS_SUCCESS;
    26.     PDEVICE_EXTENSION dev_ext = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
    27.  
    28.     status = IoAcquireRemoveLock(&global_dev_ext->RemoveLock, 0);
    29.     if (!NT_SUCCESS(status)) {
    30.         CompleteIrp(Irp, status);
    31.         return status;
    32.     }
    33.  
    34.     // ... тут обработка контрольных кодов ...
    35.  
    36.     CompleteIrp(Irp, status);
    37.     IoReleaseRemoveLock(&global_dev_ext->RemoveLock, 0);
    38.     return status;
    39. }
    40.  
    41. // Функция выгрузки драйвера
    42. VOID UnloadDriver(PDRIVER_OBJECT DriverObject) {
    43.     // Снимаем хуки
    44.     CHooking::Unhook();
    45.  
    46.     // Ждем завершения всех new-функций
    47.     KWAIT_BLOCK WaitBlockArray[EVENT_COUNT];
    48.     KeWaitForMultipleObjects(EVENT_COUNT, (PVOID*)events, WaitAll, Executive, KernelMode, FALSE, NULL, WaitBlockArray);
    49.  
    50.     // Освободим память, которая была выделена для событий
    51.     for (int i = 0; i < EVENT_COUNT; i++) {
    52.         if (events[i]) {
    53.             ExFreePool(events[i]);
    54.             events[i] = NULL;
    55.         }
    56.     }
    57.  
    58.     // Освободим связанный список с правилами
    59.     CProtect::Free();
    60.  
    61.     IoReleaseRemoveLockAndWait(&global_dev_ext->RemoveLock, 0);
    62.  
    63.     IoDeleteSymbolicLink(&dos_dev_name);
    64.     IoDeleteDevice(DriverObject->DeviceObject);
    65. }
    Массив events нужен для контроля завершения всех new-функций.

    Проблема, видимо, в том, что не все потоки завершены к тому времени, когда драйвер
    выгружается. Может, я неправильно использую RemoveLock'и?

    Можно было бы, конечно, драйвер сделать невыгружаемым. Но нужно его выгружать.
     
  2. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    ээ а у тебя фильтр чтоли?
     
  3. Crash

    Crash New Member

    Публикаций:
    0
    Регистрация:
    23 авг 2004
    Сообщения:
    73
    Нет, не фильтр.
     
  4. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    крешдамп анализировал? причину желательно знать точно
     
  5. Crash

    Crash New Member

    Публикаций:
    0
    Регистрация:
    23 авг 2004
    Сообщения:
    73
    Нет, но я точно знаю, что это происходит после того, как я пытаюсь заморозить защищаемый драйвером процесс функцией ZwSuspendProcess или ZwSuspendThread. Но после этого сразу снимаю хуки и выгружаю драйвер.

    Кстати, как можно крешдамп получить? Я с низкоуровневыми отладчиками не работал. Новичок в этом.
     
  6. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    он автоматически должен генерироваться при бсоде. это настраивается.
    Win+Break => Advanced => Startup and recovery => Settings => Write debugging information => Kernel memory dump
     
  7. Crash

    Crash New Member

    Публикаций:
    0
    Регистрация:
    23 авг 2004
    Сообщения:
    73
    А что может дать мне этот дамп? Я же точно знаю, что проблема в моем драйвере.
     
  8. Deyton

    Deyton Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    94
    Crash
    Жесть! В первую очередь учись отлаживать свои дрова и анализировать крешдампы, тогда и вопросов подобных не будет возникать вообще. А до этого даже не пытайся ничего писать, за тебя твои баги никто фиксить не будет...
     
  9. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    боже мой.. оно тебе подскажет причину краха.
    Deyton +1..
     
  10. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    Crash
    интересная тема... разберешься - расскажи что там было

    титиретически могу предположить такое - механизм ремув-локов так устроен, что проверяет
    все ли они закрыты ПЕРЕД вызовом DriverUnload
     
  11. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    механизм этот, как я понимаю, ничего не проверяет, ожидание инициируется явно внутри DriverUnload при вызове IoReleaseRemoveLockAndWait(-Ex).
     
  12. Crash

    Crash New Member

    Публикаций:
    0
    Регистрация:
    23 авг 2004
    Сообщения:
    73
    да, а мне нужно их (RemoveLock) попробовать в самих new-функциях
    попробую позже и скажу результат
     
  13. k3internal

    k3internal New Member

    Публикаций:
    0
    Регистрация:
    11 янв 2007
    Сообщения:
    607
    Crash

    Ты на какие функции хуки ставишь ? Бывают такие моменты, когда к примеру ты обрабатываешь данные после системного обрабтчика. То есть, был вызов твоей функции, затем ты отдал вызов системной функции, и , пока он там где то крутится, ты снимаешь хуки, и наверное выгружаешь драйвер. В этот момент происходит возврат из системной функции в родительскую, то есть в твою, код которой был уже размаплен в памяти. Вот тут то ты и получаешь тот самый статус. Ты организуй хотя бы простейшие симафоры для контроля был ли возврат из системных функций и обязательно навесь на них синхронизаторы, а то в многопоточности глючить может.
     
  14. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    Great
    я почему так предположил - в МСДН-е в нескольких местах идет такое - Локи сами жЫвут в екстеншине так что они прибьются при делетедевица, вейт ждите при делетедевица, "The driver has received an IRP_MN_REMOVE_DEVICE for the device and has called IoReleaseRemoveLockandWait. "

    и такого дофуя в описании

    то есть зачем-то микрософт настойчиво толкает к размещению кода ТАМ, и соответственно НЕ в дриверунлоад

    но это предположения, чтение между строк мсдн-а по привычке :)
     
  15. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    ну все правильно - PnP драйвера должны при получении IRP_MN_REMOVE_DEVICE вызывать (сами) IoReleaseRemoveLockAndWait перед тем, как сносить свой FDO/FiDO
     
  16. Crash

    Crash New Member

    Публикаций:
    0
    Регистрация:
    23 авг 2004
    Сообщения:
    73
    Я ставлю на множество функций:
    ZwOpenProcess
    ZwTerminateProcess
    ZwDebugActiveProcess
    ZwSuspendProcess
    ZwAllocateVirtualMemory
    ZwReadVirtualMemory
    ZwWriteVirtualMemory
    ZwOpenThread
    ZwTerminateThread
    ZwSuspendThread
    ZwCreateThread
    ZwQueryInformationProcess
    ZwDuplicateObject
    PsLookupProcessByProcessId
    ZwOpenFile
    ZwCreateFile
    ZwOpenSection

    Вообще, я делаю так (пример перехвата ZwOpenProcess):

    Код (Text):
    1. NTSTATUS NewZwOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId) {
    2.     KeClearEvent(events[0]);
    3.  
    4.     // ... какой-то код
    5.  
    6.     KeSetEvent(events[0], 0, FALSE);
    7.     return status;
    8. }
    Т.е. использую Events. И так для каждой перехватываемой функции. А UnloadDriver выглядит так:

    Код (Text):
    1. VOID UnloadDriver(PDRIVER_OBJECT DriverObject) {
    2.     Unhook();
    3.  
    4.     KWAIT_BLOCK WaitBlockArray[EVENT_COUNT];
    5.     KeWaitForMultipleObjects(EVENT_COUNT, (PVOID*)events, WaitAll, Executive, KernelMode, FALSE, NULL, WaitBlockArray);
    6.  
    7.     // Освободим память, которая была выделена для событий
    8.     for (int i = 0; i < EVENT_COUNT; i++) {
    9.         if (events[i]) {
    10.             ExFreePool(events[i]);
    11.             events[i] = NULL;
    12.         }
    13.     }
    14.  
    15.     IoReleaseRemoveLockAndWait(&global_dev_ext->RemoveLock, 0);
    16.  
    17.     IoDeleteSymbolicLink(&dos_dev_name);
    18.     IoDeleteDevice(DriverObject->DeviceObject);
    19. }
    По идее все events должны перейти в свободное состояние, когда все New-функции закончат работу (сигнальное или не сигнальное - не помню, я их путаю). И судя по тому, что функция KeWaitForMultipleObjects дожидается их перехода в это состояние, то все должно работать, но не работает.

    Единственное, на что я не ставлю events - функции ZwTerminateProcess и ZwTerminateThread, т.к. они никогда не возвращаются, а следовательно, не выполняется
    KeSetEvent(events[X], 0, FALSE). Поэтому не ставлю

    Функция IoReleaseRemoveLockAndWait ждет освобождения всех RemoveLock'ов, которые создаются в CreateDriver и используются в DriverIoControl (тут вроде тоже все правильно):

    Код (Text):
    1. NTSTATUS CreateDriver(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    2.     NTSTATUS status = STATUS_SUCCESS;
    3.  
    4.     IoInitializeRemoveLock(&global_dev_ext->RemoveLock, 0, 0, 0);
    5.     status = IoAcquireRemoveLock(&global_dev_ext->RemoveLock, 0);
    6.     if (!NT_SUCCESS(status)) {
    7.         IoReleaseRemoveLock(&global_dev_ext->RemoveLock, 0);
    8.     }
    9.  
    10.     CompleteIrp(Irp, status);
    11.     return status;
    12. }
    13.  
    14. NTSTATUS DriverIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    15.     NTSTATUS status = STATUS_SUCCESS;
    16.     PDEVICE_EXTENSION dev_ext = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
    17.  
    18.     status = IoAcquireRemoveLock(&global_dev_ext->RemoveLock, 0);
    19.     if (!NT_SUCCESS(status)) { // maybe device is being removed.
    20.         CompleteIrp(Irp, status);
    21.         return status;
    22.     }
    23.  
    24.     // здесь обработка IOCTL-кодов, поступивших от приложения
    25.  
    26.     CompleteIrp(Irp, status);
    27.     IoReleaseRemoveLock(&global_dev_ext->RemoveLock, 0);
    28.     return status;
    29. }
    Но эти методы, если их можно так назвать, не помогают.
    Может имеет смысл вообще сделать драйвер невыгружаемым, чтобы избавиться от этой мороки? Кто еще что-нибудь посоветует?
     
  17. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Я бы так и поступил, честно говоря.

    А вообще сделай анализ крешдампа в конце концов - какой код пытается быть вызванным?
     
  18. Crash

    Crash New Member

    Публикаций:
    0
    Регистрация:
    23 авг 2004
    Сообщения:
    73
    Какие инструменты порекомендуешь для этого?
    Я слышал, что есть Kanalyze.
     
  19. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    обычный WinDbg (kd) из Debugging Tools for Windows
     
  20. Crash

    Crash New Member

    Публикаций:
    0
    Регистрация:
    23 авг 2004
    Сообщения:
    73
    Вроде теперь понятно, почему исключение вылетает.

    Но не анализом крешдампа - до него руки пока не доходят.

    Я сделал для каждой перехватываемой функции счетчик (засинхронизированный, естественно), который хранит количество запущенных потоков в системе для конкретной функции. И в UnloadDriver проверяю перед вызовом IoDeleteDevice, все ли счетчики в нуле. Если все - то можно вызывать IoDeleteDevice.

    Оказывается, перехватываемая ZwAllocateVirtualMemory еще выполнялась, когда вызывалась IoDeleteDevice в DriverUnload. Теперь, когда все счетчики = 0, иногда все равно вылетает это исключение. Предполагаю, что выгрузка драйвера происходит в тот момент, когда счетчик для конкретной функции обнулился, но EIP еще внутри этой функции (перед инструкцией ret - где-то рядом с ней).

    Поэтому, чтобы дать время EIP уйти оттуда, делаю задержку на 500 мс., например. Это решение конечно кривое. И теперь понятно, почему все умные люди говорят, что нужно следить именно за EIP.

    Но как это сделать? Нигде про это информации нет. Или плохо ищу.