Тут пролистывал хакера августовского - статью Криса про приемы антиотладки. Любителя пошевелить серые клеточки вопросами - вопрос к нему. Вот такая задача - имеется 2 хадварных бряка по одному и тому же адресу исполняемого кода (не важно какому, но пусть будет код ядра) 1) й бряк на исполнение пусть будет в DR0 2) второй бряк на чтение (запись) пусть будет в DR1 Теперь вопрос - при пошаговой отладке какой бряк исполнится быстрее (сама программа по адресу бряка не читает). Чтобы правильно быть понятым - какие будут значения битов B0-B1 в DR6 при исполнении под отладчиком и при реальной работе программы? PS: вопрос с маленьким подвохом
PROFi Если говорить про полноценную отладку то: Они сразу все сработать могут. Для большинства инструкций при срабатывании точки останова не имеет значения флаг TF. Регистр Dr6 нужен чтобы понять что произошло. Если говорить про реальные отладчики то обработчики исключений кривые и может быть всякое.
PROFi Я ляпну. Если что, то сильно не бейте. В общем это разве не зависит от способа трассировки? Т.е. Olly, например, ставит одноразовые int3 при пошаговом исполнении. Соответственно тогда раньше сработает аппаратный брэйкпоинт на чтение/запись. А если трассировка через TF, то на чтение/запись вообще по идее не должен сработать.
PROFi вопрос, конечно, интересный, но не могу сообразить как его можно реально заюзать. ответ (молчаливо полагается, что ты имеешь ввиду команду типа l1:mov eax, [l2], при этом бряк один стоит на l1, бряк два на l2, бряк на чтение/запись на l1 все равно не сработает, так что это даже не обсуждается): 1) бряк на исполнение срабатывает первым; 2) при этом регистр EIP указывает на l1; 3) отладчик взводит RF флаг (см. "Masking Instruction Breakpoints" в Intel sys. man); 4) исключение от второго бряка подавляется; 5) это работает как под отладчиком, так и без него; 6) теоритически отладчик может и не взводить RF флаг, тогда сработают обе точки останова одна за другой. гм, а вообще интересный момент. если как-то вынудить хакера постаить точку останова на команду, которая обращается, например, к паролю в памяти, на который хакер так же поставил бряк, то второй бряк будет пропущен... наверное, можно будет пустить в антиотладку с thx2PROFi. след. выпуск как раз посвящен точкам останова и ситуациям когда они _не_ срабатывают. а должны
kaspersky "при этом бряк один стоит на l1, бряк два на l2" - ошибочка при этом бряк один стоит на l1 и бряк два на l1 "бряк на чтение/запись на l1 все равно не сработает" тоже ошибочка - сработает при определенных условиях "6) теоритически отладчик может и не взводить RF флаг, тогда сработают обе точки останова одна за другой." - это только одно из них, и почему только отладчик "может и не взводить"? Реально юзается в динамическом распаковщике кода, с защитой от снятия дампа А вчем подвох - в том, что ряд отладчиков читает пямять вперед и выводит дизасемблерный листинг, в том числе и программа креш дампа (естественно на винчестер может быть записан дамп но толко через DMA доступ к памяти, а есили проц к ней обратится ...) - а как заюзать - ну я больше технологии люблю - код это дело вторичное - не судите строго
PS: согласно мануалам интела (старым еще 386 проца) если срабатывют 2 и более условий, то устанавливаются все биты Bx, для тех бряков которые выполнились. (ответ Clerk) но не стоит забывать, что останов по инструкции это исключение, а останов по данным это ловушка
PROFi > согласно мануалам интела (старым еще 368 проца) если срабатывют 2 и более условий в твоем примере этого не происходит. бряк на выполнение срабатывает раньше бряка на доступ к данным, т.к. бряк на выполнение срабатывает (по мануалам) до выполнения команды, а бряк на доступ - после. а вот если мы поставим два бряка на выполнение, тогда да, они сработают оба, хотя исключение будет выброшено только одно (а больше и не надо). как поведут себя отладчики? ну они вообще-то не позволяют ставить сразу два бряка, но туже ольгу легко обмануть, отредактировав udd файл. ольга видит только первый (в порядке возростания номеров DR регистров) бряк, что логично.
Ок следующий вопрос можно: Что делает программа (пусть будет режим пользователя) программа запущена на Win32, процессор ну пусть будет core 2 duo? Код (Text): .00403854: BC59384000 mov esp,000403859 .00403859: 8F442404 pop d,[esp][04] .0040385D: 33C0 xor eax,eax .0040385F: 8B00 mov eax,[eax] ...
PROFi >> "бряк на чтение/запись на l1 все равно не сработает" > тоже ошибочка - сработает при определенных условиях а, ну если инструкцию кто-то прочтет... но это уже другой случай. даже если инструкция читает сама себя или перезаписывает сама себя, то все равно бряк на исполнение первый. мыщъх хотел сказать, что выполнение инструкции ЦП не считается чтением. >> "6) теоритически отладчик может и не взводить RF флаг, >> тогда сработают обе точки останова одна за другой." > это только одно из них, и почему только отладчик "может и не взводить"? как вариант, он может погасить бряк на исполнение, тогда при выполнении инструкции выпрыгнет бряк по чтению. > Реально юзается в динамическом распаковщике кода, с защитой от снятия дампа в смысле программа сама себя через DR регистры распаковывает? на днях дизассемблил малварь, запротекченную протектором, который я не опознал, и который именно через бряки на исполнение расшифровывает код. правда, отладчку это совершенно не мешает. и код расшифровщика элементарно рипается и переносится в статический распаковщик, т.к. он обращается всего к десятку глобальных переменных, которые приходится переносить руками, а остальное - тупой копи-пасте. > А вчем подвох - в том, что ряд отладчиков читает пямять вперед > и выводит дизасемблерный листинг, в том числе и программа креш дампа на счет крэша подвох в другом. ему нельзя вообще доверять. возмьмем, например, разогнанный проц или проц с битым/глючным L2 кэшем. допустим, MOV EAX, EBX в результате сбоя превращается в инструкцию, вызывающую исключение. но в дампе (или в JIT отладчике) все будет пучком, т.к. происходит перечитывание памяти. и можно очень долго ломать голову что за фигня творится... так же существует куча способов (от легальных до хаков) нарушить когерентность кэш-памяти, в результате чего в кэше будет одно, а в оперативке - другое. и тогда ни крэш-дамп, ни даже просто дамп ничего не даст и можно убить кучу времени, пытаясь понять что тут происходит и какого черта творится.... возможности прочитать кэш команд у нас все равно нет ;( и что там было - можно только гадать.
kaspersky бряк на выполнение срабатывает раньше бряка на доступ к данным Основная работа идет в программе расшифровки кода который после бряка идет. Если первым отработал бряк на выполненние - то все ок если на чтение - что-то неладное. " бряк на выполнение срабатывает (по мануалам) до выполнения команды, а бряк на доступ - после." это тоже самое "но не стоит забывать, что останов по инструкции это исключение, а останов по данным это ловушка " Все что приведено после PS это в контексте, а не ответ на вопрос, или конкретная ситуация, я не знаю квалификации читателей форума - потому пишу подробнее. Ответ на превый вопрос простой: если код исполняется без просмотра кода отладчиком (не каждый отладчик сможет), то сначала исполняется бряк на исполненея, потом на доступ в память. Если нас кто хочет читать, сдампить и т.д. то первым естествено будет бряк на чтение.
PROFi > Что делает программа (пусть будет режим пользователя) юзает одну из многих ошибок когерентности кэша в core? (модификация команды, следующей за командой, вызывающей исключение). а вообще много неоднозначностей. ты мне скажи: а) запись в .00403ххх разрешена? б) и сикоко у нас памяти? обработчику исключения нужен стек, а если он в ходе разбирательств столкнуется с тем, что записать данные не получится, то система просто прибьет процесс...
"а) запись в .00403ххх разрешена?" - да разрешена - забыл указать "б) и сикоко у нас памяти? обработчику исключения нужен стек" - ну хватит если нужно эта прога просто уничтожает сама свой код "просто прибьет процесс..." - именно это и происходит самоуничтожение "на днях дизассемблил малварь, запротекченную протектором, который я не опознал, и который именно через бряки на исполнение расшифровывает код." - не одну ли и туже мы малварь ресечили
PROFi Это неправда. Соответственно .00403859: 8F442404 pop d,[esp][04] запишет себя по адресу после инструкций .0040385D: 33C0 xor eax,eax .0040385F: 8B00 mov eax,[eax] , а не вместо. Т.е. исполнится команда mov eax,[eax], и возникнет исключение. "Самоуничтожение" можно было бы сделать командой .00403859: 8F442400 pop d,[esp][00]
PROFi > "просто прибьет процесс..." - именно это и происходит самоуничтожение так если запись разрешена и стека хватит, то самоуничтожения не произойдет. стека не хватает. проверь свой код еще раз. если в одном из потоков происходит исключение в ходе обработки которого ядру не хватает стека пользовательского пространства, то процесс уничтожается в очень интересной манере отследить которую сложно даже айсом. у ольги, кстати, тут есть забавный глюк. процесс уже сдох, но ольга об этом не знает и мы даже можем продолжить трассировать код какое-то время... но поскольку процесса у нас нету, а есть только его дохлая тушка, то попытка вызова API (например) обламывается. ну нельзя вызывать API из процесса, которого нет. весь прикол в том, что если хакер проморгает кончину процесса и продолжит его трассировать ему буде очень трудно разобраться почему отладчик перестал фурычить. (при таком завершии процесса его адресное пространство остается, именно поэтому ольга может еще протянуть чуть-чуть... но паджинг уже не работает, т.е. загрузка странц PE-файла, которые не были загружены ранее. выделение стека не работает... короче ни черта не работает. но трассировать уже загруженные страницы можно. и вызывать API целиком реализованные в юзер-спейсе и находящиеся в загруженных страницах так же можно) l_inc ну так я и написал, что модифицируюется команда следующая за командой вызывающей исключение. в данном случае это mov eax,[eax]. затирания кода не происходит (если ты это имешь ввиду). пример с затиранием кода приведен ниже. // TF lost before PUSH EAX mov edx, 0D18E6669h ; ??/MOV SS, CX // prepare the EAX for PUSH mov eax, 9C176699h ; ??.??/POP SS/PUSHFD mov cx, ss mov ax, cx ; <= for POP SS // jmp -> back to home mov edi, offset back_home mov ebx, 6969E7FFh ; ??.??/JMP EDI // to stack push ebx ; // LOST TF mov ebx, 696950F3h ; ??/??/??/PUSH EAX push ebx ; // LOST TF, PUSHFD, OVERWRITE PUSHFD push edx ; // jmp to home mov esi, esp inc esi ; skip first uf byte ; inc esi ; skip prefix (don't skip if it doesn't work!) jz back_home ; facked jx, it never takes // put esp on the place pop ebx pop edx jmp esi ; goooo
kaspersky "почему отладчик перестал фурычить." - т.е. удаления объекта процесс не происходит, но либы все уже выгрузились или как? l_inc Угу промахнулся - пасибо
kaspersky Ну так я ж и не Вам замечание делаю. Еще не хватало. Просто PROFi рассчитывал на самозатирание.
PROFi > т.е. удаления объекта процесс не происходит, но либы все уже выгрузились или как? скажем так - объект процесс переходит в состояние комы. система уже не занимается планированием его потоков, паджингом страниц, выделением памяти, etc, но если процесс находится под отладкой, то хэнделы процесса остаются валидными и Read/WriteProcMem работают. так же процесс сохраняет регистровый контекст, и потому его можно трассировать. точнее что при этом происходит сказать не могу, т.к. глубоко в этот вопрос не зарывался.
l_inc > Просто PROFi рассчитывал на самозатирание. гм, а получилось, что он натолкнулся на ошибку семейства core. номер по errata сейчас не скажу, но она там описана. модификация команды за командой вызывающей исключение расположенной в пределах досягаемости конвейера. при этом мы имеем шанс нарушить когерентность кэша, т.е. может быть выполнена как новая, так и старая инструкция (от чего зависит не совсем понятно...). кому интересно - может поэкспериментировать. только прежде - взглянуть на степпинг ЦП, чтобы убедиться, что ошибка там не пофиксена и запретить обновление микрокода в BIOS.
маленький трюк на последок. возьмем код типа: 00000000: 3EFF10 call d,ds:[eax] ... 0000000X: call 000000001 кажется - ну и що? а вот что - бряк на 00h очевидно не сработает, т.к. первый байт просто не выполняется... но догадаться _почему_ бряк не выполняется не так-то просто, особенно если мы не видим кода, который его вызывает (а этот код может быть очччень далеко). причем, можно давать как call 00h так и call 01h, т.е. в первом случае бряк таки сработает, а во втором нет. и мы можем замаскировать вызов функции [eax] самое смешное, что я споткнулся об него в своей же программе. специально подложил мышеловку и сказал: нате, ломайте. меня спрсили: все понятно, не понятно, почему точки останова не всегда срабатывают... а мыщъх к тому времени уже и забыл, что он нахимичил и потому стал разбираться убив минут с полчаса ))