Исследование SEH

Тема в разделе "WASM.RESEARCH", создана пользователем Y_Mur, 30 ноя 2006.

  1. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Про SEH написано много (Iczelion, Matt Pietrek 1, 2, 3), но весьма замудрёно,
    популярнее в: SEH&VEH,
    \masm32\vkdebug\example\demo1\dbdemo.asm,
    \masm32\vkdebug\dbproc\trapex.asm
    Однако попытка прикрутить к программе простейший SEH обработчик привела к интересным результатам, которые я не нашёл (может плохо искал?) в статьях про SEH.
    Простое возвращение управления в цикл выборки сообщений, через коррекцию eip в CONTEXT (пример SEH_1 в аттаче) работает, но естественно приводит к утечке стека, поскольку исключение происходит в функции, и в стеке находятся её параметры, локальные данные, адрес возврата, да и системный обработчик SEH "мусорит" :).
    Элементарная коррекция esp в CONTEXT приводит к нарушению работы SEH. Дело в том, что хитрая windows после вызова callback функции SEH_proc, но до восстановления регистров из CONTEXT, достраивает цепочку SEH новыми точками в стеке. После исправления esp, добавленная виндой точка SEH оказывается за пределами стека и при следующем исключении win обработчик считает её недействительной (пример SEH_2a).
    Если стек не исправлять, а в метке SEH_Exit: после восстановления предыдущего обработчика вызвать сбой (пример SEH_1a), то можно увидеть процесс раскрутки этой цепочки: в диалоге исключения придётся нажимать Нет несколько раз (зависит от количества нажатых Да и версии win) пока винда будет извлекать из цепочки SEH всё, что она туда напихала, а только затем она передаст управление обработчику по умолчанию и окончательно уронит программу.
    Таким образом, чтобы корректно вернуть управление в цикл выборки сообщений нужно исправлять не только указатель стека, но и указатель fs:[0], причём делать это не в callback функции а после выхода из неё :). Пример SEH_2 в аттаче.
    Описанный здесь SEH совсем не похож на "классический" tru\exception, имхо потому и так сильно отличается от описанного у Matt Pietrek, впрочем это всего-лишь другой ракурс того же SEH.

    ЗЫ: наличие на машине CrashGuard является хорошим индикатором качества восстановления предидущего SEH: если SEH восстановлен не корректно - программа падает с системным сообщением, а если правильно - то как и положено через CrashGuard.
    ЗЗЫ: Впрочем достаточно следить за регистрами и стеком в отладчике, главное не перепутать ссылки на свой SEH со ссылками на SEH отладчика, которые легко отличить по адресам указывающим далеко за пределы тестируемой программы.
     
  2. droopy

    droopy New Member

    Публикаций:
    0
    Регистрация:
    2 окт 2004
    Сообщения:
    21
  3. fr0b-p

    fr0b-p New Member

    Публикаций:
    0
    Регистрация:
    1 окт 2006
    Сообщения:
    118
    многа буков ниасилил :dntknw:

    какие траблы с сехом? у всех все работает гуд... скомпиль ц пример с __try __exept из MSDN и глянь асм... сравни с тем что ты делаешь... и могут глюки быть если /safeseh:no не указать
     
  4. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    droopy
    В твоей ссылке того, что я написал в #1 тоже нет - если бы где-то нашёл - то не постил :)
    fr0b-p
    с С проблем нет - это есно :) но при использовании асм варианта альтернативного к __try __exept обнаружил любопытный эффект и поделился с народом, может кому пригодиться, а может кто ещё дельное наблюдение добавит :)
     
  5. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    мне лично кажется что ты путаешь SEH котрый try/except и UnhandledExceptionFilter

    то что у тебя в примерах - это функционально UnhandledExceptionFilter а потоковый
    SEH просто не предназначен для того что ты хочешь
     
  6. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    z0mailbox
    Спасибо, кое-что прояснилось :))
    Хотя вопрос зачем винда достраивает seh цепочку в #1, и почему XP надстраивает больше чем 98 мне по прежнему кажется любопытным.

    зы: исправил ссылку
     
  7. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    Y_Mur
    Перевод статьи Джереми Гордона (Jeremy Gordon) есть и на этом сайте...;)
     
  8. _Serega_

    _Serega_ New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2006
    Сообщения:
    288
    А лично меня больше интересует, почему описатель SEH не может быть за пределами стека ? Может кто вникал в суть :)
     
  9. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    _Serega_, точно сказать не могу, но, возможно, это связанно с тем, как реализована раскрутка стека.
     
  10. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    _Serega_
    Основная суть в том, что винда при обработке исключения сравнивает FS:[0] с esp и адресом начала стека и если адрес из FS:[0] в этом диапазоне не лежит, то наотрез отказывается вызывать заданную в FS:[0] callback функцию.
    А вот зачем она это делает остаётся только догадываться :), возможно лишняя страховка при оживленни глючной проги, возможно действительно для совмещения раскрутки с поуровневой очисткой стека, может ещё какие соображения имеет... а нам оставляет только признать это за факт :)

    ЗЫ: Ещё одно соображение - цепочка SEH в стеке действует как разметка и облегчает обработчику исключения задачу проаналировать параметры переданные упавшей функции.
     
  11. _Serega_

    _Serega_ New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2006
    Сообщения:
    288
    Y_Mur
    Если разбирался, не подскаж. в каком модуле винды (ntos), это все реализовано?
     
  12. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    z0mailbox
    Проверил версию с UnhandledExceptionFilter - формат callback функции другой, а грабли те-же самые, точнее ещё круче :dntknw:(
    Винда точно также достраивает цепочку SEH в стеке, только в отладчике это уже не посмотришь, и если при возврате в безопасную точку корректировать стек, то при следующем исключении прога падает минуя обработчик (пример SEH_4), причём в w98 через системное окошко, а в XP через зависание :dntknw:. Без коррекции стека (пример SEH_3) всё работает, но есно с утечкой стека. Попытка восстановить UnhandledExceptionFilter сразу после выхода из обработчика исключения результата не даёт (пример SEH_5)
    Т.е. получается единственный корректный способ вернуться в безопасную точку - пример SEH_2 в #1.

    ЗЫ: Странно, что сие вопиющее бзобразие не освещено в низкоуровневых статьях про SEH
     
  13. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Y_Mur
    А ты про раскрутку стека и "всякие там" EH_UNWINDING, RtlUnwind и т.п внимательно читал ?
    Лично я нет ;), но насколько понимаю - если твой обработчик находится не в вершине цепочки, то он должен инициировать раскрутку вышестоящих обработчиков, чтобы они могли выполнить блоки finally и удалить себя из цепочки
     
  14. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    leo
    C EH_UNWINDING, RtlUnwind пока не разобрался, это ещё предстоит, но фишка в том, что глючат не предшествующие обработчики, которые собственно в моих простейших примерах вроде как никто не ставил, а последующие, которые винда зачем-то добавляет после моего возврата из callback функции ;).

    Ещё глянул От зеленого к красному 3, там есть пример где якобы "можно исправить ошибку и продолжить выполнение с безопасного места", на поверку пример оказался одноразовым аналогом моего SEH_2a, и работает тот пример соответственно лишь потому, что в нём не предусмотрено повторное исключение после возврата из SEH ;)
     
  15. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    Y_Mur
    Сейчас я глянул на приведенные вами примеры. Первое что мне бросилось в глаза: вы используете API-функцию MessageBox в обработчике SEH, а исключение вызываете в обработчике оконного сообщения. Это черева-то не очевидными последствиями. Если вам о них ничего не известно, то прочитайте об этом вот этот топик: http://www.wasm.ru/forum/viewtopic.php?id=7041
     
  16. EvilsInterrupt

    EvilsInterrupt Постигающий азы дзена

    Публикаций:
    0
    Регистрация:
    28 окт 2003
    Сообщения:
    2.428
    Адрес:
    Russia
    Oleg_SK
    Занятный топик, спасибо!
    зы: Почему в асю не выходишь?
     
  17. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    Y_Mur
    Если я правильно понял, винда это делает для того, чтобы иметь возможность обработать исключение в вашем SEH-обработчике.

    З.Ы.: Чтобы более подробно разобраться в этом вопросе, прочитайте третью часть статьи Мэтта Питрека (Matt Pietrek). Там приведен примерный псевдокод функции KiUserExceptionDispatcher.

    З.З.Ы.: В качестве дополнительного материала полезного для изучения SEH рекомендую статью Volodya, "Об упаковщиках в последний раз", часть вторая.
     
  18. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Oleg_SK
    Нет не правильно - надстройка происходит не до, а после вызова моей callback функции (см. #1) и соответсвенно перехватить ошибку в мём обработчике таким способом проблематично...

    За предупреждение про MessageBox, спасибо - учту в реальных прогах :), хотя в данной теме это не критично.
    В псевдокоде KiUserExceptionDispatcher из ntdll.dll значится установка только одной точки SEH перед вызовом callback функции, которая изничтожается незамедлительно после возврата из неё, попутно очищая стек после функции в С стиле. А про точки SEH которые ставятся после callback и не уничтожаются после выхода из системного обработчика там ничего нет, либо Питрек что-то недописал, либо это проделки уровня ядра...
    А статья от Volodya, хороший ответ на вопрос от _Serega_, но ответа на свой вопрос, я там не нашёл...
     
  19. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Нда грабли с MessageBox прикольные :))
    Конечно bogrus в #18 соответствующего топика всё разьяснил, но наблюдать как MessageBox полностью перехватывает выборку сообщений (пример SEH_8) забавно, но и обойти это легко, поместив проверку флага в саму процедуру окна (пример SEH_9). А чтобы окно перерисовывалось можно блокировать флагом только критичные к параллельному выполнению с SEH части оконной процедуры :)
     
  20. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Итак если обработчик SEH возвращает управление в безопасную точку программы не корректируя стек и не уничтожая после этого EXECEPTION_REGISTRATION, то винда при каждом исключении в программе достраивает цепочку SEH, причём все эти дополнительные обработчики не обрабатывают новые исключения, а передают их по цепочке, пока не сработает SEH самой программы (пример SEH_12.asm, SEH_12a.exe)
    Однако, в классическом варианте, на основе _try, _finally, _except (пример SEH_10.asm) этого безобразия не наблюдается независимо ни от количества ошибок, ни от того изменяется eip в пределах защищённого блока или управление возращается на команду вызвавшую сбой.
    Почему так происходит и как винда определяет когда ей нужно достраивать цепочку, а когда нет?
    Предположение о том, что винда всегда достраивает цепочку SEH после возврата из обработчика, а снимает свою надстройку перехватывая запись в fs:[0] не подтвердилось (пример SEH_13).

    Тем не менее реализовать SEH, который при почти любом сбое обеспечивает продолжение работы с безопасной точки совсем не сложно - достаточно после возврата из обработчика SEH исправить указатель стека и указатель в fs:[0] (пример SEH_12b.exe)
    Код (Text):
    1.     SEH_Restart:    ; Позиция для продолжения работы в случае исключения
    2.     mov esp, [SEH_esp]  ; Восстановить позицию стека
    3.     mov fs:[0], esp ; Восстановить свой SEH
    Главное избежать соблазна исправить esp не выходя из обработчика SEH:
    Код (Text):
    1. SEH_proc proc C pExcept: dword, pFrame: dword, pContext: dword, pDispatch: dword
    2. ; ВНИМАНИЕ callback функция должна сохранять ebx, esi, edi
    3.     mov edx, pContext
    4.     mov [edx].CONTEXT.regEip, offset SEH_Restart
    5. ;    mov eax, [SEH_esp]     ; или mov eax, [pFrame]
    6. ;    mov [edx].CONTEXT.regEsp, eax  ; < --- так корректировать esp нежелательно !!!
    7. ;    mov fs:[0], eax            ; < --- а эта коррекция не сработает !!!
    8.     mov eax, ExceptionContinueExecution ; Флаг продолжить выполнение программы
    9.   ret
    10. SEH_proc endp
    Как уже отмечалось в #14, эта ошибка встречается в примерах, где SEH используется однократно и потому порча fs:[0] на выходе из SEH остаётся незамеченной ;)

    И ещё, как видно из SEH_12b, SEH_10 и SEH_13, винда хотябы раз (98) или два раза (XP) добавляет свой SEH после установленного программой ;) Ага так это и есть разгадка появления цепочки !!! - винда делает вид, что защищает своим SEHом оконную функцию WinProc и когда возврат происходит в цикл выборки сообщений (минуя корректное завершение WinProc) то системный SEH остаётся не снятым ;) Чтобы убедиться в этом достаточно закомментировать метку SEH_Restart: перед циклом выборки сообщений и снять комментарий с метки SEH_Restart = $ перед ret в WinProc - всё безобразие с утечкой стека и достраиванием цепочек тут же прекращается (SEH_12c.exe)
    Однако, если нужна страховка достаточно сложной программы от неизвестного сбоя на неизвестно каком уровне вложенности подпрограммы, то имхо надёжнее всё таки почистить стек самостоятельно и продолжить работу "с чистого листа" ;)