Сайт производителя - http://www.southbaypc.com/
Искал чем бы почистить комп от разных ненужных файлов, и наткнулся на эту прогу. После сканирования и удаления файлов, мой хард обогатился лишними 400мб места. Собственно решил я прожку оставить, но вот беда, программа Shareware и просит денежку, а без денежки будет работать только 30дней.
Но нам ведь это не помеха?
Что нам потребуется:PEiD - анализатор файлов
OllyDbg - отдладчик
И все.
Анализ
Запускаем PEiD, открываем в нем SuperCleaner.exe и с радостью видим что программа ничем не запакованна. Что очень облегчает нам задачу.
При запуске программы, она выкидывает нам наг-скрин с указанием сколько осталось до окончания триала, предложение купить сию замечательную программу, ну и кнопочкой "Enter Registration". Приступим к насилию. Запускаем OllyDbg и грузим в него нашу программу.
Нажимаем F9, чтобы запустить программу на исполнение, и ждем пока загрузится. Как загрузилась, тыркаем в кнопочку "Enter Registration", вводим данные от балды, и жмем "OK". Для начала нам нужно выяснить, как программа реагирует на неверные данные. Нам повезло, программа выдает MessageBox о невернно введенных данных.
Как видим, тут подряд стоят два вызова функции GetDlgItemTextA. УРА! Теперь мы знаем где копать.
Трасируем по F8 до строчки004250EB . E8 70DCFFFF CALL SuperCle.00422D60
Скажу сразу, заходить в эту функцию не стоит, она не несет никакой смысловой нагрузки, но очень большая и вообще не понятно зачем она там. Поэтому проходим это место дальше(F8) до строчек
Тут мы видим введенные нами данные, и теперь мы уж точно близко к цели. Заходим(F7) в функцию
004250FF . E8 9C050000 CALL SuperCle.004256A0
Там трассируем(F8) до строчки
004256F9 |. E8 B2000000 CALL SuperCle.004257B0
И ее тоже пропускаем по F8 Дело в том, что после выполнения функции SuperCle.004257B0, в ebx находится правильный серийник, который программа генерировала сама.
Теперь его можно скопировать, и ввести в программу. Регистрация пройдет успешно, и вы станете счастливым обладателем полной версии этой программы.
Но зачем останавливаться на достигнутом? Почему бы не написать кейген, чтобы мы могли в любое время регистрировать программу на любые данные? Поставьте бряк(F2) на строчке
004256F9 |. E8 B2000000 CALL SuperCle.004257B0
и перезапустите(Ctrl+F2) процесс. Заново дойдите до это функции, но теперь не обходите ее, а зайдите в нее по (F7) Теперь мы находимся внутри функции, которая отвечает за генерацию верного серийника, по введенному нами имени(Name).
Сначала вычисляется длинна имени
004257DF |. 57 PUSH EDI ; /String 004257E0 |. FF15 A8D24300 CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>>; \lstrlenA
И если она >0, то начинается процесс генерации
Серийник состоит из четырех частей, и каждая часть генерируется отдельно. Я опишу только первый цикл, так как остальные в принципе идентичны, за исключением константы и умножения\сложения. Сам алгоритм генерации я опишу ниже.
Первая часть004257F0 |. 8B15 8C654400 MOV EDX,DWORD PTR DS:[44658C] ;В edx заносится константа 0x26(смотрим в окне регистров) 004257F6 |> 0FBE2C07 /MOVSX EBP,BYTE PTR DS:[EDI+EAX] ;В ebp заносится символ из имени 004257FA |. 03EA |ADD EBP,EDX ;складываем ebp+edx 004257FC |. 83C0 01 |ADD EAX,1 ;увеличиваем счетчик цикла 004257FF |. 03CD |ADD ECX,EBP ;складываем ecx+ebp(в ecx как раз и будет первая часть серийника 00425801 |. 3BC6 |CMP EAX,ESI ;дошло до конца имени? 00425803 |.^7C F1 \JL SHORT SuperCle.004257F6 ;Да? Тогда идем дальше, иначе прыгаем к началу 00425805 |> 51 PUSH ECX ; /<%ld> 00425806 |. 68 D01A4400 PUSH SuperCle.00441AD0 ; |Format = "%ld-" 0042580B |. 53 PUSH EBX ; |s 0042580C |. FF15 8CD44300 CALL DWORD PTR DS:[<&USER32.wsprintfA>] ; \wsprintfA ;Не ленимся, и читаем в MSDN описание этой функции
Вот в принципе и весь алгоритм. Теперь по пунктам
: -Имеется 4 константы:0x26, 0x34, 0x0C, 0x0E(все это видим в окне регистров в отладчике) Соответственно каждая константа для своей части серийника.
-Берем первую константу, складываем с очередным символом имени и все что получили прибавляем в еще одной переменной. Это что касается первой и третей части серийника. Во второй и четвертой частях, вместо сложения add ebp, edx, используется умножение imul ebp, edx. Все остальное в точности как и раньше.
Надеюсь тут все понятно. Кто не понял, вернитесь к отладчику, и просмотрите каждую строчку шаг за шагом.
Теперь настало время писать сам кейген. Так как я ленивый, то я выбрал самый простой вариант VC++ 6.0 MFC.
-Создаем новый проект MFC.-Говорим студии что мы ходим Dialog Based проект
-На форму кидаем два EditBox'a, удаляем кнопку Cancel и оставляем кнопку ОК.
-Два раза кликаем на кнопке ОК, у нас создастся обработчик этой кнопки, и после строчек
-Удаляем все что там напихала нам студия, и пишем свой код
Как говорится, простите меня за мой французский, но не люблю я Венгерскую Нотацию, так что переменные называл как больше нравится.
char cName[40], cTemp[20]; //Объявляем переменные char cLetter = 0, cBigCode[25]; //---------------/------------ DWORD cLittleCode = 0; //---------------/------------ int i = 0, ii = 0; //---------------/------------ GetDlgItemText(IDC_EDIT1, cName, sizeof(cName)); //Получаем Имя из первого EditBox'a /* Вот тут вот мне стало лень делать отдельные функции, хотя это было бы намного рациональней */ _asm { xor ecx, ecx //Сказать честно, я часа 3 просидел, пытая написать все это на СИ, xor edx, edx //Но моё терпение лопнуло, и я таки написал это на асме xor ebx, ebx //По большому счету - это всего лишь скопированый кусок из програмы SuperCleaner xor eax, eax //Чистим регистры lea eax, cName //Адрес cName в eax mov edx, 0x00000026 //АхнтунГ! Первая константа _circ: cmp byte ptr [eax], 00 //Закончились символы в имени? jz _home //Янки, гоу хоум mov bl, byte ptr [eax] //Ах вы не Янки? Ну тогда добро пожаловать inc eax //Увиличиваем указатель add ebx, edx //Вот ОНО! Складываем add ecx, ebx //И опять складываем jmp _circ //И все заного _home: mov dword ptr [cLittleCode], ecx //Копируем все это дело в нашу переменную } wsprintf(cBigCode, "%ld-", cLittleCode); //И форматим под наш серийник _asm { xor ecx, ecx //И опять все заного xor edx, edx //------------//-------------- xor ebx, ebx //------------//-------------- xor eax, eax //------------//-------------- lea eax, cName mov edx, 0x00000034 //Опять константа _circ1: cmp byte ptr [eax], 00 jz _home1 xor ebx, ebx //Необходимо, так как умножение оно такое умножение.... mov bl, byte ptr [eax] inc eax imul ebx, edx //А вот и умножение add ecx, ebx jmp _circ1 _home1: mov dword ptr [cLittleCode], ecx } memset(cTemp, 0, sizeof(cTemp)); //Чистим буфер wsprintf(cTemp, "%ld-", cLittleCode); //И опять форматим strcat(cBigCode, cTemp); //А теперь объединяем строки _asm { xor ecx, ecx //Уже даже не смешно.... xor edx, edx xor ebx, ebx xor eax, eax lea eax, cName mov edx, 0x0000000C _circ2: cmp byte ptr [eax], 00 jz _home2 mov bl, byte ptr [eax] inc eax add ebx, edx add ecx, ebx jmp _circ2 _home2: mov dword ptr [cLittleCode], ecx } memset(cTemp, 0, sizeof(cTemp)); wsprintf(cTemp, "%ld-", cLittleCode); strcat(cBigCode, cTemp); _asm { xor ecx, ecx xor edx, edx xor ebx, ebx xor eax, eax lea eax, cName mov edx, 0x0000000E _circ3: cmp byte ptr [eax], 00 jz _home3 xor ebx, ebx mov bl, byte ptr [eax] inc eax imul ebx, edx add ecx, ebx jmp _circ3 _home3: mov dword ptr [cLittleCode], ecx } memset(cTemp, 0, sizeof(cTemp)); wsprintf(cTemp, "%ld", cLittleCode); strcat(cBigCode, cTemp); SetDlgItemText(IDC_EDIT2, cBigCode); //Вуаля, во-втором EditBox'e у нас сгенереный серийник
Вот впрниципе и все. К статье прилагается архив, в котором лежат сорцы и готовый exe'шник кейгена
PS: Прошу не заывать, что все что вы прочитали, всего лишь бред сумашедшегО, и никоим образом не может быть использованно в качестве доказательства в суде
© s0 21.12.2009
Вот до кучи, я думаю так красивее, правда оно кансольное ))))
#include <iostream>
#include <string>
int sum(const std::string& name, int k)
{
int res = k * name.length();
for (int i = 0; i < name.length(); ++i)
res += name[i] ;
return res;
}
int mul(const std::string& name, int k)
{
int res = 0;
for (int i = 0; i < name.length(); ++i)
res += k * name[i];
return res;
}
int main(void)
{
std::string name;
std::cout << "You name: ";
std::cin >> name;
std::cout << "key: " << sum(name, 0x26) << '-' << mul(name, 0x34) << '-';
std::cout << sum(name, 0xc) << '-' << mul(name, 0xe) << std::endl;
system("pause");
return 0;
}
Thanks

Гы, статья конешно для новичков. Но зря ты не стал рассказывать про то, что это за функция:
Я сильно не вдавался в подробности, так минут пять глянул, там короче есть массив "заголовков" окон, которые я так понел используются в кряках, вот часть из них:
Так вот этот код:
004250EB . E8 70DCFFFF CALL SuperCle.00422D60 // вызов "ненужной" функции 004250F0 . 85C0 TEST EAX,EAX //проверка результата
004250F2 . 75 58 JNZ SHORT SuperCle.0042514C //прыжок если не нулевой
Ну а в самой функции используетца простой пробег по массиву "плохих" заголовков, с использованием FindWindow. Ну и получается, что если найден один из таких окон, то ключ даже считаться не будет и проверятца, прога нас сразу пашлёт :)
Так что смотрите, чтобы не было запущено подобных окон :))) P.S. если уж хочетца что-то такое запустить, то можно, например, тот
Ну и не понятно, в чём сложность на писать на С.