Источник
Ссылка на оригинальный пост crackme#03.
Вступление
У меня нет опыта в реверсе, есть некие базовые знания ассемблера, позволяющие сделать битхак условных переходов, чтобы данное приложение всегда выходило на success-path независимо от введенного значения. Но так как это слишком банально для того, чтобы быть опубликованным здесь, а до полного воспроизведения алгоритма проверки введенного ключа я еще не дорос, я решил попробовать метод брута, т.е. последовательного подбора ключей. Конечно же, я не могу сказать, что и этот способ отличается своей крутизной, но по крайней мере этот способ не еще рассматривали в ветке "Руководства".
В ветке, где опубликован этот крякми, пользователь ARCHANGEL опубликовал метод брута на C++, который действительно выдает правильный пароль для этого крякми. Он мог позволить себе написать брут на языке высокого уровня, так как смог воспроизвести алгоритм и нашел значение 0x23a06032, с которым сравнивается полученное crc.
Я же пока не научился так глубоко анализировать алгоритмы на asm-е, поэтому буду работать не отходя от кассы, используя алгоритм проверки в самом крякми как черный ящик.
Анализ условных переходов
Запускаем крякми и выключаем звук на компьютере (видимо именно этого добивался автор, добавив звуковое сопровождение).
Запускаю OllyDbg 2.01 c плагином OllyExt, в котором выставлены приведенные ниже настройки:
Этот плагин поможет избавиться от некоторых потенциальных приемов анти-отладки.
Аттачимся из Ольги к выполняемому процессу crackme#03.exe: File -> Attach...
Далее пробуем ввести любое значение в поле ввода и без труда находим код, в котором оно обрабатывается. Сделать это можно выставив breakpoint-ы на потенциальные функции получения текста (в частности GetDlgItemTextA) или пролистать код исходного модуля - благо здесь он небольшой. Нажимаем кнопку Check.
Срабатывает breakpoint, жмем один раз F8 и смотрим листинг. Видим, что введенная нами строка хранится по адресу 004095BC. Также смотрим на инструкцию по адресу 0040101F. Она сравнивает длину введенной строки со значением 12 и в случае неравенства выбрасывает нас на 004010AF.
Далее, обратите внимание на инструкцию по адресу 00401028, которая сравнивает 12-ый байт введенного значения со значением 72, а это буква r в ASCII-кодировке. В случае неравенства снова выбрасывает нас на 004010AF. Что же это за адрес такой? Об этом чуть позже.
Теперь обратим внимание на инструкции в диапазоне 00401031 - 0040104C. Покурив справочник команд ассемблера по командам repne и scas, а также потрассировав выделенную область кода, приходим к выводу, что aehnprwy - это допустимый алфавит требуемого ключа.
Теперь посмотрим, что же у нас расположено по адресу 004010AF. Там вывод сообщения о неудачном вводе пароля.
Итак, подведем итоги первичного анализа:
1) Ключ должен состоять из 12 символов;
2) Последний символ ключа должен быть r;
3) Символы ключа должны принадлежать алфавиту aehnprwy.
Брутфорс
Приступим к реализации брут-метода. Реализуем его сначала на концептуальном уровне. Можно на блок-схеме, можно на языке высокого уровня. Я реализовал его прототип на C++. Для понимания дальнейшего материала необходимо переварить код под спойлером.
Основные этапы:
1) Инициализация ключа (пароля) начальным значением.
2) Проверка ключа.
3) Если ключ неверен, то генерим следующий ключ и переходим к шагу 2. Если ключ верный, переходим к шагу 4. Если мы перебрали все ключи и ни один из них не подошел, переходим к шагу 5.
4) Crackme взломан.
5) Crackme не взломан. Где-то мы ошиблись.
Инициализация ключа
Программа хранит и обращается к ключу по адресу 004095BC - там и будем его инициализировать. Алфавит расположен по адресу 00409071.
004095CC - это адрес памяти, где мы будем хранить наш массив CodeArray.
Для инициализации ключа находим неиспользуемую область секции кода и вставляем туда наши инструкции. Я выбрал адрес 00404122.
Чтобы собственно эта инициализация выполнилась, идем к тому месту, где вызывается GetDlgItemTextA, аккуратно заменяем ее вызов на безусловный переход к нашему коду по адресу 00404122, а предшествующие ей push-команды заменяем nop-ами. Все делаем аккуратно, чтобы адрес команды CMP EAX, 0C остался прежним: 0040101F.
На рисунке ниже представлен сам код инициализации. По ее окончанию переходим к алгоритму валидации ключа в результате безусловного перехода на 0040101F.
Получение следующего значения ключа
Теперь нам нужно реализовать код получения следующего значения ключа. Выбираем для этого адрес 00404165 (сразу после кода инициализации) и делаем переход на него с того места, где программа обзывает нас ламерами.
Модифицируем инструкции по адресам 004010AF и 004010B1.
Ну и по адресу 00404165 реализуем код получения нового ключа.
Запуск
Жмем кнопку Check и идем пить чай. Черт, и чай-то давно уж вскипел, придется кипятить заново.
Через некоторое время получаем:
Смотрим, что же у нас по адресу 004095BC
happynewyear - это и есть искомый ключ.
P.S. Продублировал статью на Хабре (под другим ником).















Reply With Quote
Thanks
