pop-quiz: __try{} __harder{}

Тема в разделе "LANGS.C", создана пользователем catwalk_mission, 4 июн 2009.

  1. catwalk_mission

    catwalk_mission New Member

    Публикаций:
    0
    Регистрация:
    23 май 2009
    Сообщения:
    19
    хм, research?.. описать-то можно, но это ведь стена текста получится. и будет слегка win32-specific =P

    когда может проявиться баг? только в очень специфичных ситуациях.
    для начала необходимо, чтобы в функции присутствовал блок try-finally, внутри него был блок try-except, а в нём – ещё один try-finally.
    таким вот образом:
    Код (Text):
    1. __try
    2. {
    3.     __try
    4.     {
    5.         __try
    6.         {
    7.                 // первое исключение должно произойти внутри этого блока
    8.         }
    9.         __finally
    10.         {
    11.                 // второе исключение – внутри этого, при выполнении раскрутки
    12.         }
    13.     }
    14.     __except(x) // фильтр должен отказаться обрабатывать первое исключение, но согласиться обработать второе
    15.     {}
    16. }
    17. __finally
    18. {
    19.     // в соответствии с планом этот блок выполнится два раза
    20. }
    фильтр в блоке 'except()', разделяющем блоки finally, должен вернуть EXCEPTION_CONTINUE_SEARCH для первого исключения.
    затем какой-либо фильтр, находящийся перед внешеним блоком 'finally' (неважно, в этой функции этот фильтр или в другой), должен согласиться обработать исключение (в самом крайнем случае фильтр, установленный ос, сделает это – после того как пользователь кликнет "close program \ [don't] send report" в error-box'е).
    в результате этого начнётся раскрутка – и, если бы не второе исключение, внешний finally-блок должен был бы выполнится в рамках раскрутки. однако из-за второго исключения раскрутка останавливается и начинается dispatching – поиск обработчика исключений. вот тут-то фильтр в разделительном except-блоке должен вернуть EXCEPTION_EXECUTE_HANDLER, соглашаясь обработать исключение. здесь опять начинается раскрутка, которая в этот раз должна дойти только до разделительного except-блока. и вот именно здесь всё идёт наперекосяк!

    дело в том, что функция, выполняющая локальную раскрутку (_local_unwind4) перед выполнением раскрутки сохраняет в стеке свой третий параметр – HandledTryLevel (идентификатор try-блока, до которого выполнять раскрутку) и устанавливает свой мини-обработчик (_unwind_handler4) в seh-цепочку. это делается как раз на случай исключения при раскрутке – чтобы "съесть" мини-обработчик, устанавливаемый перед глобальной раскруткой функцией ntdll.RtlUnwind!
    если бы '_unwind_handler4' не устанавливала этот мини-обработчик, то в случае исключения при раскрутке 'RtlUnwind' узнала бы от своего мини-обработчика о том, что производилась раскрутка такого-то фрейма, когда произошло исключение – и не стала бы раскручивать "сломанный" фрейм в этот раз, а просто пропустила бы его. [wtf]. в случае вышеприведённой раскладки это, кстати, заставило бы 'RtlUnwind' тут же поднять исключение со статусом STATUS_INVALID_UNWIND_TARGET.

    так вот, _unwind_handler4 зачем-то вызывает _local_unwind4, при этом передавая значение HandledTryLevel, сохранённое в стеке _local_unwind4 при прошлом вызове. т.е. старое значение, сохранённое при прошлой раскрутке. естественно, _local_unwind4 при этом вызывает внешний finally-блок! а после того, как раскрутка заканчивается и управление передаётся хендлеру, второй вызов внешнего finally-блока следует в порядке вещей (не в рамках раскрутки).
    _

    [​IMG]
    _unwind_handler4, выделенная часть – просто-напросто лишний код.
    задача в упрощённом виде, обычные цифры – ожидаемое, цифры в скобках – действительное.
    компилированный код
    _
    _

    мельком проверил некоторые версии рантайма:
    наличие бага:
    новые версии msvcrt.dll
    msvcr80.dll
    msvcr90.dll
    msvcr100.dll

    отсутствие бага:
    старые версии msvcrt.dll
    msvcr71.dll
    _
    _

    случайные заметки:
    - баг присутствует в статической и динамической CRT (одна кодовая база).
    - _local_unwind4 и _unwind_handler4 – немногие из тех функций CRT, которые написаны на ассемблере.
    - вместо _local_unwind4\_unwind_handler4 могут быть использована предыдущая версии функций – _local_unwind2\_unwind_handler2. суть не меняется.
    - определить теоретическую подверженность бинарников подобному unexpected-behaviour'у можно, пожалуй, в автоматическом режиме. для этого нужно найти все таблицы EH4_SCOPETABLE (указатель на таблицу кладётся в стек при создании фрейма) и искать в них характерные последовательности except-finally блоков.
     
  2. TSS

    TSS New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    494
    Во, совсем другое дело, спасибо =)

    Тут вот не очень понятно. В scope table насколько я помню храниться 3 dword'a - первый это число вложенных try блоков, второе фильтр, третье обработчик ( либо except, либо finally ). Как мы определим из этой информации есть баг или нет?) Наверное определять (если возникнет такая необходимость) нужно всеже через сканирование стартап кода на предмет сигнатур и сравнением найденных сигнатур с сигнатурами компилятора, который не подвержен данному багу.
     
  3. Clerk

    Clerk Забанен

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

    TSS New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    494
    Clerk
    Можно изменить поток выполнения кода, сформировав определенным образом блок try/except/finally.
    Применение: запутывание логики программ(в защитах), обман эмуляторов антивирусов(проверить нужно).
    Недостатки: в старых версиях crt бага нет.
    Cам баг, если кратко, заключается в лишнем SEH фрейме, установленном над RtlUnwind. Подробнее выше (хотя нужно самому наверное глянуть, потому как лично для меня нет полной картины).
     
  5. Clerk

    Clerk Забанен

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