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]