Zoom Icon

Corso UIC Avanzato 03

From UIC Archive

UIC Avanzato 03

Contents


Corso UIC Avanzato 03
Author: Quequero
Email: Que addr.gif
Website: http://quequero.org
Date: 10/10/1999 (dd/mm/yyyy)
Level: Some skills are required
Language: Italian Flag Italian.gif
Comments: Update: tutorial revisionato nel dicembre 2007.



Essay

Benvenuti a tutti in questo terzo corso della UIC, quest'oggi parleremo ampiamente di codice auto-modificante (SMC). Dunque, prima di iniziare vorrei dire che gli esempi presentati sono stati presi da uno dei migliori (IMHO) tutorial esistenti che è quello di +mammon_ tradotto magnificamente da Little John, e che troverete anche nell'Assembly Journal, la versione italiana invece la troverete sul sito di ringzer0. Allora, il codice automodificante (da ora SMC cioè: Self-Modifying code) è una delle tecniche di programmazione piu' divertenti, l'unico problema è che è "inutile", o meglio, non avrebbe avuto motivo di esistere se non fossero esistiti anche i Reverser ed i virus, infatti l'SMC viene spesso usato all'interno delle routine di protezione per rendere difficoltosa l'individuazione del seriale, oppure nei virus per camuffare il codice, in più un programma che usa l'SMC tende anche ad essere instabile, quindi vi starete chiedendo, perché dobbiamo studiarlo? Bhè, 1° perché è divertente, 2° perché la cultura è la cosa più preziosa che abbiamo e che dobbiamo ampliare in tutti i campi, 3° perché la troverete in numerosi crackme ed in qualche programma: Conseal PC Firewall ecc..... Ah QuèQuè, ma sto SMC che cavolo è? Ok arrivo al dunque, non vi scaldate :). Il presupposto dell'SMC è quello di sovrascrivere una sezione, o comunque parte di essa, in parole povere significa che se abbiamo due parti di codice in un programma come questo: Parte1:

       mov eax, seriale
       cmp eax, seriale_inserito
       jne registrazione_fallita

Parte2:

       push offset caption
       push offset messaggio
       call MessageBoxA

con opportune istruzioni che spiegheremo tra poco potremo far in modo che durante l'esecuzione il programma sovrascriva le ultime tre righe con le prime tre. Il vantaggio sta nel fatto che potremo mettere le prime tre righe di codice in una parte isolata, che non verrebbe mai usata dal prg, e le ultime tre nella parte dove dovrebbe stare la routine di comparazione del seriale. In questo modo saremo in grado di proteggere il nostro programma almeno da un attacco col disassembler, in quanto sembrerebbe solo una routine che genera un messaggio, e non una routine di comparazione, risultato diverso lo otterremo a run-time: sotto debugger vedremo improvvisamente cambiare tre righe, e non ne capiremo molto. Oltre a questo possiamo dire che il programma sarà piu' complesso da patchare: supponiamo che ci sia un jump da forzare all'indirizzo 00401234, se andassimo nel disassembler per ricavarne l'offset troveremo la prima istruzione della seconda parte del programma, in pratica succederebbe questo: Debugger: xxxx:00401234 jne registrazione_fallita Disassembler: xxxx:00401234 push offset caption In quel momento vi verrà da dire: "Ma mi stai prendendo in giro????" Bhè in effetti si, il programma si prende gioco di voi, ma un reverser scaltro non fara' altro che segnarsi una manciata di byte vicini all'istruzione che lo interessa, per poi cercarli e patcharli in un hex-editor. Può anche non essere così semplice, capitò infatti un programma nel quale l'autore aveva inserito moltissime volte quella manciata di byte che cercavo, anche questo non è un problema, bastera' cercare una stringa sempre più lunga di byte finché non troverete quella interessata... Non e' un approccio ottimo, ma almeno all'inizio funziona :))).

Primo Esempio

Ora andiamo sul pratico e vediamo com'è fatto un programma SMC, quelli che vi presento sono alcuni degli esempi fatti da +Mammon_ e tradotti da Little John, li uso perché sono molto chiari e ben spiegati: mov reg1, codice-da-sostituire mov [indir-su-cui-scrivere], reg1 in pratica spostiamo l'offset del codice che ci interessa sostituire in un registro, e poi spostiamo il contenuto di quel registro in un puntatore all'indirizzo che vogliamo cambiare.

Secondo Esempio

Per farvi meglio capire vi mostro un altro esempio di mammon_: call changer mov dx, offset [string] ; questo sarà eseguito ma ignorato

Label: mov ah, 09  ; questo non sarà mai eseguito int 21h  ; questo chiuderà il programma .... changer: mov di, offset to_write  ; carica l'indirizzo del codice da scrivere in DI mov byte ptr [label], [di] ; scrivi il codice nella locazione label ret  ; ritorna dalla chiamata to_write: mov ah, 4Ch  ; codice di fine programma (int 21, ah=4c ndt) adesso cercherò di spiegarvi cosa succede in questo programma: iniziamo col dire che parliamo di codice a 16bit e non a 32bit, per cui dobbiamo avvalerci degli interrupt per poter fare qualcosa. Supponiamo di aver disassemblato il file, come possiamo vedere abbiamo una prima chiamata, poi muoviamo in dx l'offset di una stringa e poi la stampiamo su schermo (sotto dos per stampare una stringa si muove l'offset della stessa in DX, poi si usa il servizio 09 dell'interrupt 21h, in pratica si muove in AL il numero 9 e poi si chiama l'interrupt che stamperà la stringa puntata da DX), questa routine non serve a nulla, anzi stampa solo una stringa, a questo punto potremmo chiudere tutto ed andare a giocare a Carmageddon, ma visto che siamo tipi seri apriamo il debugger e debuggiamo tutto. Ok siamo in Sice, passiamo con F10 sopra la chiamata changer e... Mmmm strano l'istruzione "mov ah, 09" si è trasformata in "mov ah, 4Ch" che sotto dos è la procedura per chiudere il programma... Altro che stampare stringhe, quella chiamata fa chiudere il programma, bene bene, riapriamo il debugger e stavolta entriamo con F8 in quella chiamata: dunque, l'offset della sezione "to_write" viene spostato in DI, quindi DI punterà alla prima riga di byte che sono in quella sezione, poi quei byte vengono spostati, guardate un po' dove??? Già nella prima riga della sezione label, in pratica al posto di "mov ah, 09" ci troviamo "mov ah, 4ch", ingegnoso eh? Tutto questo casino si poteva risparmiare scrivendo semplicemente: mov ah, 4ch int 21h quindi due istruzioni contro le otto del primo esempio, come vediamo l'SMC si paga sia in termini di velocità di esecuzione che in termini di tempo di scrittura da parte del programmatore, ed anche in livello di rincitrullimento sempre da parte del programmatore, quante belle cose si inventano per confonderci le idee??? :)

Terzo Esempio

Altro lampante esempio di SMC è l'encrittazione, in pratica sono le stesse righe che vi ho riportato all'inizio, solo che vengono usati altri operatori per alterare il contenuto dei registri, eccovi il solito esempio da mammon_: mov reg1, indir-da-sovrascrivere mov reg2, [reg1] manipola reg2 mov [reg1], reg2 Cosa succede in questa situazione? Semplice: carichiamo in un registro l'offset della sezione che ci interessa sovrascrivere, spostiamo poi i byte puntati da questo registro in un altro registro e lo manipoliamo un po' come ci pare (con shift, rotate, xor...) poi lo rispostiamo nuovamente nella locazione originale. Ecco uno snippet del programma di mammon_ tradotto da Little John (stavolta è l'ultima che la ripeto): secret_word db 06Ch, 04Dh, 082h, 0D0h magic_key db 18h, 25h, 0EBh, 0A3h

snip snip

mov al, [magic_key]  ; metti il byte1 della maschera XOR in al mov bl, [secret_word]  ; metti il byte1 della password in bl xor al, bl mov byte ptr secret_word, al  ; cambia il byte1 della password mov al, [magic_key+1]  ; metti il byte2 della maschera XOR in al mov bl, [secret_word+1]  ; metti il byte2 della password in bl xor al, bl mov byte ptr secret_word[1], al mov al, [magic_key+2] mov bl, [secret_word+2] xor al, bl mov byte ptr secret_word[2], al mov al, [magic_key+3] mov bl, [secret_word+3] xor al, bl mov byte ptr secret_word[3], al mov cx, 4 mov si, offset KbBuffer mov di, offset secret_word rep cmpsb or cx, cx jnz bad_guess il programma intero non era altro che un exe per dos che una volta avviato chiedeva una pass, a noi il resto non interessa ma la routine si, e pure tanto. Vediamo cosa succede qui: per prima cosa abbiamo una secret_word ed una magic_key, dunque la secret_word sarebbe la nostra password ed è "this", il problema è che qui non è visibile in quanto xorata, in questo modo non è visibile ad un disassembler. Poi c'è la magic_key che è la maschera che serve a decrittare la pass, infatti se aprite la calcolatrice di windows in modalità avanzata, e settate su esadecimale, e poi ancora fate 6c4d82d0 xor 1825EBA3 otterrete 74686973 che in ASCII è proprio: this. Come vedete il programma mette il primo byte della maschera in AL ed il primo byte della password in BL, li xora e pone il risultato di nuovo nel primo byte della pass che da 6c è diventato 74, poi mette il secondo byte della maschera in AL, il secondo della pass in BL, li xora e mette il risultato ancora nel secondo byte della pass che cambia da 4d in 68 e così via per gli altri, cosa stà facendo? Come vedete sta decrittando la password, infine con un semplice "rep cmpsb" controlla che la pass inserita (KbBuffer) sia uguale alla pass che è stata appena decrittata (secret_word), possiamo ottimizzare il tutto aggiungendo questo loop: loop: xor cl, cl mov al, [magic_key+cl] mov bl, [secret_word+cl] xor al, bl mov byte ptr secret_word[cl], al inc cl cmp cl, 3 jl loop in tal modo prepariamo un contatore a partire da 0 con CL che si incrementa ad ogni loop, appena xorato il 4° carattere (0, 1, 2, 3) esce dal loop, ma usando codice a 32bit potevamo fare di meglio cioè: secret_word dd 6C4D82D0h magic_key dd 1825EBA3h

mov eax, dword ptr [magic_key] mov ebx, dword ptr [secret_word] xor eax, ebx mov dword ptr secret_word, eax in pratica spostavamo l'intera doubleword (4 byte, cioè 4 caratteri) della pass in EBX e l'intera doubleword della maschera in EAX, le xoravamo e ponevamo l'intero risultato in EAX :))).

Quarto Esempio

Ora abbiamo spiegato le basi dell'SMC, adesso invece parleremo di una tecnica un poco più complicata che mammon_ illustra chiaramente in quest'altro esempio che ho editato leggermente aggiungendo il mio loop: mov cx, offset EndPatch[1]  ;inizio dell'indiriz-da-sovrascriv + 1 sub cx, offset patch_pwd  ;fine dell'indiriz-da-sovrascriv mov ax, cs  ;salva il code segment mov dx, ss  ;salva lo stack segment -- importante! mov ss, ax  ;setta lo stack segment come code segment mov bx, sp  ;salva lo stack pointer mov sp, offset EndPatch  ;inizio dell'indiriz-da-sovrascriv mov si, offset Exit-1  ;inizio dell'indiriz della maschera XOR

XorLoop: pop al  ;prendi il byte-da-patchare in AL xor al, [si]  ;XORa al con la XorMask push ax  ;scrivi il byte-to-patch in memoria dec sp  ;carica il successivo byte-da-patchare dec si  ;carica il successivo byte della maschera XOR cmp si, offset Start  ;fine della maschera XOR? jae GoLoop  ;Se No, continua mov si, offset Exit-1  ;reinizializza la maschera XOR

GoLoop: loop XorLoop  ;XORa il byte successivo mov sp, bx  ;ripristina lo stack pointer mov ss, dx  ;ripristina lo stack segment jmp patch_pwd

db 0CCh,0CCh  ;Identifcation mark: START

patch_pwd: xor cl, cl mov al, [magic_key+cl] mov bl, [secret_word+cl] xor al, bl mov byte ptr secret_word[cl], al inc cl cmp cl, 3 jl patch_pwd

mov cx, 4 mov si, offset KbBuffer mov di, offset secret_word rep cmpsb or cx, cx jnz bad_guess mov word ptr cs:PatchSpot[1], offset szString1

bad_guess: call Reply ret

Compare endp

EndPatch:

db 0CCh, 0CCh  ;Identification Mark: END Adesso state molto attenti perché il discorso non è dei più semplici, ma cercherò comunque di farvi capire: per prima cosa dovete sapere che questo programma effettua una cifratura dal basso, cioè dall'ultima alla prima istruzione, e non vice versa. Per prima cosa viene spostato in CX l'offset dell'inizio dell'indirizzo da sovrascrivere+1, e poi viene sottratto a questo stesso indirizzo l'offset della fine dell'indirizzo da sovrascrivere. Poi vengono salvati il Code Segment e lo Stack Segment e viene quindi settato come Code Segment lo Stack Segment, ed infatti giocheremo proprio con lo stack, si salva poi lo Stack Pointer e viene mosso in quest'ultimo l'offset dell'indirizzo da sovrascrivere (che è EndPatch, difatti parlavamo di cifratura dal basso), viene poi mosso nel Source Index (SI) l'offset della fine del FILE, in questo modo la fine del file sarà usata come maschera xor, è stata scelta la fine del file per camuffare il valore dello xor. Arrivati qui iniziamo la cifratura vera e propria, viene infatti prelevato con un pop da AL il byte da patchare, il byte viene poi xorato con il primo valore puntato da SI che come abbiamo detto conteneva l'offset della fine del file, successivamente viene riscritto in memoria (con un push) il byte xorato, e vengono decrisati (to decrease ;p) sia lo Stack Pointer che il Source Index, state capendo la dinamica del programma??? Normalmente avremmo incrisato (to increase) sia l'uno che l'altro, per far xorare il successivo byte con il successivo valore, invece qui viene xorato il byte precedente con il precedente valore, si va all'indietro per capirci :). Viene poi confrontato lo Stack Index (SI) con l'offset Start, cioè dell'inizio del file, e se sono uguali significa che è stato xorato tutto, a questo punto si esce dal loop e si xora l'ultimo byte, dopodichè Stack Pointer e Stack Segment vengono rimessi dov'erano, e quindi si può entrare nel loop di decrittazione della pass. Rileggete una paio di volte questo testo perché non è poi troppo semplice. Sicuramente avrete notato quegli strani Identification Mark, bhè CCh significa "Int 3", e sono stati inseriti per far funzionare un eventuale patcher che dovrebbe cercare gli identification di inizio e fine programma, dovrebbe poi contare quanti byte ci sono tra tutti e due ed eseguire lo xor per decrittare il tutto.

Esempio Finale

Adesso basta scopiazzare, vi presento un mio pupillo realizzato per funzionare a 32-bit: .386 locals jumps .model flat,STDCALL

extrn ExitProcess:Proc extrn MessageBoxA:Proc

MB_OK EQU 00000000h MB_ICONHAND EQU 00000010h MB_ICONQUESTION EQU 00000020h

.data Msg db 'Bravo sei proprio intelligente!!!',0 Cpt db 'Complimenti che bel tipo che sei!!!',0 Message db 'Ho incontrato il tuo gatto ieri sera',0 Caption db 'Fai i complimenti al gatto, è grande come una tigre!',0

.code

Start:

          call complimentati

chng1: push MB_OK OR MB_ICONQUESTION

          push offset Cpt

chng2: push offset Msg

          push 0
          Call MessageBoxA
          Call ExitProcess



complimentati proc

       mov edi, offset sovrascrivi1   
       mov eax, dword ptr[edi]
       mov dword ptr[chng1], eax
       mov edi, offset sovrascrivi2   
       mov eax, dword ptr[edi]
       mov dword ptr[chng2], eax
       ret

complimentati endp



sovrascrivi1:

   push MB_OK OR MB_ICONHAND
   push offset Caption

sovrascrivi2:

   push offset Message

End Start Troverete sia il sorgente che il file compilato e funzionante nell'allegato, decompilate l'allegato e dategli uno sguardo, bhè non sembra nient'altro che un semplice programmino che ti fa tanti bei complimenti....Mmmm avvialo e dimmi se sei convinto...Bhè non lo e' più vero? Come vedi dice altro, eppure il disassembler dice che dovrebbe dirti delle belle parole, come mai? Tutto ciò è molto semplice, e per spiegarvelo nel migliore dei modi vi ripropongo la chiamata "Complimentati": complimentati proc

       mov edi, offset sovrascrivi1   
       mov eax, dword ptr[edi]
       mov dword ptr[chng1], eax
       mov edi, offset sovrascrivi2   
       mov eax, dword ptr[edi]
       mov dword ptr[chng2], eax
       ret

complimentati endp per prima cosa muoviamo in EDI l'offset della label "sovrascrivi1", quindi ora EDI punterà ai byte di quella label cioè: push MB_OK OR MB_ICONHAND push offset Caption nel successivo passaggio muoviamo i primi 4 byte puntati da EDI in EAX (mov eax, dword ptr[edi]) e riscriviamo questi 4 byte al posto dei primi 4 che si trovano dopo la label "chng1", visto dal debugger cambiano questi byte: 6A20 push MB_OK OR MB_ICONQUESTION 6822204000 push offset Cpt in questi: 6A10 push MB_OK OR MB_ICONHAND 6878204000 push offset Caption ed il secondo passaggio della stessa chiamata fa la stessa cosa :). Adesso scaricate da questo sito il Tasm 5.0, aprite il sorgente del programma che si trova nell'allegato e secondo le istruzioni di compilazione... Compilatelo :)) fatelo partire e... Mah!!! Non funziona eh? Il compilatore non vi da errore eppure il programma termina in GPF giusto??? Scopriamo perche'.

I Flags

Quando inizio' l'era dei 32-bit qualcuno decise che le sezioni di codice potevano essere Readable, Executable ma non Writeable, perché? Bho chiedetelo a chi l'ha deciso, ad ogni modo come avrete potuto constatare con i programmi a 16-bit non esistono di questi problemi, perché il codice a 16-bit (girando in real-mode) può fare quello che vuole mentre, a 32-bit ciò non succede, ovviamente a causa del protected-mode. Si ma se è così come mai il mio programma funziona??? Semplice, ho modificato il PE del file. Per farlo scaricate il ProcDump (o un qualunque altro PE editor), clickate su "PE Editor" ed aprite il file che vi interessa, clickate sul pulsante "Sections" ed avrete davanti qualcosa come questo:

NAME Virtual Size Virtual Offs Raw Size Raw Offset Characteristics
CODE 00001000 00001000 00000200 00000600 60000020
DATA 00001000 00002000 00000200 00000800 C0000040
.idata 00001000 00003000 00000200 00000A00 C0000040
.reloc 00001000 00004000 00000200 00000C00 50000040

come dicevamo non abbiamo sulle sezioni il permesso di scrittura, e dato che il presupposto dell' SMC è proprio quello di sovrascrivere una sezione allora dovremo intervenire sui flag che ci danno i vari "permessi", come ci orientiamo? Semplice, sappiamo che la sezione che dobbiamo modificare è quella che contiene il codice scritto da noi, questa sezione si chiama CODE se l'eseguibile è stato compilato con compilatore Borland e .text se è stato usato un altro compilatore (volendo puo' anche avere altri nomi...), quindi come vediamo la sezione che ci interessa è la prima, a questo punto dobbiamo identificare il campo dei flag che ProcDump identifica come: Characteristics, quel numero: 60000020 significa qualcosa, più precisamente vuol dire che quella sezione è Readable, Executable e contiene codice, come faccio a saperlo? Quei numeri significano qualcosa e quel "qualcosa" è scritto nella tabella in basso.

Caratteristica Valore
IMAGE_SCN_CNT_CODE EQU 000000020h
IMAGE_SCN_CNT_INITIALIZED_DATA EQU 000000040h
IMAGE_SCN_CNT_UNINITIALIZED_DATA EQU 000000080h
IMAGE_SCN_MEM_DISCARDABLE EQU 002000000h
IMAGE_SCN_MEM_SHARED EQU 010000000h
IMAGE_SCN_MEM_EXECUTE EQU 020000000h
IMAGE_SCN_MEM_READ EQU 040000000h
IMAGE_SCN_MEM_WRITE EQU 080000000h

Come vedete abbiamo dei valori esadecimali, e sono proprio questi che opportunamente combinati danno le caratteristiche delle varie sezioni, ma quello che ci interessa è aggiungere il flag di readable alla sezione CODE, come si fa? Con una semplice addizione (in verita' si dovrebbe fare un OR):

60000020+80000000 = E0000020

con questo cambiamento la nostra sezione è diventata: Readable+Executable+Writeable ed è quindi pronta per funzionare, in ProcDump si deve clickare sulla sezione prima col sinistro e poi col destro, clickare sulla voce "Edit section" del menu di popup, e nel campo "Section Characteristics" sostituite 60000020 con E0000020. Spieghiamo cosa significano tutti quei valori, tanto un po' di "sapere" in più non vi fa certo male:

IMAGE_SCN_CNT_CODE: Questa sezione contiene codice, in genere questa opzione è settata insieme al flag "executable".

IMAGE_SCN_CNT_INITIALIZED_DATA: Questa sezione contiene dati inizializzati, in genere tutte le sezioni, tranne quella eseguibile e la .bss, hanno questo flag settato.

IMAGE_SCN_CNT_UNINITIALIZED_DATA: Questa sezione contiene dati non inizializzati come ad esempio la sezione .bss.

IMAGE_SCN_MEM_DISCARDABLE: Questa sezione può essere scartata dal momento che non è necessaria al processo una volta che è stato caricato, in genere la sezione .reloc ha questo flag settato.

IMAGE_SCN_MEM_SHARED: Questa sezione è condivisa, cioè quando è usata con una DLL tutti i dati in questa sezione sono condivisi tra tutti i processi usati dalla DLL, in pratica una sezione shared dice al gestore della memoria di settare un page mapping per la sezione, dimodo che tutti i processi che utilizzino la DLL usino anche la stessa pagine fisica di memoria.

IMAGE_SCN_MEM_EXECUTE: Questa sezione è eseguibile, questo flag in genere è settato insieme alla flag "Code" (000000020).

IMAGE_SCN_MEM_READ: Questa sezione è leggibile.

IMAGE_SCN_MEM_WRITE: Questa sezione è scrivibile, in genere questo flag è settato solo sulle sezioni .data e .bss, ma noi la usiamo anche su altre :)).

Ci sono molti altri flag, ma i principali sono questi. Adesso facciamo un esempio per capire quali flag sono stati settati per la sezione DATA:

C0000040 = 80000000+40000000+000000040 cioè:

IMAGE_SCN_MEM_WRITE+IMAGE_SCN_MEM_READ+IMAGE_SCN_CNT_INITIALIZED_DATA, semplice no??? In fondo sono solo addizioni (ma se usate un OR e' meglio), fate quindi quel cambiamento al vostro file e vedrete che andrà bene :))).

Goals

  • Trovate il codice corrispondente al vostro nick
  • Crackate il programma in modo da ottenere codici sempre validi e spiegatene MOLTO dettagliatamente in metodo utilizzato
  • Spiegate come meglio potete l'algoritmo di generazione del serial
  • facoltativo Scrivete un key-maker per il programma


Greetings

Thnx to: +Mammon_ per il suo grande tutorial, Little-John per la sua traduzione e Neural_Noise per avermi aiutato all'inizio a crackare il PE :)))

Fuck to: Tripod che mi ha cancellato tutti e tre gli account!!!

Quequero


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.