Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
AirXonix v1.36 - Кейген на Паскале
Автор: Fess

- Папа, а хакеры хорошо получают?
- Хорошо, сынок, лет этак пятнадцать...

Target: AirXonix v1.36

Tools:

  • Some brains
  • TRW2000/SoftIce
  • Win32Dasm 8.93
  • Pascal 7.0
  • Все, кроме мозгов, можно найти на www.exetools.com

Вступление

Как это начиналось:

Написал мне один собрат по призванию DIMA и попросил посмотреть эту прогамму. Сразу оговорюсь это самая сложная прога, о которой я пишу тьюториал. Нет, патч не очень сложен, но кейген?!!! Но два дня прошло и я это осилил. Вообщем, читайте. Забыл упомянуть это мой 20! тьютор, т.е. небольшой юбилейчик. :)

Что за прога:

Это гамес типа xonix. Вполне неплохой гам, если мне позволят высказать свое мнение. Стоит эта радость 14 фотографий президентов США, но у меня столько нет, да и предпочитая получать бесплатно - ведь это приятно в двойне. В лицензионном соглашении написано про то, что нельзя типо дизассемблировать и пр. Но нам в принципе наплевать - свободу ПО. Требования: Р166, 32Мб, 3D Video, 10Мб.

Начало

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

Вы вернулись?!!

Если да, то продолжаем. Посмотрим основной файл программы самый большой это program.exe. С ним и работаем. Проверяем программу на запакованность, можно воспользоваться программой, а можно вручную. Я посмотрел - она ничем не пакована, а значить это рульно. Суем ее в Win32Dasm. И в секции строк поищем, что-нибудь интересное. Мое внимание привлекли две строки "REG_OK" и "REG_FAULT". Смотрим откедова оно вызывается


* Possible StringData Ref from Data Obj ->"REG_DIALOG"
                        |
:00406E7E 6864C34300 push 0043C364
:00406E83 51         push ecx
:00406E84 FFD7       call edi
:00406E86 83F801     cmp eax, 00000001
:00406E89 0F8557010000 jne 00406FE6
:00406E8F E82CCB0000 call 004139C0
:00406E94 85C0       test eax, eax
:00406E96 746D       je 00406F05
:00406E98 53         push ebx
:00406E99 E8A2F7FFFF call 00406640
:00406E9E E87D490000 call 0040B820
......(несколько лишних строк)
:00406EE1 6A00       push 00000000
:00406EE3 6810674000 push 00406710
:00406EE8 56         push esi

* Possible StringData Ref from Data Obj ->"REG_OK"
                      |
:00406EE9 6858C34300 push 0043C358
:00406EEE 52         push edx
........(несколько лишних строк)
:00406F14 6810674000 push 00406710
:00406F19 56         push esi

* Possible StringData Ref from Data Obj ->"REG_FAULT"
                       |
:00406F1A 684CC34300 push 0043C34C
:00406F1F 50         push eax
:00406F20 FFD7       call edi

Сразу находим процедуру проверки регистрационного кода (выделена). Заходим туда и что мы видим... (процедура из-за ее серьезности приведена почти полностью) Я снабдил процедуру необходимыми комментариями и, надеюсь, разобраться в ней будет не сложно.


* Referenced by a CALL at Addresses:
|:00406E8F   , :00407321   , :00422903
|
...несколько строк выкинуто

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413A56(C)
|

*****************************************************************************
*** Блок 1. В этом блоке из имени генерируется его кодовое представление ****
*****************************************************************************

:004139EB 8A8E10914400   mov cl, byte ptr [esi+00449110] < Берем символ имени. В памяти
				Имя распологается по адресу 449110 и с увеличением счетчика
				(esi) берется след. символ.
:004139F1 80F930         cmp cl, 30   <
:004139F4 7C11           jl 00413A07  < Проверка на вхождение в диапазон от 0..9
:004139F6 80F939         cmp cl, 39   <
:004139F9 7F0C           jg 00413A07  <
:004139FB 8AD9           mov bl, cl
:004139FD 80EB30         sub bl, 30   < Если это так, то вычитаем 30h
:00413A00 889830914400   mov byte ptr [eax+00449130], bl < Записываем
:00413A06 40             inc eax      < Увеличиваем счетчик записи

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004139F4(C), :004139F9(C)
|
:00413A07 80F941         cmp cl, 41   <
:00413A0A 7C11           jl 00413A1D  < Проверка на вхождение в диапазон A..Z
:00413A0C 80F95A         cmp cl, 5A   <
:00413A0F 7F0C           jg 00413A1D  <
:00413A11 8AD9           mov bl, cl
:00413A13 80EB37         sub bl, 37   < Если это так, то вычитаем 37h
:00413A16 889830914400   mov byte ptr [eax+00449130], bl < Записываем
:00413A1C 40             inc eax      < Увеличиваем счетчик записи

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00413A0A(C), :00413A0F(C)
|
:00413A1D 80F961         cmp cl, 61   <
:00413A20 7C11           jl 00413A33  < Проверка на вхождение в диапазон a..z
:00413A22 80F97A         cmp cl, 7A   <
:00413A25 7F0C           jg 00413A33  <
:00413A27 8AD9           mov bl, cl
:00413A29 80EB3D         sub bl, 3D   < Если это так, вычитаем 3Dh
:00413A2C 889830914400   mov byte ptr [eax+00449130], bl < Записываем
:00413A32 40             inc eax      < Увелич. счетчик записи

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00413A20(C), :00413A25(C)
|
:00413A33 80F92E         cmp cl, 2E   < Если символ "."
:00413A36 7508           jne 00413A40
:00413A38 889030914400   mov byte ptr [eax+00449130], dl <- Записываем 3Fh (DL=3Fh)
:00413A3E EB0C           jmp 00413A4C

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413A36(C)
|
:00413A40 80F940         cmp cl, 40   < Если символ "@"
:00413A43 7508           jne 00413A4D
:00413A45 C680309144003F mov byte ptr [eax+00449130], 3F < Пишем 3Fh

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413A3E(U)
|
:00413A4C 40             inc eax    < Увелич. счетчик записи

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413A43(C)
|
:00413A4D 8B0D80914400   mov ecx, dword ptr [00449180] < Берем длинну имени
:00413A53 46             inc esi         < Увелич. счетчик чтения
:00413A54 3BF1           cmp esi, ecx    < Сравниваем
:00413A56 7293           jb 004139EB     < Если меньше, то повторяем процедуру


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004139E9(C)
|
:00413A58 C6803091440000 mov byte ptr [eax+00449130], 00 < В конец сгенерированного
						кодового представления имени записываем байт 00


**********************
*** Конец блока 1 ****
**********************

...(несколько лишних строк)


*******************************************************************************
*** Блок 2. В этом блоке из пароля генерируется его кодовое представление *****
*  Этот блок полностью аналогичен блоку 1 за исключением конца. Я привожу *****
*  здесь только отличия.                                                  *****
*******************************************************************************

...(аналогичный блоку 1 код выброшен)
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00413AA7(C), :00413AAC(C)
|
:00413ABA 80F92B       cmp cl, 2B  < Если символ "+"
:00413ABD 7508         jne 00413AC7
:00413ABF 889068914400 mov byte ptr [eax+00449168], dl < То записываем 3Eh
:00413AC5 EB0C         jmp 00413AD3

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413ABD(C)
|
:00413AC7 80F92A       cmp cl, 2A  < Если символ "*"
:00413ACA 7508         jne 00413AD4
:00413ACC C680689144003F  mov byte ptr [eax+00449168], 3F < То пишем 3Fh

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413AC5(U)
|
:00413AD3 40          inc eax < Увеличиваем счетчик записи

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413ACA(C)
|
:00413AD4 8B0D84914400 mov ecx, dword ptr [00449184] < Берем длинну пароля
:00413ADA 46           inc esi  < Увелич. счетчик чтения
:00413ADB 3BF1         cmp esi, ecx  < Сравниваем счетчик чтения с длинной
:00413ADD 7293         jb 00413A72   < Если меньше, то цикл повторяется

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413A70(C)
|
:00413ADF C6806891440000          mov byte ptr [eax+00449168], 00

**********************
*** Конец блока 2 ****
**********************

+++++++++++++++++++++++++++++++++++++++++++++++++++
+++ В итоге кодовое представление имени         +++
+++ находится по адресу 413BD9, а пароля 449168 +++
+++++++++++++++++++++++++++++++++++++++++++++++++++



:00413AE6 83F811       cmp eax, 00000011  < Проверяем длинну пароля, он должен
					содержать 17 символов
:00413AE9 A38C914400   mov dword ptr [0044918C], eax <- Записываем длинну пароля
:00413AEE 0F85DD010000 jne 00413CD1  <- Если длинна неправильная, то переходим

********************************************************************************
*** Блок 3. В этом блоке генерируется кодовое представление паролей, которые ***
*** программа считает за стандартные. Типа 12345 и т.д. всего 4 штуки, но    ***
*** нам этот блок не нужен. И я его выпускаю                                 ***
********************************************************************************

:00413AF4 55         push ebp
... (куча кода выброшена)
:00413B87 B91C005400 mov ecx, 0054001C

**********************
*** Конец блока 3 ****
**********************

*********************************************************************************
*** Блок 4. В этом блоке кодовое представление паролей программы сравнивается ***
*** с нашим. Поскольку я выбросил блок 3 выбрасываю и его.                    ***
*********************************************************************************


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413BB0(C)
|
:00413B8C 33C0                    xor eax, eax
... (немного кода спилено)
:00413BB2 8B3588914400            mov esi, dword ptr [00449188]


**********************
*** Конец блока 4 ****
**********************

**********************************************************************************
*** Блок 5. В этом блоке генерируются последние пять символов истинного пароля ***
*** из кодового представления имени и кодового представления пароля.           ***
*** Для облегчения моего скорбного труда. Далее под именем буду подразумевать  ***
*** его кодовое представление, аналогично и с паролем. Под истинным паролем    ***
*** понимаются последние пять символов, которые и являются критерием Верности  ***
**********************************************************************************


:00413BB8 33C0         xor eax, eax
:00413BBA 33C9         xor ecx, ecx
:00413BBC C644240C0D   mov [esp+0C], 0D <
:00413BC1 85F6         test esi, esi
:00413BC3 C644240D17   mov [esp+0D], 17 < Вот они пять символов, такими они являются
:00413BC8 C644240E32   mov [esp+0E], 32 < в начале. (истинный пароль)
:00413BCD C644240F02   mov [esp+0F], 02 <
:00413BD2 C64424102C   mov [esp+10], 2C <
:00413BD7 761D         jbe 00413BF6

****************
*** Блок 5.1 ***
****************

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413BF4(C)
|
:00413BD9 8A9130914400 mov dl, byte ptr [ecx+00449130] < Берем символ имени в dl
:00413BDF 8A5C040C     mov bl, byte ptr [esp+eax+0C]   < Берем символ истинного пароля в bl
:00413BE3 02DA         add bl, dl                      < bl = bl+dl
:00413BE5 885C040C     mov byte ptr [esp+eax+0C], bl   < Пишем полученное
:00413BE9 40           inc eax                         < Увелич. счетчик истинного пароля
:00413BEA 83F805       cmp eax, 00000005               < Если равно 5
:00413BED 7502         jne 00413BF1
:00413BEF 33C0         xor eax, eax                    < Счетчик сбрасывается

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413BED(C)
|
:00413BF1 41           inc ecx       < Увелич. счетчик имени
:00413BF2 3BCE         cmp ecx, esi  < Сравниваем счетчик имени с длинной (в esi)
:00413BF4 72E3         jb 00413BD9   < Если меньше, то повтор цикла

****************
*** Блок 5.2 ***
****************

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413BD7(C)
|
:00413BF6 A072914400    mov al, byte ptr [00449172] < В al 11-й символ пароля
:00413BFB 8A1D6D914400  mov bl, byte ptr [0044916D] < В bl 6-й символ пароля
:00413C01 8A1568914400  mov dl, byte ptr [00449168] < В dl 1-й символ пароля
:00413C07 8A4C240C      mov cl, byte ptr [esp+0C]   < В cl 1-й символ истинного пароля
:00413C0B 02C3          add al, bl                  < al = al+bl
:00413C0D 8A1D69914400  mov bl, byte ptr [00449169] < В bl 2-й символ пароля
:00413C13 02C2          add al, dl                  < al = al+dl
:00413C15 8A54240D      mov dl, byte ptr [esp+0D]   < В dl 2-й символ истинного пароля
:00413C19 02C8          add cl, al                  < cl = cl+al
:00413C1B A06E914400    mov al, byte ptr [0044916E] < В al 7-й символ пароля
:00413C20 884C240C      mov byte ptr [esp+0C], cl   < На 1 место истинного пароля пишем cl

:00413C24 8A0D73914400  mov cl, byte ptr [00449173] < В cl 12-й символ пароля
:00413C2A 02C8          add cl, al                  < cl = cl+al
:00413C2C A06A914400    mov al, byte ptr [0044916A] < В al 3-й символ пароля
:00413C31 02CB          add cl, bl                  < cl = cl+bl
:00413C33 8A5C240E      mov bl, byte ptr [esp+0E]   < В bl 3-й символ истинного пароля
:00413C37 02D1          add dl, cl                  < dl = dl+cl
:00413C39 8A0D6B914400  mov cl, byte ptr [0044916B] < В al 4-й символ пароля
:00413C3F 8854240D      mov byte ptr [esp+0D], dl   < На 2 место истинного пароля пишем dl

:00413C43 8A156F914400  mov dl, byte ptr [0044916F] < В dl 8-й символ пароля
:00413C49 02D0          add dl, al                  < dl = dl+al
:00413C4B A070914400    mov al, byte ptr [00449170] < В al 9-й символ пароля
:00413C50 02DA          add bl, dl                  < b = b+d
:00413C52 8A156C914400  mov dl, byte ptr [0044916C] < В dl 5-й символ пароля
:00413C58 885C240E      mov byte ptr [esp+0E], bl   < На 3 место истинного пароля пишем bl

:00413C5C 8A5C240F      mov bl, byte ptr [esp+0F]   < В bl 4-й символ истинного пароля
:00413C60 02C1          add al, cl                  < al = al+cl
:00413C62 8A0D71914400  mov cl, byte ptr [00449171] < В dl 10-й символ пароля
:00413C68 02D8          add bl, al                  < bl = bl+al
:00413C6A 8A442410      mov al, byte ptr [esp+10]   < В bl 5-й символ истинного пароля
:00413C6E 02CA          add cl, dl                  < cl = cl+dl
:00413C70 885C240F      mov byte ptr [esp+0F], bl   < На 4 место истинного пароля пишем bl

:00413C74 02C1          add al, cl                  < al = al+cl
:00413C76 B13F          mov cl, 3F                  < cl = 3Fh
:00413C78 88442410      mov byte ptr [esp+10], al   < На 5 место истинного пароля пишем al

****************
*** Блок 5.3 ***
****************

:00413C7C 33C0          xor eax, eax

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413C8C(C)
|
:00413C7E 8A5C040C      mov bl, byte ptr [esp+eax+0C] < Берем символ истинного кода
:00413C82 22D9          and bl, cl   < bl = bl and 3Fh ( cl=3Fh )
:00413C84 885C040C      mov byte ptr [esp+eax+0C], bl  < Записываем результат
:00413C88 40            inc eax      < Увеличиваем счетчик
:00413C89 83F805        cmp eax, 00000005 < Сравниваем счетчик с 5
:00413C8C 72F0          jb 00413C7E    < Если меньше, то повтор цикла

**********************
*** Конец блока 5 ****
**********************

************************************************************************************
*** Блок 6. В этом блоке 5 символов истинного пароля сравниваются с 5 последними ***
*** символами введенного пароля. Если не равно, то очень плохо                   ***
*** Код настолько простой, что все должно быть понятно без объяснений            ***
************************************************************************************


:00413C8E 8A54240C      mov dl, byte ptr [esp+0C]
:00413C92 A074914400    mov al, byte ptr [00449174]
:00413C97 3AD0          cmp dl, al
:00413C99 7536          jne 00413CD1
:00413C9B 8A44240D      mov al, byte ptr [esp+0D]
:00413C9F 8A0D75914400  mov cl, byte ptr [00449175]
:00413CA5 3AC1          cmp al, cl
:00413CA7 7528          jne 00413CD1
:00413CA9 8A4C240E      mov cl, byte ptr [esp+0E]
:00413CAD A076914400    mov al, byte ptr [00449176]
:00413CB2 3AC8          cmp cl, al
:00413CB4 751B          jne 00413CD1
:00413CB6 8A54240F      mov dl, byte ptr [esp+0F]
:00413CBA A077914400    mov al, byte ptr [00449177]
:00413CBF 3AD0          cmp dl, al
:00413CC1 750E          jne 00413CD1
:00413CC3 8A442410      mov al, byte ptr [esp+10]
:00413CC7 8A0D78914400  mov cl, byte ptr [00449178]
:00413CCD 3AC1          cmp al, cl
:00413CCF 741A          je 00413CEB < Если все тип-топ, то прыжок

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004139CE(C), :004139DB(C), :00413AEE(C), :00413B91(C), :00413C99(C)
|:00413CA7(C), :00413CB4(C), :00413CC1(C)
|
:00413CD1 B920000000  mov ecx, 00000020 < Сюда попадают, если код неправильный
:00413CD6 33C0        xor eax, eax
:00413CD8 BF10914400  mov edi, 00449110
:00413CDD A360005400  mov dword ptr [00540060], eax
:00413CE2 F3          repz
:00413CE3 AB          stosd
:00413CE4 5F          pop edi
:00413CE5 5E          pop esi
:00413CE6 5B          pop ebx
:00413CE7 83C408      add esp, 00000008
:00413CEA C3          ret

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413CCF(C)
|
:00413CEB B801000000  mov eax, 00000001 < Сюда попадают, если код правильный
:00413CF0 5F          pop edi
:00413CF1 5E          pop esi
:00413CF2 A360005400  mov dword ptr [00540060], eax
:00413CF7 C7056400540000000000 mov dword ptr [00540064], 00000000
:00413D01 5B          pop ebx
:00413D02 83C408      add esp, 00000008
:00413D05 C3          ret

Вот в принципе и все (Естественно еще много). Вот я накатал, такой кейген.


PROGRAM KEYGEN_AIRXONIX;
Uses Crt;
Var
  S,S1:String;
  A,A1,A2:Array[1..20] of Byte;
  I, Z, D, C: Word;


Function Symv(A2I:Byte):Byte;
{Процедура по кодовому представлении символа возвращает его настоящее значение}
Begin
    IF (A2I>=$0) and (A2I<10) then A2I:=A2I+$30 else
    IF (A2I>9) and (A2I<$24) then A2I:=A2I+$37 else
    IF (A2I>$23) and (A2I<$3E) then A2I:=A2I+$3D;
    IF A2I=$3E then A2I:=$2B;
    IF A2I=$3F then A2I:=$2A;
    Symv:=A2I;
End;

Procedure Create_Name;
Begin
  ClrScr;
  Write('Please enter name (chars>5): ');
  Read(S);

  For I:=1 To Length(S) Do
   Begin
     Z:=Ord(S[I]);
     If (Z>$2F) and (Z<$3A) then Z:=Z-$30;
     If (Z>$40) and (Z<$5B) then Z:=Z-$37;
     If (Z>$60) and (Z<$7B) then Z:=Z-$3D;
     If (Z=$2E) then Z:=$3E;
     If (Z=$40) then Z:=$3F;
     A[I]:=Z;
   End;
  Inc(I);
  A[I]:=0;
End;

Procedure Create_Code(SS:String);
Begin
  For I:=1 To $D Do
   Begin
     Z:=Ord(SS[I]);
     If (Z>$2F) and (Z<$3A) then Z:=Z-$30;
     If (Z>$40) and (Z<$5B) then Z:=Z-$37;
     If (Z>$60) and (Z<$7B) then Z:=Z-$3D;
     If (Z=$2B) then Z:=$3E;
     If (Z=$2A) then Z:=$3F;
     A1[I]:=Z;
   End;
  A1[$12]:=0;
End;

BEGIN
  Create_Name;     { Блок 1 }
  S1:='120022334455';
  Create_Code(S1); { Блок 2 }

  {Блок 5.1}
  A2[1]:=$0D;
  A2[2]:=$17;
  A2[3]:=$32;
  A2[4]:=$02;
  A2[5]:=$2C;
  I:=1;
  Z:=1;
  repeat
   if Z=6 then Z:=1;
   A2[Z]:=A[I]+A2[Z];
   inc(I);
   inc(Z);
  until I>Length(S);

  {Блок 5.2}
  I:=A1[$B]; {I - AL}
  Z:=A1[6]; {Z - BL}
  D:=A1[1]; {D - DL}
  C:=A2[1]; {C - CL}
  I:=I+Lo(Z);
  Z:=A1[2];
  I:=I+Lo(D);
  D:=A2[2];
  C:=C+Lo(I);
  I:=A1[7];
  A2[1]:=Lo(C);

  C:=A1[$C];
  C:=C+Lo(I);
  I:=A1[3];
  C:=C+Lo(Z);
  Z:=A2[3];
  D:=D+Lo(C);
  C:=A1[4];
  A2[2]:=Lo(D);

  D:=A1[8];
  D:=D+Lo(I);
  I:=A1[9];
  Z:=Z+Lo(D);
  D:=A1[5];
  A2[3]:=Lo(Z);

  Z:=A2[4];
  I:=I+Lo(C);
  C:=A1[$A];
  Z:=Z+Lo(I);
  I:=A2[5];
  C:=C+Lo(D);
  A2[4]:=Lo(Z);
  I:=I+Lo(C);
  A2[5]:=Lo(I);

  {Блок 5.3}
  For I:=1 To 5 Do
  Begin
   Z:=A2[I];
   asm
    mov bx, Z;
    and bl, 3Fh;
    mov Z, bx;
   end;
   A2[I]:=Z;
  End;

  For I:=1 To 5 Do A2[I]:=Symv(A2[I]); {Преобразуем кодовое представление последних пяти
  символов в настоящие символы}

  ClrScr;
  Writeln('KeyGen For AirXonix v1.36 by Fess [PTDS]');
  WriteLn('Name: ', S);
  Write('Code: ',S1);
  For I:=1 To 5 Do
   Write(Chr(A2[I]));

  If readkey=#0 then ; {Ждем нажатия на клавишу}
END.

Вроде бы. Пароль подошел и все чинно и мирно. Я тоже так думал...

Начинаем играть и после первого же этапа вылетает сообщение LICENSE AGREEMENT VIOLATION! и далее по тексту. ОБРАЩАЮСЬ КО ВСЕМ КРЭКЕРАМ!!! ВСЕГДА ПРОВЕРЯЙТЕ СЛОМАНЫЕ ПРОДУКТЫ НА РОБОТОСПСОБНОСТЬ!!!! Ищем, где выводится эта нехорошая строка.


* Referenced by a CALL at Address:
|:004127AD
|
:0040F6D0 51          push ecx
:0040F6D1 53          push ebx
...(мусор выкинут)
:0040F6F3 680000FF00  push 00FF0000
:0040F6F8 6A28        push 00000028

* Possible StringData Ref from Data Obj ->"      LICENSE AGREEMENT VIOLATION "
                                        ->"!     "
                        |
:0040F6FA 68C4DF4300  push 0043DFC4

Мы видим, что это процедура. Идем к месту, где она вызывается, т.е. к 4127AD


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004122DD(C)
|
:004127AD E81ECFFFFF  call 0040F6D0

А она вызывается ого-го с самого 4122DD. Что ж идем туда.


:004122CB 391D60005400 cmp dword ptr [00540060], ebx
:004122D1 0F846F010000 je 00412446
:004122D7 391D64005400 cmp dword ptr [00540064], ebx
:004122DD 0F85CA040000 jne 004127AD
:004122E3 E95E010000   jmp 00412446

Пора использовать дебаггер. Ставим бряк на 4122CB. Доходим до вывода сообщения и узнаем, что переход осуществляется потому, что в 540064 находится 1 вместо нужного 0. Посмотрим на код проверки пароля (строка 413CF7), там этому адресу присваивается значение 0. Как это поймать, есть два варианта 1) Смотреть в листинге, сразу скажу - не поможет 2) Ставить бряк на этот адрес памяти и жать, пока там не появиться 1. Так и поступим, нажимаем кучу раз F5, пока рука не занемеет. Начинаем жать второй рукой. А вот...


:004172F1 B14D    mov cl, 4D     < cl = 4Dh
:004172F3 8B7778  mov esi, dword ptr [edi+78] < Длинна имени
:004172F6 85F6    test esi, esi
:004172F8 760D    jbe 00417307

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00417305(C)
|
:004172FA 8A540720 mov dl, byte ptr [edi+eax+20] < Символ кодированного имени
:004172FE 02D0     add dl, al    < К символу имени прибавляем счетчик
:00417300 02CA     add cl, dl    < Все это прибавляем к сумме
:00417302 40       inc eax       < Увелич. счетчик
:00417303 3BC6     cmp eax, esi  < Сравниваем счетчик и длинну имени
:00417305 72F3     jb 004172FA   < Пока меньше повторяем

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004172F8(C)
|
:00417307 8A4758  mov al, byte ptr [edi+58] <- Берем 1 символ кодированного пароля
:0041730A 80E13F  and cl, 3F     < cl = cl and 3Fh
:0041730D 3AC8    cmp cl, al     < Сравниваем
:0041730F 8B45F8  mov eax, dword ptr [ebp-08]
:00417312 7523    jne 00417337   < Если неправильно, то плохо

Как оказалось первый символ кода берется не просто так, а генерируется. Это была только первая фишка. Не буду утомлять Вас подробностями, сразу скажу, что их 6. Ловяться установкой бряка на адрес памяти нужного символа кодированного пароля.

Второй символ делает туже бяку, что и первый, только на 5 этапе. Смотрим адрес 415EC8.


:00415EB2 8B45F4     mov eax, dword ptr [ebp-0C] < Берем длинну имени
:00415EB5 8B5DE4     mov ebx, dword ptr [ebp-1C]
:00415EB8 0517020000 add eax, 00000217           < Добавляем к ней 217h
:00415EBD 8A55FC     mov dl, byte ptr [ebp-04]   < Берем второй символ кодир. пароля
:00415EC0 35E5000000 xor eax, 000000E5           < eax = eax xor E5h
:00415EC5 83E03F     and eax, 0000003F           < eax = eax and 3Fh
:00415EC8 3AC2       cmp al, dl                  < Сравниваем
:00415ECA 7403       je 00415ECF                 < Если все в порядке прыгаем
:00415ECC C60301     mov byte ptr [ebx], 01

Третий символ делает туже бяку, что и первый, только на 10 этапе. Смотрим адрес 416837.


:0041681F B233     mov dl, 33 < dl = 33h
:00416821 33C9     xor ecx, ecx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041682F(C)
|
:00416823 8A440E20 mov al, byte ptr [esi+ecx+20] < Берем символ кодир. имени
:00416827 F6E9     imul cl    < ax = al*cl (cl=33h)
:00416829 02D0     add dl, al < dl = dl+al
:0041682B 41       inc ecx           < Увеличиваем счетчик
:0041682C 83F906   cmp ecx, 00000006 < Сравниваем счетчик с 6
:0041682F 7CF2     jl 00416823       < Если меньше, то повторяем цикл
:00416831 8A465A   mov al, byte ptr [esi+5A] < Берем 3-й символ кодиров. пароля
:00416834 80E23F   and dl, 3F  < dl = dl and 3Fh
:00416837 3AD0     cmp dl, al  < Сравниваем
:00416839 7406     je 00416841 < Если все Ок, то переход

Тут я закруглился, думая, что это все. Но я ошибался...

Четвертый символ отвечает за набраные очки. Если он неверен, то очки перед каждым этапом сбрасываются. Смотрим адрес 417EC2.


:00417EC2 B36B     mov bl, 6B   < bl = 6Bh
:00417EC4 7613     jbe 00417ED9

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00417ED7(C)
|
:00417EC6 A801     test al, 01 < Проверка на четность (al - счетчик)
:00417EC8 7406     je 00417ED0 < Если нечет, то переход
:00417ECA 025C0820 add bl, byte ptr [eax+ecx+20]   < Добавить к bl символ кодир. имени
:00417ECE EB04     jmp 00417ED4

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00417EC8(C)
|
:00417ED0 2A5C0820 sub bl, byte ptr [eax+ecx+20]  < Вычесть из bl символ кодир. имени

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00417ECE(U)
|
:00417ED4 40       inc eax       < Увеличить счетчик
:00417ED5 3BC2     cmp eax, edx  < Сравнить счетчик с длинной имени
:00417ED7 72ED     jb 00417EC6   < Если меньше, то повтор цикла

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00417EC4(C)
|
...(пару ненужных строк выкинуты)
:00417EE2 80E33F   and bl, 3F   < bl = bl and 3Fh
...(еще несколько строк вырезаны)
:00417F1B 8A425B   mov al, byte ptr [edx+5B] < Берем 4-й символ кодиров. пароля
:00417F1E 3AD8     cmp bl, al  < Сравниваем
:00417F20 5B       pop ebx
:00417F21 7419     je 00417F3C < Если равно, то переход

Пятый символ отвечает за время на 10 этапе уроня CLASSIC. Если он неверен, то вместо 60 секунд дают 2. Этот макар можно вычислить достаточно просто в листинге. Т.е. искать адрес, которому присваивается значение 0000003С, таких один или два. Затем смотреть, где ему присваивают два. И найдете адрес 419DDB. Кстати тут можно поджулить и сделать времени побольше :)


:00419D99 B30F     mov bl, 0F                    < bl = 0Fh
...(несколько строк удалены)
:00419DB0 8A441620 mov al, byte ptr [esi+edx+20] < Берем символ кодиров. имени
:00419DB4 8845FF   mov byte ptr [ebp-01], al     < Сохраняем символ. Зачем? Я не знаю.
:00419DB7 8B4DF4   mov ecx, dword ptr [ebp-0C]   < Берем в ecx счетчик (в начале там 0)
:00419DBA 83E107   and ecx, 00000007             < ecx = ecx and 07
:00419DBD 8A45FF   mov al, byte ptr [ebp-01]     < Берем сохраненый символ. Зачем? Нет ответа.
:00419DC0 D2C8     ror al, cl                    < al = ror al,cl
:00419DC2 8845FF   mov byte ptr [ebp-01], al     < Сохраняем. Зачем????
:00419DC5 8A45FF   mov al, byte ptr [ebp-01]     < Берем сохраненный. Зачем???
:00419DC8 02D8     add bl, al                    < bl = bl + al
:00419DCA 8B4678   mov eax, dword ptr [esi+78]   < Берем длинну имени
:00419DCD 42       inc edx                       < Увеличиваем счетчик
:00419DCE 3BD0     cmp edx, eax                  < Сравниваем счетчик и длинну имени
:00419DD0 8955F4   mov dword ptr [ebp-0C], edx   < Записываем счетчик
:00419DD3 72DB     jb 00419DB0                   < Если меньше, то повторяем цикл

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00419DAE(C)
|
:00419DD5 8A465C   mov al, byte ptr [esi+5C] < Берем 5-й символ кодиров. пароля
:00419DD8 80E33F   and bl, 3F                < bl = bl and 3F
:00419DDB 3AD8     cmp bl, al                < Сравниваем
:00419DDD 740A     je 00419DE9               < Если равно, то переход

Тут я опять думал, что работа окончена. Но и сегодня я ошибся, все было еще впереди.

Шестой символ. На уровне EXTRIME при переходе на 5-й этап - игра вылетает в меню. Смотрим 413E35.


:00413E01 B21C     mov dl, 1C       < dl = 1C
:00413E03 8975FC   mov dword ptr [ebp-04], esi
:00413E06 7627     jbe 00413E2F

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413E2D(C)
|
:00413E08 8A443720 mov al, byte ptr [edi+esi+20] < Берем символ кодир. имени
:00413E0C 88450B   mov byte ptr [ebp+0B], al     < Сохраняем его.
:00413E0F 8B4DFC   mov ecx, dword ptr [ebp-04]   < Берем счетчик (в начале 0)
:00413E12 03C9     add ecx, ecx                  < Увеличиваем его в двое
:00413E14 83E107   and ecx, 00000007             < ecx = ecx and 07
:00413E17 8A450B   mov al, byte ptr [ebp+0B]     < Берем сохраненый символ
:00413E1A D2C0     rol al, cl                    < al = rol al,cl
:00413E1C 88450B   mov byte ptr [ebp+0B], al     < Сохраняем. Не понял зачем.
:00413E1F 8A450B   mov al, byte ptr [ebp+0B]     < Берем сохраненный. Зачем?!!!
:00413E22 02D0     add dl, al                    < dl = dl + al
:00413E24 8B4778   mov eax, dword ptr [edi+78]   < Берем длинну имени
:00413E27 46       inc esi                       < Увеличиваем счетчик
:00413E28 3BF0     cmp esi, eax                  < Сравниваем счетчик и длину имени
:00413E2A 8975FC   mov dword ptr [ebp-04], esi   < Сохраняем счетчик.
:00413E2D 72D9     jb 00413E08                   < Если меньше, то повтор цикла

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413E06(C)
|
:00413E2F 8A475D   mov al, byte ptr [edi+5D] < Берем 6-й символ кодир. пароля
:00413E32 80E23F   and dl, 3F                < dl = dl and 3Fh
:00413E35 3AD0     cmp dl, al                < Сравниваем
:00413E37 740F     je 00413E48               < Если равно, то переходим

Вот и все. Всего их было 6, по крайней мере, других пока не нашел. И в свете всего этого вот такой получился кейген...


PROGRAM KEYGEN_AIRXONIX;
Uses Crt;
Var
  S,S1:String;
  A,A1,A2:Array[1..20] of Byte;
  I, Z, D, C: Word;


Function Symv(A2I:Byte):Byte;
{Процедура по кодовому представлении символа возвращает его настоящее значение}
Begin
    IF (A2I>=$0) and (A2I<10) then A2I:=A2I+$30 else
    IF (A2I>9) and (A2I<$24) then A2I:=A2I+$37 else
    IF (A2I>$23) and (A2I<$3E) then A2I:=A2I+$3D;
    IF A2I=$3E then A2I:=$2B;
    IF A2I=$3F then A2I:=$2A;
    Symv:=A2I;
End;

Procedure Create_Name;
Begin
  ClrScr;
  Write('Please enter name (chars>5): ');
  Read(S);

  For I:=1 To Length(S) Do
   Begin
     Z:=Ord(S[I]);
     If (Z>$2F) and (Z<$3A) then Z:=Z-$30;
     If (Z>$40) and (Z<$5B) then Z:=Z-$37;
     If (Z>$60) and (Z<$7B) then Z:=Z-$3D;
     If (Z=$2E) then Z:=$3E;
     If (Z=$40) then Z:=$3F;
     A[I]:=Z;
   End;
  Inc(I);
  A[I]:=0;
End;

Procedure Create_Code(SS:String);
Begin
  For I:=1 To $D Do
   Begin
     Z:=Ord(SS[I]);
     If (Z>$2F) and (Z<$3A) then Z:=Z-$30;
     If (Z>$40) and (Z<$5B) then Z:=Z-$37;
     If (Z>$60) and (Z<$7B) then Z:=Z-$3D;
     If (Z=$2B) then Z:=$3E;
     If (Z=$2A) then Z:=$3F;
     A1[I]:=Z;
   End;
  A1[$12]:=0;
End;

BEGIN
  Create_Name;    { Блок 1 }

  S1:='120022334455';

  { ---1---- }
  D:=$4D;
  For Z:=1 To Length(S) Do
  Begin
   D:=D+A[Z]+Z-1;
  End;

  asm
   mov ax, D
   and al, $3F
   xor ah, ah
   mov D, ax
  end;
  {End}

  S1[1]:=Chr(Symv(Lo(D)));

  {---- 2 ----}
  D:=Length(S)+$217;

  asm
   mov ax, D
   xor ax, $E5
   and ax, $3F
   xor ah, ah
   mov D, ax
  end;

   S1[2]:=Chr(Symv(Lo(D)));

  { end }

  {--- 3 ---}
  D:=$33;
  For Z:=1 To 6 Do
  Begin
   D:=D+A[Z]*(Z-1);
  End;

  asm
   mov ax, D
   and al, $3F
   xor ah, ah
   mov D, ax
  end;

  S1[3]:=Chr(Symv(Lo(D)));
 { end }


  {--- 4 ---}
  D:=$6B;
  For Z:=1 To Length(S) Do
    IF (Z+1) mod 2=0 then D:=D-A[Z] else D:=D+A[Z];
  asm
   mov ax, D
   and al, $3F
   xor ah, ah
   mov D, ax
  end;

  S1[4]:=Chr(Symv(Lo(D)));
 { end }


  {--- 5 ---}
  D:=$0F;
  For Z:=1 To Length(S) Do
  Begin
   C:=A[Z];
   asm
    mov ax, C
    mov cx, Z
    dec cx
    and cx, 7
    ror al, cl
    mov C, ax
   end;
    D:=D+C;
  End;

  asm
   mov ax, D
   and al, $3F
   xor ah, ah
   mov D, ax
  end;


  S1[5]:=Chr(Symv(Lo(D)));
 { end }


  {--- 6 ---}
  D:=$1C;
  For Z:=1 To Length(S) Do
  Begin
   C:=A[Z];
   asm
    mov ax, C
    mov cx, Z
    dec cx
    add cx,cx
    and cx, 7
    rol al, cl
    mov C, ax
   end;
    D:=D+C;
  End;

  asm
   mov ax, D
   and al, $3F
   xor ah, ah
   mov D, ax
  end;

  S1[6]:=Chr(Symv(Lo(D)));
 { end }

  Create_Code(S1); { Блок 2 }

  {Блок 5.1}
  A2[1]:=$0D;
  A2[2]:=$17;
  A2[3]:=$32;
  A2[4]:=$02;
  A2[5]:=$2C;
  I:=1;
  Z:=1;
  repeat
   if Z=6 then Z:=1;
   A2[Z]:=A[I]+A2[Z];
   inc(I);
   inc(Z);
  until I>Length(S);

  {Блок 5.2}
  I:=A1[$B]; {I - AL}
  Z:=A1[6]; {Z - BL}
  D:=A1[1]; {D - DL}
  C:=A2[1]; {C - CL}
  I:=I+Lo(Z);
  Z:=A1[2];
  I:=I+Lo(D);
  D:=A2[2];
  C:=C+Lo(I);
  I:=A1[7];
  A2[1]:=Lo(C);

  C:=A1[$C];
  C:=C+Lo(I);
  I:=A1[3];
  C:=C+Lo(Z);
  Z:=A2[3];
  D:=D+Lo(C);
  C:=A1[4];
  A2[2]:=Lo(D);

  D:=A1[8];
  D:=D+Lo(I);
  I:=A1[9];
  Z:=Z+Lo(D);
  D:=A1[5];
  A2[3]:=Lo(Z);

  Z:=A2[4];
  I:=I+Lo(C);
  C:=A1[$A];
  Z:=Z+Lo(I);
  I:=A2[5];
  C:=C+Lo(D);
  A2[4]:=Lo(Z);
  I:=I+Lo(C);
  A2[5]:=Lo(I);

  {Блок 5.3}
  For I:=1 To 5 Do
  Begin
   Z:=A2[I];
   asm
    mov bx, Z;
    and bl, 3Fh;
    mov Z, bx;
   end;
   A2[I]:=Z;
  End;

  For I:=1 To 5 Do A2[I]:=Symv(A2[I]); {Преобразуем кодовое представление последних пяти
  символов в настоящие символы}

  ClrScr;
  Writeln('KeyGen For AirXonix v1.36 by Fess [PTDS]');
  WriteLn('Name: ', S);
  Write('Code: ',S1);
  For I:=1 To 5 Do
   Write(Chr(A2[I]));

  If readkey=#0 then ; {Ждем нажатия на клавишу}
END.

Спасибо за интерес к моему творчеству!

Удачи в Reversing Engeneering!

Послесловие

Спасибо авторам за предоставленный для исследования продукт. Я получил несказанное удовольствие.

Господа Авторы: Молодцы! Хорошая защита, если не проверять, то наверняка крэкер клюнет на простоту генерации. Я клюнул. Если не играть, то сто пудово кейген будет не полностью рабочий.

Господа Крэкеры: Этот продукт наглядно демонстрирует пример того, что все свои патчи, лоадеры и кейгены НАДО ПОЛНОСТЬЮ ПРОВЕРЯТЬ! Иначе это грозит нехорошими последствиями пользователям.

Все ругательства отправлять в null
Все остальное на lomovskih@yandex.ru

P.S. Запомните все материалы публикуются только в учебных целях и автор за их использование ответственности не несет!!

P.P.S. Возможно имеют место опечатки, заранее извините!

With best wishes Fess

И да пребудет с вами великий дух bad-сектора.

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay