Форум   Статьи   Новости   Файлы   Bugtraq   Сниффер   Друзья   О Клубе

Последнее на Форуме

Пожертвования

Liberty Reserve: U9999024
Кошельки WebMoney:
Z583322939655
E121331800314
R274644017049
U349709454906


YM: 410011220120073

Пожертвовать

Контакты

Связь с Администрацией

hpcteam1[@]gmail.com

Статьи rss

[ Добавить Статью на сайт ]

Статьи / Безопасность / Реверсинг

Splish. Тройная задача

Привет всем! Сегодня я бы хотел написать свою статью для начинающих реверсеров. Сам я не профессионал в этой сфере, но активно интересуюсь исследованием программ и вам того же советую. На мой взгляд реверсинг - это очень интересная тема. Когда ты чувствуешь, что понимаешь как работает программа, когда ты подбираешь правильный серийник, когда пишешь кейген ты испытываешь невообразимое чувство, которое в народе называется фаном, ради которого, в общем то, я и занялся этим делом. Но стоит всегда помнить про УК РФ и т.п. и т.д. Мы, пожалуй, ничего нарушать не будем, а подойдем к делу с научной точки зрения. Т.е. займемся исследованием.

Мы не будем сегодня ломать супер-мега защиту, а ограничимся довольно известным и простым крекмисом Splish. Итак, перед нами стоит тройная задача (по словам автора):

1. Убрать сплеш – скрин
2. Достать жестко заданный серийник
3. Написать кейген или достать сериник для 2й части защиты

Ну что ж, приступим. Для успешного понимания моей статьи вам потребуется базовое знание команд ассемблера, Олли (Olly Debugger) и немножко желания. Если вы обладаете всеми этими приспособлениями и скилами, то читаем дальше. Если у вас нету OllyDebugger с плагином CommandBar, то найти это+инструкцию по установке можно тут.

Задача №1. Сплеш скрин


Ну что ж, займемся сплеш скрином? Если вы были достаточно внимательны, то могли заметить это желтенькое окошко, появляющееся при запуске крекми. Итак, загрузим крекмис в олю. Первым делом открываем список импортированных функций. Если вы не умеете этого делать, то вызывается этот список нажатием ctrl+N или пкм на окне дизассемблера -> Search for ->Name (label) in current module. (На будущее будьте внимательны при использовании этой функции, смотрите в каком треде вы находитесь. Если вы в главном треде, то Оля все сделает правильно, но если вы вдруг попали в стороннюю библиотеку, то получите список всех функций, импортируемых библиотекой. Так что следите за заголовком Оли, там написан текущий поток, в котором происходит отладка.)

Перед нами открывается список всех импортируемых программой функций. Списочек небольшой – что уже радует. В списке строк мне бросается в глаза функция CreateWindowExA... По-моему она отвечает за вызов сплеш-скрина.

Ну что ж, проверим. Находим команд бар в оле и пишем в нем bpx CreateWindowExA, при этом соблюдаем регистр – это очень важный аспект, ведь, например, CReateWindowExA – это уже отличная от нужной нам функции.

Нажимаем f9 и останавливаемся перед вызовом этой процедуры. В стеке видны параметры функции, среди которых выдны позиция окна, его стиль и т.п... Нас интересеут имя этого окошка – Splish Splash. Значит мы не ошиблись, создается то самое нужное нам окно. Теперь, чтобы сильно не уродовать бедный крекмис нопами(nop - пустая инструкция. Т.е. ничего не значит, но место занимает), перед вызовом этой функции поставим безусловный переход на следующую инструкцию для того, чтобы эта функция просто не вызывалась. А нет функции – прощай сплеш скрин. В моем случае я ставлю jmp 401539



Ну вот и все. Сохраняем измененный крекми без сплеш скрина. Это делается так:
На окне дизассемблера нажимает пкм -> Copy to executable -> All modifications
В открывшемся окошке жмем Copy, потом нам открывается окно, в котором жмем пкм ->Save file и выбираем как его сохранить.

Теперь запускаем пропатченный сплиш, и, вауля, сплеш-скрин исчез. Поздравляю!

Задание №2. Найдем жестко заданный серийник


Итак, для начала запустим пропатченный крекмис в Оле. Перво–наперво нам нужно посмотреть список импортируемых программой API функций. Это мы уже делали, и вы это умеете. Если забыли, читаем сначала. Просматриваем этот список, и вдруг видим функцию GetWindowTextA . Эта функция используется для получения текста из компонентов окна. Имеет она 3 параметра. Первый – хендл контрола, второй буфер, куда сохранится текст, и третий – сколько максимум символов сохраняется в буфер. Подробнее можете посмотреть в гугле или на msdn справочку почитать.

Ставим на неё точку останова. Делается это путем нажатия пкм на имени нужной функции -> Toggle breakpoint on import. Ну вот, все приготовления сделаны, запускаем крекмис. Вводим любой пароль в поле HardCoded и жмем окей. Я в качестве пароля выбрал Yougan. Ну так вот. Если вы все сделали правильно, то сработала точка останова на вызове функции GetWindowTextA. Идем в стек (правый нижний угол оли) и видим, с какими параметрами вызывается функция:



Нас интересует параметр Buffer – он, если вы помните, указывает нам адрес буфера, который будет записан наш пароль. Теперь нажимаем на Buffer пкм и выбираем пункт Follow in Dump, тем самым мы переходим в область памяти, на которую указывает буфер:



Тут, как видите, пока пусто. Нажимаем ctrl+f9 или Debug -> Execute till return. Этим действием мы выполняем функцию до ret’a. Посмотрим теперь в дамп по адресу буфера. Ага, пароль записался успешно:



Теперь выделяем мышой наш пароль в окошке дампа и ставим на него bpx Memory on access. Делается это так:



Теперь сработает точка останова, когда программа обратится к нашему паролю, хранящемуся в памяти, чтобы сверить его с серийным кодом. Со спокойной душой ждем f9 и останавливаемся прямехонько на записи первого байта нашего серийника в регистр DL. С чего бы это так? Присмотритесь к коду внимательно. Если вы хоть немного знаете асм, то у вас проблем не будет с разбором данного кода. Ну а для остальных я комментарии вставил. Если посмотреть выше, то видим, что в регистр CL переносится первый байт некоей строки HardCoded. Вы уже наверное догадались, какой тут жестко заданный серийник? Но все-таки смотрим код дальше, наша задача понять как это работает.

0040137B  |> /8038 00       /CMP BYTE PTR DS:[EAX],0  ; Смотрим, не закончилась ли проверка
0040137E  |. |74 0C         |JE SHORT Splish.0040138C ; Если закончилась, то переходим к окошку с ;сообщением об успехе
00401380  |. |8A08          |MOV CL,BYTE PTR DS:[EAX]; Перемещаем очередной байт правильного ;серийника в регистр CL
00401382  |. |8A13          |MOV DL,BYTE PTR DS:[EBX]; Перемещаем очередной байт  нашего  ;серийника в регистр DL
00401384  |. |38D1          |CMP CL,DL ; Сравниваем эти байты
00401386  |. |75 4A         |JNZ SHORT Splish.004013D2; Если они не сходятся, переходим к ;сообщению что сегодня не ваш день
00401388  |. |40            |INC EAX ;Увеличиваем счетчик
00401389  |. |43            |INC EBX; Увеличиваем счетчик
0040138A  |.^\EB EF         \JMP SHORT Splish.0040137B ; Безусловный переход к началу цикла

Тут у нас, как видите, организован цикл. На каждом этапе которого в CL и DL заносятся очередные байты серийника и нашего пароля соответственно, а потом сравниваются. Но не надо торопиться нажимать f9 чтобы скорее ввести дрожащей рукой слово HardCoded в заветный edit! Давайте немножко оттрассируем программку при помощи клавиши f7. Дойдите до условного перехода jnz 004013D2. Итак, сразу после выполнения инструкции cmp CL,DL изменяем значение флага нуля Z на единицу для того, чтобы jnz не выкинул нас из цикла проверки (для этого щелкаем мышой тут, как на картинке)



(Ну можно конечно занопить jnz…В общем как желаете)
Повторяем эту процедуру до окончания цикла. Нажимаем f9, и, вуаля, появляется заветное окошко. И со второй частью задания мы справились успешно.

Задание №3. Разберем алгоритм генерации серийника и напишем кейген


Теперь переходим к самой сложной и интересной части. Рассмотрим части защиты крекмиса, где пароль зависит от введенного имени динамически. Итак, откроем пропатченный крекми в оле. У нас должен стоять bpx на функцию GetWindowTextA. Если он у вас исчез, или вы забыли про него – то поставьте. Теперь можете жать f9. Вводим имя и наш серийный номер сюда:



И нажимаем Name\Serial Check. Я ввел имя Alex, а пароль Foerd. Останавливаемся на вызове функции GetWindowTextA. Идем в стек, смотрим параметры. Нас, как и в прошлом задании, интересует лишь буфер. Вот и переходим по адресу буфера в дамп. Тут ещё пока пусто. Выполняем функцию до ret’a(ctrl+f9). Смотри в дамп по адресу буфера. Ага, имя записалось! Теперь ставим на имя bpx Memory on Access, точно так же, как и в прошлом примере. Ещё раз останавливаемся на процедуре получения нашего серийного номера. Делаем то же с ним (смотрим куда сохраняется+ставим на него bpx on memory access) и нажимаем f9. Теперь мы прямехонько на перемещении первого байта имени в регистр EAX. А ниже идем и сама процедура обработки имени. Её я подробно прокомментировал.

00401632  |> /0FBE041E      /MOVSX EAX,BYTE PTR DS:[ESI+EBX]; Берем очередной байт имени и ;кладем его в EAX
00401636  |. |99            |CDQ;Эта инструкция расширяет регистр EAX
00401637  |. |F7F9          |IDIV ECX;Происходит целочисленное деление EAX на EAX(в нем лежит ;значение 0A), при котором результат деления сохраняется в EAX, а остаток от деления идет в EDX,
00401639  |. |33D3          |XOR EDX,EBX; Происходит логическая обработка значения в EDX на ;значение в EBX(кто ничего не знает про хор – в гугл), причем результат сохраняется в EDX
0040163B  |. |83C2 02       |ADD EDX,2; К EDX прибавляется 2
0040163E  |. |80FA 0A       |CMP DL,0A; Происходит сравнение последних байт регистра EDX с 10(0A)
00401641  |. |7C 03         |JL SHORT Splish.00401646; И если значение больше 10(0A)
00401643  |. |80EA 0A       |SUB DL,0A;То от него вычитается 10(0A)
00401646  |> |88141F        |MOV BYTE PTR DS:[EDI+EBX],DL;тут полученное сохраняется по адресу ;EDI+EBX(проходим по этому адресу)
00401649  |. |43            |INC EBX; Счетчик увеличивается
0040164A  |. |3B1D 63344000 |CMP EBX,DWORD PTR DS:[403463] ; Происходит проверка на конец ;строки(индекс текущего элемента сравнивается с длиной стоки)
00401650  |.^\75 E0         \JNZ SHORT Splish.00401632; Если строка не окончена, то процедура ;продолжается

Как только процедуру вы оттрассируете, то запишите на листочек значения, которые она записала, они вам вскоре понадобятся. Получается что берется каждый символ имени и его целочисленно делят на A, остаток от деления ксорится(xor) на его порядковый номер, к нему прибавляется 2, сравнивается с 10, если он меньше, то сохраняется, если больше, то вычитается из него 10, и он сохраняется.
Теперь оттрассировав эту процедуру нажимаем f9 и останавливаемся на перемещении первого байта нашего серийника в регистр EAX. С серийником происходит похожая операция:

00401669  |> /0FBE041E      /MOVSX EAX,BYTE PTR DS:[ESI+EBX];Перемещается очередной байт серийника в EAX
0040166D  |. |99            |CDQ ;Происходит расширение регистра
0040166E  |. |F7F9          |IDIV ECX ;Происходит целочисленное деление EAX на A с сохранением остатка в EDX
00401670  |. |88141F        |MOV BYTE PTR DS:[EDI+EBX],DL ;Этот остаток сохраняется в область ;памяти по адресу EDI+EBX
00401673  |. |43            |INC EBX ;Увеличивается счетчик
00401674  |. |3B1D 67344000 |CMP EBX,DWORD PTR DS:[403467] ;Происходит сравнение индекса с длиной
0040167A  |.^\75 ED         \JNZ SHORT Splish.00401669 ;И если индекс равен длине, то цикл обрывается, иначе продолжается

Как только процедуру вы оттрассируете, как и прошлый раз, запишите на листочек значения, которые она записала.

Итак, эта процедура перебирает поочередно каждый символ серийника, его код целочисленно делится на 0A(10) и сохраняется. Обязательно посмотрите куда он сохраняется. Ну что ж, после этой процедуры нажимаем f7, переходим по jmp и попадаем на сравнение деятельности этих двух процедур, т.е. то что записала первая (из вашего имени) сравнивается с тем, что записала вторая (из пароля), и, если они равны, вы переходите на месадж бокс. Тут перед нами стоит самая интересная часть моего повествования – мы должны найти серийник. Если прикинуть логически, то месадж бокс выпадет, если значения от обоих процедур будут одинаковые, значит мы должны их сделать равными! Рассмотрим процедуру обработки введенного нами серийника с обратной стороны. Так будет легче. Сделайте это сами.

Итак. В моем случае f(имени)= 7115,а В(серийника)= 1140. На скрине красным обведен серийник, черным – пароль. Если слегка подумать, то в шестнадцатеричной системе 32 idiv A=0 (остаток от деления 32 на A), значит 33 idiv A=1 и так далее. Если не верите – берете ручку и бумагу и вперед проверять. Так до 3B idiv A=9. Так как в нашем случае f(имени)=7155, то коды символов серийника должны быть 39 33 37 37, что в ASCII системе кодирования будет как 9377. Пробуем! Ага, получилось. Ещё одна победа.

Думаю, что написать серийник для вас проблемы не составит. Алгоритм простой - нужно просто сделать такую же функцию обработки вводимого имени, как в крекми, а потом к полученным значениям от этой функции прибалять 50. Вот то, что я написал за пару минут на с++. Думаю смысл понятен.

Если нет - то читайте дальше. Сначала мы с каждым символом нашего имени делаем следующие операции:

1. Получаем остаток от деления его кода на 10 (В шестнадцатеричной системе = 0A)
2. Ксорим полученное значение на его порядковый номер
3. Прибавляем 2
4. Если полученное больше 10, то вычитаем 10
5. Сохраняем значение в массив

Потом чтобы подобрать по этому "ключу" пароль, нам надо просто получить код символа. Это делается путем прибавления четного 10 числа к очередному значению массива с "ключем"

#include <iostream>

using namespace std;



int main()
{
        cout <<"**************Splish************\n";
         again:
    
char _name[20];
char _nameP[20];
    int k=0;
    int _xa[20];
    
   
    cout << "Insert your name:\n";
    cin >> _name;

    
    
    /****Генерируем остатки***/
    for (int i=0;_name[i];i++)
    {
        int xa;  //Переменная для хранения остатка
        xa=_name[i]%10; // Получаем остаток
        xa=xa xor i; //Ксорим его на порядковый номер
        xa+=2; //Добавляем 2
        if (xa>=10) xa-=10;  //Делаем егоменьше 10, если это надо
        _xa[i]=xa;    //Сохраняем полученное в массив
        k++;
    }
    /*Подбираем по эти остаткам пароль*/
    cout << "Your Password\n";
    for (int i=0;i!=k;i++)
    {
        _nameP[i]=_xa[i]+10*5;//Достаем серийник. Для этого нужно лишь к каждому элементу массива _xa добавить //кратное 10 число
        cout << _nameP[i];
    }    
    char ch;
    cout << "\nAgain? Y/N\n";
    cin >> ch;
    
    
    switch(ch) 
  {
    case 'Y':goto again;
    break;
    case 'N':break;
//    default: exit;
}
    
    
    
    /*********End*************/
}

Ну, думаю, что с кодом на с++ проблем не возникнет - это лишь алгоритмическая реализация описанного мною выше алгоритма подбора пароля. Если же проблемы все-таки возникли, то я прокомментировал код.

Заключение


Ну что ж, сегодня мы с вами справились с очередным крекми. Пусть эта победа станет не последней на вашем пути к вершинам крекерства. Надеюсь, что я в своей статье доступно объяснил сам процесс работы с крекми. И ещё. У Риккардо Нарвахи есть отличный сборник статей по реверсингу. Советую вам его почитать
Желаю вам успехов во всех ваших начинаниях.
Обсуждение статьи тут

Автор: Yougan

Материал добавил Yougan


Комментарии(1)

Дата: 2011-12-15 14:26:52

Добавить Комментарий к Материалу

Вы должны быть авторизованы на форуме чтобы добавлять комментарии. Регистрация Уже авторизованы?

Комментарии к Материалу

 Wan              2012-01-20 18:48:06

Yougan, спасибки за статью. С нахождением жестких серийников и пропатчиванием серийников, привязанных к имени, у меня немного получается. Но вот с нахождением сгенерированного серийника чего то до конца не могу вникнуть...
Вот у тебя после прохождения процедуры проверки введенного тобой имени- Alex, на адресе EDI+EBX сгенерировалось HEX-число 7155. До этого я дошел. но как исходя из этого числа, вывести коды символов серийника-39 33 37 37, я не понял. Обьясни пожалста еще более подробней, ну для совсем чайников-пречайников Улыбающийся
( Кстати у тебя вот в этих строках-"Итак. В моем случае f(имени)= 7115,а В(серийника)= 1140." вроде еще одна опечатка, кроме той про которую я в обсуждении написал. Имя то 7155 ).

Последнее на Сайте

Новости

Статьи

Bugtraq

Файлы

Copyright © 2008 - 2017 «HPC». При копировании материалов ставьте ссылку на источник.
Все материалы представлены только в ознакомительных целях, администрация за их использование ответственности не несет.
Пользовательское соглашение Реклама на сайте