AirXonix v1.36 - Кейген на Паскале
|
- Папа, а хакеры хорошо получают?
- Хорошо, сынок, лет этак пятнадцать...
|
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-сектора.
|