нашел в Olly неприятный баг. при трассировке SEH/VEH программ в момент генерации исключения в регистровый контекст попадает EFlags со взведенным битом TF, который Olly "забывает" вычистить, в результате чего, после выхода из SEH/VEH обработчика флаг трассировки оказывается взведен и генерируется отладочное прерывание, передаваемое в отлаживаемую программу, в результате чего SEH/VEH обработчик вызывается еще раз, что в общем случае приводит к краху программы. решение: в момент генерации исключения "вычистить" TF-бит из регистрового контекста, переданного функции диспетчеризации исключений, в которой Olly оказывается по Shift-F7/F8
Столкнулся с подобной проблемой в отладчике IDA. В ней, в процессе манипуляции с флагами, при выполнении pushf в стек заносится flags с установленным TF, несомотря на то, что в flags TF в этот момент не установлен. Причем, как ни странно, если участок кода выполняется не пошагово, такой проблемы не возникает.
Mika0x65 ничего странного нет, при непошаговом выполнении TF не взведен. у ИДЫ это не баг, а misfeature, что позволяет защите задетектить трассировку, но не ведет (в отличие от Olly) к краху отлаживаемой (незащищенной!!!) программы. согласитесь, это несколько разные вещи
kaspersky это баг windows, проявляется ещё при мутках с SetThreadContext, когда ставишь флаг трассировки, а он начинает трассироваться с другого места
rain ага, во всех проблемах форточки виноваты, нашли крайних. взводить флаг трассировки с помощью SetThrContext вообще не самая лучшая идея, особенно если поток не "заморожен". но это ладно. вернемся к Olly. при трассировке программы TF-бит ес-но взведен и отладчику посылается отладочное исключение, которое он обрабатывает, не передавая его в отлаживаемую программу, но вот при генерации исключения (любого) внутри самой программы первой user-land функцией, получающей управление, будет NTDLL.DLL!KiUserExceptionDispatcher, вызываемая из ядра и получающая регистровый контекст. флаг трассировки сбрасывается ядром и функция спокойно вызывает SEH/VEH обработчик, назначенный программистом, по выходу из которого восстанавливается регистровый контекст, соответствующий состоянию ЦП на момент генерации исключения. со взведенным TF-битом, в результате чего выполнение следующей команды приводит к генерации отладочного исключения, которое Olly ловит, но, поскольку, он не помнит, чтобы он ставил бит трассировки, то он передает это исключение в программу, что приводит к повторному вызову SEH/VEH обработчика. при более-менее корректно написанном SEH/VEH обработчике мы просто зациклившемся, а при некорректном - получаем крах (например, обработчик ловит исключение возникающее при выполнении xor eax,eax/mov eax,[eax] и тупо увеличивает EIP на два, не проверяя кода исключения). это баг Olly. он должен помнить, что после возврата из обработчика трассировочное прерывание принадлежит ему, а не программе
сори за отступление от темы Вот пример. Для старта трейса создаётся новый процесс (который будем трейсить) с флагом CREATE_SUSPENDED, затем через GetThreadContext\SetThreadContext устанавливается в контексте треда флаг TF. Особенность в том что когда мы вызываем ResumeThread то, мы получаем Single Execution Exception НЕ после той инструкции которая шла за EIP который был в GetThreadContext\SetThreadContext а за первой инструкцией KiUserApcDispatcher. Чем это объясняется? Что предлагается в замен менять EIP на заглушки с popfd? Касательно той проблемы о которой топик то, похоже мы говорим о разных вещах, скажу прямо для мну олли не представляет большого интереса. Но баг имеет место бы следующего плана. здесь не ясно в какой именно контекст, тот контекст который текущий и текущий eflags или тот который передаётся на стеке? При определённых условиях можно заметить тот факт что мы ловим трап по TF на KiUserExceptionDispatcher. например так: Код (Text): pushfd or dword ptr [esp], 100h popfd int 3 nop nop Отбросим олик и посмотрим сколько сехов вызовет такой код? На всех х86 системах у мну 3 бряка (wow64 не берём там правильно и поэтому защитные механизмы не могут юзать эту особенность). Бряки по адресам 0x7C90EAF0 - ntdll!KiUserExceptionDispatcher+0x4, 0x00401256 - адрес int 3 и 0x00401258 адрес второго нопа. пример и бинарник в атаче. Проверил только что под оликом(без трейса) -- результаты идентичны. А как заставить олик при трейсе на int 3 генерировать исключение посылая его программе? upd: с трейсом по shift - f7 результаты теже
rain > Для старта трейса создаётся новый процесс > с флагом CREATE_SUSPENDED, затем через > GetThreadContext\SetThreadContext устанавливается > в контексте треда флаг TF. Особенность в том что > когда мы вызываем ResumeThread то, мы получаем > Single Execution Exception НЕ после той инструкции > которая шла за EIP который был в GetThreadContext\ >SetThreadContext а за первой инструкцией KiUserApcDispatcher. > Чем это объясняется? это объясняется особенностями реализации процедуры разморозки потока. если взглянуть на функцию KiUserApcDispatcher, то мы увидим следующее: Код (Text): .text:77F91B8C public KiUserApcDispatcher .text:77F91B8C KiUserApcDispatcher proc near .text:77F91B8C .text:77F91B8C arg_C = dword ptr 10h .text:77F91B8C .text:77F91B8C lea edi, [esp+arg_C] .text:77F91B90 pop eax .text:77F91B91 call eax .text:77F91B93 push 1 .text:77F91B95 push edi .text:77F91B96 call ZwContinue .text:77F91B9B nop .text:77F91B9B KiUserApcDispatcher endp на момент выполнения KiUserApcDispatcher (вызываемой из ядра), регистр флагов уже восстановлен и флаг трассировки взведен. далее она выталкивает из стека Stack ntdll.LdrInitializeThunk, которая мотает LdrQueryImageFileExecutionOptions и после выхода из нее, KiUserApcDispatcher вызывает ZwContinue, "ныряющую" в ядро, где уже полностью восстанавливается контекст потока и поток получает управление. что удивительного в том, что ResumeThread реализована как асинхронная процедура?! что же касается восстановления регистра флагов _до_ передачи управления на EIP потока, то это сделано умышленно, чтобы user-level отладчики могли трассировать user-land код. поскольку, у потока есть только один контекст, то... по-другому просто не получается. а какие проблемы отловить исключение в обработчике и пропускать его до тех пор, пока мы не дойдет до обозначенного EIP? > Что предлагается в замен менять EIP на заглушки с popfd? взводить флаг трассировки из SEH/VEH обработчика. хотя это тоже не идеальный вариант. > здесь не ясно в какой именно контекст, тот контекст который текущий > и текущий eflags или тот который передаётся на стеке? тот контекст (набор регистров и флагов), который имеет место быть у потока в момент выполнения инструкции, вызывающей исключение > При определённых условиях можно заметить тот факт что мы ловим > трап по TF на KiUserExceptionDispatcher. вообще-то при любом исключении мы поймаем на ней трап, если мы, конечно, ее трапаем. скажем там. KiUserExceptionDispatcher - это первая user-land функция, получающая управление после начальной обработки исключения ядром, и она уже в свою очередь смотрит какие у нас SEH/VEH обработчики установлены и какие из них вызывать. Код (Text): > pushfd > or dword ptr [esp], 100h > popfd > int 3 > nop > Отбросим олик и посмотрим сколько сехов вызовет такой код? три, конечно. первый на int 03 (само int 03 его и вызывает), два других трассировочные прерывания. тут правда не ясно как ты сбрасываешь TF флаг, т.к. за последним nop что-то должно быть расположено ну ладно, бум считать, что ты сбрасываешь TF-флаг из ыур-обработчика. > А как заставить олик при трейсе на int 3 > генерировать исключение посылая его программе? ALT-O, Exceptions, Ignore (pass to program) following exceptions: INT 3 break и все будет ок. > с трейсом по shift - f7 результаты теже что и следовало ожидать.
ну так собсно и было, но мне кажется это не тру, при многопоточности скорее всего такой трейсер можно будет поимёть её хуком дык его сначала нужно как-то вызвать и установть в трассируемом процессе, нафиг такое счастье мы её специально не трапаем посмотри пример, в сехе если не установить явно в контексте на стеке TF он сам сбросится и больше трапаться не будет. Глянь где произошли сами бряки в предидущем моём посте, один там явно лишний, если ты считаешь такое поведение правельным, то почему тогда на вов64 только два?)
можно автору было написать или попробовать тут отпостится http://www.woodmann.com/forum/forumdisplay.php?f=37
Asterix мыщъх не по уши деревянный. отписал автору olly на мыло. кстати, не первый раз уже. и все безрезультатно. мне в принципе все равно. пофиксить этот баг можно даже без написания собственного плагина: http://souriz.wordpress.com/2008/04/23/a-new-bug-in-olly-776/ в любом случае, даже если баг будет пофиксен в 2.xx - фигли толку? народ юзает 1.xx и еще долго будет юзать и полезно знать как бороться с багами. кстати, ни soft-ice, ни ms visual studio/ms windbg такого бага не имеют, так что не нужно говорить, что это винда кривая такая когда я только начинал изучать Win Dbg API (а это было лет ~10 тому назад), то быстро столкнулся с этой проблемой при написании экспериментального отладчика (типа "макетника") и так же быстро решил ее, передавая INT 01h программе только когда был совершенно уверен, что это она его взвела, а не я, в смысле не отладчик.
баг признан багом и бу скоро исправлен. ждем-с! # Dear Kris, # thank you for your bug report. I will try to remove it from v2.0 ASAP. # Sincerely, Olly