Zoom Icon

Corso UIC Avanzato 12 Winebag

From UIC Archive

UIC Lezione 12 (Winzip Encryption Engine Reversing)

Contents


Corso UIC Avanzato 12 Winebag
Author: Winebag
Email: [email protected]
Website:
Date: 04/03/2003 (dd/mm/yyyy)
Level: Luck and skills are required
Language: Italian Flag Italian.gif
Comments: Bravissimo davvero, il tutorial e' estremamente dettagliato, ottimo lavoro... Ma hai guardato il problema dal punto di vista sbagliato... O meglio, diciamo che hai soltanto studiato l'algoritmo e anzi, hai trovato anche il modo di fare un attacco ridotto (complimenti, ci hanno messo svariati anni per mettere a punto quel tipo di attacco) ma non hai fatto nessuna prova per vedere se effettivamente esisteva o meno il buco di cui parlavo... Vediamo le soluzioni successive, se ce ne saranno, cosi potrai farti magari un'idea piu' approfondita, nel frattempo complimenti!


Oggi non dovremo registrare nessun programmillo, perché il buon Quequero ci propone di reveresare un algoritmo di crittografia, per tentare di crittanalizzarlo.


Introduzione

Allora, dopo anni e anni di sosta dei corsi, ci rivediamo per questo corso, devo dire bellino,anche se non troppo facile per quanto riguarda la crittanalisi. Prima di cominciare, due parole sul tutorial: spesso troverete riferimenti a parti di codice che non sono presenti sul tute, e se sono presenti non sono commentate oltre lo stretto indispensabile. Questo perché non volevo appesantire ulteriormente il tutto, così ho preferito spiegare con dei lunghi vaniloqui piuttosto che aggiungere altro codice, visto che mi sembra ce ne sia già abbastanza.


Tools

SoftIce e chi sennò?
IDA PRO
Un editor esadecimale


Link e Riferimenti

Questo è la soluzione del Corso UIC Avanzato n°12 disponibile alla pagina Corso UIC Avanzato 12


Notizie sul Programma

Ma devo anche spiegarvi cos'è Winzip?


Essay

Ci scarichiamo il file con la versione 8.1 di Winzippo, lo installiamo e incominciamo a vedere il problema dal punto di vista del curioso. Apriamo WZ e vediamo come funziona la crittazione:ad un archivio inseriamo una password e aggiungiamo dei file. Quando riapriremo l'archivio ci chiederà la password solo per i file aggiunti dopo,quindi critta il singolo file e non tutto l'archivio. A questo punto scriviamo un file di pochi byte, in modo che se zippato non venga compresso, e produciamo 2 file:un file zippato(z) e un file zippato e criptato(zc).Dal confronto emergono alcune differenze:per prima cosa zc è 12B più lungo di z, e questa sembra essere una costante: infatti qualunque zip criptato ha una lunghezza maggiore di 12*n, con n=numero dei file criptati. Se escludiamo i byte del file, i due zip differiscono ovviamente per le dword indicanti la lunghezza del file compresso,per 2 dword, che dovrebbero essere un'indicazione se il file sia stato criptato o meno e per un byte,che ha tutta l'aria di essere un check-sum Potevi leggerti le specifiche che avevo lasciato, magari ti risparmiavi qualche prova ;pppp NdQue.
Da qui possiamo capire che il programma prima zippa e poi critta, ma questo mi sembra francamente il segreto di pulcinella, che l'algoritmo di crittazione lascia invariata la lunghezza, se non per quei 12B, che possono essere o un'hash della password, in modo che in fase di decrypt ci si possa accorgere della correttezza della password senza dover decrittare e scompattare il file,o una serie di valori random generati in fase di crittazione,o comunque utili alla decrittazione, o entrambe le cose.Proviamo a crittare lo stesso file di prima con la stessa passwoprd e notiamo che gli output sono diversi,quindi è molto probabile che sia vera la seconda ipotesi.
Per quanto riguarda il punto di vista del semplice curioso,non possiamo più dire molte cose, quindi proviamo l'approccio da reverser. Que ci chiede di spiegare come si arriva all'algoritmo; io personalmente ho usato SIce, ho messo un bpx su hmemcpy ho provato a inserire la password in wz e ho visto cosa succedeva: se shadowiamo la password, la funzione chiama un'altro dialog, che ci chiede di reinserirla, controlla se sono uguali e la salva in memoria senza toccarla. Possiamo seguire i vari passaggi di stringhe settando dei bpm all'indirizzo in cui è salvata la password(prima 4C9A74, poi 4CC728,se non ne ho dimenticati altri). Poi proviamo a crittare un file SIce ricompare,altri passaggi di stringhe, finché arriviamo ad una funzione che utilizza ogni carattere come input ad un'altra, dopodiché la pass, non viene più toccata.Ci segnamo l'indirizzo di questa funzione:20001F10, e notiamo che appartiene ad una dll chiamata WZ; la cerchiamo nella cartella di WinZip e la disassembliamo con IDA: sub1F10 proc near

push ebp mov ebp, esp push esi mov esi, [ebp+arg_0]<---qui c'è l'indirizzo della password mov dw1, 12345678h<--inizializza 3 dw che impareremo a conoscere molto bene ''Le specificheee ;p NdQue'' mov dw2, 23456789h mov dw3, 34567890h mov al, [esi] test al, al jz short loc1

loop: and eax, 0FFh push eax call sub1EA0--F1<---funzione molto importante! mov al, [esi+1]<--mette in al i caratteri add esp, 4 inc esi test al, al<--fino alla fine della stringa jnz loop

loc1: pop esi pop ebp retn sub1F10 endp

Questa è la funzione che viene chiamata all'interno del ciclo, con argomento ogni carattere della password:

sub1EA0--F1 proc near

push ebp mov ebp, esp mov edx, dw1 mov eax, [ebp+arg_0] mov ecx, edx push esi xor ecx, eax and ecx, 0FFh shr edx, 8 mov ecx, Sbox[ecx*4] xor ecx, edx mov edx, dw2 mov dw1, ecx and ecx, 0FFh add ecx, edx mov edx, dw3 imul ecx, 8088405h inc ecx mov esi, edx mov dw2, ecx and esi, 0FFh shr ecx, 18h xor ecx, esi pop esi shr edx, 8 mov ecx, Sbox[ecx*4] xor ecx, edx mov dw3, ecx pop ebp retn sub1EA0--F1 endp Come potete vedere, la funzione riceve un parametro di 1byte e sostituisce i valori delle 3 dw, utilizzando i valori delle dw generate dal ciclo precedente e il parametro. Per fare questo si serve tra le altre cose di una S-box. Questo è un procedimento molto usato in crittografia: la S-box (sostitution box) altro non è che un array, che ad un certo valore dell'indice fa corrispondere un valore. In questo caso,l'array è un array monodimensionale di 256 dword. In particolare i valori in essa, sono quelli utilizzati negli algoritmi di CRC. La S-box in pratica è una funzione ed ha fondamentalmente 2 pregi:può essere usata per espandere i dati (come in questo caso da un indice di 8 bit,si ottiene un valore a 32) e inoltre cambia totalmente i valori e per questo non lascia punti di riferimento al crittanalista Non e' totalmente vero in realta', se l'indice e' fisso basta rigirarlo :) NdQue. Ma analizzeremo dopo più attentamente la funzione,ora ritorniamo all'assembler. Seguiamo con SIce la funzione 1FE0 e vediamo dove ci porta, troviamo questa funzione di cui ho tagliato alcune parti: ...<---prima vengono chiamate funzioni per generare numeri casuali loop_rand: mov eax, ptr<--ptr punta ad un'array di dword riempito con valori random mov eax, [eax] cmp eax, edi jl loc1 add ptr, 4 jmp loc2

loc1: call fill_ptr<--questa riempie l'array

loc2: sar eax, 16h mov [ebp+esi+var_C],al<--mette il byte alto in un'array di byte sulla memoria locale inc esi cmp esi, 0Ah<--ripete 10 volte jl loop_rand

... mov ecx, [ebp+8]<--la password push ecx call sub1F10<---qui chiama la funzione vista prima add esp, 4

loop2: call sub1E80--F2<--questa ce la vediamo dopo mov bl, [ebp+edi+var_C] mov [ebp+arg_8], eax<--salva l'output della funzione F2 mov edx, ebx and edx, 0FFh push edx<---il parametro è uno dei byte random inseriti sopra nell'array locale call sub1EA0--F1<--funzione vista prima mov cl, byte ptr [ebp+arg_8] push 1<--1 char xor bl, cl<---xora l'output con il byte random push ebx push esi call sub1D100--pseudomemcpy<-per mezzo di diverse call arriva ad una routine copia stringhe add esp, 10h test eax, eax jz wrong inc edi cmp edi, 0Ah<--ripete per tutti e 10 i byte random jl loop2

call sub1E80--F2 mov edi, [ebp+arg_4]<--parametro molto interessante mov [ebp+arg_8], eax mov ebx, edi shr ebx, 10h mov eax, ebx and eax, 0FFh<---prende il 2°byte a partire dall'alto push eax<--lo parametrizza in F1 call sub1EA0--F1 mov cl, byte ptr [ebp+arg_8]<--output di F2 push 1 xor bl, cl<--come sopra push ebx push esi call sub1D100--pseudomemcpy add esp, 10h test eax, eax jnz short OK ...

OK: call sub1E80--F2 mov ebx, edi mov [ebp+arg_8], eax shr ebx, 18h<--byte più alto del parametro push ebx call sub1EA0--F1 mov cl, byte ptr [ebp+arg_8] push 1 xor bl, cl<--tutto come prima push ebx push esi call sub1D100--pseudomemcpy add esp, 10h ... Questo algoritmo utilizza un byte random come input per F1,dopodiché lo xora con l'output di F2 e lo copia in una stringa, ripete il tutto per 10byte e successivamente per i 2 byte più alti del parametro di questa funzione. Questo parametro è il CRC,cioè una sorta di chiave hash del file non compresso. Questo valore si trova nel file zip,dopo i dati compressi, e ovviamente non dipende dal fatto che vi sia stata crittazione(vi ricordate prima quando abbiamo visto le differenze tra z e zc, non veniva menzionato questo valore). L'algoritmo che lo genera si trova nella funzione all'indirizzo 200246B0. Se volete dargli un occhiata, vi accorgerete che esso usa i valori di Sbox esattamente come la funzione F1,che quindi utilizza due volte la funzione CRC. Quindi capiamo che Sbox è in effetti una tabella CRC Bravo davvero! NdQue.
Per quanto riguarda le 3 dw,esse vengono modificate ancora da F1 che come abbiamo visto agisce per 12 volte, che si aggiungono a quelle della password,in pratica è come se la password fosse 12byte più lunga.Chiaramente in fase di decrypt dovrà essere possibile risalire a questi 12 byte:abbiamo detto che dopo essere stati xorati i byte vengono salvati in una stringa,che sarà anteposta al messaggio criptato nel file, spiegato finalmente cos'erano quei 12byte! Per completare l'analisi diamo uno sguardo a F2: sub1E80--F2 proc near mov ecx, dw3<--usa dw3 ma non la modifica or ecx, 2 mov eax, ecx xor eax, 1 imul eax, ecx<---moltiplica and eax, 0FFFFh shr eax, 8<--restituisce il 3°byte a partire dall'alto del prodotto retn sub1E80--F2 endp Questa funzione non altera i valori delle dw,non ha parametri in ingresso, restituisce un valore di 8bit,l'unico input proviene da dw3,quindi dall'ultimo passaggio di F1.Poichè F1 ,al contrario, ha un parametro e il valore restituito non viene mai considerato, e in questo punto F1 e F2 si ripetono sempre alternate, possiamo considerare F1 ed F2 come parti di una stessa funzione. Attenzione però, prima F1 e poi F2, anche se nel ciclo compare prima F2, quindi il valore restituito da F2 all'inizio del ciclo deriva dall'input del ciclo precedente,ovvero dal byte random prima, e per il primo, esso è l'ultimo della pass. Questo valore sarà usato per xorare quello dopo. In simboli c[n]=r[n]^F(r[n-1]) questo può essere considerato analogo ad un algoritmo di crittografia a blocchi(di un byte) in modalità CFB,dove cioè prima si critta il blocco n-1 e poi lo si xora con il blocco n ottenendo il blocco cifrato c[n], che va a costituire la stringa di 12byte. Notate che fino ad adesso non abbiamo crittato un cazzo, ma abbiamo solo preparato la strada producendo 3dw, che voglio sperare servano a qualcosa, allora mettiamo un bpm di esse e premiamo F5. Arriviamo alla solita F1,risalendo troviamo questa funzione: sub2100 proc near

... mov esi, [ebp+arg_0]<--puntatore al messaggio da crittare(Plaintext) imul eax, [ebp+lunghezza] test eax, eax jz short Loc1 push ebx mov [ebp+arg_4], eax<--lunghezza

Loop1: call sub1E80--F2 mov ebx, eax<--salva l'output di F(P[n-1]) xor eax, eax mov al, [esi]<--prende P[n] push eax call sub1EA0--F1<--chiama F(P[n]) mov dl, [esi] mov eax, [ebp+arg_4] xor dl, bl<--già vista! add esp, 4 mov [esi], dl<--sovrascrive P[n] con C[n] inc esi dec eax<--sempre la lunghezza mov [ebp+arg_4], eax jnz Loop1 pop ebx

loc1: imul edi, [ebp+lunghezza] mov ecx, [ebp+arg_0]<--puntatore al messaggio crittato(C) mov edx, [ebp+arg_C] push 0 push edi push ecx push edx call sub1D010<---un'altra funzione di copia stringhe ... Come possiamo vedere l'algoritmo è lo stesso di prima, in modalità CFB(anzi non proprio perché in quel caso da cosa ho capito si usa F(C[n]) e qui venendo generato P[-1] a partire dalla pass,si usa F(P[n]),ma cambia poco) con la stessa funzione. La particolarità di questa modalità è che la crittazione vera e propria è fatta dallo xor, e non dalla funzione, che può quindi essere irreversibile senza per questo pregiudicare la reversibilità di tutto l'algo. Come ad esempio in questo caso, senza invertire F si può invertire l'algoritmo:conoscendo la password genero le 3 dw uguali a quelle in fase di crittazione,uso l' ultimo char in F e uso il valore ottenuto per xorare C[0] e ottengo P[0], che passerò a F,per ottenere il valore per xorare C[1], e ripeterò questo procedimento fino all'ultimo char. Per curiosità ho disassemblato anche la routine di decrypt,(trovata con un bpm sulle 3dw) e ho ottenuto ciò che mi aspettavo Questa e' stata un'ottima idea, complimenti NdQue. sub2200decrypt proc near

push ebp mov ebp, esp sub esp, 0Ch mov eax, dword_0_2007DDA8<--password push esi push eax call sub1F10<---genera le 3 dw mov ecx, [ebp+arg_0]<--indirizzo dei 12byte add esp, 4 xor esi, esi mov edx, [ecx] mov [ebp+var_C], edx<--li ricopia sulla stack nel solito array R mov eax, [ecx+4] mov [ebp+var_8], eax mov ecx, [ecx+8] mov [ebp+var_4], ecx

loop1: call sub1E80--F2<--F(R[n-1]) mov dl, byte ptr [ebp+esi+var_C]<--valore crittato dei byte random xor dl, al<--decritta mov al, dl mov byte ptr [ebp+esi+var_C], dl<--lo sovrascrive decrittato and eax, 0FFh push eax<--chiama F(R[n]) call sub1EA0--F1 add esp, 4 inc esi cmp esi, 0Ch<--per 12 byte jl loop1 mov edx, off_0_2004606C movzx ax, byte ptr [ebp+var_4+3]<--ultimo dei 12 byte test byte ptr [edx+20h], 2 jz loc1 xor ecx, ecx mov cl, byte_0_2009A937 cmp ax, cx<--controlla che decrittato sia uguale al valore del CRC jz OK ...

loc1: mov edx, dword_0_2009A938+2 shr edx, 18h cmp ax, dx jz OK ...

OK: ...

loop2: call sub1E80--F2<--F(P[n-1]) mov cl, [esi]<--C[n] xor cl, al<--C[n]^^F(P[n-1])=P[n] mov al, cl mov [esi], cl<--copia P[n] and eax, 0FFh push eax<--chiama F(P[n]) call sub1EA0--F1 add esp, 4 inc esi dec edi<--lunghezza jnz short loop2 pop edi

loc2: mov eax, 1<--Tutto OK pop esi mov esp, ebp pop ebp retn decrypt endp Da questa analisi vediamo ancora che prima di decrittare controlla che l'ultimo dei 12 byte, decrittato, abbia il valore del CRC, altrimenti vuol dire che è sbagliata la password. L'approccio del reverser finisce qua; ora proveremo quello più difficile:il crittanalista. Uno sguardo al cifrario molto in generale l'abbiamo già dato, esso è una variante della modalità CFB, usa la funzione F con il byte di plaintext e ottiene un valore per xorare il byte successivo. Questa funzione, oltre al byte in ingresso, si serve di 3 chiavi,che abbiamo chiamato dw, e genera le 3 chiavi che saranno utilizzate al ciclo seguente. Le chiavi per il primo ciclo sono generate con la password e i 12 byte random. Vediamo come lavora la funzione utilizzando una notazione in pseudo-C; il disassemblato è sopra, diviso in F1 e F2,non penso di doverlo ripetere qui, dato che per le considerazioni che dobbiamo fare, è meglio scriverlo in forma più compatta, non formalizzatevi se c'è qualche errore di forma; se trovate qualche errore di concetto scrivetemi. char F(char a){ dw1=(dw1>>8)^Sbox[a^(dw1&0xFF)]; dw2=0x8088405*(dw2+(dw1&0xFF))+1; dw3=Sbox[(dw2>>24)^(dw3&0xFF)]^(dw3>>8); return (char)(((dw3|2)*((dw3|2)^1))&FFFF)>>8;} Il nuovo valore di dw1 viene generato a partire dal suo valore precedente e dal byte in ingresso. La nuova dw2, utilizzando dw2 e il quarto byte di dw1; dw3,generata dal suo valore precedente e dal byte alto di dw2, servirà ad ottenere l'output. Queste poche righe basterebbero a spiegare l'algoritmo, ma Que ci ha chiesto di più: tentare un metodo d'attacco e cercare una falla. Io personalmente ho trovato questa parte ostica, data la mia poca familiarità con queste cose. L'attacco classico si basa sul brute-force della password e presenta chiaramente problemi maggiori all'aumentare della lunghezza della pass. Qui non vedo grosse falle,l'unica cosa che può tornarci utile è il fatto che conosciamo il valore di due byte, come abbiamo visto prima, gli ultimi due di quei 12 aggiunti. Conoscendo il valore di P[10] e di P[11], effettuando uno xor otteniamo P[10]^C[10]=F(P[9]), e allo stesso modo otteniamo F(P[10]). Il principio che sta alla base di questo attacco è che, per come funziona quest'algoritmo,senza conoscere la password conoscendo i valori in input di F, possiamo generare i successivi, dato che le dw sono ottenute direttamente, e il valore decrittato sarà il parametro successivo, ma con un bruteforce sono 13byte. Proviamo a percorrere a ritroso F:conosciamo F(P10) , esso è il terzo byte del prodotto tra (dw3|2) e ((dw3|2)^1), il penultimo bit di entrambi questi valori è settato a 1 dall'or, l'ultimo sarà 1 in uno e 0 nell'altro(a causa dello xor con 1), indipendentemente da dw3; quindi se un valore multiplo di 4 di dw3 da come risultato x, così faranno anche i 3 numeri seguenti. Trattandosi del 3°byte è come operare modulo 2^16(2 alla 16,NON 2 xor 16), quindi interesseranno solo gli ultimi 2 byte di dw3. Mediamente il 3°byte sarà uguale al valore desiderato una volta ogni 256, quindi provando 2^16 word(anche se come abbiamo visto ne proveremo 2^14), in media 256 daranno esito positivo. Provando circa 256 combinazioni per il primo valore e 256 per il secondo, ce ne sarà una in cui conosceremo il reale valore della word bassa di dw3 in 2 cicli successivi.
Rappresentiamo con una tabella le 3 dword generate di cicli 9 e 10.

P[10]
/ / / / / / / /
/ / / / / / / /
/ / / / / / / /
F(P9) F(P10)

I valori in rosso sono quelli che conosciamo, quelli in blu sono quelli che troviamo con un bruteforcing di 65536 combinazioni. Analizziamo come viene generata dw3: dw3=Sbox[(dw2>>24)^(dw3&0xFF)]^(dw3>>8), ovvero prendo l'ultimo byte di dw3 precedente, lo xoro con il valore attuale del byte alto di dw2, e prendo il valore di S-box corrispondente a quell'indice e lo xoro con la vecchia dw3 shiftata di 8. Noi procediamo a ritroso, prendiamo c3 del ciclo9 e lo xoriamo con D3,del ciclo 10, otteniamo l'ultimo byte di S-box, che identifica in modo univoco l'indice: (dw2>>24)^(dw3&0xFF), ma noi conosciamo anche il d3(9) e ricaviamo il byte A2(10); sappiamo anche che il byte alto della S-box non viene xorato e viene copiato in A3, che quindi conosciamo. Xorando C3 con il terzo byte della S-box otteniamo b3.Possiamo quindi aggiungere 3 byte al nostro schemozzo:

P[9] P[10]
a1 b1 c1 d1 A1 B1 C1 D1
a2 b2 c2 d2 A2 B2 C2 D2
a3 b3 c3 d3 A3 B3 C3 D3
F(P9) F(P10)

Mancano ancora 10byte,ma a questo punto la strada si fa in salita: infatti questa funzione è fatta in modo da "incatenare" le tre dword, in modo che siano utilizzati tutti i byte, una e una sola volta; fanno eccezione a2 e d1 che sono utilizzati due volte,ma in due cicli successivi. L'uso delle S-box è sempre mediato da uno xor: l'indice è ottenuto xorando due byte di due cicli diversi,(P-d1 e A2-d3), il risultato è xorato da altri 3 byte(a1,b1,c1 e a3,b3,c3), mentre l'effetto di "incatenamento", è ottenuto utilizzando i byte D1 per ottenere Dw2, e A2 per Dw3. Un altro fattore che aumenta la sicurezza dell'algo è il fatto che dw2 è generata mediante una moltiplicazione modulare, che non ha proprietà in comune con lo xor, usato per dw1 e dw3; il numero utilizzato come secondo fattore,non è casuale: esso è dispari, pertanto non ha fattori in comune con il modulo(2^32), che potevano essere utilizzati come indicazioni per limitare il suo range. La scelta di questo numero mi lascia però qualche perplessità: infatti moltiplicandolo per i primi 256 numeri, si ottengono 256 dword, che non hanno nel byte alto tutti i valori possibili, ma solo 242. Questo vuol dire, che mantenendo le stesse dw, e variando il byte in input da 0 a FF, posso ottenere solo 242 valori di F(x); riscriviamo F(x) utilizzando i byte anziché le dw e cerchiamo di capire perché:

A1B1C1D1=00a1b1c1^Sbox[x^d1]; D1=c1^(Sbox[x^d1]&0xff);<--abbiamo visto sopra che l'ultimo byte può assumere tutti i valori al variare delll'indice. A2B2C2D2=0x8088405*(a2b2c2d2+D1)+1=(0x8088405*a2b2c2d2)+(0x8088405*D1)+1<--Ovviamente tutto mod2^32, quindi A2 può assumere 242 valori A3B3C3D3=Sbox[A2^d3]^00a3b3c3;<--sempre solo 242 Questo fatto non significa che l'algoritmo sia insicuro, perché per come funziona è molto difficile da sfruttare,anche perché lascia scoperti solo 14 valori su 242,io ce l'ho messo per completare l'analisi.
Ritornando al nostro metodo, abbiamo detto che procedendo in questo modo non possiamo più andare avanti: ci rimangono 8byte scoperti, che moltiplicati per le 2^16 combinazioni, è come fossero 10, e un bruteforce non sarebbe troppo conveniente rispetto ad un dictionary attack: infatti 10byte equivalgono ad un seriale di 18 caratteri che utilizzi maiuscole minuscole e numeri. L'unico vantaggio è che si coprono TUTTE le possibilità con un numero finito di tentativi.
Ciao a tutti


Note Finali

Penso che per oggi possa bastare, non ho fatto il paragone con pkzip, perché non avrei potuto aggiungere nulla alla descrizione di Que, per quanto riguarda il tentativo di crackare l'algoritmo,prendetelo appunto come tentativo, utile soprattutto a me per capire meglio il suo funzionamento. Vi saluto tutti, salutando in modo particolare Zer0, autore di un tutorial sulla crittografia, apparso a puntate su OndaQuadra, che mi ha fornito dei fondamenti di crittografia che mi si sono fatti utili e consiglio a chi non ha le idee tanto chiare di darci un'abbondante letta, forse è un po' prolisso e filosofeggiante, ma ne vale la pena. Saluto anche GuZuRa, che gli ho risolto il MM2, ma forse non ci è arrivata l'e-mail.


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.