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

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

 

Исследование алгоритма защиты программы Gif2swf v2.1.

  Gif2Swf v2.5                 [458  KB-ZIP] http://biocyborg.narod.ru/soft/Gif2Swf25.zip
 OllyDbg v1.10                [1087 KB-ZIP] http://biocyborg.narod.ru/soft/OllyDbg110.zip
 Версия статьи для печати     [44   KB-ZIP]
http://biocyborg.narod.ru/rev/srcGif2Swf.zip

 С распространением интернета  все больше программ для своей регистрации требуют выхода в Сеть и, порой, даже не предусматривают альтернативного способа регистрации. Т.е. чтобы зарегистрировать программу, необходимо подключиться к сайту разработчика, заполнить специальные формы регистрации, после чего, в случае ввода корректной информации, в системе создастся запись о том, что программа зарегистрирована. Примером такой программы может служить Gif2swf-утилита для преобразования gif-анимации в формат swf (сайт разработчика - http://www.gfx2swf.com).

 Как вы уже, наверное, догадались, эта программа не бесплатная и в демонстрационной версии существует ряд ограничений. В меню "Help" имеется пункт "Registration", при выборе которого появляется окно с предложением перейти на сайт разработчика для регистрации. И хотя с помощью редактора ресурсов в самой программе можно найти диалоговые окна ввода регистрационных данных, вызвать эти окна без подключения к сайту стандартными способами нельзя.

 Но мы подключаться к вражескому сайту не собираемся, ведь так?:). Тогда поищем обходные пути. С помощью утилиты RegMon мне удалось выяснить, что при запуске программа ищет в реестре в разделе HKEY_LOCAL_MACHINE\SOFTWARE\GIF2SWF ключи RegisteredUserName и RegisteredUserKey. Но этих ключей там нет, поэтому мы их создадим сами. Идем в реестр в указанный раздел и создаём два строковых  параметра: RegisteredUserName и RegisteredUserKey. Почему именно строковых? Ну, имя юзера обычно записывается именно в строковый параметр, а тип параметра для рег. кода пришлось выбрать наугад (если будут возникать ошибки, мы попробуем выбрать другой тип для этого параметра). Параметру RegisteredUserName присвоим значение "BioCyborG", а параметру RegisteredUserKey - "123456789".

 Теперь нам надо подумать над тем, как поймать программу при считывании этой информации из реестра. Поступим следующим оразом. Откроем gif2swf.exe в отладчике OllyDbg. После того, как отладчик покажет нам ассемблерный листинг программы, щелкнем на нем правой кнопкой мыши и в контекстном меню выберем "Search for->All referenced text strings".

Список текстовых строк в исполнимом файле

  Так-так... Здесь мы видим, что программа обращается к интересующим нас ключам реестра в трех местах (адреса 403242, 4037С1, 403DFB). Перейдем по каждой из этих сылок (двойной щелочек левой кнопкой по выделенной ссылке) и установим точки останова на каждый из этих адресов (двойной щелчек по опкоду команды напротив выбранного адреса). Запускаем программу (F9) и прерываемся здесь:

00403DFB   PUSH gif2swf.0042A5C0        ;  ASCII "RegisteredUserName"
00403E00  >MOV DWORD PTR DS:[434B1C],0
00403E0A  >MOV DWORD PTR DS:[434B18],0
00403E14   CALL gif2swf.004038C0
00403E19   PUSH gif2swf.0042C090
00403E1E   PUSH gif2swf.0042A5F8        ;  ASCII "RegisteredUserKey"
00403E23   CALL gif2swf.004038C0
00403E28   ADD ESP,0C
00403E2B   MOV ECX,ESP
00403E2D   MOV DWORD PTR SS:[ESP+14],ESP
00403E31   PUSH gif2swf.0042C090
00403E36   CALL gif2swf.0041D214
00403E3B   PUSH ECX
00403E3C   MOV DWORD PTR SS:[ESP+230],0
00403E47   MOV ECX,ESP
00403E49   MOV DWORD PTR SS:[ESP+20],ESP
00403E4D   PUSH gif2swf.0042C094
00403E52   CALL gif2swf.0041D214
00403E57  >MOV DWORD PTR SS:[ESP+230],-1
00403E62   CALL gif2swf.004035E0  ;возвращает в ЕАХ единицу, если код неверный
00403E67   ADD ESP,8
00403E6A   XOR ECX,ECX 
00403E6C   TEST EAX,EAX             ;собственно, проверка и...
00403E6E   SETE CL
00403E71   MOV DWORD PTR DS:[434B1C],ECX
00403E77   MOV EAX,DWORD PTR SS:[EBP+70]
00403E7A   CMP BYTE PTR DS:[EAX],0  ;...вынесение приговора ;)
00403E7D   JNZ SHORT gif2swf.00403EE7
00403E7F   MOV EDX,DWORD PTR DS:[42A1C8]            ;  gif2swf.0042A1CC

 Все видно из листинга. Нужно исследовать процедуру, вызываемую по адресу 403E62:

004035E0  PUSH -1
004035E2  PUSH gif2swf.00421B50       ;  SE handler installation
004035E7  MOV EAX,DWORD PTR FS:[0]
004035ED  PUSH EAX
004035EE >MOV DWORD PTR FS:[0],ESP
004035F5  SUB ESP,68
004035F8  PUSH EBX
004035F9  PUSH ESI
004035FA  PUSH EDI
004035FB >LEA ECX,DWORD PTR SS:[ESP+84]
00403602 >MOV DWORD PTR SS:[ESP+7C],1
0040360A  CALL gif2swf.0041D8E2 ;строчные символы имени преобразуем в прописные
0040360F >LEA ECX,DWORD PTR SS:[ESP+84]
00403616 CALL gif2swf.0041D8BB
0040361B >LEA ECX,DWORD PTR SS:[ESP+88]
00403622  MOV ESI,EAX
00403624  CALL gif2swf.0041D8BB       ;достаем из памяти введенный нами код
00403629  MOV EBX,EAX
0040362B  MOV EDI,ESI
0040362D  OR ECX,FFFFFFFF
00403630  XOR EAX,EAX
;;;;;;;;;;;;;;;;;вырезано 16 строк неинтересного кода;;;;;;;;;;;;;;;;;
0040365B  NOT ECX
0040365D  DEC ECX
0040365E  CMP ECX,20                  ;сравниваем длину имени с числом 3210
00403661  JLE SHORT gif2swf.00403668  ;если длина имени больше 32 символов
00403663  MOV ECX,20                  ;то будем обрабатывать только 32
00403668  XOR ESI,ESI                 ;ESI будет счетчиком обработанных символов 
0040366A  TEST ECX,ECX
0040366C  JLE SHORT gif2swf.00403696
0040366E >/MOV EAX,DWORD PTR SS:[ESP+88];в ЕАХ помещает имя юзера
00403675 |MOV BL,BYTE PTR DS:[ESI+EAX]  ;берем символ из имени юзера
00403678 |MOV EAX,163EF7                ;в ЕАХ помещаем константу 163EF7h
0040367D |MOVSX EDI,BL                  ;пересылаем код символа имени в EDI
00403680 |CDQ            ;копирование значения старшего бита EAX на все биты EDX
00403681 |IDIV EDI       ;делим EAX на EDI, целую часть от деления в EAX
00403683 |MOVSX EDX,BL   ;пересылаем код символа имени в EDX
00403686 |ADD EAX,ESI    ;складываем результат деления с номером
00403688 |IMUL EAX,EDX   ;умножаем получ. сумму на код символа имени
0040368B |ADD EBP,EAX    ;и прибавляем к EBP; ЕВР-это будет верный код
0040368D |INC ESI        ;увеличиваем ESI на 1
0040368E |CMP ESI,ECX    ;если не все символы обработаны, то берем следующий
00403690 \JL SHORT gif2swf.0040366E
00403692  MOV EBX,DWORD PTR SS:[ESP+10] ;помещаем в ЕВХ наш код
00403696  ADD EBP,85D9ED      ;к ЕВР прибавляем 85D9EDh  и получаем верный код
0040369C  LEA EAX,DWORD PTR SS:[ESP+14]
004036A0  PUSH EBP            ;сохраняем верный код в стек
004036A1  PUSH gif2swf.0042A5F4 
;;;;;;;;;;далее идет хитробайтое :) сравнение нашего кода с верным кодом и если
;;;;;;;;;;они не равны, то в ЕАХ помещается 1, и по этому результату программа
;;;;;;;;;;определяет, зарегистрирована она или нет

 Итак, подведем итоги: мы правильно угадали тип параметра RegisteredUserKey. Программа генерирует правильный регистрационный номер на основе введенного имени пользователя и сравнивает его с тем, что записан в реестре. Правильный номер генерируется по следующему алгоритму: берем ASCII-код символа из UserName, делим на него число 163EF7h. К целой части от деления прибавляем порядковый номер символа в имени юзера (считаем номера символов начиная с 0). Затем результат сложения умножаем на ASCII-код символа и прибавляем к числу X, которое вначале равно 0. Так обрабатываем все символы имени пользователя, после чего к X прибавляем число 85D9EDh и получаем правильный регистрационный код. Да, чуть не забыл: если длина имени пользователя больше 32 символов, то в процедуре генерации правильного кода используются только первые 32.

 А теперь приведу код процедуры генерации правильного регистрационного кода. Нам нужно учесть тот факт, что пользователь не сможет обычным способом ввести свое имя и код, поэтому наш кейген будет автоматически добавлять эту информацию в реестр. Для написания примера я взял Delphi+KOL,  но использовал ассемблерные вставки, чтобы не заморачиваться с обработкой символов национальных алфавитов (да и вообще так прикольнее смотрится :).

 На форме поместим 2 EditBox’a: первый для ввода имени пользователя, второй – для отображения сгенерированного регистрационного кода (он юзеру не нужен, ведь мы сразу пишем рег. инфу в реестр, но все равно код покажем для контроля ;). В опциях EditBox1 обязательно установите eoUpperCase:=true, чтобы введенные символы имени автоматически преобразовывались в верхний регистр. Конечно, можно было бы использовать команду upcase, но она работает только с ASCII-символами a..z и не поддерживает символы национальных алфавитов. Еще на форме нужно поместить кнопку, при нажатии которой будет генерироваться рег. код и полученная информация будет записываться в реестр операционной системы. 

procedure TForm1.Button1Click(Sender: PObj);
 var
 ddd:integer;    //ddd - переменная для промежуточного результата
 i,y: integer;   //i-номер симвора имени,y-слагаемое (y=i-1)
 SerNum:integer; //В этой переменной окажется правильный номер
 bukva:char;     //bukva - переменная для символов из UserName
 UserInfo:HKey;  //UserInfo - переменная для работы с реестром
begin
if Editbox1.Text='' then ShowMessage('Input User Name!') else
  begin
  i:=1;             //i - номер символа из имени юзера
  y:=0;             //y - слагаемое, y=i-1
  SerNum:=$85D9ED;  //значек "$" показывает, что число в шестнадцатеричном формате
  while (i<=Length(Editbox1.Text)) and (y<32) do
  begin
    bukva:=editbox1.text[i];  //считываем очередной символ
   {не забудьте установить в опциях EditBox1 eoUpperCase:=true}
    asm
    pushad
    mov   eax,163EF7h
    mov   bl, bukva
    movsx edi,bl
    idiv  edi
    add   eax,y
    cdq
    movsx edx,bl
    imul  eax,edx
    mov   ddd,eax
    inc   y
    inc   i
    popad
    end;
    SerNum:=SerNum+ddd;
  end;
  {теперь записываем в реестр полученную инфу}
  UserInfo:=RegKeyOpenWrite(HKEY_LOCAL_MACHINE, 'SOFTWARE\GIF2SWF');
  RegKeySetStr(UserInfo, 'RegisteredUserName', editbox1.text);
  RegKeySetStr(UserInfo, 'RegisteredUserKey',  int2str(SerNum));
  RegKeyClose(UserInfo);
  editbox2.Text:=int2str(SerNum);  //выводим результат
  label1.Text:='Cracked ;)';
  end;
end;

Исходники можно взять здесь.

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

 © BioCyborG
www.biocyborg.narod.ru

Hosted by uCoz