Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 12 ноября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

Исследование Astral Masters 1.1

Обсудить статью на форуме

Очень удобно, когда все крэкерские инструменты, книги и статьи в одном месте. Используйте сборник от EXELAB - вот тут.

Автор: bkslash <bkslash#bk.ru>

Intro

Когда-то попросили меня сломать игрушку Владыки Астрала (http://www.astralmasters.com/), но то ли не было времени, то ли просто было влом, и в итоге скачал я cracked exe от tsrh (как позже выяснилось недоломанный) и дал его. И вот недавно появились у меня траблы с инетом, ничего не скачаешь, ничего не поломаешь, и стал я лазить по винту в поиске жертвы. =) Нетрудно догадаться, что этой жертвой оказалась та самая игрушка.

Tools

Итак, вот что нам понадобиться:
OllyDbg 1.10 – мой любимый отладчик.
IDA (версия не имеет значения) – лучший дизассемблер.
Masm32 + RadAsm - для написания кейгена.

Analysis

Грузим masters.exe в иду, пока она дизасмит, просмотрим нашу прогу в ольке. Сразу же бросается в глаза цикл, который, очевидно, парсит командную строку:

 004C0CD8 /LEA EDX,DWORD PTR SS:[EBP-18]
 004C0CDB |MOV EAX,ESI
 004C0CDD |. E8 661EF4FF    |CALL masters.00402B48
 004C0CE2 |MOV EAX,DWORD PTR SS:[EBP-18]
 004C0CE5 |LEA EDX,DWORD PTR SS:[EBP-14]
 004C0CE8 |CALL <masters.@Sysutils@UpperCase$qqrx17System@Ans>
 004C0CED |MOV EDX,DWORD PTR SS:[EBP-14]
 004C0CF0 |MOV EAX,masters.04153194
 004C0CF5 |CALL masters.00404E48
 004C0CFA |MOV EDX,DWORD PTR DS:[4153194]
 004C0D00 |MOV EAX,masters.004C0DDC                           ;  ASCII "-V"
 004C0D05 |CALL <masters.@System@@LStrPos$qqrv>
 004C0D0A |DEC EAX
 004C0D0B |JNZ SHORT masters.004C0D38
 004C0D0D |MOV EAX,DWORD PTR DS:[4D1EB4]
 004C0D12 |MOV BYTE PTR DS:[EAX],1
 004C0D15 |PUSH masters.04153190
 004C0D1A |MOV EAX,DWORD PTR DS:[4153194]
 004C0D1F |CALL <masters.lstrlen>
 004C0D24 |MOV ECX,EAX
 004C0D26 |SUB ECX,2
 004C0D29 |MOV EDX,3
 004C0D2E |MOV EAX,DWORD PTR DS:[4153194]
 004C0D33 |CALL <masters.@System@@LStrCopy$qqrv (001B: C3)>
 004C0D38 |MOV EAX,DWORD PTR DS:[4153194]
 004C0D3D |MOV EDX,masters.004C0DE8                           ;  ASCII "-WND"
 004C0D42 |CALL <masters.@System@@LStrCmp$qqrv>
 004C0D47 |JNZ SHORT masters.004C0D51
 004C0D49 |MOV EAX,DWORD PTR DS:[4D210C]
 004C0D4E |MOV BYTE PTR DS:[EAX],1
 004C0D51 |MOV EAX,DWORD PTR DS:[4153194]
 004C0D56 |MOV EDX,masters.004C0DF8                           ;  ASCII "-DBG"
 004C0D5B |CALL <masters.@System@@LStrCmp$qqrv>
 004C0D60 |JNZ SHORT masters.004C0D6A
 004C0D62 |MOV EAX,DWORD PTR DS:[4D20D4]
 004C0D67 |MOV BYTE PTR DS:[EAX],1
 004C0D6A |MOV EAX,DWORD PTR DS:[4153194]
 004C0D6F |MOV EDX,masters.004C0E08                           ;  ASCII "-CFG"
 004C0D74 |CALL <masters.@System@@LStrCmp$qqrv>
 004C0D79 |JNZ SHORT masters.004C0D83
 004C0D7B |MOV EAX,DWORD PTR DS:[4D2394]
 004C0D80 |MOV BYTE PTR DS:[EAX],1
 004C0D83 |MOV EAX,DWORD PTR DS:[4153194]
 004C0D88 |MOV EDX,masters.004C0E18                           ;  ASCII "-REG"
 004C0D8D |CALL <masters.@System@@LStrCmp$qqrv>
 004C0D92 |JNZ SHORT masters.004C0D9C
 004C0D94 |MOV EAX,DWORD PTR DS:[4D23DC]
 004C0D99 |MOV BYTE PTR DS:[EAX],1
 004C0D9C |INC ESI
 004C0D9D |DEC EBX
 004C0D9E \JNZ masters.004C0CD8
 

Больше всего меня заинтересовал параметр -REG, так что делаем в ольке Debug->Arguments, вводим туда -REG и перезапускаем. Теперь после запуска появляется окошко, в которое нас просят ввести имя и ключ. Ну что ж, вводим какую-нибудь лажу и созерцаем мессаджбокс, орущий о неверности введенных данных. Ставим бряк на MessageBoxA и проделываем аналогичную операцию. Брякнувшись на мессаджбоксе, несколько раз выходим из процедур по Ctrl-F9 пока не оказываемся в подозрительном месте:

 004BFCD6   . 68FD4B00       DD masters.004BFD68                                 ;  ASCII "^Error^"
 004BFCDA     E8             DB E8
 004BFCDB     D1             DB D1
 004BFCDC     37             DB 37                                               ;  CHAR ’7’
 004BFCDD     FA             DB FA
 004BFCDE     FF             DB FF
 004BFCDF     8B             DB 8B
 004BFCE0     45             DB 45                                               ;  CHAR ’E’
 004BFCE1     FC             DB FC
 004BFCE2     50             DB 50                                               ;  CHAR ’P’
 004BFCE3   . 8D55 F8        LEA EDX,DWORD PTR SS:[EBP-8]
 004BFCE6     B8             DB B8
 004BFCE7   . 78FD4B00       DD masters.004BFD78                                 ;  ASCII "^Sorry, the name or key is wrong.
 Please check them, use copy&paste for sure.^"
 004BFCEB     E8             DB E8
 004BFCEC     C0             DB C0
 004BFCED     37             DB 37                                               ;  CHAR ’7’
 004BFCEE     FA             DB FA
 004BFCEF     FF             DB FF
 004BFCF0     8B             DB 8B
 004BFCF1     45             DB 45                                               ;  CHAR ’E’
 004BFCF2     F8             DB F8
 004BFCF3     5A             DB 5A                                               ;  CHAR ’Z’
 004BFCF4     E8             DB E8
 004BFCF5     9F             DB 9F
 004BFCF6     BE             DB BE
 004BFCF7     F9             DB F9
 004BFCF8     FF             DB FF
 004BFCF9     33             DB 33                                               ;  CHAR ’3’
 004BFCFA     F6             DB F6
 004BFCFB     EB             DB EB
 004BFCFC     38             DB 38                                               ;  CHAR ’8’
 004BFCFD     84             DB 84
 004BFCFE     DB             DB DB
 004BFCFF     75             DB 75                                               ;  CHAR ’u’
 004BFD00     27             DB 27                                               ;  CHAR ’’’
 004BFD01     8D             DB 8D
 004BFD02     55             DB 55                                               ;  CHAR ’U’
 004BFD03     F4             DB F4
 004BFD04     B8             DB B8
 004BFD05   . D0FD4B00       DD masters.004BFDD0                                 ;  ASCII "^Thank you!^"
 

Жмем Ctrl-A для анализа кода и получаем уже нормальный асмовый код:

 004BFC98 MOV EDX,DWORD PTR DS:[4153188]
 004BFC9E MOV EAX,3
 004BFCA3 CALL masters.00462024
 004BFCA8 XOR EAX,EAX
 004BFCAA MOV DWORD PTR DS:[4153184],EAX
 004BFCAF CALL masters.004BFBC8
 004BFCB4 MOV EAX,DWORD PTR DS:[4153184]
 004BFCB9 AND EAX,0FFF
 004BFCBE MOV EDX,DWORD PTR DS:[415318C]
 004BFCC4 AND EDX,0FFF
 004BFCCA CMP EAX,EDX
 004BFCCC JE SHORT masters.004BFCFD
 004BFCCE TEST BL,BL
 004BFCD0 JNZ SHORT masters.004BFCF9
 004BFCD2 LEA EDX,DWORD PTR SS:[EBP-4]
 004BFCD5 MOV EAX,masters.004BFD68                                   ;  ASCII "^Error^"
 004BFCDA CALL masters.004634B0
 004BFCDF MOV EAX,DWORD PTR SS:[EBP-4]
 004BFCE2 PUSH EAX
 004BFCE3 LEA EDX,DWORD PTR SS:[EBP-8]
 004BFCE6 MOV EAX,masters.004BFD78                                   ;  ASCII "^Sorry, the name or key is wrong.Please check them, use copy&paste for sure.^"
 004BFCEB CALL masters.004634B0
 004BFCF0 MOV EAX,DWORD PTR SS:[EBP-8]
 004BFCF3 POP EDX
 004BFCF4 CALL <masters.@Qrprntr@AnsiPos$qqr17System@AnsiStringt1>
 004BFCF9 XOR ESI,ESI
 004BFCFB JMP SHORT masters.004BFD35
 004BFCFD TEST BL,BL
 004BFCFF JNZ SHORT masters.004BFD28
 004BFD01 LEA EDX,DWORD PTR SS:[EBP-C]
 004BFD04 MOV EAX,masters.004BFDD0                                   ;  ASCII "^Thank you!^"
 004BFD09 CALL masters.004634B0
 004BFD0E MOV EAX,DWORD PTR SS:[EBP-C]
 004BFD11 PUSH EAX
 004BFD12 LEA EDX,DWORD PTR SS:[EBP-10]
 004BFD15 MOV EAX,masters.004BFDE8                                   ;  ASCII "^Thank you for buying the Astral Masters game!^"
 004BFD1A CALL masters.004634B0
 

Этот код динамически расшифровывается во время исполнения программы, что, впрочем, совершенно нам не мешает. Итак, чтобы получить сообщение о зареганности, нужно чтобы содержимое (точнее последние 3 байта) двордов по адресам 4153184 и 415318C было одинаково. Поставив бряк на 004BFCAF и снова нажав на кнопочку "Ввод ключа" видим, что в 415318C уже что-то есть, а в 4153184 - ноль (что неудивительно, если посмотреть на код чуть выше). После исполнения процедуры masters.004BFBC8 значения есть уже в обеих переменных, значит нужно смотреть внутрь этой процедуры, интерес представляет этот цикл:

 004BFBFA /XOR ESI,ESI
 004BFBFC |MOV EBX,4D1D34                                            ;  "YZ23456789ABCDEFGHWJKLMNXPQRSTUV"
 004BFC01 |/MOV EAX,DWORD PTR DS:[4153180]
 004BFC06 ||MOV AL,BYTE PTR DS:[EAX+EDI-1]                           ;  берем символ ключа
 004BFC0A ||CMP AL,BYTE PTR DS:[EBX]
 004BFC0C ||JNZ SHORT 004BFC46                                       ;  masters.004BFC46
 004BFC0E ||MOV ECX,EDI
 004BFC10 ||DEC ECX
 004BFC11 ||MOV EAX,ESI
 004BFC13 ||AND EAX,1
 004BFC16 ||SHL EAX,CL
 004BFC18 ||ADD DWORD PTR DS:[4153184],EAX
 004BFC1E ||LEA ECX,DWORD PTR SS:[EBP-8]
 004BFC21 ||MOV EAX,ESI
 004BFC23 ||SAR EAX,1
 004BFC25 ||JNS SHORT 004BFC2A                                       ;  masters.004BFC2A
 004BFC27 ||ADC EAX,0
 004BFC2A ||MOV EDX,1
 004BFC2F ||CALL 00409370                                            ;  masters.00409370
 004BFC34 ||MOV EDX,DWORD PTR SS:[EBP-8]
 004BFC37 ||MOV EAX,DWORD PTR DS:[4D2458]
 004BFC3C ||CALL 004050BC                                            ;  masters.004050BC
 004BFC41 ||MOV EAX,DWORD PTR DS:[4D2458]
 004BFC46 ||INC ESI
 004BFC47 |INC EBX
 004BFC48 ||CMP ESI,20
 004BFC4B |\JNZ SHORT 004BFC01                                       ;  masters.004BFC01
 004BFC4D |INC EDI
 004BFC4E |DEC DWORD PTR SS:[EBP-4]
 004BFC51 \JNZ SHORT 004BFBFA                                        ;  masters.004BFBFA
 

Этот цикл посимвольно просматривает серийник, если очередной символ есть в таблице "YZ23456789ABCDEFGHWJKLMNXPQRSTUV", то в зависимости от его позиции в таблице к глобальной переменной 4153184 (как раз та, что нам нужна) добавляется некоторое значение: если символ в таблице на четной позиции (нумерация элементов массива с нуля), то добавляется 0, если на нечетной - 2*позиция_символа_в_ключе. Соответственно, чтобы получить нужное значение (то, что находится в 415318C) нужно проделать следующие нехитрые операции:
1) Преобразовать нужное значение (в случае имени bkslash - 0A901h) в двоичную систему - 1010100100000001.
2) Каждую единицу заменить на Z или 3 или 5 и т.д., ноль - на Y, 2, 4 и т.д. Получаем что-то вроде 5252522522222225.
3) Записать это в обратном порядке, получается 5222222252252525.

После ввода этого ключа прога радостно благодарит за покупку. Но если бы все было так просто, я бы не писал этого туториала. После запуска игры все равно видно надпись "ПРОБНАЯ ВЕРСИЯ", да и ограничения никуда не делись. Значит продолжаем исследование. Тем, у кого русская версия игры для облегчения исследования нужно удалить (или переименовать) файл language.dic в каталоге с игрой, в нем содержатся русские надписи, после удаления надписи становятся английскими. Итак, после удаления этого файла надпись сменилась на TRIAL VERSION, что ж, поищем эту строчку в иде. Она находится по адресу 004BC4E8, переходим по cross reference и видим такой код:

 CODE:004BC481                 mov     eax, ds:off_4D2460
 CODE:004BC486                 movzx   eax, byte ptr [eax+3]
 CODE:004BC48A                 imul    eax
 CODE:004BC48C                 and     ax, 0FFh
 CODE:004BC490                 cmp     ax, 39h
 CODE:004BC494                 jz      short loc_4BC4BD
 CODE:004BC496                 push    0C0C0D0E0h
 CODE:004BC49B                 push    offset aTrialVersion ; "^TRIAL VERSION^"
 CODE:004BC4A0                 push    0
 CODE:004BC4A2                 push    0
 CODE:004BC4A4                 mov     eax, ds:off_4D20B0
 CODE:004BC4A9                 mov     eax, [eax]
 CODE:004BC4AB                 mov     ecx, 2D4h
 CODE:004BC4B0                 mov     edx, 258h
 CODE:004BC4B5                 mov     ebx, [eax]
 CODE:004BC4B7                 call    dword ptr [ebx+8Ch]
 

Некий байт [eax+3] возводится в квадрат и младший байт полученного числа сравнивается с 39h, в случае равенства надпись TRIAL VERSION не выводится. После манипуляций с калькулятором я нашел число 739h, из которого извлекается целый квадратный корень, равный 2Bh. Это, конечно, хорошо, но нужно найти каким образом этот формируется байт. Так что ставим в ольке бряк на 004BC481, запускаем прогу. После загрузки мы действительно брякаемся в этом месте. Нужный байт находится по адресу 4E3A2F и равен 61h. Если поменять его на 2Bh, то надпись TRIAL VERSION действительно не выводится. Теперь нужно понять, как сделать этот байт равным 2Bh. Что ж, поставим аппаратный бряк на запись байта по адресу 4E3A2F и перезапустим прогу. После загрузки брякамся внутри процедуры @System@@FillChar$qqrv (спасибо сигнатуркам), которая заполняет нулями массив, элементом которого является тот самый заветный байт. Выйдя из нее видим 2 цикла, первый заполняет массив по какому-то алгоритму, а второй - ксорит его содержимое. Посмотрим на второй цикл:

 CODE:004A496A loc_4A496A:                             ; CODE XREF: sub_4A48C0+BEj
 CODE:004A496A                 mov     edx, ebx
 CODE:004A496C                 imul    edx, 25h
 CODE:004A496F                 mov     ecx, ds:off_4D1F90
 CODE:004A4975                 add     dl, [ecx]
 CODE:004A4977                 xor     [eax], dl
 CODE:004A4979                 inc     ebx
 CODE:004A497A                 inc     eax
 CODE:004A497B                 cmp     ebx, 0Bh
 CODE:004A497E                 jnz     short loc_4A496A
 

В инструкции add dl, [ecx] по адресу [ecx] находится младший байт той самой переменной, которая формируется от имени, ее, естественно, менять нельзя, значит, основной интерес представляет первый цикл, формирующий изначальное содержимое массива:

 CODE:004A490B loc_4A490B:                             ; CODE XREF: sub_4A48C0+A1j
 CODE:004A490B                 lea     eax, [ebp+var_4]
 CODE:004A490E                 mov     edx, ds:off_4D2458
 CODE:004A4914                 mov     edx, [edx]
 CODE:004A4916                 mov     dl, [edx+ebx-1]
 CODE:004A491A                 call    @System@@LStrFromChar$qqrr17System@AnsiStringc ; System::__linkproc__ LStrFromChar(System::AnsiString &,char)
 CODE:004A491F                 mov     eax, [ebp+var_4]
 CODE:004A4922                 call    sub_45AFB8
 CODE:004A4927                 test    bl, 1
 CODE:004A492A                 jnz     short loc_4A4945
 CODE:004A492C                 mov     edx, ebx
 CODE:004A492E                 dec     edx
 CODE:004A492F                 sar     edx, 1
 CODE:004A4931                 jns     short loc_4A4936
 CODE:004A4933                 adc     edx, 0
 CODE:004A4936
 CODE:004A4936 loc_4A4936:                             ; CODE XREF: sub_4A48C0+71j
 CODE:004A4936                 mov     ecx, ds:off_4D2460
 CODE:004A493C                 lea     edx, [ecx+edx]
 CODE:004A493F                 push    edx
 CODE:004A4940                 pop     edx
 CODE:004A4941                 add     [edx], al
 CODE:004A4943                 jmp     short loc_4A495F
 CODE:004A4945 ; ---------------------------------------------------------------------------
 CODE:004A4945
 CODE:004A4945 loc_4A4945:                             ; CODE XREF: sub_4A48C0+6Aj
 CODE:004A4945                 mov     edx, ebx
 CODE:004A4947                 dec     edx
 CODE:004A4948                 sar     edx, 1
 CODE:004A494A                 jns     short loc_4A494F
 CODE:004A494C                 adc     edx, 0
 CODE:004A494F
 CODE:004A494F loc_4A494F:                             ; CODE XREF: sub_4A48C0+8Aj
 CODE:004A494F                 mov     ecx, ds:off_4D2460
 CODE:004A4955                 lea     edx, [ecx+edx]
 CODE:004A4958                 push    edx
 CODE:004A4959                 shl     eax, 4
 CODE:004A495C                 pop     edx
 CODE:004A495D                 add     [edx], al
 CODE:004A495F
 CODE:004A495F loc_4A495F:                             ; CODE XREF: sub_4A48C0+83j
 CODE:004A495F                 inc     ebx
 CODE:004A4960                 dec     esi
 CODE:004A4961                 jnz     short loc_4A490B
 

Немного потрейсив этот цикл становится ясно, что он всего лишь преобразует строку по адресу [[4D2458]] (в моем случае - "21111111211212120000" в бинарный вид. Нужно разбираться каким же образом получается эта строка, Делаем hw [4d2458] и перезапускаем прогу. Брякаемся внутри @System@@LStrAsg$qqrpvpxv, выходим оттуда и видим, что по нужному адресу уже лежит "21111111211212120000", значит эта строчка сформировалась раньше, а это - уже результат. Вот сама процедура:

 CODE:004A453C                 push    ebp
 CODE:004A453D                 mov     ebp, esp
 CODE:004A453F                 add     esp, 0FFFFFEA8h
 CODE:004A4545                 push    ebx
 CODE:004A4546                 push    esi
 CODE:004A4547                 push    edi
 CODE:004A4548                 xor     eax, eax
 CODE:004A454A                 mov     [ebp+var_4], eax
 CODE:004A454D                 mov     [ebp+var_C], eax
 CODE:004A4550                 xor     eax, eax
 CODE:004A4552                 push    ebp
 CODE:004A4553                 push    offset loc_4A4870
 CODE:004A4558                 push    dword ptr fs:[eax]
 CODE:004A455B                 mov     fs:[eax], esp
 CODE:004A455E                 push    40000h
 CODE:004A4563                 lea     eax, [ebp+var_4]
 CODE:004A4566                 mov     ecx, 1
 CODE:004A456B                 mov     edx, off_4A4514
 CODE:004A4571                 call    unknown_libname_160 ; Borland Visual Component Library & Packages
 CODE:004A4576                 add     esp, 4
 CODE:004A4579                 mov     edx, offset aLookup_dat ; "lookup.dat"
 CODE:004A457E                 lea     eax, [ebp+var_158]
 CODE:004A4584                 call    @System@@Assign$qqrr15System@TTextRecx17System@AnsiString ; System::__linkproc__ Assign(System::TTextRec &,System::AnsiString)
 CODE:004A4589                 xor     eax, eax
 CODE:004A458B                 push    ebp
 CODE:004A458C                 push    offset loc_4A46EA
 CODE:004A4591                 push    dword ptr fs:[eax]
 CODE:004A4594                 mov     fs:[eax], esp
 CODE:004A4597                 mov     edx, 1
 CODE:004A459C                 lea     eax, [ebp+var_158]
 CODE:004A45A2                 call    @System@@ResetFile$qqrr15System@TFileReci ; System::__linkproc__ ResetFile(System::TFileRec &,int)
 CODE:004A45A7                 call    @System@@_IOTest$qqrv ; System::__linkproc__ _IOTest(void)
 CODE:004A45AC                 push    0
 CODE:004A45AE                 mov     edx, [ebp+var_4]
 CODE:004A45B1                 mov     ecx, 40000h
 CODE:004A45B6                 lea     eax, [ebp+var_158]
 CODE:004A45BC                 call    @System@@BlockRead$qqrr15System@TFileRecpviri ; System::__linkproc__ BlockRead(System::TFileRec &,void *,int,int &)
 CODE:004A45C1                 call    @System@@_IOTest$qqrv ; System::__linkproc__ _IOTest(void)
 CODE:004A45C6                 mov     edx, offset byte_4D1C8C
 CODE:004A45CB                 mov     eax, [ebp+var_4]
 CODE:004A45CE                 add     eax, 3FF70h
 CODE:004A45D3                 mov     ecx, 4
 CODE:004A45D8                 call    @System@Move$qqrpxvpvi ; System::Move(void *,void *,int)
 CODE:004A45DD                 lea     eax, [ebp+var_158]
 CODE:004A45E3                 call    @System@@Close$qqrr15System@TTextRec ; System::__linkproc__ Close(System::TTextRec &)
 CODE:004A45E8                 call    @System@@_IOTest$qqrv ; System::__linkproc__ _IOTest(void)
 CODE:004A45ED                 mov     bl, 1
 CODE:004A45EF                 lea     eax, [ebp+var_C]
 CODE:004A45F2                 call    @System@@LStrClr$qqrpv ; System::__linkproc__ LStrClr(void *)
 CODE:004A45F7                 mov     eax, [ebp+var_4]
 CODE:004A45FA                 movzx   esi, byte ptr [eax+111h]
 CODE:004A4601                 mov     eax, [ebp+var_4]
 CODE:004A4604                 movzx   eax, byte ptr [eax+0C0h]
 CODE:004A460B                 shl     eax, 8
 CODE:004A460E                 add     esi, eax
 CODE:004A4610                 mov     eax, [ebp+var_4]
 CODE:004A4613                 movzx   eax, byte ptr [eax+11Ch]
 CODE:004A461A                 shl     eax, 10h
 CODE:004A461D                 add     esi, eax
 CODE:004A461F                 mov     eax, [ebp+var_4]
 CODE:004A4622                 movzx   eax, byte ptr [eax+333h]
 CODE:004A4629                 mov     edx, [ebp+var_4]
 CODE:004A462C                 movzx   edx, byte ptr [edx+243h]
 CODE:004A4633                 shl     edx, 8
 CODE:004A4636                 add     eax, edx
 CODE:004A4638                 mov     edx, [ebp+var_4]
 CODE:004A463B                 movzx   edx, byte ptr [edx+2A1h]
 CODE:004A4642                 shl     edx, 10h
 CODE:004A4645                 add     eax, edx
 CODE:004A4647                 mov     [ebp+var_8], eax
 CODE:004A464A                 cmp     esi, [ebp+var_8]
 CODE:004A464D                 jnz     short loc_4A46C6
 CODE:004A464F                 mov     eax, esi
 CODE:004A4651                 mov     ecx, 9
 CODE:004A4656                 cdq
 CODE:004A4657                 idiv    ecx
 CODE:004A4659                 test    edx, edx
 CODE:004A465B                 jnz     short loc_4A46C6
 CODE:004A465D                 mov     eax, esi
 CODE:004A465F                 mov     ecx, 9
 CODE:004A4664                 cdq
 CODE:004A4665                 idiv    ecx
 CODE:004A4667                 mov     esi, eax
 CODE:004A4669                 lea     eax, [ebp+var_C]
 CODE:004A466C                 mov     edx, offset a000000000000_0 ; "00000000000000000000"
 CODE:004A4671                 call    @System@@LStrLAsg$qqrpvpxv ; System::__linkproc__ LStrLAsg(void *,void *)
 CODE:004A4676                 mov     edi, 1
 CODE:004A467B
 CODE:004A467B loc_4A467B:                             ; CODE XREF: sub_4A453C+172j
 CODE:004A467B                 lea     eax, [ebp+var_C]
 CODE:004A467E                 call    sub_40530C
 CODE:004A4683                 mov     edx, [ebp+var_4]
 CODE:004A4686                 mov     dl, [edx+esi]
 CODE:004A4689                 mov     [eax+edi-1], dl
 CODE:004A468D                 lea     eax, [edi+129h]
 CODE:004A4693                 add     esi, eax
 CODE:004A4695                 mov     eax, [ebp+var_C]
 CODE:004A4698                 mov     al, [eax+edi-1]
 CODE:004A469C                 add     al, 0D0h
 CODE:004A469E                 sub     al, 0Ah
 CODE:004A46A0                 jb      short loc_4A46AA
 CODE:004A46A2                 add     al, 0F9h
 CODE:004A46A4                 sub     al, 6
 CODE:004A46A6                 jb      short loc_4A46AA
 CODE:004A46A8                 xor     ebx, ebx
 CODE:004A46AA
 CODE:004A46AA loc_4A46AA:                             ; CODE XREF: sub_4A453C+164j
 CODE:004A46AA                                         ; sub_4A453C+16Aj
 CODE:004A46AA                 inc     edi
 CODE:004A46AB                 cmp     edi, 15h
 CODE:004A46AE                 jnz     short loc_4A467B
 CODE:004A46B0                 mov     eax, [ebp+var_4]
 CODE:004A46B3                 movzx   edi, byte ptr [eax+esi]
 CODE:004A46B7                 mov     eax, [ebp+var_4]
 CODE:004A46BA                 movzx   eax, byte ptr [eax+esi+2]
 CODE:004A46BF                 shl     eax, 8
 CODE:004A46C2                 add     edi, eax
 CODE:004A46C4                 jmp     short loc_4A46C8
 CODE:004A46C6 ; ---------------------------------------------------------------------------
 CODE:004A46C6
 CODE:004A46C6 loc_4A46C6:                             ; CODE XREF: sub_4A453C+111j
 CODE:004A46C6                                         ; sub_4A453C+11Fj
 CODE:004A46C6                 xor     ebx, ebx
 CODE:004A46C8
 CODE:004A46C8 loc_4A46C8:                             ; CODE XREF: sub_4A453C+188j
 CODE:004A46C8                 test    bl, bl
 CODE:004A46CA                 jz      short loc_4A46E0
 CODE:004A46CC                 mov     eax, ds:off_4D2458
 CODE:004A46D1                 mov     edx, [ebp+var_C]
 CODE:004A46D4                 call    @System@@LStrAsg$qqrpvpxv ; System::__linkproc__ LStrAsg(void *,void *)
 

Вначале открывается файл lookup.dat, из которого каким-то образом и считываются нужные данные. Вот только размер этого файла целых 256 кб, похоже, основная часть этого файла - мусор. Кстати, если нужно ввести другие регистрационные данные, нужно сначала удалить этот файл, иначе никакого эффекта не будет. Мда, разбираться со всей этой фигней что-то мне совсем не хотелось, и решил я вводить разные серийники на свое имя из разных комбинаций символов (помните, единички и нолики могут кодироваться разными символами?) в надежде увидеть какую-нибудь закономерность. И увидел. =) Буквы Y и Z ключа соответствуют нулю в искомой строке (из которой формируется массив), цифры 2 и 3 - единице и т.д. Вот полный список:
YZ -> 0
23 -> 1
45 -> 2
67 -> 3
89 -> 4
AB -> 5
CD -> 6
EF -> 7
GH -> 8
IJ -> 9
KL -> A
MN -> B
OP -> C
QR -> D
ST -> E
UV -> F
Все начинает проясняться, попробуем получить серийник, при котором нет надписи TRIAL VERSION. Для этого в уже упомянутом "втором" цикле посмотрим с чем ксорится третий (считая от нуля) байт массива. В моем случае это оказывается 70h, а нам нужно получить 2Bh, 70h xor 2Bh = 5Bh - это значение нужно получить в массиве. Таким образом, наш серийник становится таким: 522222AM52252525. Удаляем lookup.dat, вводим серийник и действительно наблюдаем отсутствие назойливой надписи. Увы, это не конец, остальные ограничения остались. Логично предположить, что остальные байты массива тоже придуманы не просто так. На массив указывает указатель по адресу 004D2460, посмотрим в иде на cross references, их аж 9 штук. Четыре из них указывают на формирование массива, еще одна - на уже преодоленную надпись TRIAL VERSION. Посмотрим на остальные по порядку байт в массиве. Итак, первая следует сразу же после "второго" цикла:

 CODE:004A4980                 mov     eax, ds:off_4D2460
 CODE:004A4985                 movzx   eax, byte ptr [eax]
 CODE:004A4988                 mov     edx, ds:off_4D2460
 CODE:004A498E                 movzx   edx, byte ptr [edx+1]
 CODE:004A4992                 shl     edx, 8
 CODE:004A4995                 add     eax, edx
 CODE:004A4997                 and     eax, 3FFFh
 CODE:004A499C                 mov     edx, ds:off_4D203C
 CODE:004A49A2                 mov     [edx], eax
 

Из нулевого и первого байт получается дворд, который записывается по адресу [4D203C]=4E3A28. К нему есть всего 2 cross references, одна из которых, собственно, сама запись значения (см. код выше). Смотрим на вторую:

 CODE:004A4F51                 cmp     dword ptr [eax], 8
 CODE:004A4F54                 jnz     short loc_4A4F5A
 CODE:004A4F56
 CODE:004A4F56 loc_4A4F56:                             ; CODE XREF: sub_4A4E78+D2j
 CODE:004A4F56                 xor     eax, eax
 CODE:004A4F58                 jmp     short loc_4A4F5C
 

Сначала кажется, что это значение должно быть равно 8. Однако если посмотреть на код чуть выше, становится ясно, что все с точностью до наоборот: это значение должно быть не равно 8. А в коде выше как раз располагается проверка 2го байта массива:

 CODE:004A4F41                 mov     eax, ds:off_4D2460
 CODE:004A4F46                 cmp     byte ptr [eax+2], 31h
 CODE:004A4F4A                 jnz     short loc_4A4F56
 CODE:004A4F4C                 mov     eax, ds:off_4D203C
 CODE:004A4F51                 cmp     dword ptr [eax], 8
 CODE:004A4F54                 jnz     short loc_4A4F5A
 CODE:004A4F56
 CODE:004A4F56 loc_4A4F56:                             ; CODE XREF: sub_4A4E78+D2j
 CODE:004A4F56                 xor     eax, eax
 CODE:004A4F58                 jmp     short loc_4A4F5C
 CODE:004A4F5A ; ---------------------------------------------------------------------------
 CODE:004A4F5A
 CODE:004A4F5A loc_4A4F5A:                             ; CODE XREF: sub_4A4E78+DCj
 CODE:004A4F5A                 mov     al, 1
 CODE:004A4F5C
 CODE:004A4F5C loc_4A4F5C:                             ; CODE XREF: sub_4A4E78+E0j
 CODE:004A4F5C                 mov     ds:byte_4D1C88, al
 

Этот байт должен принимать значение 31h, он ксорится на 4Bh, 31h xor 4Bh = 7Ah, таким образом наш ключ становится уже таким: 5222EKAM52252525. После его ввода чудесным образом исчезает ограничение на карты и наг. На первый взгляд программа взломана. Но все же авторы сумели оставить еще одну подлянку: поиграв в эту игрушку, я прошел в ней начальную лигу - и тут прога страшно заглючила. Что ж, осталась последняя cross reference к массиву:

 CODE:0049E1D8                 mov     edx, ds:off_4D2460
 CODE:0049E1DE                 mov     dl, [edx+3]
 CODE:0049E1E1                 xor     dl, 0F6h
 CODE:0049E1E4                 and     edx, 0FFh
 CODE:0049E1EA                 add     edx, 3E8h
 CODE:0049E1F0                 mov     eax, 0Ch
 CODE:0049E1F5                 call    sub_462024
 CODE:0049E1FA
 CODE:0049E1FA loc_49E1FA:                             ; CODE XREF: sub_49E1B4+18j
 CODE:0049E1FA                                         ; sub_49E1B4+22j
 CODE:0049E1FA                 mov     edx, 0Ch
 CODE:0049E1FF                 lea     eax, [ebx+47h]
 CODE:0049E202
 CODE:0049E202 loc_49E202:                             ; CODE XREF: sub_49E1B4+55j
 CODE:0049E202                 mov     byte ptr [eax], 0
 CODE:0049E205                 add     eax, 40h
 CODE:0049E208                 dec     edx
 CODE:0049E209                 jnz     short loc_49E202
 CODE:0049E20B                 mov     eax, [ebx+4]
 CODE:0049E20E                 dec     eax
 CODE:0049E20F                 jz      short loc_49E219
 CODE:0049E211                 dec     eax
 CODE:0049E212                 jz      short loc_49E222
 CODE:0049E214                 dec     eax
 CODE:0049E215                 jz      short loc_49E22B
 CODE:0049E217                 jmp     short loc_49E232
 CODE:0049E219 ; ---------------------------------------------------------------------------
 CODE:0049E219
 CODE:0049E219 loc_49E219:                             ; CODE XREF: sub_49E1B4+5Bj
 CODE:0049E219                 mov     eax, ebx
 CODE:0049E21B                 call    loc_49DCA8
 CODE:0049E220                 jmp     short loc_49E232
 

Здесь проверяется третий байт, который мы уже получили. Однако он получился подбором нужного числа, и вполне возможно, что таких чисел есть несколько. Чтобы это проверить, я написал простенькую программку на сях, состоящую всего из одного цикла:

 for(i=1;i<256;i++)
 		if(((i*i)&0xff)==0x39)
 			printf("%X\n",i);
 

В результате ее работы получились вот такие значения: 2B 55 AB D5. 2Bh уже было, с 55h оказалась аналогичная ситуация, а вот ABh дало ожидаемый результат - игра перестала глючить при переходе в новую лигу. Финальный серийник выглядит вот так: 5222EKQM52252525.

Keygenning

Собственно, практически все для написания кейгена уже описано, осталась только небольшая деталь - генерация того числа, зависящего от имени. Ну здесь в общем-то проблем никаких нет, ставим бряк на запись в 415318C, оказываемся тут:

 CODE:004BFE76                 mov     ds:dword_415318C, 1
 CODE:004BFE80                 mov     eax, ds:dword_415317C
 CODE:004BFE85                 call    lstrlen  
 CODE:004BFE8A                 test    eax, eax
 CODE:004BFE8C                 jle     short loc_4BFEB8
 CODE:004BFE8E                 mov     ebx, 1
 CODE:004BFE93
 CODE:004BFE93 loc_4BFE93:                             ; CODE XREF: sub_4BFE18+9Ej
 CODE:004BFE93                 mov     edx, ds:dword_415317C
 CODE:004BFE99                 movzx   edx, byte ptr [edx+ebx-1]
 CODE:004BFE9E                 mov     ecx, ebx
 CODE:004BFEA0                 dec     ecx
 CODE:004BFEA1                 mov     edi, ecx
 CODE:004BFEA3                 shl     ecx, 3
 CODE:004BFEA6                 sub     ecx, edi
 CODE:004BFEA8                 add     ecx, 25h
 CODE:004BFEAB                 imul    edx, ecx
 CODE:004BFEAE                 add     ds:dword_415318C, edx
 CODE:004BFEB4                 inc     ebx
 CODE:004BFEB5                 dec     eax
 CODE:004BFEB6                 jnz     short loc_4BFE93
 

Описывать здесь особо нечего, можно просто рипнуть этот код. Вкратце еще раз опишу весь алгоритм:
1) От имени рассчитать нужное число.
2) Разбить полученное число на биты.
3) Создаем массив на 10 байт, второй байт - 31h, третий - ABh, нулевой и первый не должный образовывать восьмерку, остальные - любые.
4) Ксорим массив по алгоритму "второго" цикла.
5) Преобразуем массив с hex-строку.
6) Заменяем его содержимое в соответствии с таблицей "YZ23456789ABCDEFGHWJKLMNXPQRSTUV", уичтывая значения соответствующих битов.
Мой вариант кейгена с исходниками на асме, а также сейв для демонстрации глюка с переходом в следующую лигу можно скачать тут: http://bkslash.nm.ru/Astral.rar

Обсуждение статьи: Исследование Astral Masters 1.1 >>>


Комментарии к статье: Исследование Astral Masters 1.1

junk 03.10.2005 05:26:42
Спасибо за статью, реальное исследование.
Сейчас версию 1.2а ковыряю, очень помогло, правда там уже и 4ый байт задействован, (д.б.=03), и еще какой-то, ищу.
А как ты исключения в Олли проходил? Я замаялся Ctrl+F9 жать, плюнул на это дело, и в Айсе доламываю. Если не секрет, конечно.
---
bkslash 03.10.2005 07:40:39
ну дык в игнор все исключения =) (Options->Debugging options->Exceptions)
для 1.2 у меня тоже уже есть кейген, ;) там кроме четвертого (т.е. пятого если от нуля считать) байта еще длина серийника проверяется
---
junk 07.10.2005 11:34:35
В игнор ставил все галки - без изменений.
Может из-за W2k? Ты под XP ломишь?
насчет того что 5ый байт д.б.=03 может был неправ, ибо после проверки, где идет умножение на 3d9h/11h и edx д.б.=0eh, далее по тексту идет проверка с умножением
на 4eh и выделением младшего байта, при этом он д.б.=5ah - при 3ке не катит...
при производных тоже - 13h,23h,..
Можт ошибка проги, хотя раз у тебя рабочий кейген, значит чет я гоню.
Вообщем поступил я неспортивно, запатчил чтоб всегда 5ah, заценил сам объект, и скажу, что больная игра на мой взгляд, в Мастер лиге комп играет как пожелает, хошь 4 одинаковых заклятья - пжалста, 5 - да нет вопросов.. Неспортивно играет.
А длина серийника=16, че ее проверять?
Или ты про длину массива, который XORим?

---
Necroman 07.10.2005 18:48:03
blyn eto mne ponravilos\’
---
bkslash 06.10.2005 23:20:00
2 junk
выбери в ольке Ignore also following custom exceptions or ranges, и там от 0 до FFFFFFFF =)
пятый байт должен быть равен 8B
---
junk 08.10.2005 08:51:27
2bkslash
Внатуре 8B, вот я блин ленивый напрочь, так же как и в первом случае, где ты программку маленткую на си показал. И здесь так же, чет торможу блин последнее время.
Olly не смотрел еще, сделаю как ты описал, посмотрю.
Добра тебе bkslash и спасибо за поддержку, ты рульный чел.

---
Tuc 19.10.2005 10:37:05
Bkslash, А к версии 1.2 кряк не выложишь?
---
Nitrogen 06.11.2005 14:06:58
у меня в кейгене
invoke my_astral,00Bh,25h*0,namehash,addr[serial+00h] ;12
invoke my_astral,000h,25h*1,namehash,addr[serial+02h] ;1234
invoke my_astral,031h,25h*2,namehash,addr[serial+04h] ;123456
invoke my_astral,0ABh,25h*3,namehash,addr[serial+06h] ;12345678
invoke my_astral,08Bh,25h*4,namehash,addr[serial+08h] ;123456789A
invoke my_astral,0C6h,25h*5,namehash,addr[serial+0Ah] ;123456789ABC
все основано на серийнике, который в блеклисте
---

Материалы находятся на сайте https://exelab.ru



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS