Heavy.exe by +RCG
28 Wrzesnia 2oo1 @ 13:06

Ktos moglby pomyslec ze jestem pierwsza osoba, ktora rozwiazala heavy.exe... Nic bardziej mylnego. To "crackme" zostalo rozwiazane 15 Stycznia '98 przez Quine'a. Swietnie, wiec po jakiego wacka pisze ten txt?! Otoz Quine w swoim rozwiazaniu zapomnial o kilku waznych szczegolach, popelnil kilka bledow i co najgorsze, podszedl do tematu zbyt ogolnie, nie dajac tym samym szans poczatkujacym. Jesli ktos chce polookac na ten txt to sluze linkiem:
http://www.fravia.kilrathi.pl/htp_essa1.htm (Quine's Quick Qrack).

Heavy zostalo napisane przez +RCG (z +HCU :=). Jest to programik z niedostepna funkcja "Open". Celem jest stworzenie keyfile, ktore spowoduje odblokowanie tej opcji. Do zabezpieczenia uzyto "crypto.exe" - "oficjalnego pakietu zabezpieczajacego +HCU". Na poczatek wyjasnie na czym polega sam proces zabezpieczania. Otoz, zeby zabezpieczyc swoj program, nalezy zpisac offset poczatkowy i koncowy procki, ktora ma byc niedostepna dla niezarejestrowanego uzytkowanika (crackera =). Nastepnie odejmujac od siebie zpisane offsety wyliczyc dlugosc kodu, ktory ma zostac zabezpieczony. Po tych badz co badz skomplikowanych operacjach, nalezy stworzyc plik "key.dat" z 128 bitowym (16 bajtowym) "kluczem". "Klucz" moze skladac sie z dowolnych wartosci. Ostanim
etapem jest odpalenie programu "crypto.exe", ktory odczytuje z pliku "key.dat" zdefiniowany przez nas klucz i uzywa go do zaszyfrowania procki, ktora ma byc niedostepna. Szyfrowanie polega na cyklicznym XORze, tzn. kazdy bajt procki jest XORowany z odpowiednim bajtem klucza. Z tego wynika, ze tylko gosc ktory posiada oryginalny keyfile, moze uzyc danej procki.

Pod http://www.fravia.kilrathi.pl/crymaco.htm mozna zalezc wiele ciekawych informacji (taki SDK :) dotyczacych crypto.exe i samej idei zabezpieczenia. Dla nas najwazniejsze sa jednak zamieszczone tam zrodla - warto sie z nimi zapoznac...

Po zdisasmowaniu heavy.exe mamy...

VxD_Block struct
K_Lenght      UINT ?
K_Address     UINT ?
C_Lenght      UINT ?
C_Offset      UINT ?
VxD_Block ends

xd VxD_Block

mov esi, 004012B9                   ; offset poczatkowy kodu do deszyfrowania
mov dword ptr [00402050], esi       ; [xd.C_Offset]
mov edi, 00401301                   ; offset koncowy
sub edi, esi                        ; dlugosc kodu do deszyfrowania
mov dword ptr [0040204C], edi       ; [xd.C_Lenght]
mov esi, 00402064                   ; klucz do esi
mov dword ptr [00402048], esi       ; [xd.K_Address]
mov dword ptr [00402044], 0000000F  ; [xd.K_Lenght]
call VXDek
mov eax, wynik
cmp eax,-1
je  buu_zly_keyfile

[...tutaj zaszyfrowana procka...]

VXDek:

push 00000000    ;? [niewazne]
push 00402074    ;jakas tam struktura [niewazne]
push 00000000    ;rozmiar bufora "wejsciowego" [brak]
push 00000000    ;bufor "wyjsciowy" [brak]
push 00000001    ;rozmiar bufora "wejsciowego"
push 00402044    ;bufor z danymi "wejsciowymi" [dlugosc keyfile]
push 00000001    ;numer funkcji
push dword ptr [0040207C]  ;uchwyt VXdka

* Reference To: KERNEL32.DeviceIoControl

Procka vxdka, ktora odpowiada za deszyfrowanie:

; __________________________________________________________________

;               S u b r o u t i n e

DECRYPT proc near
        pusha
        mov     edi, [esi+10h]      ;wskaznik do struktury xd
        mov     ecx, [edi+8]        ;C_lenght
        mov     esi, [edi+4]        ;K_address
        mov     edx, esi
        mov     eax, [edi]          ;K_lenght
        add     edx, eax            ;edx = koncowy offset klucza
        mov     ebx, edi            ;ebx = wskaznik do struktury
        mov     edi, [edi+0Ch]      ;C_offset
DALEJ:
        push    ecx                 ;C_lenght na stos
        mov     al, [edi]           ;pierwszy zakodowany bajt do al
        xor     al, [esi]           ;decrypt
        mov     [edi], al           ;sejw :->
        inc     edi                 ;nastepny bajt do rozkodowania
        pop     ecx                 ;C_lenght ze stosu
        dec     ecx                 ;zmniejsz ilosc bajtow do rozkodowania
        test    ecx, ecx            ;czy juz koniec ?
        jz      KONIEC!             ;tak, koniec :->
        inc     esi                 ;nastepny bajt klucza
        cmp     esi, edx            ;czy juz caly klucz ?
        jbe     DALEJ               ;jeli nie to petla
        mov     esi, [ebx+4]        ;odtworz klucz
        jmp     DALEJ               ;i zrob petle zeby odxorowac reszte
; ------------------------------------------------------------------

KONIEC!:
        popa
        retn

Loo chyba nie trzeba nic tlumaczyc :). Kod jest samo-wytlumaczalny (z moimi komentarzami =). Teraz zapewne zapytasz: "I co?" (btw. to pytanie cracker powinien zadawac sobie na kazdym kroku). A ja zgodnie ze staropolskim zwyczajem odpowiem: "wlasciwie nic" =). Skoro wiemy juz jak przebiega sam proces dekodowania, nalezy sie zastanowic jak policzyc prawidlowy klucz... Brute
force z miejsca odpada - mozliwosci jest 256^16, poza tym tuty o brute forcach sux :=). Mozna sprobowac zrekonstruowac zaszyfrowana procke tylko na podstawie danych z exeka - np. gdzies jest string "You are a good...". Takie txty wyswietla sie zazwyczaj w msgboxach... Ten sposob jest bardzo czasochlonny, wymaga duzo szczescia i ZEN, a i tak nie ma gwarancji ze sie uda :=). Ja idac sladami Quine'a zdisasmowalem zaszyfrowana procke...

unk_0_4012B9    db  6Ah ; j \
                db  31h ; 1  | zakodowane bajty
                db  6Ah ; j /

                db  51h ; Q \ OFF32 SEGDEF [0,6452551]
                db  25h ; %  |
                db  45h ; E  | 4 bajtowy offset
                db    6 ;   /

                db  6Dh ; m \
                db    8 ;    \
                db 0E1h ;    \
                db 0A3h ;     \_
                db  0Ah ;      / \ zakodowane bajty
                db  0Ch ;     /
                db  0Dh ;    /
                db  66h ; f /

                db 0F7h ;  \ OFF32 SEGDEF [0,24120F7]
                db  20h ;    |
                db  41h ; A  | kolejny offset
                db    2 ;   /

                [...bajty...]

                db 0B5h ;  \ OFF32 SEGDEF [0,F4E1FB5]
                db  1Fh ;   |
                db  4Eh ; N | znow offset :>
                db  0Fh ;   /

                [...bajty...]

                db  52h ; R OFF32 SEGDEF [0,5442352]
                db  23h ; #
                db  44h ; D
                db    5 ;

                [...bajty...]

                db    6 ;   OFF32 SEGDEF [0,A491B06]
                db  1Bh ;
                db  49h ; I
                db  0Ah ;

                [...bajty...]

                db  43h ; C OFF32 SEGDEF [0,2412043]
                db  20h ;
                db  41h ; A
                db    2 ;

                [...bajty...]

                db  60h ; ` OFF32 SEGDEF [0,7462560]
                db  25h ; %
                db  46h ; F
                db    7 ;

                [...bajty...]

                db  42h ; B OFF32 SEGDEF [0,D4C2B42]
                db  2Bh ; +
                db  4Ch ; L
                db  0Dh ;

                [...bajty...]

                db  44h ; D OFF32 SEGDEF [0,3422144]
                db  21h ; !
                db  42h ; B
                db    3 ;

                [...bajty...]

Ok., w jaki sposob uzyc informacji o offsetach aby rozkodowac cala procke? Gdy przyjmiemy ze kazdy offset zaczyna sie od 0040h, bedziemy mogli odszyfrowac czesc keyfile. W jaki sposob? W takim sam sposob w jaki jest deszyfroway kod procki - xorujac (x XOR y = z, z XOR y = x) odpowiednie bajty zaszyfrowanych offsetow, przez stale bajty 0040h! Nalezy pamietac, ze offsety w pliku zapisywane sa w formacie intela.

51h XOR ?? = ??
25h XOR ?? = ??
45h XOR 40 = 5
  6 XOR 0 = 6

  -,-,-,-,-,5,6,-,-,-,-,-,-,-,-,-

0F7h XOR ?? = ??
 20h XOR ?? = ??
 41h XOR 40 = 1
   2 XOR 0 = 2

  -,1,2,-,-,5,6,-,-,-,-,-,-,-,-,-

 0B5h XOR ?? = ??
  1Fh XOR ?? = ??
  4Eh XOR 40 = e
  0Fh XOR 0 = f

  -,1,2,-,-,5,6,-,-,-,-,-,-,-,e,f

 52h XOR ?? = ??
 23h XOR ?? = ??
 44h XOR 40 = 4
   5 XOR 0 = 5

  -,1,2,-,4,5,6,-,-,-,-,-,-,-,e,f

   6 XOR ?? =
 1Bh XOR ?? = ??
 49h XOR 40 = 9
 0Ah XOR 0 = a

  -,1,2,-,-,5,6,-,-,9,a,-,-,-,e,f

 43h XOR ?? =
 20h XOR ?? = ??
 41h XOR 40 = 1
   2 XOR 0 = 2

 -,1,2,-,-,5,6,-,-,9,a,-,-,-,e,f

 60h XOR ?? =
 25h XOR ?? = ??
 46h XOR 40 = 6
   7 XOR 0 = 7

  -,1,2,-,-,5,6,7,-,9,a,-,-,-,e,f

 42h XOR ?? =
 2Bh XOR ?? = ??
 4Ch XOR 40 = c
 0Dh XOR 0 = d

  -,1,2,-,-,5,6,-,-,9,a,-,c,d,e,f

44h XOR ?? =
21h XOR ?? = ??
42h XOR 40 = 2
  3 XOR 0 = 3

  -,1,2,3,-,5,6,-,-,9,a,-,c,d,e,f

Ok, sytuacja staje sie coraz bardziej klarowna :). Mamy 9 z 16 bajtow klucza, niezle :). Teraz mozna by stworzyc key.dat z wyliczonymi wartosciami, polookac na czesciowo rozszyfrowany kod i wydedukowac pozostale bajty klucza... Tak, to tez dobry sposob, ale ja zauwazylem w otrzymanym ciagu pewna subtelna analogie :))) Intuicja podpowiada mi ze klucz powinien wygladac tak:

  0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f

Ok, sprobojmy. Stworz key.dat z taka zawartoscia, odpal heavy, kliknij Open i.... yesss :) przeczytaj gratulacje =)))) Na zakonczenie powiem tylko ze ten sposob zabezpieczania progow jest nie do zlamania (przy dobrej implementacji). Wymaga malego nakladu pracy i gwarantuje ze osoba nie posiadajaca prawdziwego keyfile nigdy danego proga nie zlamie. Ma to tez swoje slabe strony - jesli legalny uzytkownik udostepni keyfile w necie to koniec - pozegnaj sie z profitami :=(.

Ged_^crackpl
ged@hoga.pl