6 февраля 2013 г.

Разбираем MaxXor's KeygenMe V4 в IDA

Давно не реверсил кейгенми, а тут как раз модераторы с http://crackmes.de вывалили небольшую пачку. Поглядел, что есть и выбрал более менее адекватную программу для взлома, со сложностью 3 под названием MaxXor's KeygenMe V4. Выше ничего не было да и времени честно говоря особа не охота было тратить больше нескольких часов.
Итак, что из себя представляет этот кейгенми:

Для начала позапускаем его и попробуем повводить разные буковки и циферки.
Тут же обнаруживаем, что в iD ввести можно комбинацию цифр от 1111 до 9999, на все остальное программа реагирует надписью iNVALiD NUMBER!!!
Хорошо, диапазон сразу виден и не требует особого реверса. Идем дальше.
Теперь откроем фаил в Stud_PE, побродив по вкладкам можно обнаружить, что фаил не пакованный и написан на Microsoft Visual C++ 8. Это упрощает задачу.

Загружаем его в IDA и видим вот такой адский спагети код.
Такой код достаточно тяжело отлаживать в визуальном режиме, приходится постоянно катать колесико или переключаться в каноничный. А в некоторых случая когда этого когда совсем много, то IDA начинает у меня адски тормозить.
Ладно, нажимаем ctrl+e и переходим на точку входа, встаем на первый вызов и нажимаем F4.
Дебагер тутже останавливается на том месте, куда мы поставили курсор. Пока ничего страшного не видно. Ok. Посмотрим как программа работает под дебагером, нажимаем F9 и программа тутже завершается. АГА! Говорят суровые австралийские мужики. Антидебаг детектед.
Я пользуюсь антиантидебаг утилитами и плагинами достаточно редко, когда совсем лень разбираться, в чем состоит антидебаг трюк.
Чтож посмотрим, каким образом здесь реализован антидебаг. Для начала посмотрим в импорт на предмет интересных WinAPI и не очень WinApi функций.
Как видно, программа импортит туеву хучу разнообразных функций из kernel32.dll.
И сразу можно заметить одну очень интересную функцию под названием IsDebuggerPresent, эта функция если находит дебагер возвращает 1, если не находит то возвращает 0. Более подробную информацию можно найти в статьях Нарвахо.(Жаль что wasm.ru сейчас не работает, дал бы туда ссылку на статью). Больше особа не за что зацепиться, функций типа ReadConsole что то не видать, как и функций сравнения строк за которые можно было бы зацепиться в поисках серийника.
Тыкаем на эту функцию 2 раза и попадем в IAT, посмотрим откуда эта функция вызывается, нажимем View->Open Subviews->Cross References
Нам выведет список всех вызовов функции IsDebuggerPresent. 
Самая интересная строчка будет выглядеть вот так:

Up p _main+40 call    ds:IsDebuggerPresent

Это значит что на 40ом байте тела main будет вызов этой функции. Тыкаем 2 раза на нее и видим следующий код:
Если посмотреть на скриншет, то видно, что после выполнения функции идет проверка на равняется eax нулю, если нет, то осуществляется очень длинный джамп кудато вниз, похоже это и есть антидебаг прием. Ставим брякопонт на функцию IsDebuggerPresent и запускаем снова программу, трасируем по F8 когда функция выполнится, то в eax можно будет обнаружить 1.
Щелкаем на eax правой кнопкой мыши и выбираем в низпадающем меню Zero Value.
Нажимаем на F9 в ожидании еще каких либо антидебаг приемов, но их нет, программа без проблем запускается и выводит окно приглашения ввести iD. Вводим любое значение от 1111 до 9999 и видим, что программа без проблем работает.
Итак, первый квест пройден, антидебаг прием распознан и теперь можно включить плагин IDAStealth чтобы не заострять больше внимание на этом.

Посмотрим в список строк, вдруг найдется, что интересное, типа слов iD или SERiAL, дабы сразу поставить на них брякпоинт и выйти сразу на ввод iD или серийника. Для этого нажмем View->Open Subviews->Strings. Но вдумчивое изучение строк и даже переключение между режимами ничего не дает, похоже эти строки или зашифрованы или собираются по некому алгоритму.

Чтож, вернемся к картинке где показывалась функция IsDebuggerPresent и на первую картинку в посте,  куча "0" ничего не напоминает? Мой наметаный взгляд сразу видит, что это идет передача параметров некой функции, возможно вывода на экран, потрейсим по F8, попутно поглядывая консоль.
Когда выполнится строчка:
.text:00271A1B call    sub_27B370
то в консоле появится первая строка банера.

Преположим, что это функция всетаки печатает в консоли, по этому переименуем ее как
Print_String, для этого тыкаем ПКМ на вызове и нажимаем в списке Rename(ну или кнопку N). В открывшимся окне пишем название Print_String.
Если немножко проскролить колесиком в основном окне IDA где находится эта функция или потрейсить дальше по F8, то можно увидеть, что в спагети коде эта функция повторяется и выглядит практически индентично, только вводные данные другие.
Долго и нудно трейсим до места, где внезапно стиль кода меняется, ну или скролим и смотрим глазами и ставим брякпоинт на выводе последней строчки которая идет после даты.
На функции вывода Print_String последней строчки например по адресу 00272016 , дальше будем искать руками ввод и вывод iD и серийника. Терйсим по F8 дальше, поглядывая в коносль в ожидании запроса ввода. Окно HEX View при свободном трейсе лучше всего синхронизировать с eax, так можно например внезапно увидеть чтонить интересное, когда функция будет например возвращать некую ссылку на данные. Делается это ПКМ в окне HEX View->Synchronize With->EAX.
В определенный момент видим, что в стек начинают загружаться некие константы.

.text:00272107 push    49CD5100h
.text:0027210C push    44DD4DC9h
.text:00272111 push    0CF51CCBAh
.text:00272116 push    0AE40DC42h
.text:0027211B push    86h     

Константы в коде, это всегда неспроста, это всегда к шифрованию или к генерации некого ключа,
так как мы iD еще не вводили и генерировать серийник рано, то скорей всего это подгружаются данные для расшифровки.
Выполним Call по адресу 0027212E call    sub_271850 и сразу же видим, что в HEX View 
появилась строка прямым текстом: (EXAMPLE: 1234)
Ага, пускай тогда эта функция будет называться Decrypt, переименуем ее как и функцию которая печатала.
Если поскролить и посмотреть ниже, можно увидеть, что функций Print_String что то не видать, но зато встречается функция Decrypt.
Продолжим трейсить по F8 опять же посматривая в консоль, вдруг, что интересное объявится.
Функция вызываемая с адреса
00272237 call    sub_27B620
напечатает в консоли (EXAMPLE: 1234)
Это означает, что мы либо шиблись с функцией печати, либо надо копать более глубоко, но пока это делать лень, обзовем просто этот вызов Print_Crypt_Str.
Если опять же проскролить по коду, то видно, что начали встречатся не только функции Decrypt но и после них Print_Crypt_Str, а это значит что копаем в нужном направлении.
Продолжим трейсить по F8 ожидая, что скоро мы выйдем на ввод iD. Видим как расшифровалась строка iD: и потом напечаталась, значит вот вот гдето должен быть ввод.
Это наконец случится на вызове по адресу:
002723B6 call    sub_274D30
Побродив по функции считывания iD введеной строки и только поняв, что она вызывает каким то образом функцию fgetc я решил, что она какаято слишком мутная, по этому забил и решил посмотреть, что будет дальше. Конечно не забыв ее обозвать именем Read_iD
Трейсим дальше, пока не вызывается ввод серийного номера по адресу:
001F251E call    001F27D0
Если ввести серийник, то после нажатия на ENTER сразу выведется FAIL.
Сделаем вывод, что ввод, кодирование и проверка серийника производится в этой функции, так что будем смотреть внимательней ее.
Не забываем для удобства обозвать функцию как Ent_Code_Chk.
Сразу после входа в функцию мой наметанный взгляд увидел некие константы и вагон всяких бинарных математических операций, так что я сразу сделал вывод, что похоже на вычисление серийника. Но пойдем по длинному пути новичка, потрейсим и посмотрим, в каком месте идет проверка и где вилка которая указывает что выводить, FAIL или что то другое.
Так что трейсим потихонечку по F8 дальше. Опять же не забывая поглядывать в консоль и в окно HEX View в надежде увидеть, что либо интересное. Функция достаточно длинная и опять в виде макаронины сделанна, так что необходимо терпение. Хотя через небольше время трассировки, можно заметить что в HEX View появились интересные цифры в прямом виде, что то типа 21619. Запишем их, возможно это часть серийника. Через некоторое время эти цифры превращаются в dchmu, тоже запишем на бумажку эту комбинацию, пригодится. Еще через несколько шагов,
на функции
000D28B3 call    sub_D45C0
эти буквы становится большими DCHMU, похоже на преобразование сиволов в заглвные, обзовем эту функцию как Upper_String.
Функции начинают повторятся, следующий похожий участок кода выдает нам новую комбинацию цифр 9322 и комбинацию букв KEDN.
Дальше идут цифры 55030 и GGBOL.
И последняя похожий участок выдает нам последнюю комбинацию: 53767 и GEIRS
Потрейсим дальше, вдруг что интересное увидим:
Если смотреть в HEX View, то видно, что набор букв начинает выстраиватся по порядку, следующим образом: DCHMU-KEDN-GGBOL-GEIRS
Что очень похоже на серийник, в принципе можно тут запустить еще одну версию программы и проверить. Проверка покажет, что серийник для iD 1234 - правильный.
Если же потрейсить дальше, особенно в графическом режиме, то можно увидеть вилку по адресу:

.text:000D2CFE cmp     al, bl
.text:000D2D00 jz      loc
Которая больно длинная, тем самым можно сделать вывод, что это и есть главная вилка, проверим это. Стоя на джампе, изменим флаг ZF, с еденицы на ноль.
Меняем также как обнуляли регистр eax после функции IsDebuggerPresent.
Щелкаем на ZF правой кнопкой мыши и выбираем в низпадающем меню Zero Value.
За место зеленой стрелки замигает красная в другую сторону, нажмем F9 и увидим что в консоли написалось WIN.

Уфф, на этом пока все, в следующем посте будем разбирать алгоритм получения серийника и писать кейген.


Комментариев нет:

Отправить комментарий