Soluzione lezione 4 UIC |
||
24/10/1999 |
by "syscalo" |
|
|
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 |
|
UIC's form |
Difficoltà |
( )NewBies (x)Intermedio ( )Avanzato ( )Master |
Programma che necessita di un keyfile per la registrazione.
Introduzione |
Tools usati |
URL o FTP del programma |
Essay |
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
|
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 |
Home Anonimato Assembly
ContactMe CrackMe Links
NewBies News Forum Lezioni
Tools Tutorial