В этой статье мы рассмотрим решение CrackMe by Fereter #2. Скачать его можно здесь.
Не беря во внимание названия файла, фактически - это КейГенМи.
Уровень: Для начинающих.
Используемые инструменты:IDA Pro как дизассемблер и деббагер.х64dbgплагин Labeless(импортирует метки и имена с IDA Pro в х64dbg)Задача: найти и разобрать алгоритм валидации пары Name : Serial key и написать Генератор Ключей.
Посмотрим на сам КрекМи:
Базовый статистический анализ с помощью PEview и Peid ничего интересного не показал.
Загружаем в IDA Pro и начнем углубленный статический анализ и динамический анализ.
1. Анализируем строки
В окне программы мы видим следующие строки:
«CrackMe by Fereter #2», «Name», «Serial key», «Unregistered», «Check», «Exit»
В IDA Pro в закладке String window(View>Open subviews>Strings) видим только:
«Made in Russia»
Можно сделать предположение, что программа содержит строки GUI-окна в зашифрованном виде - в процессе работы их расшифровывает.
Если начать деббажить та увидим, что перед началом каждого WinApi вызова, который берет на вход строку как один из аргументов, вызывается sub_402590 , а после выполнения WinApi_вызова, вызывается sub_40257E, ниже на рисунке пример
Проанализируем, что это за функции.
Это функции XOR расшифровывания и зашифровывания строк, в который в качестве ключа используется байтовое значение C6h, а указатель на строку передается в функции через регистр EAX
Переименовываем эти функции соответственно как XorDecode и XorEncode.
Дальше-больше, локализуем и расшифруем строки которые использует XorDecode.
Сначала смотрим адреса, где XorDecode вызывается (IDA Pro > переходим на листинг XorDecode > Правый клик мыши > List crossreferences to...)
Смотрим адреса которые XorDecode использует в качестве аргумента передаваемого через регистр EAX - все они находятся в диапазоне 00401000 - 00401073
Посмотрим на них в HEX-редакторе
Можно наблюдать, что очень часто встречается байт C6h, который равен нашему ключу шифрования с функций XorDecode и XorEncode что говорит о том что перед нами зашифрованы массив ноль-терминированных(NULL-terminated) строк, так как C6h XOR 00h = C6h.
Теперь расшифровываем, чтобы посмотреть содержимое этого массива и сформировать массивы и присвоить им имена в IDA Pro. Для этого используем XOR idc script отсюда:
В итоге было:
То что мы сделали после расшифровки:
Теперь снова зашифруем (ведь наша цель была сформировать структуры данных(строки) и присвоить им имена отражающие их содержимое, а не изменять бинарный файл)
В итоге видим полный набор строк которые использует программа.
2. Анализируем функции
В начале недалеко от start видим RegisterClassA вызов, который берет на вход структуру WNDCLASS
Рассмотрим эту структуру:
Видим offset sub_4021B7 - это WNDPROC lpfnWndProc (см.MSDN) обработчик сообщений посылаемых окну
Благополучно переименовываем sub_4021B7 на fnWndProc.
Из оставшихся функции с наскока можно понять, что делает sub_40254A
Подсчитывает 8-битную контрольную суму строки, указатель на которую содержится в регистре EBX, и результат сохраняет в регистре DL. Переименовываем sub_40254A на ByteControlSumm.
А также sub_4025A2
Переименовываем sub_4025A2 на ClearEAX.
В итоги имеем следующее окно функций
3. Анализируем поток программы
функция start как и в любой другой GUI-программе содержит Message Loop, в который вставлены некоторые средства анти-отладки:
В 1-ом и 3-ем случае сканирование в цикле кода на наличие INT3(0xCC), во 2-ом на наличие NOP(0x90). Думаю элегантным решением будет заменить на NOP-ы инструкции условного переход jz (short)end_program
В итоге получаем:
Есть одно средство анти-деббагина, которое обходиться любым плагином типа IDAStealth, ScyllaHide
Ура, товарищи!, мы обошли средства анти-деббагинга.
4. Анализируем нашу функцию-обработчик сообщений(ивентов) fnWndProc
Первая проверка сравнивает аргумент MSG с единицей(1==WM_CREATE). Далее ветвление направо на блок создания GUI окна программы. Все что нам нужно с этого блока - это идентификатор кнопки «CHECK». А вот и он
Ветвление на лево ведет нас к блоку, где сравнивается аргумент MSG с 111h (111h==WM_COMMAND) - это то, куда мы двигаемся далее loc_4023F2
Вот тут и начинается обработка нашего нажатия на кнопку «Check»
5. Анализируем нашу обработку нажатия на кнопку «Check»
анализируем sub_402557 видим, что фактически это обертка функции SendMessageA. Переименовываем ее как SendMessageAWrapper.
Видим как извлекается ввод полей Name, Serial key.
И, внимание, проверка № 1 сравнивается длина ввода поля Serial key с числом 11.
Если не равно, то осуществляется прыжок на loc_40253А(это епилог(окончание) функции fnWndProc)
Далее идут проверки на соответствие введенного имени и серийного ключа. Рассмотрим их.
проверка № 2 - см. псевдокод на рисунке выше
проверка № 3 - см. псевдокод на рисунке выше
Следующий блок:
проверка № 4 - см. псевдокод на рисунке выше
проверка № 5 - см. псевдокод на рисунке выше
Теперь рассмотрим sub_402541 что это за зверь такой?
Это зверь в середине себя содержит две проверки.
uint8_t serialKeyControlSumm = ByteControlSumm(serialKey); # см. предпоследний рисунок
проверка № 6: # см. последний рисунок
if ( serialKeyFieldInput[6] == serialKeyControlSumm/9%9 + 30h)
проверка № 7: # см. последний рисунок
if ( serialKeyFieldInput[5] == serialKeyControlSumm%9 + 30h)
И вот финал.
Алгоритм валидации пары Name : Serial key на псевдокоде выглядит так:
var name;
var serialKey;
var nameControlSumm;
var serialKeyControlSumm;
GetInput(name);
GetInput(serialKey);
nameControlSumm = ByteControlSumm(Name);
serialKeyControlSumm = ByteControlSumm(serialKey);
if( serialKey.length == 11&& serialKey[0] == NameControlSumm%9 + 30h&& serialKey[1] == NameControlSumm/9%9 + 30h&& serialKey[3] == '-'&& serialKey[7] == '-'&& serialKey[5] == serialKeyControlSumm%9 + 30h&& serialKey[6] == serialKeyControlSumm/9%9 + 30h) {Registered();} else {Unregistered();}
P.S.: Исходники самого КейГен(уже готов) - приведу в красивый вид и выложу в ближайшее время
P.P.S.: Комментарии, замечания, вопросы, предложения приветствуются,
возможно, вы бы делали что-то иначе - в общем прошу комментировать



























Reply With Quote
Thanks