Поддержка SEH в смапленном драйвере

Тема в разделе "WASM.BEGINNERS", создана пользователем HoShiMin, 28 окт 2018.

  1. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.422
    Адрес:
    Россия, Нижний Новгород
    Написал в драйвере загрузчик сторонних модулей: выделяем память, правим релоки, заполняем таблицу импорта и вызываем каллбэки, прокидывая запросы через основной драйвер в смапленный модуль.
    Выглядит примерно так:
    Код (C++):
    1.  
    2. __try {
    3.     // Вызываем каллбэк в модуле:
    4.     Status = OnDeviceControl(CtlCode, Argument);
    5. } __except (
    6.     ExceptionCode = GetExceptionCode(),
    7.     ExceptionPointers = GetExceptionInformation(),
    8.     EXCEPTION_EXECUTE_HANDLER
    9. ) {
    10.     // Передаём эксепшн в обработчик в модуле:
    11.     OnException(ExceptionCode, ExceptionPointers);
    12.     Status = STATUS_UNSUCCESSFUL;
    13. }
    14.  
    И сам модуль выглядит так:
    Код (C++):
    1.  
    2. NTSTATUS OnDeviceControl(ULONG CtlCode, PVOID Argument)
    3. {
    4.     __try {
    5.         *(PCHAR)(NULL) = 0; // Поднимаем исключение
    6.     } __except (EXCEPTION_EXECUTE_HANDLER) {
    7.         KdPrint(("Exception catched!\r\n"));
    8.     }
    9. }
    10.  
    11. NTSTATUS OnException(ULONG ExceptionCode, PEXCEPTION_POINTERS ExceptionPointers)
    12. {
    13.     // Здесь надо передать управление на обработчик в OnDeviceControl
    14. }
    15.  
    Есть ли какие-то простые способы передать управление на нормальный обработчик?
    Целевая платформа (пока) х64, патчгуард триггерить нельзя.
     
  2. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.422
    Адрес:
    Россия, Нижний Новгород
    Проблема отошла на второй план, когда обнаружил странное поведение обработчиков в динамическом коде.

    Драйвер генерирует ничего не делающую функцию, поднимающую исключение:
    Код (C++):
    1.  
    2. /*
    3.     В ShellBytes такой код:
    4.     sub rsp, 20h
    5.     xor rax, rax
    6.     mov [rax], rax // Поднимаем исключение
    7.     add rsp, 20h
    8.     ret
    9. */
    10.  
    11. UCHAR ShellBytes[] = { ... };
    12.  
    13. using _Shell = VOID(*)();
    14. _Shell ShellCode = AllocExecutable(sizeof(ShellBytes));
    15. memcpy(ShellCode, ShellBytes, sizeof(ShellBytes));
    16.  
    17. ULONG ExceptionCode = 0;
    18. PEXCEPTION_POINTERS ExceptionPointers = NULL;
    19. __try {
    20.      ShellCode();
    21. } __except (
    22.     ExceptionCode = GetExceptionCode(),
    23.     ExceptionPointers = GetExceptionInformation(),
    24.     EXCEPTION_EXECUTE_HANDLER
    25. ) {
    26.     // Здесь повреждённый стек
    27. }
    28.  
    29. Free(ShellCode);
    30.  
    Если пошагово идти по инструкциям в отладчике, управление передаётся в обработчик и со стеком всё в порядке.
    Но стоит "отпустить" отладчик (F5) - в обработчик мы попадаем с повреждённым стеком.

    При этом, аналогичная функция внутри модуля хэндлится нормально в обоих случаях.
    В чём же дело?
     
  3. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    HoShiMin,

    > Драйвер генерирует ничего не делающую функцию

    Так может вначале следует разобраться с компилером, прежде чем лезть в ядро. Если он у вас своей жизнью живёт и не контролируем, то о каком кернел коденге вообще может быть речь. Или быть может ваш подход такой - оно же собирается для км, тупо хеловорлд но собирается, а почему и я не могу" ?
    С таким подходом никуда не уедешь. Так и будешь ковырять крэшдампы годами свои.
     
  4. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.422
    Адрес:
    Россия, Нижний Новгород
    А компилер здесь при чём? В юзермоде этот же шелл прекрасно хэндлится, в ядре - ломается стек в обработчике. Причём, при пошаговой отладке всё нормально, обработчик срабатывает и стек валидный.
    И сишный аналог, собранный, как часть драйвера:
    Код (C++):
    1.  
    2. void Func() {
    3.     *(PCHAR*)NULL = 0;
    4. }
    5.  
    тоже хэндлится без проблем и с отладчиком, и без.

    Значит, у ядерного обработчика исключений есть какие-то особенности, о которых я не знаю, и которые создают разное поведение с пошаговой отладкой и без.
     
  5. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    HoShiMin,

    Удивительно, вы же ведь уже давно тут и работали в км. Но я понять не могу как может быть проблема с нулевым дереференсом, обычный фаулт.

    Хорошо, пусть имеется проблема, но для этого есть отладчик, либо прочие средства для анализа проблемы. Так а как так получилось что спустя кучу времени и поднимая сложные весьма задачи у вас получилась засада с простым фолтом #AV и вы это не можете разобрать отладчиком. Я не понимаю.
     
  6. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.422
    Адрес:
    Россия, Нижний Новгород
    Нет, Инди, я специально генерирую AV, чтобы проверить правильность работы хэндлеров! И в этом самая большая засада, что, отлаживая пошагово, в обработчке получаю ровный стек (делаю шаг на инструкции, генерирующей AV), но, запуская без отладки (точнее, без ПОШАГОВОЙ отладки), гарантированно получаю, как сейчас выяснилось, съехавшую ровно на 32 байта вершину, словно диспетчер забывает делать add rsp, 20h.
    --- Сообщение объединено, 4 ноя 2018 ---
    В ядре вообще можно ловить эксепшны из памяти, не принадлежащей валидному модулю? Может, я вообще пытаюсь сделать то, что нельзя? Насколько знаю, патчгуард, начиная с Win8, триггерится на ручное добавление своих обработчиков в таблицу хэндлеров - возможно, и сама обработка исключений зависит от того, где они происходят.
    --- Сообщение объединено, 4 ноя 2018 ---
    Похоже, что без отладчика эксепшн просто не обрабатывается и сразу генерит багчек.

    С отладчиком вилка:
    1. Пошаговая отладка - правильная обработка
    2. НЕ пошаговая отладка - в обработчик попадаем со сдвинутой на 32 байта вершиной

    Закономерный вопрос - что за чудеса?
     
    Последнее редактирование: 3 ноя 2018
    Indy_ нравится это.
  7. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    HoShiMin,

    > В ядре вообще можно ловить эксепшны из памяти, не принадлежащей валидному модулю?

    Хороший вопрос. На старших версиях системы вероятно нет, это вероятно прибивается защитой. Даже в юм проверки на ip~image.

    Поставить свою ловушку в IDT не вариант, это зарубит патчгвард. Так что изначально не верный подход к задаче. Если вы шелл в ядре использовать решили, то нужно среду реализовать для его запуска, как минимум каким то образом установить ловушки и что бы на это не срабатывала защита.

    Все возможные пути для установки ловушек мониторит патчгвард, это IDT и KiDbgRoutine. Так что единственный вариант - его выключить или как то вмешаться в логику работы. Я бы посмотрел на его выборки, но это весьма сложная задача.

    > гарантированно получаю, как сейчас выяснилось, съехавшую ровно на 32 байта вершину

    Если бы это била защита, то вы управление вообще бы не получили в сех. Так что инфы не достаточно что бы что то предсказать, пройдите отладчиком и посмотрите что происходит.
     
    Последнее редактирование: 4 ноя 2018
  8. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.422
    Адрес:
    Россия, Нижний Новгород
    Indy_ ,
    Дурацкий вариант: выделить в драйвере пустую RWX-секцию достаточного размера и записывать загружаемые модули в неё, пока хватает памяти. Флажок SEC_IMAGE есть, память принадлежит валидному драйверу, эксепшны должны работать. Проверю.