Альтернативы хукингу SSDT

Тема в разделе "WASM.NT.KERNEL", создана пользователем katrus, 27 янв 2008.

  1. katrus

    katrus New Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    612
    Пишу драйвер (не рут) который должен жить в мире и согласии с антивирусами не влияя на их нормальное функционирование. в драйвере хукаю несколько sys API через SSDT. К сожалению, есть несколько антивирусов с которыми драйвер регулярно бсодит. Удалось восоздать в лабораторных условиях (под дебагером) сценарий приводяший к падению системы:
    1. драйвер антивируса поставил свой хук на функцию в SSDT.
    2. мой драйвер сделал то-же самое. Вместо настояшей функции мой драйвер вызывает функцию в драйвере антивируса. Как уже упоминалась: важно, чтоб антивирус функционировал полноценно.
    3. драйвер антивируса решил отчалить. Уж не знау что у него там случилось. Может update? Увидев, что в SSDT вектор поменялся антивирус понимает, что кто-то поставил свой хук, решает, что если Вы свой хук поставили то сами с ним и живите и делает ручкой.
    4. Мой драйвер передает управление функции в драйвере антивируса который уже выгружен из памяти... Синий экран ...

    Вижу два решения проблемы:
    1. Пропатчить сами функции. Достоинства: должно быть достаточно надежно (по крайней мере в теории). Недостатки: сложнее чем хукинг SSDT, не совсем понятно как блокировать все другие потоки от доступа к функции в процессе патчинга.

    2. Пропатчить обработчик sysenter. Достоинства: 100% получаем управление перед антивирусом. Недостатки: относительно сложно - нужно патчить не начало функции а где-то в середине там где sysenter делает call по адресу из SSDT; синхронизация.

    Вопрос, собственно - из Вашего опыта какие подводные камни могут встретится при реализации? Может есть другие способы обойти проблему?

    Спасибо!
     
  2. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Нельзя ли по короче вопрос сформулировать ?
     
  3. katrus

    katrus New Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    612
    Clerk
    Так долго бился над проблемой, что короткие слова закончились :) ... Учту
     
  4. wasm_test

    wasm_test wasm test user

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

    нет, проще хукать ssdt
     
  5. katrus

    katrus New Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    612
    Great
    Можно, но тогда придется возится с копированием аргументов и на стек и прочее, не хочу сказать, чтоб это было уж слишком сложно, но, кажется, надежнее патчить существующий обработчик чем писать свой.

    Естественно, только антивирусы это не любят.
     
  6. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Что мешает перед вызовом функции проверить: а не выгрузился ли драйвер антивируса?
    Если выгрузился - восстановить sdt и вызывать уже настоящую функцию, а не переходник антивируса.
    Это требует конечно проверки на присутствие антивируса, но проблем быть не должно (ZwQuerySystemInformation)
     
  7. katrus

    katrus New Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    612
    cresta
    Немного геморойно. Есть масса антивирусов и нет никакой возможности сделать ad-hok решение для каждого из них. Нужно что-то универсальное и стабильное.
     
  8. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Каждый поток может иметь уникальную SDT
     
  9. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Какая разница, сколько антивирусов? Хоть сто штук :)
    Механизм хука на sdt один и тот же для всех желающих похукать его, и не нужно писать разных процедур восстановления sdt. Только одну.
    Если адрес из sdt никому из загруженых системных модулей не принадлежит - восстановить sdt, чтобы адрес указывал на ntoskrnl, и ничего падать не будет. В любом другом случае - ничего не трогать.

    Или я что-то не так понял?
     
  10. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Clerk
    наскока я знаю это бывает редко.. обычно адрес таблицы равен либо KeServiceDescriptorTable либо KeServiceDescriptorTableShadow для гуйных потоков
     
  11. katrus

    katrus New Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    612
    А есть ли какой callback вызываемый при выгрузке драйвере (не моего)? Т.е., аналог PsSetLoadImageNotifyRoutine только вызываемый при выгрузке драйвера.
     
  12. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    нет такого
     
  13. katrus

    katrus New Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    612
    cresta
    Вроде нашел замену - хукаем ZwUnloadDriver и можно подсмотреть какой драйвер выгружается. Проблема в том, что ето будет работать только если драйвер выгружается из user mode приложения.
     
  14. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    В чём проблема ?
    Посмотреть что вызывается и перехватить это.
     
  15. katrus

    katrus New Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    612
    Вызывается IopLoadUnloadDriver, а она в свою очередь из NtUnloadDriver. Буду хукать ZwUnloadDriver
     
  16. Twister

    Twister New Member

    Публикаций:
    0
    Регистрация:
    12 окт 2005
    Сообщения:
    720
    Адрес:
    Алматы
    Была же мысль хукать сплайсингом. Думаю, это поможет - антивири обычно так не делают.
    Задрать IRQL или просуспендить все потоки.
     
  17. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    не вариант. надо хукать WorkItem для выгрузки, тем более что несложно это сотворить - всего лишь поменять адрес Worker Routine
    Уже 1000 раз обсуждали что никакой саспенд не спасет от того, что другой поток может в момент заморозки выполнять код перехватываемой функции. А ты нагло туда суешь джамп поверх выполняемого другим потоком кода. После резюма/понижения IRQL другой поток продолжит выполнение, а там уже джамп лежит. В режиме ядра последствия очевидны. Правда бывает это редко, но, как говорится, метко
     
  18. katrus

    katrus New Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    612
    Great
    Извиняюсь, не понял, что нужно хукать? Что такое WorkItem/worker routine?

    А какие могут быть причины НЕ патчить обрабoтчик sysenter? Проблему синхронизации там можно решить просто - копируем старый обработчик, патчим копию, wrmsr. Правда, не совсем очевидно, что антивирусы не будут ругатся на это.
     
  19. Deyton

    Deyton Member

    Публикаций:
    0
    Регистрация:
    7 мар 2007
    Сообщения:
    94
    И какой антивирус так делает, если не секрет? Вы тут уже начали изобретать неведать что, и неведать зачем. В нормальных условиях ничего подобного возникать не должно, цепочки хукеров должны нормально соосуществовать, в противном случае, либо ошибка в твоем драйвере, либо кг/ам-щик тот, с чьим софтом у тебя проблемы.

    Я почитал топик, и у меня возник вопрос, - ты точно уверен, что причина BSOD-а именно в том, что ты написал? Я на своей практике сталкивался с двумя софтинами, из-за кривых хуков которых все падает в BSOD, если кто-то хукает после них - это Kaspersky и COMODO. У первого не совпадало кол-во параметров одной из функций в Shadow SDT (4 вместо 5), что приводило к неверной коррекции стека, а поскольку KiSystemService не чуствителен к этому, ничего не падает до тех пор, пока ф-цию не похукает еще кто-то, когда стек будет использоваться следующим в цепочке. С COMODO было еще хуже - у него стоит хук на ZwWriteFileGather, и обработчик мало того, что неверно корректирует указатель стека, да еще и перезаписывает DWORD в его вершине, т.к. считает, что эта функция принимает 10 параметров, вместо положенных 9. Не исключаю, что есть и другие, похожие, чудо-хукеры =). Чтобы разрулить эту ситуацию и застраховаться от подобных багов, я сделал wrapper для безопасного вызова системных сервисов, которые похуканы кем-то еще...

    В итоге вышло по типу такого:

    Код (Text):
    1. LONG GenericServiceCall(
    2.     PVOID       Address         // адрес вызываемой функции
    3.     ULONG       ParCnt,         // кол-во параметров
    4.     PULONG_PTR  ParArray        // массив параметров
    5.     )
    6. {
    7.     LONG    ESPreg, Result;
    8.    
    9.     __asm {
    10.         mov ESPreg, esp     // сохраняем ESP
    11.         sub esp, 16     // вызывающая функция может перезаписывать до 4 DWORD в стеке
    12.         mov ebx, ParArray
    13.         mov ecx, ParCnt
    14. __loop:
    15.         dec ecx
    16.         js __done
    17.         push [ebx+ecx*4]
    18.         jmp __loop
    19. __done:
    20.         call Address
    21.         mov Result, eax
    22.         mov esp, ESPreg     // восстанавливаем ESP
    23.     }
    24.    
    25.     return Result;
    26. }
    Это решило все проблемы совместимости =). Ну а выгрузка SSDT хукеров - это вообще кг/ам, и в коммерческом софте я такого вроде пока не встречал.
     
  20. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Ну ты заикнулся про IopLoadUnloadDriver, так вот скажу, Как происходит обработка запроса на загрузку дрова.
    Zw(Un)LoadDriver ставит в очередь WorkItem - рабочий элемент, который будет исполняться рабочим потоком (worker thread) процесса System. Рабочий поток, получив workitem, начинает исполнять указанную в нем Worker Routine - рабочую функцию. Обычно это IopLoadUnloadDriver. Она вызывает IopLoadDriver, которая уже дергает DriverEntry и все такое.
    Так вот, можно похукать код, устанавливающий адрес WorkerRoutine в структуре WorkItem (там один mov, кажется). Это перезапись дворда без изменения опкода - безопасная операция
    написал бы явно пролог/эпилог и поставил бы naked, а то вдруг компилятору взбредет в голову (или что у него на этом месте) убрать его в целях оптимизации.. вот тогда весело будет в доступе к локальным переменным с сорванным стеком. Я бы, кстати, лучше в регистре сохранял бы старый ESP. Например, в EDI. Или других регистрах, которые апи менять не имеют права - esi, ebp, ebx, че там еще..