Kwazy Webbit's PACME


31/08/2000

by "blAAd!"

 

 

UIC's Home Page

Published by Quequero


cRaCk'Em AlL bUt DoN't StEaL!!

Una NewEntry nel nostro (sempre più grande) gruppo, il crackme non era eccessivamente difficile, ma è stato spiegato in modo assolutamente meticoloso e preciso, bravo Blaad!

cRaCk'Em AlL bUt DoN't StEaL!!
UIC's form
E-mail: [email protected]
UIC's form

Difficoltà

(X)NewBies ( )Intermedio ( )Avanzato ( )Master

 

Ritengo che spesso i crackmes siano più interessanti di molti schemi di protezione presenti nei programmi commerciali. E ancor più spesso sono fatti da 'reverser' che ne sanno più di noi. Quindi perchè non attingere direttamente dalla fonte??


Kwazy Webbit's PacMe
Crack e costruzione di un KEYFILE MAKER
Written by blAAd!

Introduzione

In questo tut cercheremo di capire come funziona il cripting alquanto originale di PACME, e costruiremo un semplice KEYFILE MAKER in C/C++, per l'editing di file di registrazione.

Tools usati

S-Ice
WDasm 8.93
Hex Work-Shop
Dev-C++ 3.95

URL o FTP del programma

http://crackmes.cjb.net

Notizie sul programma 

Beh, non c'è molto da dire. E' un crackme classico, che richiede un file di registrazione, e il nostro compito sarà quello di crearne uno (e visto che ci stiamo, creiamo anche un KEYFILE MAKER). Del resto, non avete mai visto un crackme??

Essay

Vi ricorda qualcosa PacMan??
Ok, non vi anticipo nulla...iniziamo...
Fate partire il programma. Vi troverete subito davanti una finestra su cui compare la scritta 'UNREGISTERED!' e dove sono presenti due bottoni, quello di 'EXIT' e quello di 'CHECK'. E' chiaro che si tratta di un tipo di registrazione legato ad un file esterno, e quindi molto probabilmente dovremo costruire un 'fake' file. Entriamo in S-Ice(Ctrl-D), e scriviamo 'bpx CreateFileA' + Enter. Premiamo di nuovo Ctrl-D per uscire e tornare al nostro programma. Clickiamo su 'CHECK'. Al 99.9% ci troveremo ancora in S-Ice e il codice che si presenterà davanti (dopo aver premuto 'F12') sarà:

:0040169A push 00000000
:0040169C push 00000080
:004016A1 push 00000003
:004016A3 push 00000000
:004016A5 push 00000003
:004016A7 push 80000000
:004016AC lea edx, dword ptr [00403192]

In 403192 è contenuto il nome del file da leggere: 'Kwazyweb.bit'. I push sopra, sono relativi a dei parametri per la funzione CreateFileA. E' bene andarsi a guardare sulle WIN32API, le specifiche delle chiamate quando ne incontriamo qualcuna.

:004016B2 push 2E2E7372
:004016B7 push 656D616C
:004016BC push 20656D6F
:004016C1 push 7320676E
:004016C6 push 69737566
:004016CB push 6E6F6320
:004016D0 push 7473754A
:004016D5 add esp, 0000001C
:004016D8 push edx

* Reference To: KERNEL32.CreateFileA, Ord:0032h
|
:004016D9 Call 004017FA
<------ Prova ad aprire il file
:004016DE cmp eax, FFFFFFFF
:004016E1 je 00401747
<------ Se non esiste, salta

Non sapendo come deve essere composto il nostro file, continuiamo ad analizzare il codice:

:004016E3 mov dword ptr [00403444], eax
:004016E8 push 00000000
:004016EA push 00403448
:004016EF push 00000001
<------ Byte da leggere
:004016F1 push 004034FA
:004016F6 push dword ptr [00403444]

* Reference To: KERNEL32.ReadFile, Ord:01FDh
|
:004016FC Call 00401812
:00401701 movzx eax, byte ptr [004034FA]
:00401708 test eax, eax
:0040170A je 00401747
<------ Se byte = 0 salta

Qui viene letto, attraverso la funzione ReadFile, un byte soltanto, e il suo valore viene immagazzinato in 4034FA ed in al. Però non abbiamo ancora informazioni sufficienti per impostare un 'fake' file. Quindi continuiamo nella lettura del nostro codice:

:0040170C push 00000000
:0040170E push 00403448
:00401713 push eax
<------ numero di byte da leggere
:00401714 push 00403288
<------ dove vengono memorizzati
:00401719 push dword ptr [00403444]

* Reference To: KERNEL32.ReadFile, Ord:01FDh
|
:0040171F Call 00401812
:00401724 call 00401000
<------ Routine A

Questa volta vengono letti un numero di byte pari al contenuto del registro eax. Continuiamo (tralasciamo per il momento la chiamata alla routine A e alla B in xxxx:00401742):

:00401729 push 00000000
:0040172B push 00403448
:00401730 push 00000012
<------ numero di byte da leggere
:00401732 push 004034E8
<------ dove vengono memorizzati
:00401737 push dword ptr [00403444]

* Reference To: KERNEL32.ReadFile, Ord:01FDh
|
:0040173D Call 00401812
:00401742 call 004010C9
<------ Routine B

In questo blocco di istruzioni sono letti 18 byte, e questa è l'ultima lettura. La struttura generale del nostro file sarà allora:

1 byte + x byte + 18 byte

dove nel primo byte è racchiuso il valore di x.

Ok. Ora che sappiamo come devono essere impostati i nostri dati, disabilitiamo il notro bpx con bd* ed usciamo da S-Ice. Apriamo HexWork Shop 3.11, scegliendo New dal menù File. Gli x bytes del mio file saranno composti da 'blAAd!', quindi il primo byte conterrà il valore ASCII 06, e gli ultimi 18 bytes, ad esempio, la stringa 'littlepiglittlepig' (wHaT??). Il file finale sarà composto allora dai 25 bytes:

06,62,6C,41,41,64,21,6C,69,74,74,6C,65,70,69,67,6C,69,74,74,6C,65,70,69,67 || .blAAd!littlepiglittlepig

Salviamo il tutto nella directory contenente il crackme col nome 'Kwazyweb.bit', e da S-Ice riabilitiamo il nostro bpx con be*. Usciamo e clickiamo 'CHECK' su PacMe. Ci troveremo di nuovo sul punto d'apertura del file. Premiamo 'F10' fino a gìungere a xxxx:00401724, dove c'è la al chiamata alla routine A. Quindi premiamo 'F8' ed entriamo nel suo codice:

:00401000 xor eax, eax<------ eax = 0
:00401002 xor edx, edx
<------ edx = 0
:00401004 xor ecx, ecx
<------ ecx = 0
:00401006 mov cl, byte ptr [004034FA]
<------ cl = 6
:0040100C mov esi, 00403288
<------ 'blAAd!'
:00401011 lodsb
:00401012 add edx, eax
<------ somma ASCII in edx
:00401014 loop 00401011
:00401016 mov byte ptr [004034FB], dl
<------ salva dl in 4034FB
:0040101C ret

Come è facile intuire, questa routine non fa altro che sommare i valori ASCII della strnga 'blAAd!', e salvare dl in 4034FB. Nel nostro caso specifico avremo come somma in edx il valore 1D5, e in 4034FB sarà salvato il valore D5 (viene prelevato solo il byte dl).

Dopo il 'ret', torniamo al programma principale, e continuiamo a premere 'F10', fino a giungere all'indirizzo xxxx:00401742 dove c'è la chiamata alla routine B e premiamo di nuovo 'F8':

* Referenced by a CALL at Address:
|:00401742
|
:004010C9 push ebp
:004010CA mov ebp, esp
:004010CC add esp, FFFFFFFC

* Possible StringData Ref from Data Obj
->"****************C*......*...****.*.****...*..."
->".*.*..**********.*..*....*...*...**.****.*.*.."
->".****.*....*.*******..*.***..*.....*.*..***.**"
->".***.*...****....*X..*****************"
|
:004010CF push 00403365

* Possible StringData Ref from Data Obj
->"****************C*......*...****.*.****...*..."
->".*.*..**********.*..*....*...*...**.****.*.*.."
->".****.*....*.*******..*.***..*.....*.*..***.**"
->".***.*...****....*X..*****************"
|
:004010D4 push 004031BC

Teniamo conto di questa strana stringa 'pushata' e proseguiamo:

* Reference To: KERNEL32.lstrcpyA, Ord:02DCh
|
:004010D9 Call 00401818
:004010DE mov dword ptr [00403184], 004031CC
:004010E8 call 0040101D
<------ Routine C
:004010ED mov [ebp-02], 00
:004010F1 xor eax, eax
:004010F3 xor ecx, ecx

In questo semplice blocco di istruzioni, il programma non fa altro che salvare in memoria la stringa 'pushata' sopra, e tenere in 403184 l'indirizzo 4031CC. Scriviamo 'd 4031CC'. Sul display vedremo la stringa partire dal carattere 'C'. La routine C (scusate le C) invece (premendo 'F8' una volta evidenziata la chiamata) è data da:

:0040101D mov dl, byte ptr [004034FB]<------ dl = D5
:00401023 mov ecx, 00000012
<------ lunghezza dell'ultima parte del del file
:00401028 mov eax, 004034E8
<------ 'littlepig....'
:0040102D xor byte ptr [eax], dl
<------ Codifica tramite XOR della stringa sopra
:0040102F inc eax
:00401030 loop 0040102D
<------ Cicla per tutta la lunghezza della stringa
:00401032 ret

Qui non facciamo altro che lo XOR tra i relativi ASCII della stringa 'littlepiglittlepig', con il valore D5 salvato in precedenza. Dopo il 'ret' proseguiamo col programma principale:

*Jump at Address:
|:00401128
|
:004010F5 mov [ebp-01], 08
<------ indice di posizione delle coppie di bit

*Jump at Address:
|:0040111F
|
:004010F9 sub byte ptr [ebp-01], 02
<------ partiamo dal settimo/ottavo bit
:004010FD movzx ecx, byte ptr [ebp-02]
<------ OFFSET per la nostra stringa XORata
:00401101 add ecx, 004034E8
<------ 4034E8 è l'indirizzo di partenza della stringa
:00401107 mov al, byte ptr [ecx]
<------ preleva singolo byte
:00401109 mov cl, byte ptr [ebp-01]
<------ le istruzioni che ora seguono saranno riprese avanti
:0040110C shr al, cl
:0040110E and al, 03
:00401110 call 00401033
<------ Routine D
:00401115 test eax, eax
:00401117 je 0040112A
:00401119 movzx edx, byte ptr [ebp-01]
:0040111D test edx, edx
:0040111F jne 004010F9
:00401121 inc [ebp-02]
:00401124 cmp byte ptr [ebp-02], 12
:00401128 jne 004010F5

*Jump at Address:
|:00401117
|
:0040112A leave
:0040112B ret

Queste istruzioni impostano un ciclo, in cui viene prelevato un byte dalla stringa dove in precedenza è stato fatto lo XOR, e vengono con i vari shr ed and, prelevati in coppia, i bit che lo compongono. Cioè:

11011000, sarà 'diviso' in 11,01,10,00

Ogni singola coppia di bit sarà analizzata all'interno della routine D:

|:00401110
|
:00401033 push ebp
:00401034 mov ebp, esp
:00401036 add esp, FFFFFFF8
:00401039 mov edx, dword ptr [00403184]
:0040103F mov dword ptr [ebp-04], edx
:00401042 or al, al
:00401044 jne 0040104F
:00401046 sub dword ptr [00403184], 00000010
:0040104D jmp 0040106E

*Jump at Address:
|:00401044
|
:0040104F cmp al, 01
:00401051 jne 0040105B
:00401053 inc dword ptr [00403184]
:00401059 jmp 0040106E

*Jump at Address:
|:00401051
|
:0040105B cmp al, 02
:0040105D jne 00401068
:0040105F add dword ptr [00403184], 00000010
:00401066 jmp 0040106E

*Jump at Address:
|:0040105D
|
:00401068 dec dword ptr [00403184]

*Jump at Addresses:
|:0040104D, :00401059, :00401066
|
:0040106E mov edx, dword ptr [00403184]
:00401074 mov al, byte ptr [edx]
:00401076 cmp al, 2A
:00401078 jne 00401080
:0040107A xor eax, eax
:0040107C leave
:0040107D ret

Senza entrare eccessivamente nel merito della singola istruzione, ci basterà sapere che se i 2 bit sono uguali ad esempio a %11, cioè 3, allora il carattere 'C' all'interno della 'strana' stringa:

"****************C*......*...****.*.****...*...
.*.*..**********.*..*....*...*...**.****.*.*..
.****.*....*.*******..*.***..*.....*.*..***.**
.***.*...****....*X..*****************"

si sposterà indietro di un 'passo', a meno che non siamo finiti su un '*'. In tal caso si uscirà dalla routine ed il programma resterà UNREGISTERED. I Possibili spostamenti sono:

0 : -16 passi
1 : +1 passo
2 : +16 passi
3 : -1 passo

Un passo nella memoria, rappresenta lo spostamento di un byte. Ma a cosa serve tutto ciò?
Proviamo a riscrivere la stringa a blocchi di 16 caratteri:

****************
C*......*...****
.*.****...*....*
.*..**********.*
..*....*...*...*
*.****.*.*...***
*.*....*.*******
..*.***..*.....*
.*..***.**.***.*
...****....*X..*
****************

Non è altro che un labirinto 'one way', in cui il nostro PacMan 'C' dovrà mangiare tutti i '.' fino a raggiungere la 'X'. I 'passi' per compiere l'impresa, costituiranno la codifica per il 'crack'. Ecco la sequenza del movimento, relativo ai valori 0,1,2,3 sopra:

2,2,2,1,2,2,2,3,2,2,1,1,0,1,0,0,1,1,1,0,0,3,3,3,0,3,0,0,1,1,1,1,1,2,1,1,0,1,1,2,1,1,1,2,2,3,3,2,3,3,0,3,3,2,2,2,3,2,2,1,1,1,0,0,1,1,1,1,2,2,3,3

Prelevando i primi quattro valori ad esempio, tradotti in coppie di bit binarie, giungiamo al termine:

10101001 -> A9,

e continuando così per tutti i gruppi di 4, otteniamo finalmente i 18 bytes:

A9, AB, A5, 10, 54, 3F, 30, 55, 65, 16, 56, BE, F3, EA, E9, 50, 55, AF.

Ora se li sostituiamo con HexWork Shop al posto di 'littlepiglittlepig' (Sigh! Sigh!), nel file Kwazyweb.bit, e salvando quest'ultimo nella directory di PacMe, premendo 'CHECK' otterremo le sudate congratulazioni. -=rAW POWEr=-


Costruzione del KeyFile Maker
*****************************

E' ormai chiaro il funzionamento del programma, quindi la costruzione del KFM sarà abbastanza ovvia. Basterà immettere una stringa di ingresso come nome per la registrazione (nel caso specifico descritto sopra era 'blAAd!'), calcolarne la lunghezza, estrarre il valore del byte per lo XOR (sopra era D5), e fare proprio lo XOR di quest'ultimo con i 18 bytes ottenuti sopra. Quindi mettere tutto insieme nella sequenza dati finora usata, ricordandoci di porre il file ottenuto (o direttamente il KFM) nella directory del nostro crackme:

#include <iostream.h>
#include <stdio.h>
#include <string.h>
#include <fstream.h>

int main()
{
char name [256];
unsigned short int pathpac[] =
{
0xa9,0xab,0xa5,0x10,0x54,0x3f,
0x30,0x55,0x65,0x16,0x56,0xbe,
0xf3,0xea,0xe9,0x50,0x55,0xaf,0
}; /* I 18 BYTES DEL PERCORSO */
printf ("kEy FiLe MaKeR of Kwazy Webbit's PacMe by blAAd!\n");
printf ("************************************************\n");
printf ("\nInsert Name: ");
gets(name); /* IMMISSIONE DELLA STRINGA PER LA CODIFICA */
int i=0;
unsigned long int asc = 0;
while (name[i]!= 0) {
asc +=name[i];
i++;
} /* SOMMA DEGLI ASCII DELLA STRINGA */
ofstream fout ("KwazyWeb.bit"); /* APERTURA DEL FILE */
fout << (char *)&i; /* SALVIAMO IL PRIMO BYTE */
i=0;
while (name[i]!=0) {
fout << name[i];
i++;
} /* SALVEZZA STRINGA */
i=0;
char coder = (unsigned char)asc;
while (pathpac[i]!=0) {
(unsigned char)pathpac[i]^=coder;
fout << (char *) &pathpac[i];
i++;
} /* XOR DEL PERCORSO COL VALORE IN CODER */
fout.close(); /* CHIUSURA DEL FILE */
printf ("-eLeNg-\n");
printf ("PRESS A KEY TO EXIT");
getchar();
return 0;
}

*******************

tHe EnD!?

-eLeNg-


Note finali

E' il mio primo tut, e non vorrei aver fatto troppi errori. Comunque ringrazio gli ideatori del sito (che gran leccata di culo), e gli autori dei vari tutz, da cui ho attinto.

Disclaimer

Questo tutorial è a solo scopo informativo. Ne io, ne i possessori del sito, siamo responsabili di un eventuale abuso di ciò che è stato trattato per scopi di pirateria, e violazione di copyright. In qualsiasi caso rubare è sbagliato, e i vari tutorial presenti sul sito, dovrebbero aiutare a comprendere l'impegno profuso da parte dei programmatori, per la creazione d'ogni singolo programma.

UIC's page of reverse engineering, scegli dove andare:
Home   Anonimato   Assembly    ContactMe   CrackMe   Forum   Iscrizione
Lezioni    Links   Linux   NewBies   News   Playstation
Search   Tools   Tutorial   UIC Faq   XXX Page
UIC