Sztuka dla sztuki

Ten tutorial nie powstal z mysla o newbies, wiec jesli nim
jestes to baw sie dalej winzipem, albo znajdz sobie kolejny
text z cyklu "ten tut powstal z mysla o newbies, wiec jesli
nim nie jestes to nie masz tu czego szukac", cholera mam
juz dosc tego typu publikacji, ile mozna meczyc pierdolonego
i wydymanego na 1000 sposobow winzipa???

Hi, wreszcie udalo sie zlamac ten cholerny KataloCD, wiem
cracki wyszly juz dawno temu, ale to zadna sztuka akurat
w tym przypadku zrobic patcha, exek nawet nie jest spakowany,
wlasnie sobie przypominalem jak w mailach z Ptaskiem gadalismy
o tym progu i jedyne czego bylismy pewni to to, ze uzywa tam
algortymu hashujacego md5 i od cholery mnozenia, no i tak
projekt utknal w miejscu, az do dzisiaj kiedy po raz setny
postanowilem zobaczyc dump z idy do wersji 2.03, wszystko
mialem przygotowane komentarze do procek i tym podobne bzdury
oprocz infosow do glownych procek, shit w pewnym momencie
mnie cos olsnilo(to pewnie ta sloneczna pogoda za oknem ;),
widzialem, ze program korzysta z dupnych stringow i tak to
jest ten shit, tu przedstawie listing z v2.50 zeby pozostac
na czasie:

04C55C0 sz1108876509506 db '11088765095067625329458021105058134597929',0
04C55C0                                         ; DATA XREF: sub_409DF0+7Bo
04C55C0                                         ; sub_427470+128o
04C55C0                                         ; sub_4340A0+380o
04C55C0                                         ; sub_434E30+B4o
04C55EA                 align 4
04C55EC sz5287446917209 db '5287446917209892529044901923459570593867',0
04C55EC                                         ; DATA XREF: sub_409DF0+64o
04C55EC                                         ; sub_427470+111o
04C55EC                                         ; sub_4340A0+369o
04C55EC                                         ; sub_434E30+9Do
04C5615                 align 4
04C5618 sz8687147940791 db '8687147940791546997772999522905216378001',0
04C5618                                         ; DATA XREF: sub_409DF0+4Do
04C5618                                         ; sub_427470+FAo
04C5618                                         ; sub_4340A0+352o
04C5618                                         ; sub_434E30+86o
04C5641                 align 4
04C5644 sz5644737586335 db '5644737586335572109266844069440047644263',0
04C5644                                         ; DATA XREF: sub_409DF0+33o
04C5644                                         ; sub_427470+E3o
04C5644                                         ; sub_4340A0+338o
04C5644                                         ; sub_434E30+6Fo

no skad ja to znam, cholera przeciez niedawno lamalem taki
progs Access Lock i tam bylo zastosowane szyfrowanie RSA...i
wlasnie tam tez byly takie dupne stringi(inna baza) heh calkiem
mozliwe, ze tu jest zastosowane RSA, nawet bardziej niz mozliwe :),
lookniecie w referencje (i bpx GetWindowTextA podczas pobierania
danych) sprowadza do punktu:

0409E0E  push    esi
0409E0F  push    1
0409E11  call    ?UpdateData@CWnd@@QAEHH@Z <-- pobiera dane 
0409E16  test    eax, eax
0409E18  jz      loc_409FB1

0409E1E  push    0Ah                    <-- baza(10 dec)
0409E20  lea     eax, [ebp+var_24]      <-- gdzie konwertowac
0409E23  push    offset sz5644737586335 <-- co konwertowac
0409E28  push    eax
0409E29  call    sub_40A050             <-- konwertuj string
0409E2E  add     esp, 0Ch
0409E31  lea     eax, [ebp+var_28]
0409E34  mov     [ebp+var_4], 0
0409E3B  push    0Ah
0409E3D  push    offset sz8687147940791
0409E42  push    eax
0409E43  call    sub_40A050
0409E48  add     esp, 0Ch
0409E4B  lea     eax, [ebp+var_2C]
0409E4E  mov     byte ptr [ebp+var_4], 1
0409E52  push    0Ah
0409E54  push    offset sz5287446917209
0409E59  push    eax
0409E5A  call    sub_40A050
0409E5F  add     esp, 0Ch
0409E62  lea     eax, [ebp+var_30]
0409E65  mov     byte ptr [ebp+var_4], 2
0409E69  push    0Ah
0409E6B  push    offset sz1108876509506
0409E70  push    eax
0409E71  call    sub_40A050

Procka pod 0409E11 wywoluje funkcje pobierajace dane z editow
jak name, adres i numer seryjny, czyli standardowy syf, nastepnie
wywolywane sa funkcje, ktore konwertuja stringi(te dupne) na format
liczb big(cos jak format liczb big z biblioteki miracl), ktore bedzie
mozna wykorzystac w obliczeniach RSA.Dalej:

0409E79  mov     eax, [ebp+var_18]
0409E7C  add     eax, 64h
0409E7F  lea     ecx, [ebp+var_3C]
0409E82  mov     byte ptr [ebp+var_4], 3
0409E86  push    eax
0409E87  mov     eax, [ebp+var_18]
0409E8A  add     eax, 5Ch
0409E8D  push    eax
0409E8E  push    ecx
0409E8F  call    ??H@YG?AVCString@@ABV0@0@Z ; operator+(CString const &,CString const &)
0409E94  mov     esi, eax
0409E96  push    20h
0409E98  mov     ecx, [ebp+var_18]
0409E9B  lea     edx, [ebp+var_38]
0409E9E  mov     byte ptr [ebp+var_4], 4
0409EA2  mov     eax, [ecx+60h]
0409EA5  push    eax
0409EA6  push    edx
0409EA7  call    sub_40A050
0409EAC  add     esp, 0Ch
0409EAF  lea     edx, [ebp+var_34]
0409EB2  mov     byte ptr [ebp+var_4], 5
0409EB6  mov     ecx, [esi]
0409EB8  mov     [ebp+var_20], eax
0409EBB  push    ecx
0409EBC  push    edx
0409EBD  lea     ecx, [ebp+var_D]
0409EC0  call    sub_40A0F0             <-- upper case + shit

Sledzac zwracane wartosci po wywolaniu procki pod 0409E8F
da sie zauwazyc, ze laczy ona name z wprowadzonym adresem
zamieszkania(taki lstrcat).Pod 0409EC0 wywolywana jest
procka, ktora zamienia na upper case wszystkie bajty z
polaczonych name+adres, oraz dodatkowo:

(w srodku tej procki)

040A122  mov     esi, [ebp+arg_4]       <-- dlugosc name+adres
040A125  test    esi, esi
040A127  jz      short end_char_check   <-- jesli 0 wyjdz
040A129 
040A129 loc_40A129:                     ; CODE XREF: sub_40A0F0+6Fj
040A129  mov     al, [esi]              <-- pobierz kolejne bajty
040A12B  test    al, al
040A12D  jz      short end_char_check
040A12F  cmp     al, 30h
040A131  jl      short check_lower      <-- jesli kolejny bajt jest z
040A133  cmp     al, 39h                    zakresu 0..9 zapisz go
040A135  jle     short save_char
040A137 
040A137 check_lower:
040A137  cmp     al, 61h                <-- sprawdz czy kolejny bajt
040A139  jl      short check_upper          jest z zakresu a..z
040A13B  cmp     al, 7Ah
040A13D  jg      short check_upper
040A13F  movsx   eax, al                <-- jesli jest
040A142  push    eax                    <-- to konwertuj go do
040A143  call    _toupper               <-- upper case
040A148  add     esp, 4
040A14B  jmp     short save_char
040A14D 
040A14D check_upper:                    <-- jesli bajt jest z zakresu
040A14D                                 <-- A..Z zapisz go
040A14D  cmp     al, 41h                <-- inaczej nie zapisuj bajtu do
040A14F  jl      short skip_char        <-- bufora
040A151  cmp     al, 5Ah
040A153  jg      short skip_char
040A155 
040A155 save_char:                      ; CODE XREF: sub_40A0F0+45j
040A155                                 ; sub_40A0F0+5Bj
040A155  push    eax
040A156  lea     ecx, [ebp+var_10]
040A159  call    ??YCString@@QAEABV0@D@Z ; CString::operator+=(char)
040A15E 
040A15E skip_char:                      ; CODE XREF: sub_40A0F0+5Fj
040A15E                                 ; sub_40A0F0+63j
040A15E  inc     esi
040A15F  jnz     short loc_40A129
040A161 
040A161 end_char_check:                 ; CODE XREF: sub_40A0F0+37j

W streszczeniu, procka odrzuca z polaczonych name+adres wszystkie
bajty, ktore sa rozne od 0..9, a..z(te konwertuje do upper case),
A..Z, tzn. jesli trafi sie jakis znak specjalny to wyjsciowa
forma name+adres nie bedzie zawierala tego bajta np. jesli
jako name wpiszemy 'bart' a adres '!motyb' to wyjsciowy bufor
bedzie wygladal tak 'BARTMOTYB', no kapujecie o co biega, ok
wafle, lecimy dalej:

0409EC9  mov     eax, [ebp+var_34]
0409ECC  lea     ecx, [ebp+var_14]
0409ECF  push    eax
0409ED0  push    ecx
0409ED1  lea     ecx, [ebp+var_D]
0409ED4  call    sub_40A1B0             <-- md5 z danych usera

Nastepna procka, zaraz za upper case, liczy hash md5 z przemielonego
name+adres, sory, ale nie bede tu walil analizy tej procki bo to nie
ma sensu, no musicie mi uwierzyc na slowo, ze to md5 i jazda dalej:

0409ED4  call    sub_40A1B0             <-- liczy hash md5 z danych usera
0409ED9  lea     ecx, [ebp+var_28]      <-- public N1
0409EDC  lea     edx, [ebp+var_24]      <-- public E1
0409EDF  push    ecx                    <-- zapamietaj na stosie N1
0409EE0  lea     eax, [ebp+var_14]
0409EE3  push    edx                    <-- zapamietaj E1
0409EE4  lea     ecx, [ebp+var_1C]
0409EE7  mov     byte ptr [ebp+var_4], 7
0409EEB  push    eax                    <-- hash md5 z danych usera
0409EEC  push    ecx                    <-- output
0409EED  call    sub_412C10             <-- rsa ( MD5^E1 mod N1)
0409EF2  mov     byte ptr [ebp+var_4], 8
0409EF6  add     esp, 10h
0409EF9  mov     ecx, [eax]
0409EFB  mov     edx, [ebp+var_14]
0409EFE  push    ecx
0409EFF  push    edx
0409F00  call    sub_442270             <-- bez znaczenia
0409F05  add     esp, 8
0409F08  mov     [ebp+var_14], eax
0409F0B  mov     byte ptr [ebp+var_4], 7
0409F0F  call    sub_409FD3             <-- bez znaczenia
0409F14  lea     eax, [ebp+var_30]      <-- public N2
0409F17  lea     ecx, [ebp+var_2C]      <-- public E2
0409F1A  push    eax
0409F1B  mov     edx, [ebp+var_20]
0409F1E  push    ecx
0409F1F  lea     eax, [ebp+var_1C]
0409F22  push    edx                    <-- wprowadzony numer seryjny
0409F23  push    eax                    <-- wyjscie
0409F24  call    sub_412C10             <-- rsa ( SERIAL^E2 mod N2)
0409F29  mov     byte ptr [ebp+var_4], 9
0409F2D  add     esp, 10h
0409F30  mov     eax, [eax]
0409F32  mov     ecx, [ebp+var_14]
0409F35  push    eax                    <-- zaszyfrowany hash jest porownywany
0409F36  push    ecx                    <-- z zaszyfrowanym serialem
0409F37  call    sub_4425C0             <-- porownanie
0409F3C  mov     byte ptr [ebp+var_4], 7
0409F40  add     esp, 8
0409F43  cmp     eax, 1
0409F46  sbb     eax, eax
0409F48  neg     eax
0409F4A  mov     [ebp+var_20], eax      <-- zapisz wynik porownania

Dzisiaj przychodzi mi latwo o tym pisac, ale cala analiza
zajela troche czasu wiec wybaczcie jesli troche zamieszam,
ale nie jestem juz w stanie dokladnie odtworzyc mojego toku
myslowego.Po pierwsze skad do cholery wiem, ze ta i ta funkcja
to szyfrowanie rsa?Przeczucie, instynkt czy jak tam to chcecie
jeszcze nazwac, no na poczatku sam nie wiedzialem co ta procka
sub_412C10 robi, meczylem to F8, ale przekonalem sie, ze nie
tedy droga, bo w tym przypadku trzeba troche szerzej spojrzec
na algorytm, a nie tylko na instrukcje jakie sa wykonywane wewnatrz.
Procka wywolywana jest mniej wiecej w taki sposob w kodzie:

rsa(N,E,dane_wej,dane_wyj) lub w zaleznosci od punktu widzenia
rsa(D,N,dane_wej,dane_wyj)

gdzie N to publiczny modul, E publiczny exponent, dane wejsciowe
w pierwszym przypadku(bo procka jest 2 razy wywolywana) stanowia
bajty hasha wyliczonego z name+adres, skad wiem, ze pierwszy parametr
to akurat N a nie E, no wystarczy sprobowac zlamac pierwszy i drugi
string zeby sprawdzic, ze jeden sie rozklada na liczby pierwsze a drugi
nie, czyli pierwsze szyfrowanie RSA mozna przedstawic tak:

RSA = (MD5^E1 mod N1)

gdzie ^ znaczy podnies do potegi, a mod reszta z dzielenia

Ok, ale procka RSA wywolywana jest tez drugi raz z innym zestawem
kluczy, tym razem szyfrowany jest sam serial wprowadzony przez nas
podczas rejestracji:

RSA = (SERIAL^E2 mod N2)

Nastepnie bajty zaszyfrowanego hasha i numeru seryjnego sa porownywane
i jesli sa takie same to program jest zarejestrowany, cala zaleznosc
wyglada tak:

RSA(MD5^E1 mod N1) == RSA(SERIAL^E2 mod N2)

Ok teraz trzeba poglowkowac jak tu wyprodukowac taki serial, ktory
pasowalby do algo, w moim przypadku najlepszym bodzcem do myslenia jest
brak myslenia, wiec...herbatka u Tadka...jakis herkules na polsacie
...eureka mam!:

SERIAL = (MD5^E1 mod N1) ^ D2 mod N2

czyli hash md5 najpierw szyfrujemy tak jak to robi program wg. 1 kluczy
N i E, nastepnie deszyfrujemy wynik korzystajac z klucza prywatnego D2
i publicznego modulu N2 tak, ze gdy program bedzie szyfrowal wprowadzony
serial to otrzyma w wyniku zaszyfrowany hash md5 z name(jeszcze raz i od
poczatku ;) i wszystko bedzie gralo.Ok potrzebny jest "tylko" prywatny
klucz D2, w rsa aby wyliczyc D potrzebne sa liczby P i Q ktorych iloczyn
stanowi publiczny klucz N(odsylam do tutoriala o AccessLock):

N = P * Q

N2 = 11088765095067625329458021105058134597929

Do lamania klucza N2 wykorzystalem RSATool i tak:

P = 434897457366581013527
Q = 25497424524422440127

No teraz trzeba obliczyc D, a robi sie to wg. wzoru:

D = E^(-1) mod ((P-1)*(Q-1))

w naszym przypadku, publiczny exponent E wynosi:

E2 = 5287446917209892529044901923459570593867

i tu juz RSATool nie pomoze, bo nie ma tam mozliwosci wprowadzenia duzego
exponentu E, przyda sie jakis kalkulatorek(po linki rowniez odsylam do
tutoriala o AccessLock) i D wyszlo:

D2 = 10055691366421374694583185976413497089839

Czyli jest wszystko to co potrzebne, upps zapomnialbym o jedej malej
rzeczy, pamietacie jak program konwertowal te duze stringi(publiczne
klucze), tam znajdowala sie takze procka, ktora konwertowala w taki
sam sposob wprowadzony serial, tylko, ze baza nie byla liczba 10
ale 32, ok ok za chwile do tego dojdziemy, czas na mala procke generujaca
seriale, do obliczen RSA wykorzystalem biblioteke miracl v4.5:


void GenerateCode(char *szName, char *szAddress)
{
big m,n,e,d,c;
MD5_CTX ctx;
unsigned int lName,i,z
unsigned char temp[256];

static char n1[] = "8687147940791546997772999522905216378001";
static char e1[] = "5644737586335572109266844069440047644263";

static char d2[] = "10055691366421374694583185976413497089839";
static char n2[] = "11088765095067625329458021105058134597929";


        strcat(szName,szAddress);       // polacz name z adresem

        lName = strlen(szName);         // dlugosc polaczonych stringow


        i = 0;                          // inicjalizuj zmienna i

        for (t = 0;t< lName ; t++ )
        {
        z = szName[t];                  // pobierz bajt z polaczonych stringow

                                        // sprawdz litery a..z
                if ( (z >= 0x61) && (z <= 0x7A ))
                {
                z -= 0x20;              // do upper case
                }

                                        // sprawdz czy przekonwertowane bajty
                                        // to cyfry 0..9, A..Z
                if ( ( (z >= 0x30) && (z <= 0x39) ) || ((z >= 0x41) && (z <= 0x5A )) )
                {
                szName[i++] = z;        // jesli sa z zakresu to kopiuj, inaczej nie kopiuj ich
                }

        }


        if ( i == 0 )                   // jesli w name albo w adresie wszystkie bajty
        {                               // sa spoza a..z,A..Z,0..9 wyswietl info o bledzie

        printf("Invalid characters detected(allowed a..z,A..Z,0..9)!");
        return;
        }


        szName[i] = 0x00;               // wstaw zero na koncu(asciiz)

        MD5Init(&ctx);                  // inicjalizuj kontext
                                        // wylicz hash md5 z name
        MD5Update(&ctx,szName,i);       // i adresu
        MD5Final(temp,&ctx);            // kopiuj do bufora hash md5
        
        miracl *mip = mirsys(400,0);    // zainicjalizuj system miracl

        m = mirvar(0);                  // zainicjalizuj zmienne,
        n = mirvar(0);                  // wazne bo inaczej wystapi gpf
        c = mirvar(0);
        e = mirvar(0);
        d = mirvar(0);

        mip->IOBASE = 10;               // ustaw baze liczbowa = 10
                                        // uzywane do konwertowania liczb
                                        // wejscie/wyjscie

        bytes_to_big(16,temp,m);        // zamien na format big hash md5
                                        // obliczony z name

        cinstr(n,n1);                   // modulus n1 na big
        cinstr(e,e1);                   // exponent 1 na big

        powmod(m,e,n,c);                // szyfrowanie wg. 1 klucza
                                        // c=m^e1 mod n1

        cinstr(n,n2);                   // zamien na big modulus n 2
        cinstr(d,d2);                   // prywatny exponent na big

        powmod(c,d,n,m);                // deszyfrowanie wg. 2 klucza
                                        // m=c^d2 mod n2

        mip->IOBASE = 32;               // ustaw baze liczbowa = 32
                                        // takiej bazy liczbowej uzywa program
                                        // do konwertowania wprowadzonego seriala
                                        // na big

        cotstr(m,szName);               // zamien na stringa

        mirkill(m);                     // zdeinicjalizuj zmienne typu big
        mirkill(n);
        mirkill(c);
        mirkill(e);
        mirkill(d);

        mirexit();                      // zakoncz sesje

                                        // wyswietl serial
        printf(szName);

        }
        else
        {
        printf("Invalid user data!");
        }


}

Podsumowanie

KatalogCD to chyba pierwszy polski program, ktory wzbudzil moje
uznanie, wlasnie ze wzgledu na algo, jeszcze nigdy wczesniej nie
spotkalem sie z takim zabezpieczeniem wsrod rodzimych produkcji
a szkoda, zycie nabraloby smaku, tak to wszyscy miela jakies zwykle
gowniane wyliczanie wartosci na podstawie name, konwertowanie na
string i to jest serial, z biegiem lat nic sie nie zmienia, poza
dlugoscia algosow generujacych te seriale, tak chwale ten KatalogCD,
ale trzeba tez wspomniec o wadach, no coz nie jest spakowany co
wykluczaloby zastosowanie crackow, jesli by byl spakowany to 
pewno ktos by zrobil loadera, idealnym rozwiazaniem byloby zastosowanie
jakiegos pe pakera np. petite + runtime deszyfrowanie algorytmu
sprawdzajacego serial, wow juz to widze w c++ hehe, wniosek jeden
asm roxx na wieki :))

Yo nie piszcie mi, ze nic nie kapujecie bo ja tez juz ledwo
kumam(hehe), piszac to juz prawie spalem, tzn. lewe oko juz spalo
a prawe probowalo nadrabiac zaleglosci za lewe, cholera takie
sa skutki siedzenia do 4 nad ranem.

Btw. sponsorem dzisiejszego tutoriala byly literki A,S,R oraz wyraz
"dupny", no i warto tez wspomniec, ze zaden lamuch nie ucierpial
podczas tworzenia tego eposu ;)

bart^CrackPl
cryogen@poland.com | cryogen@box43.pl | http://www.ctrl-d.prv.pl