CRACKME nr 6  (slightly edited by Quequero)

 

Scaricate qui il programma (234kb)
Scaricate qui i sorgenti (3.6kb)
 
Prima di presentarvi il tutorial vero e proprio mi permetto di pastare una mail che mi spedì Olga il giorno dopo aver esaminato il crackme.
 
Okkei, è venuto il momento di farvi umiliare......Voi uomini.....duri, rudi, virili.....ehhhhhh.....questo scherzetto non me lo dovevate fare, guardatevi sta mail, nella sua semplicità spiega quasi tutto :).....vabbè non esageriamo, è comunque un ottimo mini essay :)

Tralascio la prima parte perchè potrebbe rivelarvi aspetti sconcertanti di questa donna apparentemente seria...ma che in realtà non lo è :)))))))))).....Ecco cosa mi disse:
 
---- snip ---- snip ----
Il problema relativo al reversing dell'algoritmo nasce dal fatto che
i valori finali dei quattro gruppi in cui spezzetti il serial vengono
generati sottraendo da ognuno un valore che è ricavato dai gruppi stessi
(come avere un'equazione con quattro incognite ed un termine noto).

Il secondo problema è dovuto al fatto che non è possibile neanche il
calcolo analitico della funzione inversa, e ciò a causa della funzione
'Mod' usata nel calcolo di ciò che alla fine dovrà finire in EBX e quindi
sottratto ad ognuno dei quattro gruppi (due dei quali preventivamente
'elaborati'), operazione che di fatto introduce una quinta incognita;
ciò, in pratica, impedisce di usare un sistema di equazioni.
>Lei con un week-end si è accorta del mio MOD e voi ne avete parlato
>solo una volta in ML e poi basta.......il silenzio :)

Quest'ultimo problema sarebbe stato parzialmente ovviabile restringendo
opportunamente il campo di calcolo, cosa fattibile se l'andamento dei
valori finali forniti dalle funzioni fosse stato in qualche modo convergente.
Ma l'output è periodico, con periodo diverso per ognuno dei quattro valori
(ti allego grafico con l'andamento di due dei quattro output forniti
dei serial da 0000 0000 0000 0000 a 0000 0000 0000  00re) per cui non
è possibile restringere i valori per un semi brute-force.

fx.gif (12174 bytes)

A questo punto mi sono realmente scocciata, ed ho smesso.

A proposito, chi credevi di prendere per...la fortuna,  con la faccenda
dell'aiutino? Il fatto che il serial fosse di 16 caratteri è una cosa
terribilemnte ovvia; è il resto che non lo è affatto!

Ok, per stasera ho delirato già abbastanza; ti saluto e vado in palestra.

Alga
 
Hihihihhi.....Tanto per farvi rendere conto di che pasta è fatta....Cmq per chi non fosse riuscito a visitare il sito prima della pubblicazione dell'intervista sulla repubblica comunico che avevo creato una mini-soluzione di Olga prendendola dalla sua mail che restava cmq ottima....Poi però ha deciso di farsi avanti....Godetevi questo concentrato di conoscenza :)

 
Ecco il listato della parte relativa all'algoritmo, commentato. Qualche considerazione alla fine.
:00401256 6A11                    push 00000011  17 caratteri max da copiare
                                                 (un 'aiutino', eh?)     
:00401258 68CC204000              push 004020CC  in un buffer che inizia qui
:0040125D FF3518204000            push dword ptr [00402018]

* Reference To: USER32.GetWindowTextA, Ord:0000h
                                  |
:00401263 E85D020000              Call 004014C5
:00401268 A1CC204000              mov eax, dword ptr [004020CC]  primo gruppo in EAX
:0040126D 69C078265793            imul eax, 93572678 Que, sei un IMBROGLIONE!
:00401273 8B1DD0204000            mov ebx, dword ptr [004020D0]   secondo gruppo
:00401279 69DB05735426            imul ebx, 26547305   e lo sei anche qui!
:0040127F 8B0DD4204000            mov ecx, dword ptr [004020D4]  terzo gruppo
:00401285 69C914985637            imul ecx, 37569814   ormai sei smascherato
:0040128B 8B15D8204000            mov edx, dword ptr [004020D8]  quarto gruppo
:00401291 69D215376589            imul edx, 89653715   bah, lasciamo perdere... 
:00401297 03C3                    add eax, ebx   ...tutti questi...
:00401299 03C1                    add eax, ecx    ...vergognosi trucchi...
:0040129B 03C2                    add eax, edx    ...che giungono...
:0040129D 50                      push eax
:0040129E A35D144000              mov dword ptr [0040145D], eax   ...fin qui!
:004012A3 33C0                    xor eax, eax   OK, riprendiamo con le cose serie
:004012A5 A1CC204000              mov eax, dword ptr [004020CC]   somma dei codici...
:004012AA 0305D0204000            add eax, dword ptr [004020D0]   ASCII I e II gruppo
:004012B0 A361144000              mov dword ptr [00401461], eax  parziale 1
:004012B5 A1D4204000              mov eax, dword ptr [004020D4]  somma dei codici...
:004012BA 0305D8204000            add eax, dword ptr [004020D8]  ASCII III e IV gruppo
:004012C0 A375144000              mov dword ptr [00401475], eax  parziale 2
:004012C5 A161144000              mov eax, dword ptr [00401461]
:004012CA 48                      dec eax  (parziale 1)-1
:004012CB 8B1D75144000            mov ebx, dword ptr [00401475] parziale 2
:004012D1 FF0D75144000            dec dword ptr [00401475]   insisti, eh? 
:004012D7 0FAFC3                  imul eax, ebx  (GrI+GrII-1)*(GrIII+GrIV)...
:004012DA A379144000              mov dword ptr [00401479], eax  ...salvato in 401479
:004012DF E805000000              call 004012E9  OK, proseguiamo...

* Reference To: KERNEL32.ExitProcess, Ord:0000h
                                  |
:004012E4 E8AC010000              Call 00401495  ...prima di terminare

* Referenced by a CALL at Address:
|:004012DF
|
:004012E9 83F8FF                  cmp eax, FFFFFFFF  rende 'unsigned'...
:004012EC 7F06                    jg 004012F4  ...il valore salvato...
:004012EE 90                      nop
:004012EF 90                      nop
:004012F0 90                      nop
:004012F1 90                      nop
:004012F2 F7D8                    neg eax  ...in 401479 prima di...

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004012EC(C)
|
:004012F4 B914000000              mov ecx, 00000014
:004012F9 99                      cdq
:004012FA F7F9                    idiv ecx  ...dividerlo per 20d (la parte intera del
                                            risultato resta in EAX)

 

A questo punto EAX contiene int(((GrI+GrII-1)*(GrIII+GrIV))/20), ed è utilizzato come CONTATORE

* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040131F(C) |

INIZIA LOOP

:004012FC 8B1DCC204000 mov ebx, dword ptr [004020CC] soliti... :00401302 0FAFCB imul ecx, ebx ...luridi... :00401305 8B0DD4204000 mov ecx, dword ptr [004020D4] ...imbrogli... :0040130B 0FAFC9 imul ecx, ecx ...di Quequero :0040130E 48 dec eax decrementa il contatore :0040130F C105CC20400005 rol dword ptr [004020CC], 05 rotaz. a sn I gruppo :00401316 C10DD420400004 ror dword ptr [004020D4], 04 rotaz. a dx III gruppo :0040131D 85C0 test eax, eax :0040131F 75DB jne 004012FC chiude il loop :00401321 833D79144000FF cmp dword ptr [00401479], FFFFFFFF riconsideriamo... :00401328 7F0A jg 00401334 ...(GrI+GrII-1)*(GrIII+Gr4)... :0040132A 90 nop ...per renderlo 'unsigned'... :0040132B 90 nop ...prima di sottrarre... :0040132C 90 nop :0040132D 90 nop :0040132E F71D79144000 neg dword ptr [00401479] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401328(C) | :00401334 812D79144000FACC2103 sub dword ptr [00401479], 0321CCFA ...QUESTO :0040133E 833D79144000FF cmp dword ptr [00401479], FFFFFFFF e ancora :00401345 7F0A jg 00401351 :00401347 90 nop :00401348 90 nop :00401349 90 nop :0040134A 90 nop :0040134B F71D79144000 neg dword ptr [00401479] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401345(C) A questo punto in 401479 è contenuto il valore che verrà definitivamente utilizzato nel resto dell'algoritmo e che è denominato 'Kpr' nel programma di TEST. Solo da esso dipende la variabilità dei valori finali delle funzioni relative a tutti e quattro i gruppi al variare anche di un solo carattere in un solo gruppo | :00401351 E801000000 call 00401357 OK, andiamo avanti :00401356 C3 ret * Referenced by a CALL at Address: |:00401351 | :00401357 A1CC204000 mov eax, dword ptr [004020CC] :0040135C E899000000 call 004013FA numeratore II termine equazione gruppo1 :00401361 E873000000 call 004013D9 denominatore II termine equaz. gruppo1 :00401366 291DCC204000 sub dword ptr [004020CC], ebx I termine - II termine :0040136C A1CC204000 mov eax, dword ptr [004020CC] ehi Que, ma non ti... :00401371 A365144000 mov dword ptr [00401465], eax ...togli mai il vizio? :00401376 A1D0204000 mov eax, dword ptr [004020D0] :0040137B E87A000000 call 004013FA numeratore II termine equazione gruppo2 :00401380 E854000000 call 004013D9 denominatore II termine equaz. gruppo2 :00401385 291DD0204000 sub dword ptr [004020D0], ebx I termine - II termine :0040138B A1D0204000 mov eax, dword ptr [004020D0] :00401390 A369144000 mov dword ptr [00401469], eax :00401395 A1D4204000 mov eax, dword ptr [004020D4] :0040139A E85B000000 call 004013FA numeratore II termine equazione gruppo3 :0040139F E835000000 call 004013D9 denominatore II termine equaz. gruppo3 :004013A4 291DD4204000 sub dword ptr [004020D4], ebx I termine - II termine :004013AA A1D4204000 mov eax, dword ptr [004020D4] :004013AF A36D144000 mov dword ptr [0040146D], eax :004013B4 A1D8204000 mov eax, dword ptr [004020D8] :004013B9 E83C000000 call 004013FA numeratore II termine equazione gruppo4 :004013BE E816000000 call 004013D9 denominatore II termine equaz. gruppo4 :004013C3 291DD8204000 sub dword ptr [004020D8], ebx I termine - II termine :004013C9 A1D8204000 mov eax, dword ptr [004020D8] :004013CE A371144000 mov dword ptr [00401471], eax :004013D3 E831000000 call 00401409 oh, finalmente! :004013D8 C3 ret

CALCOLO DEL DENOMINATORE DEL SECONDO TERMINE

* Referenced by a CALL at Addresses: |:00401361 , :00401380 , :0040139F , :004013BE | :004013D9 8B1D79144000 mov ebx, dword ptr [00401479] Kpr * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004013EB(C) |

INIZIA LOOP PER CALCOLO Mod(Kpr/255d)

:004013DF 81EBFF000000 sub ebx, 000000FF :004013E5 81FBFF000000 cmp ebx, 000000FF :004013EB 7FF2 jg 004013DF chiude loop :004013ED 99 cdq :004013EE F7FB idiv ebx Gruppo/Mod(Kpr/255d) :004013F0 0FAF0579144000 imul eax, dword ptr [00401479] e moltiplicato per Kpr :004013F7 8BD8 mov ebx, eax risultato in EBX :004013F9 C3 ret

CALCOLO DEL DENOMINATORE DEL SECONDO TERMINE

* Referenced by a CALL at Addresses: |:0040135C , :0040137B , :0040139A , :004013B9 | :004013FA 0FAFC0 imul eax, eax Gruppo al quadrato :004013FD 83F8FF cmp eax, FFFFFFFF vedi prima :00401400 7F06 jg 00401408 :00401402 90 nop :00401403 90 nop :00401404 90 nop :00401405 90 nop :00401406 F7D8 neg eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401400(C) | :00401408 C3 ret * Referenced by a CALL at Address: |:004013D3 |

A QUESTO PUNTO, NELLE LOCAZIONI (DWORD) CHE CONTENEVANO I QUATTRO GRUPPI SI HA INVECE IL RISULTATO DEL CALCOLO, CHE VIENE CONFRONTATO CON IL TERMINE NOTO

:00401409 A1CC204000 mov eax, dword ptr [004020CC] :0040140E 3D585C180C cmp eax, 0C185C58 I termine noto=202923096d :00401413 7568 jne 0040147D errato! :) :00401415 90 nop :00401416 90 nop :00401417 90 nop :00401418 90 nop :00401419 A1D0204000 mov eax, dword ptr [004020D0] :0040141E 3D0F60F255 cmp eax, 55F2600F II termine noto=1441947663d :00401423 7558 jne 0040147D errato! :) :00401425 90 nop :00401426 90 nop :00401427 90 nop :00401428 90 nop :00401429 A1D4204000 mov eax, dword ptr [004020D4] :0040142E 3DB268CE35 cmp eax, 35CE68B2 III termine noto=902719666d :00401433 7548 jne 0040147D errato! :) :00401435 90 nop :00401436 90 nop :00401437 90 nop :00401438 90 nop :00401439 A1D8204000 mov eax, dword ptr [004020D8] :0040143E 3DFAEC3E1F cmp eax, 1F3EECFA IV termine noto=524217594d :00401443 7538 jne 0040147D errato! :) :00401445 90 nop OK, tutti esatti :00401446 90 nop :00401447 90 nop :00401448 90 nop :00401449 6A20 push 00000020 * Possible StringData Ref from Data Obj ->"Bravo!"

Come può essere (facilmente?) verificato seguendo l'algoritmo passo per passo, il serial viene suddiviso in quattro gruppi da quattro caratteri, ognuno dei quali deve soddisfare la seguente equazione:

Grn - ( ( Grn² / Mod(Kpr / 255) ) * Kpr ) + kn = 0

con n che varia da uno a quattro. kn sono le quattro costanti finali. Grn vale invece:

- i quattro codici ASCII dei gruppi corrispondenti per n = 2 e 4

- I quattro codici ASCII del gruppo I ruotati a sinistra di cinque posizioni per Mod(int(Knum/20)/160) volte per n = 1

- I quattro codici ASCII del gruppo III ruotati a destra di quattro posizioni per Mod(int(Knum/20)/8) volte per n = 3

dove Knum vale (GrI+GrII-1)*(GrIII+GrIV).

In realtà nel crackme la rotazione viene eseguita per Knum/20 volte (ed infatti il programma impiega un'eternità per la verifica del serial), ma il metodo dato qui è molto più breve (è quello usato nel programma di TEST per eseguire il calcolo - se non l'avessi fatto così, il programma avrebbe impiegato DUE eternità per calcolare 50 serial di seguito, ed io non so a quanto tempo corrispondano esattamente due eternità :); spiegare in dettaglio perchè la formula utilizzata sia equivalente suonerebbe come un insulto all'intelligenza di chi legge, quindi non lo farò :).

Allora, è possibile percorrere l'algoritmo in senso inverso? Ed è possibile ricavare per via analitica il numero di serie?
Bene, i due problemi (e le loro risoluzioni) sono, da un punto di vista matematico, equivalenti.

Perchè un algoritmo possa venire percorso in senso inverso (la sua trasposizione Assembly, intendo - il discorso non è valido per il codice sorgente scritto in un linguaggio ad alto livello), ogni passo deve essere riconducibile ad un'equazione di primo grado ad un'incognita - il valore da ricavare per risalire al passo immediatamente precedente. Il calcolo eseguito nella routine che inizia in 4013D9 preclude questa possibilità, poichè viene effettuato servendosi di tutti e quattro i gruppi di numeri del serial (e cioè di quattro incognite).

La risoluzione analitica è possibile, essendo risaliti alle equazioni, se le equazioni possono comporre un sistema, e se il numero di equazioni che compongono il sistema è uguale al numero delle incognite (direbbe Monsieur De Lapalisse, noto per affermare le cose più ovvie).
Nel nostro caso abbiamo quattro equazioni e quattro incognite; il problema (oltre alla relativa complessità che si incontrerebbe nel risolvere il sistema per sostituzione) risiede nella presanza della funzione Mod.
Essa infatti non è invertibile (non esiste corrispondenza biunivoca tra i singoli elementi dell'insieme che costituiscono i valori di ingresso e quelli che costituiscono i valori in uscita).
Si noti, tuttavia, che Mod(Kpr/255) è equivalente a Kpr-n*255 (anzi, questo è proprio il metodo usato dall'algoritmo per il calcolo della funzione all'interno del programma).
Ciò introduce, di fatto, una quinta incognita, che rende impossibile la soluzione analitica.

Una soluzione alternativa potrebbe essere quella di un brute force su n, calcolando sequenzialmente il sistema per diversi valori di n
Per restringere il range dei valori di n su cui effettuare il brute force si potrebbe verificare quali siano i valori minimi e massimi che Kpr può assumere, ed utilizzare i valori di n che non rendano negativo il denominatore(questo è il motivo della presenza, nel programmino di TEST, della memorizzazione dei valori minimo e massimo).

Sarebbe meglio memorizzare i valori minimo e massimo assunti dal denominatore per vari valori del serial, ma questo non è stato implementato.

Anche perchè, arrivata a questo punto, mi sono accorta del fatto che non si vinceva niente, ed ho mollato.

Ciao a tutti
Alga

 

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

Home   Anonimato   Assembly    CrackMe   ContactMe   Forum       
    Iscrizione   Lezioni    Links   NewBies   News   Playstation        
  Tools   Tutorial   Search   UIC Faq

UIC