Zero_G Moderator
Joined: 20
May 2004 Posts: 886 Location: Dark Side of the
Moon
|
Posted: Sat Apr 30, 2005
5:21 pm Post subject:
Ritornando alla S***vision... |
|
|
|
Oh via, dopo una
settimanetta niente male (un compito ed un orale a fila), ho
riguardato il tanto acclamato BigRace, anche perché shrek2, piratino e michfigo si sono
praticamente innamorati di quest'aggeggio infernale...
Una volta appurato con PEiD che il fetente è scritto
in Delphi, andate a casa di DeDe e fatevi dare un bel
"bigrace.map" alla fragola, con dentro tutti i riferimenti
alle funzioni dell'oracolo (non li fanno più i gelati di una
volta! ); tornate a casa e con il GODup plugin che avete
sul comodino fate mangiare il map-gelato a OllyDbg, che vi
mostrerà tutta la sua gratitudine offrendovi in cambio un bel
disassemblato commentato e pieno di utili labels.
Giusto per sfizio, mentre voi lavorate al
tavolino, potete mettere IDA a fare la cyclette
sull'eseguibile principale in modo da avere ancora più
informazioni a disposizione; che donna!
Prima
mossa, come sempre, la ricerca delle stringhe: seguite il
precedente su BigMatch per le linee guida, cmq è semplice,
basta cercare "Il codice" e vi troverete subito a 0072E171;
risalite fino a 0072DF94 che è l'inizio della procedura
assegnata al click del bottone (lo vedete il
TFormUnlock@SbloccaClick di DeDe? )
Lanciate BigRace da dentro il debugger, inserite
nome/seriale a caso, cliccate su "Sblocca" ed Olly entra in
scena; si capisce bene che i programmatori hanno riusato il
codice già scritto per BigMatch, dato che questa funzione è
del tutto identica, solo l'offset da cui comincia è diverso.
Lo
StrLen a 0072DFDE serve a controllare che il nome sia lungo
almeno 8 caratteri, spazi esclusi, quindi con "Pinco Pallino"
ce la caviamo; si prosegue a 0072DFF7, dove in EAX viene
caricato un parametro che si trova nello stack poco sopra il
nostro nome (EBP-118) e la successiva CALL lo memorizza in una
area di memoria riservata (verificatelo entrando con F7 dentro
la CALL).
continuando più in basso, lo stesso valore
viene caricato anche in ECX, DL viene settato a 1 e la stringa
"TCripter" viene messa in EAX come parametro per la successiva
CALL che si trova a 0072E00F (controllate sempre la status bar
che fornisce un sacco di utili informazioni sull'istruzione
attuale)
Code: |
0072DFF7 > LEA
EAX,SS:[EBP-118] ; carica in EAX il valore
di init 0072DFFD > CALL
<LoadStuff()> ; e ci
inizializza il crypter 0072E002
LEA ECX,SS:[EBP-118]
0072E008 MOV DL,1
0072E00A MOV
EAX,DS:[5F2264] ; EAX = "TCripter"
0072E00F > CALL
<TCripter()> ; esegue la
codifica del seriale | per quanto sono riuscito a capire finora,
quelle 2 CALL impostano i parametri del TCripter che di lì a
poco verrà messo in funzione e per questo ho messo le label.
A 0072E032 c'è il fatidico GetText() del codice che
viene piazzato nello stack poco più in basso del ESP, dopo il
nostro nome, mentre a 0072E043 c'è una misteriosa
UnknownProc...
Code: |
0072E02C > MOV
EAX,DS:[EBX+300] ;
*TFormUnlock.EditCodice:TEdit 0072E032 >
CALL 004504EC
;
->TControl.GetText(TControl):TCaption;
0072E037 MOV
EDX,SS:[EBP-120] 0072E03D
LEA ECX,SS:[EBP-5]
0072E040 MOV
EAX,SS:[EBP-4] 0072E043 > CALL
<cryptserial()> ;
->Unit_005F2264.Proc_005F2594 |
Se entriamo dentro quella CALL con F7,
vedrete un FOR che mette 167 * 2 = 334 zeri nello spazio dello
stack riservato alle variabili locali della funzione:
Code: |
005F2598 |. MOV
ECX,0A7 005F259D |> /PUSH
0 005F259F |. |PUSH
0 005F25A1 |. |DEC
ECX 005F25A2 |.^ \JNZ
SHORT 005F259D | il nostro seriale per ora è in EDX, ma a
005F25AC viene copiato in [LOCAL.1], cioè nella prima
variabile locale che è in fondo a quella grande pila appena
allocata; la situazione dello stack è questa:
Code: |
ESP ==> 00000000 (LOCAL.334)
00000000
(LOCAL.333) .......
*seriale (LOCAL.1)
EBP --> indirizzo di
rientro | PS:
in realtà le variabili locali di questa funzione arrivano a
336 perché ci sono sempre le due locazioni riservate a EBP e
ECX pushati all'inizio della procedura.
L'algoritmo
del TCripter è molto lungo e comprende un sacco di operazioni
sulle parti costitutive del seriale; ve ne rendete man mano
che steppate perché quei 334 posti cominciano a riempirsi di
risultati intermedi.
Alla fine della fiera, il
risultato sono 8 bytes che corrispondono al nome generato a
partire dal seriale, cioè, contrariamente a quanto si poteva
immaginare, è il seriale a corrispondere ad un nome, più
precisamente a 8 caratteri ASCII.
Per
vedere dove si trovano, basta lasciar terminare l'algoritmo
(CTRL+F9 due volte) e rientrare nel flusso principale a
0072E048; se date un occhiata allo stack, TCripter posiziona
l'indirizzo contenente la codifica del seriale subito sotto
l'indirizzo del seriale stesso... a questo basta cliccarc
isopra con il destro e scegliere "Follow in Dump" per scoprire
quanto vale.
Code: |
(STACK) (VALUE)
0012E864 00E98D64 ASCII
"123456789012345678901234567890123456"
0012E868 00E98E3C <--- fate
"Follow in Dump" di questo valore 0012E86C
1345A711 0012E870 7525A956
0012E874 00E97010 ASCII "Pinco
Pal" 0012E878 00E97028 ASCII
"PincoPal" |
Nel mio caso, il 'nome' calcolato
corrisponde a "19 DA 21 1F A8 CE B7 16"
Proseguendo
con il debugging, le istruzioni da 0072E048 a 0072E06A
prendono i primi 8 caratteri di questa serie di valori
calcolati e li mettono nello stack sopra il seriale immesso
(bel compilatore, erano già 8 seguiti da zeri! )
Da 0072E083 a 0072E0C0 ci sono il GetText() del nome
immesso (un'altra volta!? ma ce l'aveva già nello stack...
), la rimozione
degli spazi (0072E09A) e l'uppercase (0072E0C0); subito sotto,
i due indirizzi (nome immesso e nome generato dal serial)
vengono passati come argomenti allo StrCmp a 0072E0CC:
Code: |
0072E0C5 MOV
EDX,SS:[EBP-128] ; EDX =
entered name 0072E0CB POP
EAX
; EAX = calculated name
0072E0CC > CALL 0040581C
;
->System.@LStrCmp; 0072E0D1 JNZ
0072E171 | beh se non avete azzeccato a caso le 36
cifre che corrispondono al vostro nome, dubito che l'esito del
compare sia positivo...
Quindi qual'è il problema? un keygen a partire da un
seriale in genere qui non funziona, perché è difficile che 36
cifre a caso generino 8 bytes che rientrano nel range degli
ASCII (infatti il nome 'giusto' del mio seriale non si può
scrivere con la tastiera... ) ed è proprio
questo a cui si sono attaccati i programmatori: gli unici
seriali giusti sono quelli che generano 8 caratteri ASCII.
In conclusione, è un algoritmo irreversibile
che ha bisogno di un brute force che scorra i numeri che vanno
0 a 10^36 - 1 e ne trovi uno che con quello schema di codifica
generi 8 numeri compresi 40h e 7Ah (range ASCII delle
lettere).
Se il
nome supera questo controllo, a 0072E0E3 ci riè il GetText
(errare è umano, perserverare è Delphi... ) che passa il
nome immesso alla CALL a 0072E0EE, la quale a sua volta
procede nuovamente a troncare a 8, togliere gli spazi e
portare in maiuscolo, dopodiché fa la somma dei valori dei
caratteri (il FOR da 005F245F a 005F246A) e la mette in EAX;
dopo, a 0072E0F3, questo valore (nel mio caso era 56) viene
confrontato con quello che si trova in [EBP-5], che tuttora
devo capire da dove diavolo salta fuori (io c'avevo 31).
Francamente, non so per quale motivo debba essere fatto,
dato che il nome è già stato confrontato con quello giusto...
Se volete patchare l'eseguibile per farlo
andare, seguite le stesse istruzioni che avevo scritto per
BigMatch, tanto la filosofia è esattamente la stessa; se
invece volete fare un bel lavoretto, sfruttate il codice
assembler di quella funzione per fare un bel brute forcer,
anche se credo che 36 cifre siano davvero tante e l'incremento
esponenziale delle combinazioni a questo livello è letale.
beh, per ora è tutto... vado a trovare
quegli altri rincoglioniti di Nt, Lone, Xoa... that's all
folks!
-=[Zero_G]=-
PS: IDA... ci sei
ancora?? IDA?? oddio, è morta sulla cyclette... _________________ "To see a World in a grain
of Sand, Hold Infinity in palm of your Hand." | |