Программирование шаблонов в машинных кодах
Главный недостаток поиска по <CTRL-S> это то, что OllyDbg ищет комбинацию команд в исходном дизассемблерном тексте, а в защищенных программах он практически всегда упакован или зашифрован. Статическим поиском мы хрена что найдем! Необходимо возвратиться к пошаговой трассировке и посмотреть какие еще механизмы условных точек OllyDbg нам предоставляет. Строка "condition is TRUE" позволяет задавать любые условия, истинность которых останавливает трассировку. Ни шаблоны, ни регулярные выражения здесь не поддерживаются и все это приходится программировать вручную, спускаясь на уровень "голых" машинных кодов. Непередаваемые ощущения— словно ты перенесся на 15 лет и очутился за IBM XT (не путать с XP), когда никаких готовых инструментов для взлома еще не существовало, а программировать приходилось не компилятором, а головой. Ладно, не будем предаваться ностальгии, лучше откроем TECH HELP! (http://binaryhell.dax.ru/books/techhelp.zip) или справочник по командам от INTEL/AMD.
Конкретно, нас будут интересовать мнемоники инструкций CALL CONST, TEST EAX, EAX и JNZ/JZ. Оттуда, мы в частности, узнаем, что команда CALL имеем опкод E8h за которым следуют четыре байта относительного адреса. Команда TEST EAX, EAX в машинном виде выглядит как 85h С0h, ну а JZ/JNZ представляются хорошо известными двухбайтовыми инструкциями 74h XXh/75h XXh, где XXh – относительное смещение адреса перехода, отсчитываемое от начала команды.
Рисунок 7 матрица опкодов машинных инструкций в электронном справочнике TECH HELP!
Обобщив все вышесказанное, можно составить выражение, срабатывающее на заданной последовательности команд. Мы будем отталкиваться от содержимого регистра EIP — если одно равно E8h, то это команда CALL const. Увеличив EIP на 5 байт (длина CALL const) мы получаем указатель на следующую команду, сверяя его с опкодом инструкции TEST EAX, EAX, равным 85h C0h или C085h с учетом обратного порядка байт в x86.
; ß CALL const à ß TEST EAX, EAX à ß JZ const OR JNZ const à
([BYTE EIP]==0E8) & ([WORD EIP+5]==0C085) & (([BYTE EIP+7]==74) | ([BYTE EIP+7]==75))
Листинг 4 выражение, устанавливающее точку останова на последовательность команд CALL const/TEST EAX, EAX/Jx const
Нажимаем <CTRL-T> и вводим это выражение в строку "condition is TRUE", не забыв взвести галочку напротив, и приступаем к трассировки по <CTR-F11> или <CTRL-F7>. Если выражение введено правильно (что с первой попытки удается далеко не всегда), OllyDbg послушно остановится перед входов в функцию CALL const (см рис. 8):
Рисунок 8 установка точек останова на последовательность машинных команд
Составлять сложные выражения непосредственно в строке редактирования очень неудобно и ненаглядно. Постоянно путаешься в скобках и забываешь, что уже написано, а что нет. Наконец, однажды написанное (и отлаженное!) выражение необходимо как-то сохранять с комментариями, что оно, собственно, делает, а всего этого OllyDbg не умеет, поэтому выражения лучше составлять в своем любимом редакторе (например, FAR'е по <F4> с плагином clolorer, позволяющим перемещаться между скобками и контролировать корректность их вложенности). Там же можно разместить и комментарии. Тем не менее, громоздкие выражения OllyDbg интерпретирует очень медленно и трассировка становится совсем непроизводительной, а ведь для успеха операции нам необходимо реализовать свой аналог ключевого слова "ANY", поскольку, как уже отмечалось, оптимизирующие компиляторы могут "разбавлять" команды TEST EAX, EAX и Jx посторонними инструкциями.
Реализация ANY на языке выражений — очень сложная задача, которую можно осуществить только написав свой собственный дизассемблер длин, а это... уже настоящий монстр получается! Такие вещи лучше писать на Си и цеплять к отладчику как плагин, тем более, что исходные тексты дизассемблера распространяются вместе с OllyDbg (http://www.ollydbg.de/srcdescr.htm), а, значит, основная часть работы уже сделана за нас.
Остальное доделает… Perl. А почему бы и нет? Лучшего "движка" для составления регулярных выражений, пожалуй, и не придумаешь! Соединив Perl с дизассемблером воедино, мы получим могучий инструмент. На вход плагина подается поток команд, "добываемый" трассером OllyDbg, дизассемблер превращает его в текст, ну а Perl ищет в этом тексте все, что мы ему скажем. Маленький нюанс — для борьбы с самомодифицирующимся кодом, необходимо отказаться от опережающего просмотра вперед и анализировать только выполненные инструкции, которые OllyDbg помещает в лог трассировки, однако, это слишком амбициозные цели. Просто прикрутите Perl к дизассемблеру и наслаждайтесь возможностью устанавливать точки останова на любую комбинацию команд.
Это поможет нам обходить все антиотладочные приемы, какие только есть. Ведь любой антиотладочый прием по сути задается той или иной комбинацией машинных команд, и количество возможных вариаций достаточно ограничено. Встретившись с защитой, которая разваливает отладчик, мы находим где он гробится и добавляем этот антиотдочный прием в свой плагин. В конечном счете, получится аналог IceExt для OllyDbg только более мощный, правда, и более тормозной за счет использования неповоротливого Perl'а.