Soluzione lezione 4 UIC
(programma con keyfile)

 

24/10/1999

by "syscalo"

 

 

UIC's Home Page

Published by Quequero


Complimenti a syscalo, un ottimo tutorial, in cui ha ben spiegato come funzionava la protezione. Lo schema non era poi molto complesso, ma presentava alcune novita' ed alcuni trucchetti, sia di disonrientamento che cattivelli (presi da un prog vero comunque) seguite il suo tutorial e capirete quali sono.

 
UIC's form
Home page se presente: http://syscalo.cjb.net 
E-mail: [email protected]
UIC's form

Difficoltà

( )NewBies (x)Intermedio ( )Avanzato ( )Master

 

Scaricate il keygenerator

Programma che necessita di un keyfile per la registrazione.


Soluzione lezione 4 UIC
(programma con keyfile)
Written by syscalo

Introduzione

La quarta lezione della UIC tratta i keyfile. Il programma per essere registrato necessita di un file contenete il codice corretto. Generalmente nei programmi
commerciali questo file viene inviato dall'autore del programma dopo che si è pagata la somma richiesta.
 
Per quanto riguarda la spiegazione cercherò di mostrare come effettivamente sono arrivato a scoprire la struttura del keyfile mostrando man mano le varie intuizioni e modificando di conseguenza il file. Forse sarebbe stato più facile sia per me che per voi dire "createvi un keyfile fatto così e poi eseguite il programma", ma visto che siamo qui per imparare tentiamo un approccio sperimentale ;-). Se poi ritenete che non serve a un cazzo fatemelo sapere che lo userò anche la prossima volta.
 
Un consiglio: se non volete incappare nello scherzetto preparato da Tin_Man leggete tutto il tutorial prima di eseguire il programma, anche perchè non sarò certo io a dirvi subito come eliminarlo :-)

Tools usati

- SoftIce: per capire un po' come gira il programma.
- ApiSpy: per vedere quali api usa il programma e per sapere il nome del keyfile.
- WDasm: per avere già scritto il codice riportato sotto.

URL o FTP del programma

Sempre e solo sul sito della UIC.

Essay

Ho pensato di illustrare prima la struttura dei dati presenti nel keyfile per poterci capire durante la spiegazione del programma; il file è meglio se lo modificate con un editor esadecimale in modo da poter inserire direttamente i byte trovati. Nonostante nel keyfile sia presente "in chiaro" il nome che scegliamo è opportuno considerare i dati presenti come byte e non come caratteri, anche perchè alcuni potrebbero non essere rappresentabili.
Il codice di registrazione è così strutturato: supponiamo di inserire un nome di tre lettere (es. sys equivalente alla codifica ASCII 73h 79h 73h)
Posizione byte

0

1

2

3

4

5

6

7

8

9

10

11

12

13

Valore esadecimale byte

X

73

79

73

00

00

00

0A

0A

73

00

00

00

k

 

Byte del nome

Sequenza di 3 byte=0

Byte da codifica del nome

Sequenza di 3 byte=0

 

Descrizione

 

Prima sequenza di byte

Seconda sequenza di byte

Ultimo byte

 

Il byte alla posizione 0 è inserito per "complicare" un po' la protezione; il suo valore non è importante, basta che sia diverso da 00. Ho capito che serve un byte in più all'inizio quando ho visto che viene letto separatamente da tutti gli altri e il suo valore non è salvato.

L'ultimo byte è anch'esso "in più" rispetto alla codifica del nome ed è responsabile del messaggio "Chiave pirata"; durante la spiegazione del programma si vedrà come viene generato.

 

Iniziamo? ok, allora prima di tutto dobbiamo sapere come si chiama il file cercato dal programma; io ho usato Apispy per vedere un po' anche le api usate, ma si può trovare anche usando Filemon oppure direttamente in SoftIce (in questo caso dovremmo spiare un po' nella memoria, quindi meglio un'altra soluzione). Ci siamo il file si chiama tin.key; creiamo un nuovo file "tin.key" nella stessa directory del programma, apriamolo con l'editor esadecimale e ora dobbiamo inserire dei dati: un suggerimento ci è già stato dato da Tin_Man, il nome può essere al massimo di 30 lettere. Io ho inserito la sequenza di byte "69737973" (69 l'ho scelto io come byte alla posizione 0). Tutto è pronto possiamo iniziare: apriamo SoftIce (^D), settiamo un breakpoint sulla funzione readfile (bpx readfile) e usciamo (F5); avviamo il file quattro.exe, premiamo register. SoftIce si fa vedere, premiamo due volte F12 per tornare dalla chiamata e vediamo il seguente codice:

 

:004012B9   push   0042043C   passa l'offset della locazione in cui c'è il nome del file (tin.key)
:004012BE   push   esi  
:004012BF   call   004130D4   procedura che contiene la chiamata all'apertura del file
:004012C4   add   esp,00000010  
:004012C7   inc   [ebp-10]  
:004012CA   mov   [ebp-1C],0014  
:004012D0   push   00000001   passa il # di byte da leggere
:004012D2   push   edi   passa l'indirizzo in cui salvare il byte letto
:004012D3   push   dword ptr [esi]   passa l'Handle del file
:004012D5   call   00419048   procedura che contiene la lettura da file
:004012DA   add   esp,0000000C   questa istruzione serve a posizionare correttamente il puntatore allo stack
:004012DD   cmp   byte ptr [edi],00   controlla se il chr letto è =0
:004012E0   jne   0040130B   se è diverso da 0 continua con il programma (salta a 0040130B) altrimenti visualizza il msg "Non Registrato"

Prosegue il programma con il codice seguente:

:0040130B   xor   ebx,ebx   azzera ebx (usato per conteggio byte letti)
:0040130D   jmp   00401340   salta a 00401340
:0040130F   push   00000001   passa # byte da leggere
:00401311   push   edi   passa l'offset della locazione in cui salvare il byte letto
:00401312   push   dword ptr [esi]   passa l'Handle del file
:00401314   call   00419048   procedura che contiene la lettura da file
:00401319   add   esp,0000000C   questa istruzione serve a posizionare correttamente il puntatore allo stack
:0040131C   movsx   eax,bl   carica bl in eax (conteggio byte letti)
:0040131F   mov   dl,byte ptr [edi]   carica in dl il byte letto (il cui indirizzo è contenuto in edi)
:00401321   mov   byte ptr [ebp+eax-000000A0],dl   salva il byte letto alla locazione il cui indirizzo è dato da [(ebp-0A0)+eax] dove eax serve per salvare a locazioni successive i byte letti incrementandolo di 1 (parte dalla locazione 0056F888)
:00401328   cmp   byte ptr [edi],00   controlla se il byte letto è =0
:0040132B   jne   00401332   se è diverso da 0 salta a 00401332
:0040132D   inc   [ebp-2E]   altrimenti incrementa la locazione di indirizzo (ebp-2E)->viene usata per contare quanti byte di valore 0 ci sono consecutivi
:00401330   jmp   00401336   salta a 00401336

 

A questo punto mi sono accorto che il programma diversifica l'esecuzione se incontra un byte uguale a 00; allora ho modificato in file "tin.key" nel seguente modo: 69737900 e rieseguito il programma sono arrivato al codice seguente:

 

:00401332   mov   [ebp-2E],00   azzera conteggio byte =0
:00401336   movsx   ecx,byte ptr [ebp-2E]   carica in ecx il numero di byte consecutivi =0
:0040133A   cmp   ecx,00000002   confronta con 2
:0040133D   jg   00401348   se è maggiore salta a 00401348 (in pratica questa parte serve per controllare che sia presente la sequenza di tre byte =0)
:0040133F   inc   ebx   incrementa il conteggio dei byte letti
:00401340   movsx   eax,bl   copia bl in eax (conteggio byte)
:00401343   cmp   eax,0000001E   se i byte letti sono meno di 32 (=1E esadecimale)
:00401346   jl   0040130F   salta a 0040130F
:00401348   mov   [ebp-2E],00   azzera il conteggio byte consecutivi=0
:0040134C   xor   ebx,ebx   azzera conteggio byte letti
:0040134E   jmp   00401381   salta a 00401381

 

Da qui si capisce chiaramente che viene ricercata una sequenza di tre byte uguali a 00; rimodifico il file "tin.key" in 69737973000000 (immaginate sempre che questo valore lo vediate in un editor esadecimale e quindi sono 7 byte di questo tipo 69|73|79|73|00|00|00; può sembrare una banalità ma non si sa mai)
Rieseguo il programma fino ad arrivare a questo punto; qui inizia la lettura della seconda sequenza di byte (guardare la tabella iniziale):

:00401350   push   00000001   passa # byte da leggere
:00401352   push   edi   passa offset locazione in cui salvare il byte letto
:00401353   push   dword ptr [esi]   passa l'Handle del file
:00401355   call   00419048   procedura che contiene la lettura da file
:0040135A   add   esp,0000000C   questa istruzione serve a posizionare correttamente il puntatore allo stack
:0040135D   movsx   eax,bl   carica bl in eax (conteggio byte letti)
:00401360   mov   dl,byte ptr [edi]   carica in dl il byte letto (il cui indirizzo è contenuto in edi)
:00401362   mov   byte ptr [ebp+eax-000000C0],dl   salva il byte letto alla locazione il cui indirizzo è dato da [(ebp-0C0)+eax] dove eax serve per salvare a locazioni successive i byte letti incrementandolo di 1 (parte dalla locazione 0056F868)
:00401369   cmp   byte ptr [edi],00   controlla se il byte letto è =0
:0040136C   jne   00401373   se è diverso salta a 00401373
:0040136E   inc   [ebp-2E]   incrementa conteggio byte successivi =0
:00401371   jmp   00401377   salta a 00401377
:00401373   mov   [ebp-2E],00   azzera conteggio byte successivi =0
:00401377   movsx   ecx,byte ptr [ebp-2E]   copia in ecx il conteggio numero byte successivi =0
:0040137B   cmp   ecx,00000002   confronta con 2
:0040137E   jg   00401389   se è maggiore salta a 00401389 (in pratica questa parte serve per controllare che sia presente la sequenza di tre byte =0)
:00401380   inc   ebx   incrementa conteggio byte letti
:00401381   movsx   eax,bl   carica bl in eax (conteggio byte letti)
:00401384   cmp   eax,0000001E   se sono meno di 32 (=1E esadecimale)
:00401387   jl   00401350   salta a 00401350

 

Vista questa parte di codice si capisce la struttura della seconda sequenza di byte; modifico di nuovo il keyfile in 69737973000000|909090000000. La scelta dei tre 90 è arbitraria; l'importante è aggiungere un numero di byte pari a quelli del nome e la sequenza di tre byte uguali a 0.

Dal codice seguente si capisce come viene generata la seconda sequenza di byte e potremo quindi andare a modificare il key file opportunamente:

 

:00401389   lea   edx,dword ptr [ebp-0A0]   carica in edx l'offset della locazione in cui è memorizzata la prima sequenza di byte letti
:0040138F   push   edx   passa edx
:00401390   call   004146D4   procedura che ritorna in eax il numero di byte letti nella prima sequenza
:00401395   pop   ecx
:00401396   mov   edx,eax   carica in edx il # di byte della prima sequenza
:00401398   xor   ebx,ebx   azzera ebx (conteggio byte)
:0040139A   cmp   dl,bl   confronta dl con bl
:0040139C   jle   004013C9   se dl<=bl salta a 004013C9 (salta quando è terminata la sequenza di byte)
:0040139E   mov   eax,ebx   carica ebx in eax (conteggio byte)
:004013A0   inc   eax   incrementa di 1 eax
:004013A1   movsx   ecx,al   copia al in ecx
:004013A4   mov   al,byte ptr [ebp+ecx-000000A0]   carica in al un byte dal secondo della prima sequenza (ecx=ebx+1 per l'istruzione inc eax)
:004013AB   movsx   ecx,bl   copia bl in ecx
:004013AE   xor   al,byte ptr [ebp+ecx-000000A0]   xora il byte letto prima con il byte dal primo della prima sequenza (ecx=ebx) ->le quattro istruzioni appena eseguite fanno in modo che ogni byte venga xorato con il successivo, quindi il 1° con il 2°, il 2° con il 3° ecc. (questo è il modo in cui devono essere generati i byte della seconda sequenza)
:004013B5   movsx   ecx,bl   copia bl in ecx
:004013B8   cmp   al,byte ptr [ebp+ecx-000000C0]   confronta il byte ottenuto dallo xor con il byte a partire dal primo della seconda sequenza
:004013BF   jne   004013C4   se sono diversi (registrazione sbagliata) salta a 004013C4
:004013C1   inc   [ebp-2D]   incrementa (ebp-2D)-> serve per un controllo successivo; in pratica serve a controllare che il confronto sia risultato vero per tutti i byte (A)
:004013C4   inc   ebx   incrementa ebx (conteggio byte)
:004013C5   cmp   dl,bl   esegue le istruzioni precedenti per tutti i byte della prima sequenza
:004013C7   jg   0040139E   salta a 0040139E
:004013C9   cmp   dl,byte ptr [ebp-2D]   esegue il controllo descritto prima; vedi (A)
:004013CC   jne   004013FE   se sono diversi (registrazione sbagliata) salta a 004013FE
:004013CE   xor   eax,eax   azzera eax
:004013D0   mov   dword ptr [00420140],eax   copia 0 alla locazione 00420140 (sta ad indicare che la registrazione è corretta)

 

Mettiamo mano ancora una volta al keyfile; nel mio caso diventa 697379730000000A0A73000000.
Ora si deve proseguire nel programma; ci sono diversi modi per farlo:

- proseguiamo con F10 fino al'istruzione all'indirizzo 0040171A (che è quella che interessa a noi) e ci leggiamo tutto il codice per capire dove siamo.

- fate lo scroll della finestra in SoftIce fino a quando arrivate all'indirizzo, posizionate il mouse e premete F7; ma se non sapete dove dovete arrivare non si può fare (e poi sareste proprio dei fannulloni :-)

- un modo più intelligente è settare un breakpoint sull'accesso alla memoria alla locazione 00420140 che è quella che fa da flag per la correttezza della registrazione, e vedere dove si arriva;

Io direi che la terza soluzione è la migliore quindi impostiamo il breakpoint sull'accesso alla memoria (bpm 420140 rw) e premiamo F5. SoftIce si fermerà proprio all'istruzione all'indirizzo 00401721 dove troviamo il seguente codice:

:0040171A   push   ebp  
:0040171B   mov   ebp,esp  
:0040171D   push   ebx  
:0040171E   mov   ebx,dword ptr [ebp+08]  
:00401721   cmp   dword ptr [00420140],00000000   controlla se la locazione 00420140 contiene 0 (=registrazione corretta)
:00401728   je   00401749   se è uguale salta a 00401749 (visualizza msg "Registrato!")
:0040172A   mov   eax,dword ptr [ebx]   altrimenti visualizza msg "Non registrato"
:0040172C   push   00000000   |con le seguenti istruzioni passa i parametri per la MessageBox
:0040172E   push   00420502   |
:00401733   push   004204EF   |
:00401738   push   [eax+0C]   |
:0040173B   push   [eax+68]   |
:0040173E   call   0040B20A   procedura che visualizza il msg "Non registrato"
:00401743   add   esp,00000014   |
:00401746   pop   ebx   |ripristina i registri
:00401747   pop   ebp   |
:00401748   ret   ritorna dalla chiamata

 

Abbiamo quindi analizzato tutta la parte che riguarda la registrazione e, con nostro immenso piacere, abbiamo costruito il nostro keyfile.

Ma non è ancora finita! Tin_Man ci ha riservato ancora una piccola sorpresa: il programma dopo aver riconosciuto come corretta la registrazione visualizza il messaggio "Chiave pirata" e poi.... bhe vediamo di scoprire che combina.
Entriamo in SoftIce (^D) cancelliamo tutti i breakpoint (bc*) ed impostiamone uno sulla visualizzazione del messaggio (bpx MessageBoxExA); usciamo da SoftIce (F5) avviamo il programma, premiamo register e poi about; eccoci in SoftIce, ma qui siamo al messaggio "Registrato!" quindi proseguiamo premendo (F5) e ok nel MessageBox mostrato; rieccoci in SoftIce! Ora siamo al punto giusto; premete F12 per ritornare dalla chiamata, premete ok nella MessageBox (ora ritorna SoftIce) e premete due volte F12; ora vedrete il seguente codice:

:00401879   push   00000001  
:0040187B   lea   edx,dword ptr [ebp-7F]  
:0040187E   push   edx  
:0040187F   push   dword ptr [edi]  
:00401881   call   00419048   procedura che legge i byte dal file (in pratica quando premiamo about vengono riletti i byte dal file tin.key e viene effettuato il controllo spiegato qui)
:00401886   add   esp,0000000C   questa istruzione serve a posizionare correttamente il puntatore allo stack
:00401889   mov   al,byte ptr [ebp-7F]   carica in al l'ultimo byte aggiunto dopo la seconda sequenza
:0040188C   mov   dl,byte ptr [ebp-0A0]   carica in dl il primo byte della prima sequenza
:00401892   xor   dl,byte ptr [ebp-0C0]   xora il primo byte della prima sequenza con il primo byte della seconda sequenza
:00401898   cmp   dl,al   confronta il byte ottenuto dallo xor con l'ultimo byte "extra"-> in pratica l'ultimo byte deve essere uguale al secondo della prima sequenza (la seconda lettera del nostro nome)
:0040189A   je   004018AE   se sono uguali salta a 004018AE
:0040189C   push   [ebp+08]   altrimenti arriva l'inaspettata sorpresa...
:0040189F   call   004018D9   visualizza il msg "Chiave pirata" :-(
:004018A4   pop   ecx  
:004018A5   push   00000000  
:004018A7   push   00000005  
:004018A9   call   0041F8F4   arresta il sistema con la chiamata all'API ExitWindowsEx!! e bravo Tin_Man ;-)

 

Mi sento di raccomandarvi di non far eseguire la chiamata all'arresto del sistema! Per evitarla potete ad esempio nopparla dando il seguente comando in softice: "e 004018A9" e poi andate a sovrascivere l'opcode dell'istruzione con dei 90 (=opcode dell'istruzione nop). Forse è meglio che noppate anche le due push precdenti.

:004018AE   xor   eax,eax   salta qui se la registrazione è corretta, compreso il byte extra

 

e prosegue con l'esecuzione del programma. Modifichiamo quindi il file "tin.key" aggiungendo un ultimo byte uguale al secondo byte del nostro nome (io aggiungerò 79=y).

Ok siamo giunti al termine; ora vi spiegherò un po' il listato del generatore del keyfile (io l'ho realizzato con Delphi ma è totalmente ininfluente, è solo una mia scelta):

 

unit Unit1;
interface
uses
Forms, Classes, Controls, StdCtrls, Buttons;
type
    TForm1 = class(TForm)
    Label1: TLabel;
    Edit1: TEdit;
    BitBtn1: TBitBtn;
    Label2: TLabel;
    procedure BitBtn1Click(Sender: TObject);
    private
        { Private declarations }
    public
        { Public declarations }
end;
var
    Form1: TForm1;
implementation
{$R *.DFM}
{la parte di programma che interessa a voi inizia qui}

procedure TForm1.BitBtn1Click(Sender: TObject);  Viene chiamata questa procedura quando premete il tasto OK
var
    reg: array[0..68]of byte;    Definisco un array di 68 byte per contenere i dati da scrivere nel file
    nome:string;                       Contiene il nome da voi inserito
    i:integer;                             Variabile per il conteggio nei cicli
    file1: file of byte;                 Variabile per l'uso del file
begin
    BitBtn1.Enabled:=False;    Disabilita il bottone OK
    nome:=Edit1.Text;              Copia in nome il nome da voi inserito
    reg[0]:=$69;                       Imposta il primo byte a 69 (il simbolo $ sta ad indicare che è un valore esadecimale)
    i:=1;                                   Inizializza a 1 la variabile i (parte da 1 per come sono definite le stringhe in Delphi)

Nel seguente ciclo copia il codice ASCII del caratteri del nome inserito (prima sequenza di byte)

    while(nome[i]<>'') do         Esegue fino alla fine del nome
    begin
        reg[i]:=Ord(nome[i]);    Copia il codice ASCII
        inc(i);                             Incrementa la varibile i usata per la posizione nella stringa e nell'array
    end;

 

Nel seguente ciclo aggiunge la sequenza di tre byte uguali a zero
    for i:=length(nome)+1 to length(nome)+3 do
        reg[i]:=$0;

    i:=1;                                   Inizializza a 1 la variabile i (parte da 1 per come sono definite le stringhe in Delphi)

Nel seguente ciclo aggiunge la seconda sequenza di byte data dallo xor tra un carattere e il suo successivo (la funzione length ritorna la lunghezza della stringa)

    while(nome[i]<>'') do         Esegue fino alla fine del nome
    begin
        reg[length(nome)+3+i]:=(Ord(nome[i]) xor Ord(nome[i+1]));    esegue lo xor di un carattere con il suo successivo e copia il valore nell'array
        inc(i);                             Incrementa la varibile i usata per la posizione nella stringa e nell'array
    end;

 

Nel seguente ciclo aggiunge la sequenza di tre byte uguali a 0
    for i:=length(nome)*2+4 to length(nome)*2+6 do
        reg[i]:=$0;

 

    reg[length(nome)*2+7]:=Ord(nome[2]);    Aggiunge l'ultimo byte uguale al secondo carattere del nome

    AssignFile(file1,'tin.key');    |
    Rewrite(file1);                     |crea e apre il file tin.key

Copia i byte all'interno del file

    for i:=0 to (length(nome)*2+7) do
        Write(file1,reg[i]);

    CloseFile(file1);                   Chiude il file
end;
end.

 

Ok adesso è tutto finito, possiamo dedicarci a qualcos'altro ;-)

 

««alla proxima»»

syscalo

Note finali

Oggi sono a corto di parole, e poi ne ho già usate tante per il tutorial, quindi vi saluto e vi lascio ai vostri pensieri.

Disclaimer

Non azzardatevi a copiare i tutorial della UIC e a spacciarli per vostri! Sarete puniti! Ricordatevi che il software va comprato e quindi se volete questo programma dovete pagarlo a Tin_Man che poi provvederà a distribuire i soldi equamente.
 
Vorrei inoltre ricordare che il software va comprato e  non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.
Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.

 
UIC's page of reverse engineering, scegli dove andare:

Home   Anonimato   Assembly    ContactMe  CrackMe   Links   
NewBies   News   Forum   Lezioni  
Tools   Tutorial 

UIC