хм, research?.. описать-то можно, но это ведь стена текста получится. и будет слегка win32-specific =P когда может проявиться баг? только в очень специфичных ситуациях. для начала необходимо, чтобы в функции присутствовал блок try-finally, внутри него был блок try-except, а в нём – ещё один try-finally. таким вот образом: Код (Text): __try { __try { __try { // первое исключение должно произойти внутри этого блока } __finally { // второе исключение – внутри этого, при выполнении раскрутки } } __except(x) // фильтр должен отказаться обрабатывать первое исключение, но согласиться обработать второе {} } __finally { // в соответствии с планом этот блок выполнится два раза } фильтр в блоке '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-блока следует в порядке вещей (не в рамках раскрутки). _ _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 блоков.
Во, совсем другое дело, спасибо =) Тут вот не очень понятно. В scope table насколько я помню храниться 3 dword'a - первый это число вложенных try блоков, второе фильтр, третье обработчик ( либо except, либо finally ). Как мы определим из этой информации есть баг или нет?) Наверное определять (если возникнет такая необходимость) нужно всеже через сканирование стартап кода на предмет сигнатур и сравнением найденных сигнатур с сигнатурами компилятора, который не подвержен данному багу.
TSS Вот мне интересно, а читать всю лабуду неохота, можно в двух словах в чём собственно ошибка заключается ??
Clerk Можно изменить поток выполнения кода, сформировав определенным образом блок try/except/finally. Применение: запутывание логики программ(в защитах), обман эмуляторов антивирусов(проверить нужно). Недостатки: в старых версиях crt бага нет. Cам баг, если кратко, заключается в лишнем SEH фрейме, установленном над RtlUnwind. Подробнее выше (хотя нужно самому наверное глянуть, потому как лично для меня нет полной картины).
TSS Векторная обработка исключений рулит, как легальный механизм предельно низкого уровня в юзермоде. Ошибки в модулях поддержки не имеют никакого значения.