               [ Trzy slowa o Crackme #2 by WiteG              ]
                 ._____/\_______/\_______/\_______/\_________.
                _/____   \ ____   \   __   \        \   _____\_
               /   ___    \ ___    \   _\   \  \-----\  \_     \
               \_______\__/_____\__/________/________/_________/
               -/\ A.A.O.C.G. 2oo2 /\---------------------tnb!--
               [ 09/01/2002 ][ Liquid / AAoCG                  ]

 Yooo...

 To ja... W jakis dziwny sposob ktory trudno opisac trafilem na strone WiteG'a,
 ktory  jak  zapewnie  wiesz lubi, i idzie mu to calkiem niezle, rozpierdzielac
 crackmesy.  Gratulacje  za checi i wytrwalosc. Ale WiteG napisal rowniez kilka
 wlasnych  crackmes,  ktore  mnie  zainteresowaly.  Najpierw spojrzalem sobie w
 Crackme  #2, jako ze opatrzone bylo komentarzem "Easy", a mi sie za specjalnie
 nie  chcialo  grzebac  w  czyms  trudniejszym.  No  wiec  pociagnalem sobie...
 rozpakowalem  i  patrze  co  sie  do  mnie  pisze...

 Wiec   dowiedzialem   sie,   zamotania  nie  bedzie,  kod  na  wierzchu,  brak
 pierdzielenia  sie  z  junkami,  antisami, pakowankiem i innym scierwem. Tylko
 czyste  liczonko,  bardzo przyjemnie. W dodatku jeszcze dla zachety informacja
 ze  "wszystko  jest  odwracalne",  oraz  jako  warunek  rozwiazania  napisanie
 generatora  plikow  rejestracyjnych.  No  i  pieknie...

 Wiec na poczatek polecialem sobie z disasmem uzywajac boskiej IDA'y. Wywolanie
 DialogBoxParamA  juz  na  wejsciu,  wiec  wchodze  sobie do procki obslugujaca
 dialog. Tez bardzo klarowna. Zarowno skok jesli [ebp+0Ch] (czyli komunikat) ==
 0111h (WM_INITDIALOG), jak i klikniecie na przycisk o ID 03EAh prowadzi szybko
 do tego samego:

 .text:004010A4                 push    0
 .text:004010A6                 push    0
 .text:004010A8                 push    3           ; otwarcie istniejacego
 .text:004010AA                 push    0
 .text:004010AC                 push    0
 .text:004010AE                 push    80000000h            ; tylko odczyt
 .text:004010B3                 push    offset aCrackme2_wtg ; nazwa pliku
 .text:004010B8                 call    CreateFileA          ; b00m

 Czyli  otwarcia  pliku  o  nazwie  "crackme2.wtg".  Jesli  go nie ma - wiadomo
 - wylatujesz.  Ok...  co  dalej?

 .text:004010D1                 call    GetFileSize
 .text:004010D6                 cmp     eax, 37Bh

 Sprawdzanie  rozmiaru  pliku, extra. Od razu wiadomo jaka ma miec wielkosc nie
 trzeba  bedzie  tego sprawdzac. 37Bh == 891 bajtow. Dalej nastepuje odczytanie
 danych  z  pliku  i  ich  sprawdzanie:

 .text:004010E1                 push    0
 .text:004010E3                 push    offset unk_4033C8
 .text:004010E8                 push    eax
 .text:004010E9                 push    offset unk_4033CC
 .text:004010EE                 push    hFile
 .text:004010F4                 call    ReadFile ; czytanie z pliku

 .text:004010F9                 mov     ecx, 9
 .text:004010FE                 mov     eax, offset unk_403737
 .text:00401103 loc_401103:
 .text:00401103                 call    sub_40126B ; procka pierwsza
 .text:00401108                 inc     eax
 .text:00401109                 dec     ecx
 .text:0040110A                 jnz     short loc_401103 ; petelka pierwsza

 .text:0040110C                 mov     edx, 36Ch
 .text:00401111                 mov     edi, offset unk_4033CB
 .text:00401116 loc_401116:
 .text:00401116                 inc     edi
 .text:00401117                 mov     ebx, 0FFFFFFF0h
 .text:0040111C                 xor     eax, eax
 .text:0040111E                 xor     ecx, ecx
 .text:00401120 loc_401120: ; deszyfrowanie pliku
 .text:00401120                 mov     cl, byte ptr unk_4030B9[ebx]
 .text:00401126                 mov     al, [ebx+edi+10h]
 .text:0040112A                 xor     al, [ebx+403010h]
 .text:00401130                 mov     al, byte ptr unk_4030B9[eax]
 .text:00401136                 mov     [ebx+edi+10h], al
 .text:0040113A                 xor     [ecx+edi], al
 .text:0040113D                 inc     ebx
 .text:0040113E                 jnz     short loc_401120 ; petelka 2 w...
 .text:00401140                 dec     edx
 .text:00401141                 jnz     short loc_401116 ; ... petelce 3ciej

 .text:00401143                 call    sub_401194 ; procka druga
 .text:00401148                 jb      short loc_401172 ; jesli skoczy...
                                       ; ...serial NIE jest poprawny

 Jesli  B  nie  ustawione  nastepuje  male  sklejenie stringu spod unk_403737 z
 "Registered to: ".  Mozna  wiec  przyjac  w ciemno ze pod 00403737h oczekiwany
 jest  name  osoby dla ktorej program zostanie zarejestrowany. Jest to ostatnie
 16  bajtow  w  pliku-kluczu, wiec na name zostaje 15 znakow plus zero konczace
 string.  Dosc  niewiele,  ale  po  co  komu  wiecej.

 Ok... przypatrzmy sie kolejno poszczegolnym czesciom:

 1. Procka pierwsza w petli pierwszej.

 Zaczyna  od  miejsca  gdzie  sie  name  rozpoczyna,  i przebiega tylko 8 razy,
 wywolujac  procke:

 .text:0040126E                 mov     edx, 0C6EF3720h ; dword-key
                               ; w EAX jest pointer na name+przesuniecie
 .text:00401273                 mov     esi, [eax]
 .text:00401275                 mov     edi, [eax+4] ; tylko 8 znakow jest
                               ; pobieranych
 .text:00401278                 mov     ebp, 20h ; przebiegow petli
 .text:0040127D loc_40127D:
 ;------------------------------------------
 ; Fragment pierwszy:
 ;--------------------------------------------
 .text:0040127D                 mov     eax, esi ; (1)
 .text:0040127F                 mov     ebx, esi
 .text:00401281                 mov     ecx, esi

 .text:00401283                 shl     eax, 4   ; (2)
 .text:00401286                 add     eax, dword ptr aTryToCrackThis+4

 .text:0040128C                 shr     ebx, 5   ; (3)
 .text:0040128F                 add     ebx, dword ptr aTryToCrackThis+0Ch

 .text:00401295                 add     ecx, edx ; (4)
 .text:00401297                 xor     ecx, eax
 .text:00401299                 xor     ecx, ebx

 .text:0040129B                 sub     edi, ecx ; (5)
 ;--------------------------------------------
 ; Fragment drugi:
 ;--------------------------------------------
 .text:0040129D                 mov     eax, edi
 .text:0040129F                 mov     ebx, eax
 .text:004012A1                 mov     ecx, eax

 .text:004012A3                 shl     eax, 4
 .text:004012A6                 add     eax, dword ptr aTryToCrackThis
                                ; "Try to crack this little crackme.\r\nWrit"

 .text:004012AC                 shr     ebx, 5
 .text:004012AF                 add     ebx, dword ptr aTryToCrackThis+8

 .text:004012B5                 add     ecx, edx
 .text:004012B7                 xor     ecx, eax
 .text:004012B9                 xor     ecx, ebx

 .text:004012BB                 sub     esi, ecx
 ;--------------------------------------------
 ; zmiana dla kolejnego przebiegu petli
 ;--------------------------------------------
 .text:004012BD                 sub     edx, 9E3779B9h
 .text:004012C3                 dec     ebp
 .text:004012C4                 jnz     short loc_40127D
 ;--------------------------------------------
 .text:004012C6                 pop     eax ; wczesniej zachowany pointer
 .text:004012C7                 mov     [eax], esi   ; zapisanie wartosci
 .text:004012C9                 mov     [eax+4], edi ; w poprzednie miejsca

 Mala analiza:

 Najpierw  do EAX, EBX, ECX, kopiowane sa pierwsze 4 znaki name (1), czy raczej
 tego co po calym przebiegu ma nam to name zwrocic. Jak widac w (5) od EDI jest
 odejmowany  wynik  kilku prostych dzialan (2),(3),(4) na reszcie rejestrow. Co
 jednak  ciekawe, dzialania te nie sa od EDI nijak uzaleznione, wiec nie musimy
 tej  funkcji odwracac, bo wynik w ECX i tak mamy uzyskac ten sam, z ta roznica
 ze  trzeba  go  bedzie  do  EDI  dodac, oraz to ze teraz trzeba bedzie zmienic
 fragmenty  miejscami,  by  najpierw  wlasciwa  wartosc zostala odjeta od ESi a
 pozniej  od EDI. Aby to odkrecic trzeba tez miec poczatkowa wartosc EDX, ktore
 zmieniane jest tylko poprzez odejmowanie od rejestru 09E3779B9h, a jak latwo i
 szybko  mozna obliczyc, po 8krotnym wykonaniu tej operacji uzyskamy 0. Od razu
 mozemy  napisac kod ktory bedzie wykonywal operacje odwrotne, czyli zaszyfruje
 nam  to  name  tak  by  odkodowane  zwrocilo  poprawne  wartosci:

   mov     eax, offset unk_403737+8 ; offset + 8, bo liczymy w druga strone
   mov     ecx, 9

   _loop_big:
   push     eax
   push     ecx

   mov      esi, [eax]
   mov      edi, [eax+4]
   sub      edx, edx
   mov      ebp, 20h
   _loop_crypt:
   add      edx, 9E3779B9h
 ;----------------------
   mov     eax, edi
   mov     ebx, eax
   mov     ecx, eax
   shl     eax, 4
   add     eax, dword ptr aTryToCrackThis
   shr     ebx, 5
   add     ebx, dword ptr aTryToCrackThis+8
   add     ecx, edx
   xor     ecx, eax
   xor     ecx, ebx
   add     esi, ecx
 ;----------------------
   mov     eax, esi
   mov     ebx, esi
   mov     ecx, esi
   shl     eax, 4
   add     eax, dword ptr aTryToCrackThis+4
   shr     ebx, 5
   add     ebx, dword ptr aTryToCrackThis+0Ch
   add     ecx, edx
   xor     ecx, eax
   xor     ecx, ebx
   add     edi, ecx
 ;----------------------
   dec      ebp
   jnz      _loop_crypt

   pop     ecx
   pop     eax
   mov     [eax], esi
   mov     [eax+4], edi

   dec      eax
   dec      ecx
   jnz      _loop_big

 2. Petelka druga.

 .text:0040110C                 mov     edx, 36Ch
 .text:00401111                 mov     edi, offset unk_4033CB
 .text:00401116 loc_401116:
 .text:00401116                 inc     edi
 .text:00401117                 mov     ebx, 0FFFFFFF0h
 .text:0040111C                 xor     eax, eax
 .text:0040111E                 xor     ecx, ecx
 .text:00401120 loc_401120:
 .text:00401120                 mov     cl, byte ptr unk_4030B9[ebx]
 .text:00401126                 mov     al, [ebx+edi+10h]
 .text:0040112A                 xor     al, [ebx+403010h]
 .text:00401130                 mov     al, byte ptr unk_4030B9[eax]
 .text:00401136                 mov     [ebx+edi+10h], al
 .text:0040113A                 xor     [ecx+edi], al
 .text:0040113D                 inc     ebx
 .text:0040113E                 jnz     short loc_401120
 .text:00401140                 dec     edx
 .text:00401141                 jnz     short loc_401116

 Petla  'zewnetrzna'  kreci  sie  <dlugosc  pliku -  rozmiar  name> razy, czyli
 ustawiajac sie na pierwsze bajty ktorych przeznaczenia jak na razie nie znamy.
 Petla  wewnetrzna przebiega 0Fh razy, liczona w EBX. Patrzac na ten fragment z
 drugiej strony mozna analizowac: Skoro bajt [ecx+edi], gdzie edi to pointer na
 bajt sekcji danych z keyfile a ecx to  bajt kolejny z  tablicy  unk_4030B9-10,
 jest XORowany z al, ktore wczesniej jest zapisywane w [ebx+edi+10h]. Logicznie
 rozumujac, wystarczy ten bajt  zaladowac do al, i zxorowac nim bajt [ecx+edi].

 Natomiast  stala  tablica  unk_4030B9-10  to:

 db    8, 0Dh, 0Ch, 0Fh, 0Ah, 0Bh, 2, 0, 3, 0Eh, 1, 7, 9, 6, 5, 4

 wiec  bardzo elegancka, gdyz nie wykracza poza zakres 10h bajtow, a wiec jeden
 przebieg  wewnetrznej  petli  zamienia  tylko  10h  bajtow.

   mov      cl, [ebx+unk_4030B9] ; pobranie do ECX, ze stalej tablicy
   mov      al, [ebx+edi+10h]    ; bajt
   xor      [ecx+edi], al        ; xorowanie

 Dobra...  tylko  teraz... skoro wczesniej nadpisywany jest bajt [ebx+edi+10h],
 to cos musialo byc tam  wczesniej, i trzeba ustalic jaka wartosc to byla. Jak?
 Pomyslmy...  patrzac  na  kod:

 .text:00401126                 mov     al, [ebx+edi+10h]
 .text:0040112A                 xor     al, [ebx+403010h]
 .text:00401130                 mov     al, byte ptr unk_4030B9[eax]

 Znak  jest XORowany z kolejnym bajtem z textu About [ebx+403010h], a nastepnie
 z  AL-tego  miejsca  tablicy  unk_4030B9  jest pobierany bajt ktory dalej jest
 uzywany.  Tablica  unk_4030B9  zawiera  t ylko 256 znakow, dla kazdej mozliwej
 wartosci  AL, jednak mimo ze wartosci sa losowo pozamieniane miejcami to zadna
 z  nich  sie  nie  powtarza.  Nalezaloby  wiec  sie dowiedziec dla jakiego AL,
 uzyskamy  wartosc  ktora  dalej jest xorowana z bajtami keyfile. Nie pozostaje
 nic innego jak przeszukac tablice w poszukiwaniu tej wartosci, zrobi to szybki
 fragment:

   push      edi

   mov       edi, offset unk_4030B9
   mov       ecx, 256
   repnz     scasb
   not       cl
   mov       eax, ecx

   pop       edi ; przywrocenie poprzedniej wartosci

   ; w AL szukana wartosc
   xor       al, [ebx+aTryToCrackThis+10h]
   ; xor z kolejnym bajtem z About
   mov       [ebx+edi+10h], al
   ; zapisanie wartosci

 Petle  te  musza  sie  jednak 'krecic'  w druga strone, by wyniki dzialan przy
 odwracaniu byly takie jakich oczekuje oryginalna procka w crackme. Zewnetrzna:
 Zaczynamy  od  bajtu  ostaniego  tablicy  36Bh  bajtowej:

   mov      edi, offset unk_4033CB + 036Ch + 1
   sub      edx, edx ; licznik, ustawiony na 1
   inc      edx
   _loop1:
   dec      edi
   ...
   ...
   inc      edx
   cmp      edx, 36Ch+1 ; licznik w druga strone
   jnz      _loop1

   Podobnie z petla wewnetrzna:
   mov      ebx, 0FFFFFFFFh ; tez zaczynamy od konca
   xor      eax, eax
   xor      ecx, ecx
   _loop2:
   ...
   ...
   dec      ebx
   cmp      ebx, 0FFFFFFEFh ; dla 0FFFFFFF0h jeszcze tez musimy liczyc
   jnz      _loop2

 Teraz  trzeba  ustawic  kolejnosc tych dzialan odwrotnie do sprawdzania, czyli
 najpierw szyfrowanie  calego  keyfile, a pozniej samej sekcji z name, aby nasz
 keygen  dzialal  poprawnie.

 3. Procka druga, czyli sprawdzenie budowy pliku, poza name.

 .text:00401195                 mov     edi, offset off_4032D0
 ;--------------------------------------
 ; Fragment pierwszy
 ;--------------------------------------
 .text:0040119A                 mov     esi, offset unk_4033CC
 .text:0040119F                 mov     ecx, 36Bh
 .text:004011A4 loc_4011A4:
 .text:004011A4                 mov     al, [ecx+esi-1]
 .text:004011A8                 test    al, al
 .text:004011AA                 jz      short loc_4011B4 ; jesli 0 to w porzadku
 .text:004011AC                 dec     al
 .text:004011AE                 jnz     zly_keyfile      ; jesli nie 0 to wypad
 .text:004011B4 loc_4011B4:
 .text:004011B4                 dec     ecx
 .text:004011B5                 jnz     short loc_4011A4

 Wniosek  z funkcji jaki? Keyfile w pierwszych 36Bh bajtach moze zawierac tylko
 0 lub  1, wszystko inne jest nieprawidlowe. Jak latwo przewidziec dalej bedzie
 sprawdzanie  rozmieszczenia  tych  zer  i  jedynek.

 ;--------------------------------------
 ; Fragment drugi
 ;--------------------------------------
 .text:004011B7                 xor     eax, eax
 .text:004011B9                 mov     edx, 23h
 .text:004011BE loc_4011BE:
 .text:004011BE                 test    ah, ah
 .text:004011C0                 jnz     zly_keyfile
 .text:004011C6                 mov     cl, 19h
 .text:004011C8                 mov     ebx, [edi] ; tablica wartosci
 .text:004011CA                 mov     ah, [ebx] ; (1)
 .text:004011CC                 inc     ebx
 .text:004011CD                 mov     ch, [ebx] ; (2)
 .text:004011CF                 inc     ebx
 .text:004011D0 loc_4011D0:
 .text:004011D0                 mov     al, [esi]
 .text:004011D2                 test    al, al
 .text:004011D4                 jz      short loc_401201
 .text:004011D6 loc_4011D6:
 .text:004011D6                 dec     ch
 .text:004011D8                 jz      short loc_4011F3 ; (7)
 .text:004011DA                 inc     esi
 .text:004011DB                 mov     al, [esi]
 .text:004011DD                 test    al, al
 .text:004011DF                 jz      zly_keyfile; (6)
 .text:004011E5                 dec     cl
 .text:004011E7                 jz      short zly_keyfile
 .text:004011E9                 jmp     short loc_4011D6
 .text:004011EB loc_4011EB:
 .text:004011EB                 dec     cl
 .text:004011ED                 jnz     short zly_keyfile
 .text:004011EF                 inc     cl
 .text:004011F1                 jmp     short loc_4011FA
 .text:004011F3 loc_4011F3:
 .text:004011F3                 mov     al, [esi+1]
 .text:004011F6                 test    al, al
 .text:004011F8                 jnz     short loc_4011EB
 .text:004011FA loc_4011FA:
 .text:004011FA                 mov     ch, [ebx] ; (3)
 .text:004011FC                 inc     ebx
 .text:004011FD                 dec     ah
 .text:004011FF                 js      short zly_keyfile
 .text:00401201 loc_401201:
 .text:00401201                 inc     esi
 .text:00401202                 dec     cl ; (4)
 .text:00401204                 jnz     short loc_4011D0
 .text:00401206                 add     edi, 4
 .text:00401209                 dec     edx
 .text:0040120A                 jnz     short loc_4011BE

 Pierwsza mysl po probie sledzenia wykonywania tej procki byla: "Kurwa... co to
 jest?". No ale trzeba sie bylo zastanowic... Do AH (1) jest wstawiany pierwszy
 bajt z malych tablic na ktorych wskazniki jest ustawiony poczatkowo EDI. Mozna
 zauwazyc, (pewnie ze nie od razu ;-),  ze  jest to liczba pozostalych bajtow w
 tej  mini-tablicy.  Kolejna  wartosc  jest ustawiana w CH (2), pozniej jesli w
 tablicy, podzielonej na czesci 19h'owe  sprawdzane sa ustawienia bajtow. Jesli
 jest 0 to licznik jest tylko zwiekszany  (4), a jesli jest 1 to dekrementowane
 jest CH, oraz  nie  moze  wystapic  0  (6) dopoki  i  CH nie jest rowne 0 (7).
 Podobnie dzieje sie z fragmentem dalszym, ktory wycialem a ktory mozna znalesc
 oczywiscie  w  kodzie ;-) z  ta roznica ze jest sprawdzany nie kolejny bajt, a
 dopiero co 19h'ty. Jesli  calosc  przejdzie  poprawnie flaga C jest zerowana i
 nastepuje wyjscie z funkcji, inaczej  flaga  C  jest  ustawiana  czyli keyfile
 jest  nieprawidlowy.  Moral z tej pieknej opowiastki jest nie inny jak... taki
 ze pierwsze 036Bh bajtow  to  tablica 19h x 23h bajtow, dla ktorej kazda linia
 pionowa  i  pozioma  jest  opisana  mala tablica gdzie pierwszy bajt to liczba
 linii utworzonych z jedynek, pooddzielanych zerami o dlugosci kolejnych bajtow
 tej malej tablicy, dla przykladu, jesli ktoras z linii jest opisana:

    db  4, 2,3,1,2,

 to znaczy ze linia moze wygladac

    0,0,0,0,0,1,1,0,1,1,1,0,0,0,0,1,0,0,1,1,0,0,0,0,0

 jak rowniez:

    1,1,0,1,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0

 Cala  sztuka  polega na tym by zgrac linie poziome z pionowymi. Ja rozwiazalem
 ten problem recznie,  siedzac pol dnia w szkole nad kartka papieru zamalowujac
 albo  przekreslajac  zera  opisujace  kolejne  bajty.  Jest wiele zasad dzieki
 ktorym  ustalic mozna miejsca w ktorych na pewno znajda sie jedynki bez wzlegu
 na  rozstawienie linii bajtow. I tak idac powoli po nitce do klebka uskladalem
 calosc.  Nie sposob tego  opisac  ale to jest mozliwe. Sam nie znam sposobu by
 obliczyc  to  automatycznie,  ale  jesli  ktos  mialby  na to pomysl to bardzo
 chetnie  poslucham.

 Zmudna  robota  strasznie...  ale  w  koncu  uskladalem  calosc  ktora wyglada
 nastepujaco:

                2 4 4 6  5 5 2 4  3 5 5 3  4 6 6 4  6 5 5 4  4 4 4 3 2

               5 2 2 3  410 192  5 7 4 4  4 4 3 4  3 2 2 B  8 4 2 6 5
               8 7 4 4  3 2 2 4  4 4 7 9  3 3 1 2  5 3 5 4  3 2 2 1 3
                 3 4 1  6 1   A 12 5 611  6 6 2 C  5 5 3 3  1 3 2 2
                 5 5 1  1 1   5    4 1   11 5 3 1  6 B 7 4  2 4 1
                     1  2 2        3 1      1 8    1 2 3
                     4                      1 1    1

 1 3        db 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,0,0, 0,0,0,0, 0,0,0,0 ,0
 1 8        db 0,0,0,0, 0,0,0,0, 0,1,1,1, 1,1,1,1, 1,0,0,0, 0,0,0,0 ,0
 1 C        db 0,0,0,0, 0,0,1,1, 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0 ,0
 1 F        db 0,0,0,0, 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 0,0,0,0 ,0

 4 2312     db 0,0,0,0, 0,1,1,0, 1,1,1,0, 0,0,0,1, 0,0,1,1, 0,0,0,0 ,0
 5 22212    db 0,0,0,0, 0,1,1,0, 1,1,0,0, 1,1,0,0, 1,0,0,1, 1,0,0,0 ,0
 6 422423   db 1,1,1,1, 0,1,1,0, 1,1,0,1, 1,1,1,0, 1,1,0,1, 1,1,0,0 ,0
 4 7523     db 1,1,1,1, 1,1,1,0, 0,1,1,1, 1,1,0,0, 1,1,0,1, 1,1,0,0 ,0

 5 15233    db 1,0,0,1, 1,1,1,1, 0,0,1,1, 0,0,0,1, 1,1,0,1, 1,1,0,0 ,0
 4 2573     db 1,1,0,0, 1,1,1,1, 1,0,1,1, 1,1,1,1, 1,0,0,1, 1,1,0,0 ,0
 3 2B3      db 1,1,0,0, 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,1,1, 1,0,0,0 ,0
 3 197      db 0,1,0,0, 0,1,1,1, 1,1,1,1, 1,1,0,0, 0,1,1,1, 1,1,1,1 ,0

 3 12,11h,  db 0,1,0,0, 0,1,1,0, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1 ,1
 4 22B2     db 0,1,1,0, 0,1,1,0, 0,1,1,1, 1,1,1,1, 1,1,1,1, 0,0,0,1 ,1
 4 3382     db 0,1,1,1, 0,1,1,1, 0,0,0,1, 1,1,1,1, 1,1,1,0, 0,0,0,1 ,1
 3 834      db 0,1,1,1, 1,1,1,1, 1,0,0,0, 0,0,0,1, 1,1,0,0, 0,1,1,1 ,1

 3 735      db 0,0,1,1, 1,1,1,1, 1,0,0,0, 0,0,1,1, 1,0,0,0, 1,1,1,1 ,1
 4 1823     db 1,0,0,1, 1,1,1,1, 1,1,1,0, 0,0,1,1, 0,0,0,1, 1,1,0,0 ,0
 3 2B3      db 1,1,0,0, 0,1,1,1, 1,1,1,1, 1,1,1,1, 0,0,1,1, 1,0,0,0 ,0
 2 5E       db 1,1,1,1, 1,0,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 0,0,0,0 ,0

 2 3,10h    db 1,1,1,0, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 0,0,0,0 ,0
 3 11E      db 1,0,1,0, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0 ,0
 3 539      db 1,1,1,1, 1,0,1,1, 1,0,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0 ,0
 5 21326    db 1,1,0,0, 1,0,1,1, 1,0,0,1, 1,0,1,1, 1,1,1,1, 0,0,0,0 ,0

 4 7224     db 1,1,1,1, 1,1,1,0, 1,1,0,1, 1,0,0,0, 1,1,1,1, 0,0,0,0 ,0
 5 21223    db 0,1,1,0, 0,0,1,0, 1,1,0,1, 1,0,0,0, 0,1,1,1, 0,0,0,0 ,0
 3 922      db 0,1,1,1, 1,1,1,1, 1,1,0,1, 1,0,0,0, 0,1,1,0, 0,0,0,0 ,0
 4 2322     db 0,0,1,1, 0,0,0,1, 1,1,0,1, 1,0,0,0, 0,1,1,0, 0,0,0,0 ,0

 3 722      db 0,0,1,1, 1,1,1,1, 1,0,0,1, 1,0,0,0, 0,1,1,0, 0,0,0,0 ,0
 3 632      db 0,0,0,1, 1,1,1,1, 1,0,1,1, 1,0,0,0, 1,1,0,0, 0,0,0,0 ,0
 2 22       db 0,0,0,0, 0,0,0,1, 1,0,0,1, 1,0,0,0, 0,0,0,0, 0,0,0,0 ,0
 3 125      db 0,0,0,0, 0,0,0,0, 1,0,0,1, 1,0,0,0, 0,0,0,1, 1,1,1,1 ,0

 5 23221    db 0,0,0,0, 0,0,0,0, 1,1,0,1, 1,1,0,0, 0,0,1,1, 0,1,1,0 ,1
 4 1252     db 0,0,0,0, 0,0,0,0, 0,1,0,1, 1,0,0,0, 0,1,1,1, 1,1,0,1 ,1
 1 10h      db 0,0,0,0, 0,0,0,0, 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1 ,1

 Wiec  wrocilem  do  domu, przepisalem moja tablice w keygena ktoremu tylko tej
 tablicy  brakowalo  i  voila!  Dziala...  W  crackme  widnieje  piekny   napis
 registered  to  Liquid  /  AAOCG.

 Podsumowujac,  crackme  jak  sam  autor zapisal, jest dosc latwe, tylko wymaga
 pomyslowosci i czasu, szczegolnie na tworzenie tej tablicy. WiteG'owi na pewno
 wypada pogratulowac pomyslu bo crackme jest skonstruowane bardzo przyjemnie, i
 az  chce  sie  nad  nim  posiedziec, a jak wielu ludzi zauwaza, ciezko teraz o
 dobre  i  przemyslane  algorytmy  do  crackme.

 To  tyle  na  ten wieczor. Przedstawic dzialanie tego crackme staralem sie tak
 przejrzyscie  jak  to mozliwe, jesli jednak gdzies sa fragmenty niezrozumiale,
 watpliwosci,  czy  niedomowienia  to  oczywiscie  na  maila.

 Dodatki:

 http://www.witeg.prv.pl - strona autora crackme - WiteG.

 http://www.aaocg.prv.pl - do odwiedzenia tej strony zapraszam zawsze :-)

 Przy  lamaniu  polecam  miec  przy  sobie:
 - IDA,  chyba  nie  trzeba przedstawiac, do takich malych programikow moze byc
   czesto  wystarczajaca.
 - SI,  jak  sie ktos uprze by sledzic wykonywanie kodu, jednak w tym przypadku
   mozna  sie  obejsc.
 - Oczywiscie  odgrywarka  muzyczki.

 Pozdrowionka  i  podziekowania  oczywiscie  dla:
 WiteG  za  pomyslowosc  i  wykonanie,
 ludzii  z  AAOCG,  i  wszystkich  crackmerow  /  reverserow  w  Polsce.

 Wszelkie  skargi,  uwagi,  komentarze,  wytykanie bledow, bluzgi, oswiadczyny,
 komplementy  i  inne  pierdoly  mozesz  smialo   slac   na liquid@aaocg.org

 Liquid / AAOCG