Soluzione Del Cm3 di Evilcry
Per scaricare l'allegato al tutorial clickate
qui.
Benvenuti al mio secondo tute, e oggi parleremo delle proprieta' dello xor e di
come sfruttarle per
reversare un algo semplice e cazzuto :) (ringrazio EvilCry per aver dato la
speranze a personaggi come me :)) )
Bene incominciamo col dire che i programmi che ci servono sono l'ida e un
hexeditor, tipo HView, in realta' anche il wsdam va bene, ma c'è un trucchetto
che non gli permette di funzionare :)))
Semplicemente il Wsdam è un po' fessacchiotto, infatti per capire dove
incomincia la parte di codice, legge le
characteristic delle varie sezioni cercando quelle che hanno la flag executable,
che ovviamente Evil non ha messo,
that's all folks :PPP
OK ce li avete? Bene analizziamo il cm3.exe
La spiegazione di come funziona tutto il prog esula da questo tute, quindi
andremo direttamente a cercarci la routine di check del seriale. Andiamo in
Names, e cerchiamo qualcosa di interessante, ecco aYeahYouHaveSol, doppio click
e vediamo dove ci porta :))
.text:00401333 cmp eax, 0F1h
.text:00401338 jnz short Msg_Errore
.text:0040133A push 0 ; uType
.text:0040133C push offset aRegistered ; lpCaption
.text:00401341 push offset aYeahYouHaveSol ; lpText
.text:00401346 push 0 ; hWnd
.text:00401348 call MessageBoxA
.text:0040134D push offset aRegistered ; lpString
.text:00401352 push HandleEdit2 ; hWnd
.text:00401358 call SetWindowTextA
.text:0040135D jmp short loc_401372
mmmm fa un compare tra eax e 0F1 se nono sono uguali salta alla procedura di
errore, allora adiamo a vedere dove incomincia la routine di protezione
.text:00401230 Protezione: ; CODE XREF: sub_401104+113 j
esattamente qui', naturalmente a voi non sara' scritto protezione, ma il Virtual
Adress è lo stesso
e sta poco piu' su. Ad essere sinceri a voi risultara' la scritta loc_401230:,
clikkateci sopra e premete
r, a questo punto potete rinomirare in Protezione
Incominciamo a vedere cosa fa questa procedura
.text:00401230 cmp [ebp+wParam], 3001 ; Se non è il bottone check Salta
.text:00401237 jnz End_Proc
Ecco le prime istruzioni, non so se avete mai visto come funziona in linea
generale un programma codato per
win32, diciamo che ogni volta che viene fatta una azione su una finestra, il
sistema operativo manda una messaggio alla procedura della finestra in
questione, attraverso degli switch(message) questa procedura sceglie
che operazioni fare, in questo caso la procedura controlla che sia stato premuto
il bottone check altrimenti
salta alla fine della procedura.
.text:0040123D mov eax, [ebp+wParam]
.text:00401240 shr eax, 16
.text:00401243 or eax, eax
.text:00401245 jnz short R_Prot
Ecco qua incominciano i casini per chi è meno bravo, allora lui sposta in eax un
valore (per il momento facciamo finta di non sapere che cosa sia) gli fa uno
shift a destra di 16 bit, ma eax è grande proprio 16 bit, quindi in pratica
qualunque sia il valore di eax lo porta a zero, e poi fa esattamente questa
operazione ( 0 or 0 ), che seguito da un jnz R_Prot(ho rinominato questa
locazione fate la stessa procedura di prima, ma da adesso non ve lo dico piu'
:PPP ), praticamente serve solo per settare in maniera corretta la Zero flag,
.text:00401247 push offset String ; lParam
.text:0040124C push 1Eh ; wParam
.text:0040124E push 0Dh ; Msg
.text:00401250 push hWnd ; hWnd
.text:00401256 call SendMessageA
.text:0040125B
Ecco, qua invece siamo davanti a una porzione di codice non usuale, perche'
generalmente per prendere una stringada una edit_box si usa un getdlgitemtexta,
ma non necessariamente. Praticamente questa funzione dice alla proceduradella
edit_box (contrassegnata dall'handle hWnd) di mettere in String il suo contenuto
non eccedendo 0x1Eh (30)caratteri :) Figo non trovate :))) Andiamo avanti adesso
sappiamo che in String c'è il nostro seriale, e per la precisione se avete fatto
partire il programma mette in String il seriale nella prima editbox (questo è
facile da intuire se si legge il programma dall'inizio) :)
.text:0040125B R_Prot: ; CODE XREF: sub_401104+141 j
.text:0040125B push offset String ; lpString
.text:00401260 call lstrlenA
.text:00401265 cmp eax, 16
.text:00401268 jnz Msg_Errore
.text:0040126E xor ecx, ecx
.text:00401270 xor ebx, ebx
.text:00401272 xor edx, edx
Qua è chiaro, mette in eax la lunghezza della stringa, la confronta con 16 (i
numeri esadecimali sono con "h" affianco) se non è lunga "abbastanza" salta ad
errore, dopo di che mette i registri a zero, (per chi non lo sa,Valore1 XOR
Valore1 = 0)
.text:00401274 loc_401274: ; CODE XREF: sub_401104+18A j
.text:00401274 ; DATA XREF: sub_401104+18C o
.text:00401274 mov cl, byte ptr aAutvrsqzi2y18a[ebx] ; "Autvrsqzi2y18az3"
.text:0040127A add eax, ecx
.text:0040127C xor cl, String[ebx]
.text:00401282 add edx, ecx
.text:00401284 sub eax, edx
.text:00401286 cmp ebx, 10h
.text:00401289 jz short loc_4012DB
.text:0040128B inc ebx
.text:0040128C xor ecx, ecx
.text:0040128E jmp short loc_401274
Ecco il primo giro di loop, praticamente prende il primo byte dalla stringa
"Autvrsqzi2y18az3", aggiunge a ecx ad eax xorra il carattere preso con il primo
di String aggiunge il risultato a edx, toglie a eax, il valore di edx, vede se
ha processato tutti i caratteri con un semplice compare altrimenti incremente il
contatore e salta all'inizio. Ricapitolando fa lo xor carattere per caratte tra
il nostro seriale e il corrispettivo carattere di "Autvrsqzi2y18az3" e somma i
singoli risultati in edx. Lasciamo perdere il valore di eax, poiche' come
vedremo poi questo valore sara' ignorato, capita che il coder di un programma
metta codice inutile per confondere le idee. Una volta finito salta qui':
.text:004012DB loc_4012DB: ; CODE XREF: sub_401104+185 j
.text:004012DB ; sub_401104+1BB j
.text:004012DB mov temp1, edx
.text:004012E1 xor edx, edx
.text:004012E3 xor ecx, ecx
.text:004012E5 xor ebx, ebx
mette il valore di edx in temp1 e resetta edx, ecx, ebx.
.text:004012E7
.text:004012E7 loc_4012E7: ; CODE XREF: sub_401104+1F7 j
.text:004012E7 ; DATA XREF: sub_401104+191 o ...
.text:004012E7 mov cl, byte ptr aUicrulezzforev[ebx] ; "uicrulezzforever"
.text:004012ED xor cl, String[ebx]
.text:004012F3 add edx, ecx
.text:004012F5 cmp ebx, 10h
.text:004012F8 jz short loc_4012FD
.text:004012FA inc ebx
.text:004012FB jmp short loc_4012E7
Qui fa la stessa procedura di prima ma usa un'altra stringa
("uicrulezzforever"), la somma finale sta sempre in edx. Ecco la parte +
divertente
.text:004012FD
.text:004012FD loc_4012FD: ; CODE XREF: sub_401104+1F4 j
.text:004012FD mov temp2, edx
.text:00401303 shr eax, 3
.text:00401306 ror ax, 8
.text:0040130A cdq
.text:0040130B add eax, ebx
.text:0040130D sub eax, edx
.text:0040130F imul eax, 2
.text:00401312 xor eax, eax
.text:00401314 mov eax, temp1
.text:00401319 xor ebx, ebx
.text:0040131B mov ebx, temp2
.text:00401321 xor edx, eax
.text:00401323 xor edx, ebx
.text:00401325 mov esi, edx
.text:00401327 xor esi, edx
.text:00401329 add ecx, esi
.text:0040132B sub edx, 55h
.text:0040132E xor eax, ebx
.text:00401330 add edx, 15h
.text:00401333 cmp eax, 0F1h
.text:00401338 jnz short Msg_Errore
.text:0040133A push 0 ; uType
.text:0040133C push offset aRegistered ; lpCaption
.text:00401341 push offset aYeahYouHaveSol ; lpText
.text:00401346 push 0 ; hWnd
.text:00401348 call MessageBoxA
.text:0040134D push offset aRegistered ; lpString
.text:00401352 push HandleEdit2 ; hWnd
.text:00401358 call SetWindowTextA
.text:0040135D jmp short loc_401372
Prima cosa mette il valore di edx (la seconda somma di xor) in temp2.
Fa un monte di operazione fa pure una conversione da double a quad (cdq,
praticamente si preparerebbe per una divisione) Se in ida selezioniamo la parola
eax, lui ci evidenziera' tutte le volte che compare scritta nel codice,
facciamolo. Cosa si nota di bello?
.text:00401303 shr eax, 3
.text:00401306 ror ax, 8
.text:0040130A cdq
.text:0040130B add eax, ebx
.text:0040130D sub eax, edx
.text:0040130F imul eax, 2
.text:00401312 xor eax, eax
.text:00401314 mov eax, temp1
Che prima che venga azzerata nella riga 401312, è usata come primo membro
dell'operazione, cioe' ogni operazionene modifica il contenuto (non tutte le
operazione modificano per forza il primo operando ma in questo caso si :)) ) poi
lo resetta a zero, e da quel momento compare sempre al secondo membro e quindi
non viene modificato (eccetto nell'ultimo xor che poi vedremo). Cosa voglio dire
con questo che tutte le stringhe che sono sopra allo xor sono inutili :)))
possiamo anche toglierle.
.text:00401319 xor ebx, ebx
.text:0040131B mov ebx, temp2
.text:00401321 xor edx, eax
.text:00401323 xor edx, ebx
.text:00401325 mov esi, edx
.text:00401327 xor esi, edx
.text:00401329 add ecx, esi
.text:0040132B sub edx, 55h
.text:0040132E xor eax, ebx
.text:00401330 add edx, 15h
.text:00401333 cmp eax, 0F1h
.text:00401338 jnz short Msg_Errore
Dopo che il valore di temp1 è stato messo in eax, viene azzerato ebx e vi viene
messo dentro il valore di
temp2. Se facciamo la stessa cosa per ebx vedremo che è sempre usato come
secondo membro e non viene quindi mai modificato. Se facciamo una analisi
attenta, le uniche cose che si riferiscono in qualche modo al nostro seriale
sono eax e ebx, quindi togliendo tutte le operazioni che non ne modificano i
valori otterremo
.text:00401312 xor eax, eax
.text:00401314 mov eax, temp1
.text:00401319 xor ebx, ebx
.text:0040131B mov ebx, temp2
.text:0040132E xor eax, ebx
.text:00401333 cmp eax, 0F1h
.text:00401338 jnz short Msg_Errore
Poiche' eax è stato azzerato tutte le operazioni precedenti che influivano su
eax posso essere tolte (questo non è un discorso, che si puo' fare cosi' come ho
fatto io alla leggera, ma se vi rileggete tutto il codice capirete che molte
operazioni sono inutili)
Ecco il codice necessario:
.text:0040123D mov eax, [ebp+wParam]
.text:00401240 shr eax, 16
.text:00401243 or eax, eax
.text:00401245 jnz short R_Prot
.text:00401247 push offset String ; lParam
.text:0040124C push 1Eh ; wParam
.text:0040124E push 0Dh ; Msg
.text:00401250 push hWnd ; hWnd
.text:00401256 call SendMessageA
.text:0040125B push offset String ; lpString
.text:00401260 call lstrlenA
.text:00401265 cmp eax, 16
.text:00401268 jnz Msg_Errore
.text:0040126E xor ecx, ecx
.text:00401270 xor ebx, ebx
.text:00401272 xor edx, edx
.text:00401274 loc_401274: ; CODE XREF: sub_401104+18A j
.text:00401274 ; DATA XREF: sub_401104+18C o
.text:00401274 mov cl, byte ptr aAutvrsqzi2y18a[ebx] ; "Autvrsqzi2y18az3"
.text:0040127A add eax, ecx
.text:0040127C xor cl, String[ebx]
.text:00401282 add edx, ecx
.text:00401284 sub eax, edx
.text:00401286 cmp ebx, 10h
.text:00401289 jz short loc_4012DB
.text:0040128B inc ebx
.text:0040128C xor ecx, ecx
.text:0040128E jmp short loc_401274
.text:004012DB loc_4012DB: ; CODE XREF: sub_401104+185 j
.text:004012DB ; sub_401104+1BB j
.text:004012DB mov temp1, edx
.text:004012E1 xor edx, edx
.text:004012E3 xor ecx, ecx
.text:004012E5 xor ebx, ebx
.text:004012E7 loc_4012E7: ; CODE XREF: sub_401104+1F7 j
.text:004012E7 ; DATA XREF: sub_401104+191 o ...
.text:004012E7 mov cl, byte ptr aUicrulezzforev[ebx] ; "uicrulezzforever"
.text:004012ED xor cl, String[ebx]
.text:004012F3 add edx, ecx
.text:004012F5 cmp ebx, 10h
.text:004012F8 jz short loc_4012FD
.text:004012FA inc ebx
.text:004012FB jmp short loc_4012E7
.text:004012FD loc_4012FD: ; CODE XREF: sub_401104+1F4 j
.text:004012FD mov temp2, edx
.text:00401312 xor eax, eax
.text:00401314 mov eax, temp1
.text:00401319 xor ebx, ebx
.text:0040131B mov ebx, temp2
.text:0040132E xor eax, ebx
.text:00401333 cmp eax, 0F1h
.text:00401338 jnz short Msg_Errore
.text:0040133A push 0 ; uType
.text:0040133C push offset aRegistered ; lpCaption
.text:00401341 push offset aYeahYouHaveSol ; lpText
.text:00401346 push 0 ; hWnd
.text:00401348 call MessageBoxA
.text:0040134D push offset aRegistered ; lpString
.text:00401352 push HandleEdit2 ; hWnd
.text:00401358 call SetWindowTextA
.text:0040135D jmp short loc_401372
Bene rileggetevelo all'infinito perche' adesso parliamo della routine di
protezione e delle proprieta' dello xor
Naturalmente ho saltato a pie' pari la routine di protezione dai breakpoint
(incredibile c'e' anche questo) ma di questo ne parleremo nella III parte
Ecco la procedura in C ( tnx ancora al mitico ^SPIDER^ )
long core(char *serial)
{
char mask1[17] = "uicrulezzforever";
char mask2[17] = "Autvrsqzi2y18az3";
for (i = 0; i < 16; i++)
{
sum1 += serial[i]^mask1[i];
}
for (i = 0; i < 16; i++)
{
sum2 += serial[i]^mask2[i];
}
if ( (sum1^sum2) == 0xF1)
{
return 0xFFFFFF;
}
else
{
return sum1^sum2;
}
}
II. Proprieta' usi e debolezze dello xor :)
Allora lo xor è una funzione asm molto particolare nota a molti come or
escusivo, diamone una definizione:
Lo xor confronta Bit per Bit i due valori, se i bit presi ad uno ad uno sono
uguali mette zero altimenti 1.
Esempio)
A = 3F ; B = 94 ;
A xor B = 3F xor 94
0011 1111 3F
1001 0100 94
---------- xor
1010 1000 A8
mmmmm la prima cosa che risulta evidente e' che
3 xor 9 = A F xor 4 = 8
cioe' se scriviamo (0x10h*3 xor 0x10h*9) + (0x0Fh xor 0x04) = 0x10h*(0x03 xor
0x09) + (0x0Fh xor 0x04)
semplicemente, lo xor è un operazione senza riporto, cioe' preso un valore e
diviso per byte, il risultato dello xor di ogni byte non puo' variare se non tra
0 e F. Il che mi sembra una debolezza accettabile, in quanto non dobbiamo tenere
conto per il momento di resti vari.
Naturalmente ogni carattere del seriale puo' variare da 0x20 a 0x7E che sono
tutti i caratteri scrivibili tramite tastiera e sono 96, quindi noi se volessimo
bruteforzare il seriale dovremmo provare 96^16 combinazioni, un po' troppo :))
Ok dobbiamo trovare una debolezza che ci permette di trovare il seriale senza
bruteforzare. Come AndreaGeddon mi ha fatto notare, le funzioni principali
dell'algo sono xor add, inc, che sono tutte reversabili in quanto non c'è
perdita di dati
Ok date una occhiata alla tabella qui' sotto:
71 45 =====> 30 01 35 =====> 40 11 25 =====> 50
70 44 =====> 31 00 34 =====> 41 10 24 =====> 51
73 47 =====> 32 03 37 =====> 42 13 27 =====> 52
72 46 =====> 33 02 36 =====> 43 12 26 =====> 53
75 41 =====> 34 05 31 =====> 44 15 21 =====> 54
74 40 =====> 35 04 30 =====> 45 14 20 =====> 55
77 43 =====> 36 07 33 =====> 46 17 23 =====> 56
76 42 =====> 37 06 32 =====> 47 16 22 =====> 57
79 4d =====> 38 09 3d =====> 48 19 2d =====> 58
78 4c =====> 39 08 3c =====> 49 18 2c =====> 59
7b 4f =====> 3a 0b 3f =====> 4a 1b 2f =====> 5a
7a 4e =====> 3b 0a 3e =====> 4b 1a 2e =====> 5b
7d 49 =====> 3c 0d 39 =====> 4c 1d 29 =====> 5c
7c 48 =====> 3d 0c 38 =====> 4d 1c 28 =====> 5d
7f 4b =====> 3e 0f 3b =====> 4e 1f 2b =====> 5e
7e 4a =====> 3f 0e 3a =====> 4f 1e 2a =====> 5f
21 15 =====> 60 31 05 =====> 70
20 14 =====> 61 30 04 =====> 71
23 17 =====> 62 33 07 =====> 72
22 16 =====> 63 32 06 =====> 73
25 11 =====> 64 35 01 =====> 74
24 10 =====> 65 34 00 =====> 75
27 13 =====> 66 37 03 =====> 76
26 12 =====> 67 36 02 =====> 77
29 1d =====> 68 39 0d =====> 78
28 1c =====> 69 38 0c =====> 79
2b 1f =====> 6a 3b 0f =====> 7a
2a 1e =====> 6b 3a 0e =====> 7b
2d 19 =====> 6c 3d 09 =====> 7c
2c 18 =====> 6d 3c 08 =====> 7d
2f 1b =====> 6e 3f 0b =====> 7e
2e 1a =====> 6f
bene spieghiamo a che cos'è: XX YY ======> SS
Allora in SS ci sono tutti i caratteri che potrei inserire come primo carattere
del seriale, e XX YY sono
rispettivamente i valori che xorati con il primo carattere delle stringhe danno
SS. in pratica in pseudoc (TNX SPIDERPER AVERMI INSEGNATO AD USARE IL PSEUDOC AL
POSTO DELL'ASM :)) )
è questo:
Code1[16]="Autvrsqzi2y18az3";
Code2[16]="uicrulezzforever";
for(SS = 0x30; SS < 0x7F; SS++){ // in realta' dovrebbe essere for(SS = 0x20; SS
< 0x7F; SS++),
// ma per semplicita' ignoriamo i valori compresi tra 0x20 e 0x30 sono
// dati cmq utili, ma per ragioni che non vi sto a spiegare possono
// essere completamente tralasciati :))))
k=0; // indicatore del carattere in esame
XX = Code1[k]^SS;
YY = Code2[k]^SS;
printf("%h\t%h =====> %h\n",XX,YY,SS);
}
Allora in realta' questa tabella è un po' grande per le nostre esigenze, le
coppie di valori si possono semplicemente ridurre a 21, perche'?
Diamo una definizione
se 0xPQ è un numero esadecimale questo si puo' scrivere come:
0xP * 0x10 + 0xP= PQ
dove P la chiamero' parte intera, e Q resto (li potevo chiamare Luca e Paolo :))
non sono termini tecnici )
In ogni colonna il resto cambia mentre la parte intera giustamente rimante fissa
infatti togliendo la parte intera dalla prima e dalla seconda colonna (ne potete
prendere due a caso)
Se guardiamo nella tabella, togliengo le parti intere ci accorgiamo che sono
uguali
x1 y5 =====> z0 x1 y5 =====> z0
x0 y4 =====> z1 x0 y4 =====> z1
x3 y7 =====> z2 x3 y7 =====> z2
x2 y6 =====> z3 x2 y6 =====> z3
x5 y1 =====> z4 x5 y1 =====> z4
x4 y0 =====> z5 x4 y0 =====> z5
x7 y3 =====> z6 x7 y3 =====> z6
x6 y2 =====> z7 x6 y2 =====> z7
x9 yd =====> z8 x9 yd =====> z8
x8 yc =====> z9 x8 yc =====> z9
xb yf =====> za xb yf =====> za
xa ye =====> zb xa ye =====> zb
xd y9 =====> zc xd y9 =====> zc
xc y8 =====> zd xc y8 =====> zd
xf yb =====> ze xf yb =====> ze
xe ya =====> zf xe ya =====> zf
Quindi scelto per ogni carattere un valore del resto compreso tra 0 e F
opportuno, troveremo sempre una coppiadi valori del tipo 0-x e x - 0.
Infatti se scegliessimo come primo carattere un qualsiasi valore di questo tipo
z1 e z5 con 3<Z<7
avremo sempre una coppia del tipo X0 - Y4 e X4 - Y0. Cio' ci permette di
mantenere i valori di temp1 e temp2 sotto controllo.
Facendo questo per ogni carattere trascurando la parte intera come se non desse
contributo nella somma
io posso ottenere temp1 e temp2 in modo che xorati tra loro diano un valore che
a me convenga.
Allora Ricapitoliamo:
Lo Xor Finale tra temp1 e temp2 si puo' scrivere:
temp1= XXXXXXXXYYYYZZZZ (naturalmente tutto in bit)
temp2= QQQQQQQQPPPPRRRR (naturalmente tutto in bit)
ora temp1 xor temp2 = 00F1 cioe' 0000000011110001
quindi se:
bit X[8],Q[8],Y[4],P[4],Z[4],R[4];
(beh sarebbe corretto anche scrivere bool ma è per farmi capire)
temp1= XXXXXXXX YYYY ZZZZ
temp2= QQQQQQQQ PPPP RRRR
-------------------------
00F1= 00000000 1111 0001
Quindi le nostre esigenze sono:
Z = R + 1
Y xor P = 0x0F (Y=NOT(P)o anche Y= rol P,2)
X = Q (valore xor valore = 0)
Appunto: La prima condizione è vera solo se R è un numero pari es. 0x04 xor 0x05
= 1
Ok adesso vi scrivo il seriale corretto senno' non capite, e non andate a
provarlo subito chiudendo il tute :)))
se capite la procedura ve ne trovate a centinaia da soli :)))
Eccolo qui' il fetente :))
uycBrlaJjboq8az2
ok facciamo una bella tabellina riassuntiva
i valori sono tutti in hex:
uycBrlaJjboq8az2 = 75 79 63 42 72 6C 61 4A 6A 62 6F 71 38 61 7A 32
temp1 = 34+0C+17+34+00+1F+10+30+03+50+16+40+00+00+00+01 = 194
temp2 = 00+10+00+30+07+00+04+30+10+04+00+03+5d+17+1f+40 = 165
temp1 xor temp2 = 00F1
Ok analizziamo solo i testi di temp1 e temp2
Indici > 0 1 2 3 4 5 6 7 8 9 A B C D E F
temp1 = 4+C+7+4+0+F+0+0+3+0+6+0+0+0+0+1 = 34
temp2 = 0+0+0+0+7+0+4+0+0+4+0+3+d+7+f+0 = 35
Mettiamoli nel seguente ordine:
Indici > 0 6 2 4 3 9 8 B 5 E 1 F C A D 7
temp1 = 4+0 + 7+0 + 4+0 + 3+0 + F+0 + C+1+0 + 6+0 +0 = 34
temp2 = 0+4 + 0+7 + 0+4 + 0+3 + 0+F + 0+0+D + 0+7 +0 = 35
Come avete visto ho scelto per ogni posizione del seriale un carattere con parte
intera opportuna a darmi
due somme differenti di 1 (34 e 35) (NB. Teniamo presente il 3)
In pratica scegliete un carattere vedete quanto vale la sua combinazione 0 - x e
trovate un altro carattere
che da in combinazione x - 0, poi una volta sistemati almeno 13 caratteri devete
solo scegliere altri 3
valori dai rimanenti caratteri che soddisfano la prima condizione.
Ok xor di prova 0x34 Xor 0x35 = 0x01
Perfetto la prima condizione è rispettata passiamo alla seconda
Analizziamo adesso solo le parti intere
Indici > 0 1 2 3 4 5 6 7 8 9 A B C D E F
temp1 = 3+0+1+3+0+1+1+3+0+5+1+4+0+0+0+0
temp2 = 0+1+0+3+0+0+0+3+1+0+0+0+5+1+1+4
Mettiamo in questo ordine per capirci qualcosa
Indici > 0 2 F 1 5 9 C 6 8 A D 3 7 4 B E
temp1 = 3+1+0 + 0+1 + 5+0 + 1+0 + 1+0 + 3+3+0 + 4+0 = 16
temp2 = 0+0+4 + 1+0 + 0+5 + 0+1 + 0+1 + 3+3+0 + 0+1 = 13
Controlliamo la 2 e 3 condizione
0x16 xor 0x13 = 0x05 eh?????????????
Io pero' vi avevo detto di tenere a mente il 3 di (34 e 35) infatti se sommate
tre a temp1 e temp2
ottenete
0x19 xor 0x16 = 0x0F OLEEEEEEEEEEEE
seconda e terza condizione verificata
In pratica dovrete scegliere una colonna per carattere in modo tale da fissare
almeno 13 caratteri e fare variare i rimanenti in modo che xorati diano 0F,
ricordandovi di aggiungere la parte intera della prima somma (cioe' "3" nel caso
di 34 e 35 o "4" nel caso di 46 e 46 e cosi' via) (non vi sto prendendo in giro,
lo faccio solo per essere chiaro :)) )
Concludo con un l'esempio aggiornato:
uycBrlaJjboq8az2= 75 79 63 42 72 6C 61 4A 6A 62 6F 71 38 61 7A 32
temp1 = 34+0C+17+34+00+1F+10+30+03+50+16+40+00+00+00+01 = 194 = 0x16*0x10 + 0x34
temp2 = 00+10+00+30+07+00+04+30+10+04+00+03+5d+17+1f+40 = 165 = 0x13*0x10 + 0x35
Allego nello zip le tavole di xor per ogni carattere, anche se non vi servono
:)))
Naturalmente se vi trovate una combinazione giusta che ha come carattere del
seriale 0x7F dovrete scartarla poiche'il carattere 0x7F non è inseribile da
tastiera.
III. Routine di protezione dai breackpoint
Questa parte del programma non viene mai chiamata in quanto se leggiamo in ida,
non ci sono istruzioni che
si riferiscono a questa parte del codice cmq diamoci un occhio
.text:00401290 mov edi, offset loc_401274 ; muove in edi la locazione di memoria
401274
.text:00401295 mov ecx, offset loc_4012E7 ; mette in ecx la locazione di memoria
4021E7
.text:0040129A sub ecx, edi ; sottrae a ecx edi, in pratica mette il numero
; di byte compresi tra 401274 e 4012E7
.text:0040129C xor eax, eax ; azzera eax
.text:0040129E mov ax, 0CCh ; mette nella parte bassa di eax 00CCh
.text:004012A2
.text:004012A2 loc_4012A2: ; CODE XREF: sub_401104+1B0 j
.text:004012A2 repe scasb ; qui' fa una ricerca del byte che si trova in al
; dall'offset edi per ecx byte
; ad ogni scansione decrementa ecx e aumenta di uno edi
.text:004012A4 jnz short loc_4012B6 ; se non ne trova nessuno salta altrimenti
continua
.text:004012A6 add byte_40313C, 1 ; aggiunge ad una variabile temporanea 1
.text:004012AD mov ecx, offset loc_4012E7 ; mette in ecx l'ultima locazione da
controllare
.text:004012B2 sub ecx, edi ; gli sottrae il nuovo valore di edi, (aggiorna il
contatore)
.text:004012B4 jmp short loc_4012A2 ; ripete la scansione
.text:004012B6
.text:004012B6 loc_4012B6: ; CODE XREF: sub_401104+1A0 j
.text:004012B6 cmp byte_40313C, 0 ; confronta il valore della locazione
temporanea con zero
.text:004012BD jg short loc_4012C1 ; se è > 0 salta ad errore
.text:004012BF jmp short loc_4012DB ; altrimenti continua
In pratica conta nei byte compresi tra 401274 e 4012E7 quanti byte di valore CC
ci sono, se questo valore è
maggiore di zero allora significa che c'è in quella parte di programma un
breakpoint altrimenti continua.
In che senso controlla se c'è un breakpoint?
Il sice o i debbugger in realtime solitamente per prendere il controllo del
flusso del programma mettono
un int3 (che in opcode è ovviamente CC) nella locazione sulla quale breakkare.
Il programma legge che c'è una
chiamata all'interrupt 3,e si va a chiamare la parte di codice corrispondente a
questo interrupt che è stata
al tempo di loading del debbugger da del codice che ci permette di debbuggare
con tutta calma.
Spero di essere stato chiaro :))))
Ringrazio chi mi ha permesso di scrivere questo tute, Evilcry e il suo mitico
CM3, Ntoskrnl che mi sostiene sempre ;PPP il mitico Spider, che mi ha ispirato
la mia soluzione, Destroyer2K il mio compagno di merende, Atomik^Head il mio
compagnone di sklerate in rete, Active85k, Que AndreaGeddon IronSpark e
Kratorius che mi danno sempre una mano nei momenti di confusione Tutto il canale
#Crack-it e chi mi conosce
La teoria è quando si sa tutto e niente funziona.
La pratica è quando tutto funziona e nessuno sa il perché.
In questo caso, abbiamo messo insieme la teoria e la pratica: non c'è niente che
funziona... e nessuno sa il perché!
Albert Einstein
T4N4T0S
[email protected]