[CRACKING] [MISC.] [DOWNLOADS] [LINKS] [CONTACTS]

Взлом и защита программного обеспечения

 

Генератор ключей для Effects Processor Pro 2.2

   Effects Processor Pro 2.2 - довольно качественный процессор звуковых эффектов, работающий в режиме реального времени. Отличный инструмент для реализации Вашего творческого потенциала. Одно из преимуществ этой программы заключается в том, что настройка любого эффекта осуществляется с помощью  шести ручек управления, что не даст вам запутаться. А их  программа реализует не так уж мало: флэнджер, хорус, тремоло, фэйзер, задержка. Все они могут функционировать  одновременно. Effects Processor Pro работает с любым входным  сигналом, будь то сигнал аudio CD или сигнал синтезатора.  Предусмотрены также операции с .wav-файлами - обработка их  различными эффектами. Скачать программу можно здесь (400 КБ).

   Программа эта не бесплатная, и чтобы воспользоваться всеми ее возможностями, придется приобрести лицензию. Защита  представляет собой обычную связку имя-код. При каждом запуске незарегистрированной копии появляется небольшое окно с тремя кнопками, одна из которых называется "Registration"; при нажатии на эту кнопку открывается форма для ввода имени пользователя и серийного номера. Попробуем ввести что-нирбудь в эти поля и отследить процедуру проверки рег. данных в отладчике (я использовал OllyDbg).

   Загрузим программу в отладчик и запустим ее оттуда (F9). Отправимся регистрироваться. Чтобы прервать выполнение программы при считывании серийного номера из поля ввода я установил точку останова на вызов функции LoadStringA. У Вас может возникнуть вопрос, почему именно на эту функцию. Просто я открыл окно "Names" со списком импортируемых функций (Ctrl+N) и выбрал ее как наиболее вероятную. (Для справки: установка брейкпоинта в OllyDbg на функцию из списка, отображаемого в окне производится щелчком правой кнопки на имени функции и выбором в контекстном меню пункта "Set Breakpoint on every reference"). Итак, мы прервались здесь:

00406FBA  CALL DWORD PTR DS:[<&USER32.LoadStringA>>; \LoadStringA
00406FC0  MOV EBX,DWORD PTR SS:[ESP+124];помещаем в EBX имя пользователя
00406FC7  MOV ESI,DWORD PTR DS:[<&KERNEL32.lstrcpy>;  kernel32.lstrcpyA
00406FCD  LEA EDX,DWORD PTR SS:[ESP+1C]
00406FD1  PUSH EBX                      ;имя кладем в стек        
00406FD2  PUSH EDX              
00406FD3  CALL ESI                              
00406FD5  LEA EAX,DWORD PTR SS:[ESP+1C]
00406FD9  LEA ECX,DWORD PTR SS:[ESP+10] ;помещаем в ECX строку J8FG3
00406FDD  PUSH EAX
00406FDE  PUSH ECX
00406FDF  CALL epp22.0040100A           ;на основе имя и J8FG3 генерируем промежуточный код (в EDX) 
00406FE4  PUSH 29                       ;в стек число 29h (ASCII=")")
00406FE6  PUSH 41                       ;в стек число 41h (ASCII="A")
00406FE8  LEA EDX,DWORD PTR SS:[ESP+2C]
00406FEC  PUSH 28                       ;в стек число 28h (ASCII="(")
00406FEE  PUSH EDX
00406FEF  CALL epp22.004010AF           ;вычисляем окончательный рег. номер (будет в ЕАХ)
00406FF4  MOV EBP,DWORD PTR SS:[ESP+140];в EBP помещается наш рег. код
00406FFB  MOV EDI,EAX                   ;помещаем правильный номер в EDI
00406FFD  PUSH EBP                      
00406FFE  CALL epp22.004113A6
00407003  CDQ
00407004  XOR EAX,EDX
00407006  ADD ESP,1C
00407009  SUB EAX,EDX
0040700B  CMP EDI,EAX                   ;сравниваем наш номер и правильный
0040700D  JE SHORT epp22.00407026       ;если равны - все ОК, иначе получаем
0040700F  ADD EDI,1B0                   ;другой рег. номер прибавлением числа 1B0
00407015  CMP EDI,EAX                   ;и снова сравниваем с нашим номером
00407017  JE SHORT epp22.00407026       ;если совпали - все ОК
00407019  POP EDI
0040701A  POP ESI
0040701B  POP EBP
0040701C  XOR EAX,EAX
0040701E  POP EBX
0040701F  ADD ESP,110
00407025  RETN

   Из приведенного листинга должен быть понятен основной смысл процедуры проверки регистрационных данных: на основе UserName и постоянной строки J8FG3 генерируется промежуточный код (это делает процедура, вызываемая по адресу 00406FDF), который затем обрабатывается по какому-то алгоритму процедурой, вызываемой по адресу 00406FEF . Полученный рег. номер сравнивается с тем, что мы ввели в форме для регистрации, и, если коды не совпадают, генерируется еще один правильный рег. код путем прибавления к первому числа 1B0 (в десятичной системе 432).

   Давайте изучим в отладчике процедуру генерации промежуточного кода (при трассировке зайдем в нее - клавиша F7):

00407080  PUSH EBX
00407081  PUSH EBP
00407082  MOV EBP,DWORD PTR DS:[<&KERNEL32.lstrlen>;  kernel32.lstrlenA
00407088  PUSH ESI
00407089  PUSH EDI
0040708A  MOV EDI,DWORD PTR SS:[ESP+18] ;в EDI помещает UserName
0040708E  PUSH EDI                      ;сохраняем UserName в стек           
0040708F  XOR ESI,ESI                   ;обнуляем ESI (ESI будет счетчиком символов из строки J8FG3) 
00407091  CALL EBP                      ;в ЕАХ помещаем число символов в UserName           
00407093  MOV EBX,EAX                   ;в EBX переносим значение ЕАХ
00407095  MOV EAX,DWORD PTR SS:[ESP+14] ;а в ЕАХ помещаем строку J8FG3
00407099  PUSH EAX                      ;и сохраняем ее в стек           
0040709A  CALL EBP                      ;подсчитываем число символов в строке "J8FG3" (результат в EAX)
0040709C  XOR ECX,ECX                   ;ЕСХ будет определять номер символа из UserName 
0040709E  TEST EBX,EBX                  ;в UserName должно быть число символов, больше нуля
004070A0  JLE SHORT epp22.004070B9      ;иначе выходим
004070A2  MOV EDX,DWORD PTR SS:[ESP+14] ;в EDX помещаем строку J8FG3
004070A6  INC ESI                       ;увеличиваем ESI на 1
004070A7  CMP ESI,EAX                   ;если ESI=5, то по адресу 004070AF производится обнуление
004070A9  MOV DL,BYTE PTR DS:[ESI+EDX-1];берем код символа из строки J8FG3 (номер символа берем из ESI) 
004070AD  JNZ SHORT epp22.004070B1      ;если ESI <>5, следующую инструкцию перепрыгиваем
004070AF  XOR ESI,ESI
004070B1  XOR BYTE PTR DS:[ECX+EDI],DL  ;логическое исключение из кода символа UserName кода символа строки
004070B4  INC ECX                       ;увеличиваем ЕСХ
004070B5  CMP ECX,EBX                   ;если в ЕСХ число, равное числу букв в имени юзера
004070B7  JL SHORT epp22.004070A2       ;то уходим, иначе повторяем цикл
004070B9  POP EDI
004070BA  POP ESI
004070BB  POP EBP
004070BC  POP EBX
004070BD  RETN

   Как видно из листинга, промежуточный код получается логическим исключением кодов символов из UserName и из заданной автором строки (инструкция XOR BYTE PTR DS:[ECX+EDI],DL по адресу 004070B1). Поясню на примере. Допустим, вы ввели в поле для регистрации имя CrackeRF. Тогда генерация промежуточного кода будет производиться так:

(C)  43 xor 4A (J) = 9    
(r)  72 xor 38 (8) = 4A
(a)  61 xor 46 (F) = 27
(c)  63 xor 47 (G) = 24
(k)  6B xor 33 (3) = 58
(e)  65 xor 4A (J) = 2F
(R)  52 xor 38 (8) = 6A
(F)  46 xor 46 (F) = 0

   Т.е. в итоге мы получим промежуточный код из символов, hex-коды которых по таблице ascii представлены в крайнем правом столбце (читать по вертикали ;).

   Теперь выясним, как на основе промежуточного кода формируется окончательный правильный регистрационный номер. Для этого изучим в отладчике процедуру, вызываемую по адресу 00406FEF:

004070D0  PUSH ESI                             
004070D1  PUSH EDI
004070D2  MOV EDI,DWORD PTR SS:[ESP+C]   ; в EDI помещаем промежуточный код
004070D6  XOR ESI,ESI                    ; обнуляем ESI
004070D8  PUSH EDI                                 
004070D9  CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>] ;определяем число символов промеж. кода
                       ;функция lstrlenA считает символы в строке, пока ей не
                       ;встретится NUL (ascii=0h). В приведенном мной примере промежуточный
                       ;код состоит из 8 символов, но функция вернет значение 7, т.к.
                       ;последний символ имеет hex-код 0. По сути, здесь задается
                       ;число символов, которые будут обрабатываться. Если первым символом
                       ;в UserName окажется символ J, то промежуточный код будет начинаться
                       ;с символа с кодом 0 (т.к. 4A xor 4A =0) и промежуточный код
                       ;вообще обрабатываться не будет (lstrlenA вернет 0). 
004070DF  XOR ECX,ECX
004070E1  TEST EAX,EAX
004070E3  JLE SHORT epp22.0040710C
004070E5  PUSH EBX
004070E6  OV EBX,DWORD PTR SS:[ESP+1C]  ;в ЕВХ помещаем число 29h
004070EA  PUSH EBP
004070EB  MOVSX EDX,BYTE PTR DS:[ECX+EDI];берем код символа из промежуточного номера
004070EF  MOV EBP,DWORD PTR SS:[ESP+1C]  ;в ЕВР помещаем число 41h
004070F3  ADD EBP,EDX                    ;складываем 41h+код символа из пром. кода
004070F5  IMUL EBP,DWORD PTR SS:[ESP+18] ;умножаем сумму на 28h
004070FA  ADD EBP,EDX                ;и прибавляем к произведению код символа из пром. кода
004070FC  ADD EBP,EBX                ;к сумме прибавляем 29h
004070FE  ADD ESI,EBP                ;и помещаем результат в ESI
00407100  INC ECX                    ;увеличиваем значение ЕСХ на 1
00407101  CMP ECX,EAX                ;сравниваем с ЕАХ (в ЕАХ хранится число повторов цикла)
00407103  JL SHORT epp22.004070EB    ;повторяем цикл
00407105  POP EBP
00407106  POP EBX
00407107  MOV EAX,ESI                ;правильный рег. номер помещаем в EAX
00407109  POP EDI
0040710A  POP ESI
0040710B  RETN

   Думаю, здесь пояснения не нужны. Все видно из листинга. Как отмечалось выше, чтобы получить второй правильный рег. номер, нужно к сгенерированному рег. номеру прибавить число 432 (в HEX - 1B0). Стоит заметить, что в случае, когда имя пользователя начинается с буквы J, то последующие символы на рег. номер не влияют и он будет постоянный (432), т.е. мы получим только один серийник.

   Генератор ключей напишем на Delphi с использованием KOL. На форме необходимо разместить EditBox1 - для ввода имени пользователя, EditBox2 - для вывода серийных номеров (оба номера будем выводить в одном EditBox'e) и кнопку, при нажатии которой будет выполняться следующий код:

procedure TForm1.Button1Click(Sender: PObj);
var
 i, n, s, sn, sn2, k: integer;
 t : string;
begin
if length(editbox1.Text)>29 then
editbox2.Text:='число символов имени должно быть меньше 29'
  else
begin
{устанавливаем начальные значения переменых}
sn:=0; s:=1; n:=0; i:=0; 
t:='J8FG3';
while  (n<length(editbox1.Text)) and (s<>0) do
{генерируем промежуточный код, пока не получится
NUL или пока не кончатся все символы в User Name}
    begin
    n:=n+1;  {n-номер буквы из User Name}
    i:=i+1;  {i-номер символа из строки J8FG3}
    s:=Ord(editbox1.text[n]) xor Ord(t[i]);
    {функция Ord возвращает код символа по табл. ASCII}
    if i>=5 then i:=0;
    if s<>0 then   {сгенерированный символ пром. кода}
      begin        {сразу обрабатываем}
      k:=s+65;     {65=41h}
      k:=k*40;     {40=28h}
      k:=k+s+41;   {41=29h}
      sn:=sn+k;
      end;
    end;
    sn2:=sn+432;   {генерируем второй серийник}
    if sn<>0 then  {если первый символ не J то выводим 2 кода}
    editbox2.text:=int2str(sn)+' or '+int2str(sn2)
    else           {иначе в EditBox2 отображаем только один код}
    editbox2.text:=int2str(sn2);
end;
end;

   Чтобы узнать, какой максимальной длинны может быть имя пользователя, нужно просто посмотреть место, откуда программа выходит на процедуру проверки регистрационного номера (это удобнее сделать в Win32dasm).

Информация приведена в образовательных целях. Повторение описанных в статье действий запрещается.
© BioCyborG, 2005

 © BioCyborG
www.biocyborg.narod.ru

Hosted by uCoz