BiSHoP crackme R1
(Algoritmo di fattorizzazione da bucherellare)

Data

by "Guzura"

 

6/6/2002

UIC's Home Page

Published by Quequero

Fatti non foste a viver come bruti...

Grande guz io sarei gia morto di noia al quattordicesimo push e cmq e' stato un bel crackme, bravo davvero

Ma per seguir la passera e la gnocca...

....

E-mail: [email protected]

....

Difficoltà

L'autore dice: da una scala da 1 a 10 vale 5 (cfr: readme.txt)

 

Un bel Crackme da unpackare in maniera classica, un paio di cose da noppare e poi un bell'algoritmo da reversare (che come al solito è la parte che prediligo); che ci volete fare mi piacciono i numeri.Poi cerchèro di fare vedere bene come trovare i punti deboli matematici dell'algo e dove attaccarlo.


BiSHoP R1
(Algoritmi di fattorizzazione da bucherellare)
Written by Guzura

 
Allegato
 

Introduzione

Vale la pena di mettere qui quello che trovate nel readme allegato al crackme:packed with UPX,MeltICE,a valid serial for a name.

Tools usati

ADump per dumpare l'exe
SICE per tutto il resto
IDA immancabile
Compilatore C per il keygen
CalcExtend un piccola calcolatrice esadecimale scritta da me che non fa i soliti (tost e pizzette) ma riporta anche i flag modificati dalle operazioni e altro...

URL o FTP del programma

www.lockless.com

Notizie sul programma

Just a CrackMe  

Essay

PRIMO PASSO MANUAL UNPACKING
Prima cosa da fare è lanciare il Crackme per vedere come si comporta, e notare subito che rileva softice per poi chiudersi chiamando ExitProcess.
Dato chè è paccato con UPX che non modifica la IT o la IAT il metodo di unpacking manuale è quello classico e quindo lo tralascio.
Trovate il metodo di procedere perfettamente spiegato nel tutorial di Quequero su ApiSPy sempre alla UIC.
SECONDO PASSO ELIMINARE ANTISICE E LA NAG
Una volta unpakkato il tutto andiamo a vedere il codice pre capire cosa noppare.
Vediamo i SICETRICK (Vi riporto il codice per comodità con le label già cambiate)
00401000 start proc near

00401000 push 0
00401002 call GetModuleHandle
00401007 mov ds:HandleAllEseguibile, eax
0040100C push offset NomeMutex ; "GooD WoRK :)\r\nYou cracked this crackme "...
00401011 push eax ; Posseduto dal processo
00401012 push 0 ; Non Ereditabile
00401014 call CreateMutex ;vuole evitare due copie in esecuzione(almeno mi sembra)
00401019 push eax ; Forse vuole solo lanciare un thread con qualche dentro? NdQue
0040101A call GetLastError
0040101F cmp eax, 0B7h
00401024 jz Non2CopieInEsecuzione
;vuole evitare due copie in esecuzione(almeno mi sembra)
0040102A push offset CreateFile
...
00401041 push offset a_Ntice ; "\\\\.\\NTICE"HEHEHE classico
00401046 call [esp+34h+var_18]
0040104A cmp eax, 0FFFFFFFFh
0040104D jnz short SiceRilevato
...
00401061 push offset a_Sice ; "\\\\.\\SICE"
00401066 call [esp+50h+var_34]
0040106A cmp eax, 0FFFFFFFFh
0040106D jz short PrimoJumpCorretto
0040106F
0040106F SiceRilevato: ; CODE XREF: start+4Dj
...
00401076 push offset aSystemDebugger ; "System Debugger detected.\r\nPlease unloa"...
00401081 call MessageBox
00401087 call ExitProcess
Mi sembra che la scelta di quali jump noppare e quale invece sia da eseguire sia abbastanza intuitiva cmq l'importante è che noppando si effettui il salto in 40106D
Ora la NAG
PrimoJumpCorretto: ; CODE XREF: start+6Dj; ci arrivavamo da sopra
0040108C push 0
0040108E push offset DialogBoxProcedure1 ;DialogBox della NAG
00401093 push 0
00401095 push 68h
00401097 push ds:HandleAllEseguibile
0040109D call DialogBoxParam
004010A2 push 0
004010A4 push offset DialogBoxProcedure2 ;DialogBox di Registrazione
004010A9 push 0
004010AB push 66h
004010AD push ds:HandleAllEseguibile
004010B3 call DialogBoxParam
004010B8 push eax
004010B9 call ExitProcess
L'unica considerazione interessante che si puo fare è che stavolta la NAG non è la solita MessageBox ma è una DialogBox e per non eseguirla basta mettere un jump da 40108C a 4010A2 o noppare (le scelte sono molteplici).
TERZO PASSO LA ROUTINE DI REGISTRAZIONE
Vediamo prima di affrontare l'algoritmo come è strutturata l'elaborazione.
push 200h ; Bello lungo puo essere :)
00401145 push offset RegCode
0040114A push 3E9h
0040114F push dword ptr [ebp+8]
00401152 call GetDlgItemText
00401157 test eax, eax
00401159 jz Sbagliato
0040115F mov ds:LunghezzaRegCode, eax
00401164 mov edx, offset GetDlgItemText
00401169 push 200h
0040116E push offset Nick
00401173 push 3E8h
00401178 push dword ptr [ebp+8]
0040117B call edx ; GetDlgItemText
0040117D cmp eax, 5 ; Nick>5
00401180 jl Sbagliato
00401186 call Prima
0040118B fnop
0040118D call Seconda
00401192 fnop
00401194 call Terza
00401199 test eax, eax
0040119B jz short DevoArrivareQui
0040119D jmp Sbagliato

Lo schema di acquisizione è quello solito con GetDlgItemText a cui segue un check sulla lunghezza del Nick in
40117D per poi avventurarci in 3 routine che elaborano i dati immessi che con molta fantasia ho chiamato Prima, Seconda, Terza.
Ci si accorge subito che dalla terza routine si deve uscire con EAX=0.
Se abbiamo inserito i dati possiamo passare a vedere subito cosa combina la prima routine.
 
PRIMA ROUTINE
00401263 Prima proc near ; CODE XREF: WHAT:00401186p
00401263 fnop
00401265 mov ds:Sommatore1, 0 ; Azzero Sommatore1
0040126F mov ecx, eax ; Ecx=LunghezzaNick
00401271 xor eax, eax ; Azzero Eax
00401273 push offset Nick
00401278 pop ebx ; Ebx punta al nick

PrimaCiclo:
00401279 mov al, [ebx] ; di volta in volta il char successivo
0040127B test al, al ; Test finito il nick
0040127D jz short PrimaEsco
0040127F imul eax, eax ; moltiplico per se stesso
00401282 cdq ; azzero Edx
00401283 div ecx
00401285 add edx, 1 ; In pratica evito di moltiplicare per 0 se il resto è uguale a 0
00401288 mul edx
0040128A add ds:Sommatore1, eax ; Aggiorno Sommatore1
00401290 xor eax, eax
00401292 inc ebx
00401293 jmp short PrimaCiclo ; di volta in volta il char successivo
PrimaEsco:
00401295 mov edx, ds:Sommatore1
0040129B imul edx, ecx ; Sommatore1*LunghezzaNick
0040129E imul edx, edx
004012A1 rol edx, cl
004012A3 mov al, 3
004012A5 push edx
004012A6 mul ecx
004012A8 pop edx
004012A9 xchg eax, ecx ; Ecx=LunghezzaNick*3 Eax=LunghezzaNick
004012AA ror edx, cl
004012AC xchg eax, ecx
004012AD bswap edx ; Scambio gli Indiani (Come Colombo...)
004012AF mov ds:Sommatore1, edx
004012B5 retn
004012B5 Prima endp
La ruotine è abbastanza semplice e abbastanza commentata ma alcune precisazioni diventano fondamentali:
1)La routine utilizza una varibile di appoggio che io ho chiamato Sommatore1 nella quale verrà messo il risultato di tutta l'elaborazione del nick che non è eccessivamente complicata
2)La routine elabora solamente il nick disinteressandosi completamente del seriale
Naturalmente uno non si butta allo sbaraglio cercando da subito alla cieca di capire ogni singola istruzione ma è sempre buona norma dare un'occhiata alla protezione nel complesso, se uno usa attentamente le label, IDA fa il resto.
Se date un'occhiata veloce alla Seconda routine vi accorgete che questa elabora solo il RegCode e va a utilizzare un'altra varibile di appoggio chiamata Sommatore2 che è un puntatore a un ARRAY rappresentativo del RegCode modificato (si capisce dopo).
Infine la Terza che è quella che deve restituire EAX=0 nelle sue elaborazioni usa Sommatore1,Sommatore2 e il RegCode di nuovo.
Qual'è la considerazione da fare?
Semplice posso disinteressarmi completamente di quali siano le elaborazioni fatte dalla Prima routine a me serve solo il valore di Sommatore1 in uscita (che mi servirà nella terza).In pratica metto il Nick esco dalla Call e mi interesso solo di segnarmi il val di Sommatore1.
Vediamo la seconda routine
SECONDA ROUTINE
Seconda proc near
004012B6 xor edx, edx
004012B8 mov eax, ds:LunghezzaRegCode ; Eax=LunghezzaRegCode
004012BD mov esi, offset RegCode
004012C2 mov edi, offset RegCode1 ; Becca una copia del RegCode
004012C7 mov ecx, eax
004012C9 repe movsb
004012CB sub edi, eax
004012CD sub esi, eax
004012CF mov edi, offset RegCodeLessFirst ;RegCode meno il primo char
004012D4 xor ecx, ecx

SecondaCiclo1:
004012D6 mov dl, [esi]
004012D8 inc esi
004012D9 movsb
004012DA dec edi
004012DB mov cl, [edi]
004012DD test cl, cl
004012DF jz short SecondaJump1
004012E1 imul edx, ecx   
004012E4 dec esi
004012E5 mov [esi], dl
004012E7 inc edi
004012E8 jmp short SecondaCiclo1

SecondaJump1:
004012EA xor ebx, ebx
004012EC xor edx, edx
004012EE sub esi, eax
004012F0 dec esi
004012F1 mov edi, offset RegCode1
004012F6 mov ecx, eax
SecondaCiclo2:
004012F8 mov bl, [esi]
004012FA mov dl, [edi]
004012FC add bl, dl
004012FE mov [esi], bl
00401300 inc esi
00401301 inc edi
00401302 loop SecondaCiclo2
00401304 sub esi, eax
00401306 mov ds:Sommatore2, esi
0040130C retn

Questa routine è organizzata su due cicli che vi spiego a parole dato che sarebbe difficile commentare il codice in maniera chiara.
All'inizio si fa una copia del RegCode che ho chiamato RegCode1, e un'altre copia dove pero il puntatore punta al secondo char del Seriale che ho chiamato RegCodeLessFirst.
 
Il primo ciclo funziona cosi (per capirlo vi basta stepparlo un paio di volte se il deadlisting non vi è chiaro).
Supponiamo di avere a che fare con un Seriale lungo 3 quindi effettuerò 2 passaggi.
Primo passaggio:
Moltiplico il primo char per il secondo
Prelevo DL e sostituisco il valore con quello del secondo char del seriale
 
Secondo passaggio:
Moltiplico il primo char per il secondo per il terzo
Prelevo DL e lo sostituisco al terzo char del seriale e esco
Se il RegCode era composto da [Primo,Secondo,Terzo] dopo il ciclo diventa [Primo,LSB(Primo*Secondo),LSB(Primo*Secondo*Terzo)] dove con LSB intendo una funzione che mi restituisce il byte inferiore della DWORD in edx (frutto della moltiplicazione)
L'estensione a Seriali piu lunghi mi sembra chiara.
 
Il secondo ciclo esegue questo
Somma ai char del RegCode modificato i rispettivi del RegCode originale e il tutto si puo formalizzare come segue
Sommatore2 va a puntare a [LSB(Primo+Primo),LSB(Secondo+LSB(Primo*Secondo)),LSB(Terzo+LSB(Primo*Secondo*Terzo))]
Ci resta da vedere la Terza routine tenendo a mente che per ora abbiamo Sommatore1=ValoreOttenutoDalNick e Sommatore2=PuntaRegModificato.

TERZA ROUTINE
Terza proc near ;
0040130D mov ecx, eax
0040130F mov ds:Sommatore3, 0
00401319 mov esi, ds:Sommatore2

TerzaCiclo1:
0040131F lodsb
00401320 add ds:Sommatore3, eax
00401326 loop TerzaCiclo1 ; somma i byte di Sommatore2 in Sommatore3
00401328 mov eax, ds:Sommatore3
0040132D mov ebx, ds:Sommatore1
00401333 xchg eax, ebx
00401334 xor edx, edx
00401336 div ebx
00401338 mov eax, edx
0040133A xor eax, ds:XorVal13B   ;Divido Sommatore1 per Sommatore3 se il resto = 13Bh allora è registrato
00401340 retn

La routine è molto semplice e quello che interessa calcolare è Sommatore3
Usando il formalismo precedente Sommatore3=LSB(Primo+Primo)+LSB(Secondo+LSB(Primo*Secondo))+LSB(Terzo+LSB(Primo*Secondo*Terzo))
A questo punto divido Sommatore1 per Sommatore3 se il resto è uguale a 13Bh allora sono registrato(dato lo XOR in 40133A).

Ora un pò di matematica spicciola : l'equazione che ci serve è questa

Sommatore1=X * Sommatore3 + 13Bh   dove X rappresenta una variabile >= 1 a scelta

Questa è matematica elementare ma vale la pena di fare un paio di esempi chiarificatori consideriamo un esempio semplice

27 = X * Som3 + 3 cioè 24 = X * Som3 quindi possiamo giostrare sia su X che su Som3 per trovare proprio quel Som3 che ci interessa
Fattorizzato 24 è uguale a 1*2*2*2*3 quindi posso dare origine alle seguenti combinazioni

X = 1     Som3 = 24 e vicecersa   X = 24     Som3 = 1
X = 2     Som3 = 12 e viceversa   X = 12     Som3 = 2
X = 3     Som3 = 8  e viceversa   X = 8      Som3 = 3
X = 4     Som3 = 6  e viceversa   X = 6       Som3 = 4
PRIMA CONSIDERAZIONE (riportata al nostro caso)
Sommatore3 sarà sempre uguale al prodotto di alcuni dei fattori del valore ottenuto da Sommatore1-13Bh
 
SECONDA CONSIDERAZIONE (riportata la nostro caso)
Considerate l'esempio sopra tutti i valori delle coppie (Som3,X) sono validi per risolvere l'equazione ma non tutti vanno bene per essere usati nella routine del Crackme infatti se usate i valori evidenziati in verde (Som3=1,2,3)ottenete un
valore di resto differente da 3 perchè il divisore è piu piccolo del resto
Nel CrackMe infatti considerate una operazione di DIV quindi se effettuiamo l'operazione otteniamo
27:1=27 con Resto=0 (e non resto 3)
27:2=13 con Resto=1 (e non resto 3)
27:3=9  con Resto=0 (e non resto 3)
Perchè Som3 è MINORE del Resto e quindi vi è contenuto (Pensateci)
Morale nel nostro caso Sommatore3 deve essere maggiore di 13Bh
 
CONCLUDENDO quello che ci serve per invertire l'algoritmo è questo
1)Inserire il nostro Nick e segnarsi il valore di Sommatore1
2)Calcolare Sommatore1-13Bh e fattorizzarlo
3)Individuare Sommatore3 come il prodotto dei fattori di Sommatore1-13Bh e che Sommatore3 sia maggiore di 13Bh
4)Individuare un Seriale che produca il valore di Sommatore3 desiderato attraverso formula        Sommatore3=LSB(Primo+Primo)+LSB(Secondo+LSB(Primo*Secondo))+LSB(Terzo+LSB(Primo*Secondo*Terzo)) dove primo secondo terzo sono i char di un ipotetico serial lungo 3
Consideriamo innanzitutto che Sommatore3 è uguale a una somma di byte (tenete presente la funzione LSB e la formula sopra)lunga quanti sono i char del serialee deve essere maggiore di 13Bh quindi al minimo puo essere generato dalla somma di 2 byte quindi ogni seriale valido avrà almeno 2 char
Seconda e più interessante considerazione è questa: un byte al massimo è FF (255) quindi si puo calcolare il numero minimo di char che servono per un seriale (per averne un'idea) come (Sommatore1-13Bh)/FF + 1
FINALMENTE LA RISOLUZIONE
Dato che il mio Nick Guzura produceva un valore abbastanza difficile da fattorizzare e non mi andava di ammazzarmi su un seriale troppo lungo ho scelto di usare questo Nick=Guzura1111111111 a cui corrisponde questo valore di Sommatore1=40AADE e un Sommatore1-13Bh=40A9A3
Andiamo a fattorizzare 40A9A3 e troviamo 40A9A3h=4237731=3*3*3*31*61*83
Un Sommatore3 più piccolo (ma maggiore di 13Bh) che posso trovare è
61*3*3=549 cioè 225h
Calcoliamo quanti char ci servono (come minimo) 16Eh/FFh+1=2 (noi ne useremo 3 per sicurezza)
Per generare il Seriale ho usato un piccolo BruteForce considerando solo 3 Char usando la formula di prima
Sommatore3=LSB(Primo+Primo)+LSB(Secondo+LSB(Primo*Secondo))+LSB(Terzo+LSB(Primo*Secondo*Terzo))
 
Riporto il codice (un pezzo) del BruteForce
for(n=48;n<57;n++)
            {
                for(x=48;x<57;x++)
                {
                    for(z=48;z<57;z++)
                    {
                    xa=n*x;
                    __asm{
                            pushf
                            mov eax,xa
                            and eax,255
                            mov x1,eax
                            popf
                        }

                    za=n*x*z;
                    __asm{
                            pushf
                            mov eax,za
                            and eax,255
                            mov z1,eax
                            popf
                        }
                   
                    nu=n+n;
                    __asm{
                            pushf
                            mov eax,nu
                            and eax,255
                            mov nu,eax
                            popf
                        }
                    xu=x+x1;
                    __asm{
                            pushf
                            mov eax,xu
                            and eax,255
                            mov xu,eax
                            popf
                        }
                   
                    zu=z+z1;
                    __asm{
                            pushf
                            mov eax,zu
                            and eax,255
                            mov zu,eax
                            popf
                        }

                    val=nu+xu+zu;
                   
                    if (val==549) {//Sommatore3
                                wsprintf(Bn,"%X",n);
                                wsprintf(Bx,"%X",x);
                                wsprintf(Bz,"%X",z);
                                MessageBox(hwndDlg,Bn,"n",MB_OK);//Primo char
                                MessageBox(hwndDlg,Bx,"x",MB_OK);//Secondo char
                                MessageBox(hwndDlg,Bz,"z",MB_OK);//Terzo char
                                n=58;            
                                }
                    }
                }
Come si vede dal codice faccio cercare i char per il seriale solo tra i numeri (da 30h a 39h) anche se tutti i caratteri andrebbero bene.
NOTA= non è detto che il Brute trovi una soluzione con 3 char potrebbero servirne 4 oppure bisogna scegliere un'altro Sommatore3
 
Il mio caso è abbastanza fortunato e addiritttura trova 2 Seriali validi :274 e 678
Quindi a questo punto il gioco è fatto.Anche se la soluzione non è molto pulita.
 
SIAMO GIUNTI AL MOMENTO DEL KEYGEN
Il codice lo trovate in allegato assieme alla versione unpakkata del crackme e già patchata
Il KeyGen non fa altro che ricalcolare Sommatore1
Calcolare Sommatore1-13Bh e vedere se questo è divisibile per un numero compreso tra 13Ch e 2FDh altrimenti avverte di cambiare un po il nick
Se si allora chiama il BruteForce sopra che vede se c'è un seriale valido
La scelta di 2FDh=FF+FF+FF (come bound) è data dal fatto che questo è il max Sommatore3 che posso generare con 3 char
Date un'occhiata al codice :)

By Guzura

 

Note finali

Stavolta saluto la mia ragazza (Ti voglio troppo bene) Anche io, anzi io piu' di lui NdQue

Disclaimer

Ognuno vede le cose a suo modo.