RingZ3r0 Presenta

 
Data: 20/03/99 Titolo: Reversing Delphi - Tutorial interattivo. Autore: T3X
 
Tools usati:
  • Micro$oft VisualC++ (5.0)
  • WDasm32
  • HexWorkshop (opzionale)
 
  Livello di difficolta': **1/2'
*=Novizio, **=Apprendista, ***=Esperto, ****=Guru
 

LE INFORMAZIONI CHE TROVATE ALL'INTERNO DI QUESTO FILE SONO PER PURO SCOPO DIDATTICO. L'AUTORE NON INCORAGGIA CHI VOLESSE UTILIZZARLO PER SCOPI ILLEGALI.


Reversing Delphi - Tutorial interattivo su CerTus (c) Acca


Bene, eccoci qua di nuovo.
Questa volta voglio trattare un programma italiano scritto in Delphi (ugh) che
nonostante sia un programma di contabilità/verifica dati pretende una cifra di
acquisto notevolmente elevata. Stiamo parlando di CerTuS, della ACCA Software.
Lo scopo del programma è verificare se un dato cantiere soddisfa o meno i
requisiti di legge, tale D.Lgs 494 per i fanatici della legislazione.

Dicevo che è scritto in Delphi, che è un ottimo sistema di sviluppo RAD, in
questo caso piu' che adatto ad un programma che prevede l'inserimento e
l'elaborazione di strutture di dati.
La versione target è la time-limited demo del programma, perfettamente
funzionante in tutte le sue funzionalità (ottimo strumento di valutazione,
continuate così :-) fino al 30mo giorno dopo l'installazione. Da quella data in
poi il programma non parte nemmeno.
Procuratevi come al solito un debugger per Win32, meglio se quello integrato nel
Visual Studio (VisualC++) poichè faro' riferimento a quello nel resto della
trattazione; serve anche  il WDasm32, questa volta è decisamente piu' utile che
in altri casi per cui tenetelo a portata di mouse. Il fixup finale come di
consueto puo' essere fatto con un hexeditor (HexWorkshop p.e.) ma per i soliti
scopi didattici non è affatto necessario.

Ah dimenticavo, vorrei lasciare in sospeso alcuni passaggi finali,
fondamentalmente alcune finezze possibili a crack finito, questo perchè so che
solitamente chi legge un tutorial per l'appunto lo LEGGE. Io vorrei che chi
ricevesse questo tutorial ESEGUISSE i passaggi che descrivo, altrimenti tanto
varrebbe raccontarvi cosa ho mangiato ieri sera e quali suoni ho emesso dopo la
digestione. Scherzi a parte, leggere un tutorial  SENZA APPLICARLO è quasi come
non leggerlo affatto, per cui pensateci prima di proseguire ed al limite
tornateci sopra quando avrete la copia del programma a portata di mano per fare
esercizio. Le cose lasciate in sospeso non sono necessarie per il crack fine a
se stesso ma sono il miglior esercizio per imparare qcosa di Reverse
Engeneering.

Orbene, carichiamo l'eseguibile (CerTuS.exe) in VC++ e diamo un Trace Into
(F11) per ritrovarci nel codicillo asm. Ecco cosa si vede :

005488DC 55                   push        ebp
005488DD 8B EC                mov         ebp,esp
005488DF 83 C4 EC             add         esp,0ECh
005488E2 B8 04 84 54 00       mov         eax,548404h
005488E7 E8 34 DF EB FF       call        00406820        <- Chiamata sospetta
005488EC A1 8C B5 54 00       mov         eax,[0054B58C]
005488F1 8B 00                mov         eax,dword ptr [eax]
005488F3 E8 80 FF EB FF       call        00408878
005488F8 84 C0                test        al,al
005488FA 74 07                je          00548903
005488FC 33 C0                xor         eax,eax
005488FE E8 11 B3 EB FF       call        00403C14
00548903 E8 7C F8 EF FF       call        00448184
.
.

Carichiamo CerTuS.exe anche nel WDasm, così intanto che debugghiamo il
disassembler fa il suo lavoro (piuttosto lunghetto in questo caso).
Siccome il programma non è ancora scaduto, conviene farsi un'idea del suo
funzionamento prima di alterare il clock di sistema per vedere cosa succede. Si
parte con uno Step Over (F10), e la prima cosa che salta all' occhio è che la
chiamata 'CALL 00406820' è decisamente pesante, e tra altre cose si preoccupa di
visualizzare la splash form di apertura. Visto che il controllo rimane in questa
prima parte di programma è conveniente continuare il trace. E' abbastanza facile
intuire che l'istruzione che lancia il programma vero e proprio è compresa nelle
:

.
.
005489BA 8B 00                mov         eax,dword ptr [eax]
005489BC E8 2B 1C EE FF       call        0042A5EC     <- programma principale
005489C1 E8 F2 5D F5 FF       call        0049E7B8     <-|
005489C6 E8 31 B1 EB FF       call        00403AFC     <--Routines di uscita
005489CB 00 FF                add         bh,bh
005489CD FF                   ???
.
.

Il programma vero e proprio viene lanciato da 'CALL 0042A5EC', il quale ci
presenta una form con i soliti disclaimer e soprattutto i giorni rimanenti.
Premendo 'Esci' (non era meglio 'Entra'? bahh!) il programma viene
effettivamente lanciato.
Per ora abbiamo già visto qcosa; intanto se il fido WDasm è stato lanciato con
sufficiente anticipo dovrebbe aver prodotto il disassemblato del programma,
pronto per le solite ispezioni. Lasciamo tutto così e premuriamoci di portare
l'orologio di sistema avanti di 2 mesi, tanto per gradire.
Questa volta eseguendo normalmente CerTus veniamo subito bloccati da una dialog
che ci informa gentilmente che il periodo di prova è terminato. Ma pensa te...
Torniamo al debugger e ricominciamo dall'inizio per vedere questa volta dove si
cela la trappola, ripartendo con F11; la porzione di codice iniziale è sempre la
stessa, ma quando eseguiamo passo passo (F10) le istruzioni scopriamo che già
all' indirizzo 005488E7 contenente la 'CALL 00406820' viene effettuato il check.
Bene, se non altro sappiamo dove andare a parare. Restartiamo il programma e
tracciamo la 'CALL 00406820' con F11, il codice è riportato qui sotto :

.
.
00406820 50                   push        eax
00406821 6A 00                push        0
00406823 E8 F8 FE FF FF       call        00406720
00406828 BA 00 91 54 00       mov         edx,549100h
0040682D 52                   push        edx
0040682E 89 05 B8 C4 54 00    mov         dword ptr ds:[54C4B8h],eax
00406834 89 42 04             mov         dword ptr [edx+4],eax
00406837 E8 98 FF FF FF       call        004067D4
0040683C 5A                   pop         edx
0040683D 58                   pop         eax
0040683E E8 AD D1 FF FF       call        004039F0    <- Qui chekka i giorni
00406843 C3                   ret
.
.

come si nota subito la routine non contiente istruzioni di salto, quindi la
chiamata al check della scadenza del termine deve essere necessariamente in una
delle chiamate, e non ci resta che scoprirlo eseguendole una ad una. Arrivati al
'POP EAX' non è ancora successo nulla, infatti basta eseguire (F10) la 'CALL
004039F0' che ecco come per magilla la nostra carissima messagebox che
nuovamente di fa notare che dobbiamo cacciare fuori i soldi :-) per cui è il
caso di tracciare ulteriormente. A questo proposito potrebbe essere il caso di
settare un breakpoint alla locazione 0040683E per evitare la tiritera di trace 
step, ad libitum.
Supponiamo di essere nuovamente in procinto di eseguire 'CALL 004039F0' ed
analizziamola kon F11; ecco la nuova porzione di codice risultante :

.
.
004039F0 89 05 88 C4 54 00    mov         dword ptr ds:[54C488h],eax
004039F6 31 C0                xor         eax,eax
004039F8 89 05 8C C4 54 00    mov         dword ptr ds:[54C48Ch],eax
004039FE 89 15 90 C4 54 00    mov         dword ptr ds:[54C490h],edx
00403A04 8B 42 04             mov         eax,dword ptr [edx+4]
00403A07 89 05 20 C0 54 00    mov         dword ptr ds:[54C020h],eax
00403A0D E8 D6 FE FF FF       call        004038E8
00403A12 C6 05 24 C0 54 00 00 mov         byte ptr ds:[54C024h],0
00403A19 E8 72 FF FF FF       call        00403990
00403A1E C3                   ret
.
.

con la stessa deduzione logica fatta in precedenza siamo portati a pensare che
il codice del check sia in una delle due call. Vi risparmio la fatica e vi dico
subito che il controllo viene effettuato eseguendo 'CALL 00403990'. Restartiamo
ancora (manca poco, coraggio!) e riportiamoci subito prima dell' esecuzione
della chamata, tracciamola con F11 ed ecco :

.
.
00403990 55                   push        ebp
00403991 8B EC                mov         ebp,esp
00403993 53                   push        ebx
00403994 56                   push        esi
00403995 57                   push        edi
00403996 A1 88 C4 54 00       mov         eax,[0054C488]
0040399B 85 C0                test        eax,eax
0040399D 74 4B                je          004039EA
0040399F 8B 30                mov         esi,dword ptr [eax]
004039A1 33 DB                xor         ebx,ebx
004039A3 8B 78 04             mov         edi,dword ptr [eax+4]
004039A6 33 D2                xor         edx,edx
.
.

mmh... Qui il gioco si fa duro, e noi che siamo dei duri dobbiamo per
definizione cominciare a giocare. Innanzitutto eliminiamo tutti i breakpoints
eventualmente impostati in precedenza e settiamone uno solo in esecuzione dell'
indirizzo 00403990. Fatto cio' cominciamo la nostra esplorazione del codice.
Eseguendo con F10 istruzione per istruzione si arriva a questa parte di codice
che è abbastanza critica :

.
.
004039B6 7E 14                jle         004039CC
004039B8 8B 04 DF             mov         eax,dword ptr [edi+ebx*8]
004039BB 43                   inc         ebx
004039BC 89 1D 8C C4 54 00    mov         dword ptr ds:[54C48Ch],ebx
004039C2 85 C0                test        eax,eax
004039C4 74 02                je          004039C8
004039C6 FF D0                call        eax
004039C8 3B F3                cmp         esi,ebx
004039CA 7F EC                jg          004039B8
004039CC 33 C0                xor         eax,eax
.
.

eseguendo il codice passo passo si entra in un loop, mentre se lasciassimo
proseguire il programma (ve lo dico io ;-) fino 004039CC con un Run To...
vedremmo immancabilmente spuntare  la solita messagebox. Che fare? Innanzitutto
bevetevi un Martini Cocktail o un Invisibile, perchè fa bene (anche se col
crackin' c'entrano sega) dopodichè concentratevi sul codice. E' evidente che una
volta entrati nel loop la sola condizione di uscita è rappresentata dalla
istruzione 'CMP ESI,EBX' seguita da un 'JG 004039B8' (per chi non lo sapesse JG
= Jump if Greater) che riporta l'esecuzione del programma indietro. Una occhiata
ai registri della CPU ci fa vedere ESI = 0000009A mentre EBX = 000000xx, con xx
un valore che dipende dalle volte che avete eseguito il ciclo. Infatti
inizialmente EBX vale zero e viene incrementato ('INC EBX') presumibilmente fino
a raggiungere e superare il valore di ESI (9A). All' indirizzo 004039C6 c'è una
strana 'CALL EAX', che potrebbe spiegarci il senso di tanto ciclare :
evidentemente EAX viene caricato (MOV EAX,DWORD PTR [EDI+EBX*8]) con il valore
di una routine presa da una tabella di procedure (o di inizializzazione di
oggetti) tra cui evidentemente anche la parte di codice che controlla se siamo
ancora in trial period. Come potrete ben intuire se avessimo semplicemente
saltato tutto cio' il programma si sarebbe miseramente piantato (eheheh ve lo
giuro ;-) dato che manca tutta questa inizializzazione.

Visto che per come la vedo io siamo qui per imparare, vorrei che quello che ho
scritto qui sopra non venisse letto frettolosamente. Non capita spessissimo di
trovare un check nidificato in questa maniera, ma proprio per questo fate
bagaglio di questa esperienza per evitare tentativi di suicidio davanti a qcosa
di simile in futuro...

Questo purtroppo non ci dice nulla della soluzione del nostro problema, se non
che saremo costretti ad eseguire le istruzioni del ciclo fino a che 'CALL EAX'
non farà comparire il messagebox, in modo da prendere nota del valore di EAX al
fine di piazzarci un nuovo breakpoint.
Io ovviamente ho fatto così, ma se volete potete impostare un breakpoint sul
valore di EBX = 18h (esadecimale!) in quanto la chiamata al check viene fatta
proprio per EBX = 00000018 con il registro EAX = 00447FD4. Questo ci da molte
utili informazioni, poiche ora sappiamo con esattezza *dove* mettere il prossimo
breakpoint, cioè appunto a 00447FD4. Quittiamo e restartiamo il programma fino
ad incontrare il breakpoint, dove troveremo :

.
.
00447FD4 55                   push        ebp
00447FD5 8B EC                mov         ebp,esp
00447FD7 33 C0                xor         eax,eax
00447FD9 55                   push        ebp
00447FDA 68 22 80 44 00       push        448022h
00447FDF 64 FF 30             push        dword ptr fs:[eax]
00447FE2 64 89 20             mov         dword ptr fs:[eax],esp
00447FE5 83 2D 7C C7 54 00 01 sub         dword ptr ds:[54C77Ch],1 <--Giorni
                                                                     rimasti
00447FEC 73 26                jae         00448014   <------- Meno di zero ?
                                                               cattivello...
00447FEE E8 AD 1D FC FF       call        00409DA0
00447FF3 D8 25 2C 80 44 00    fsub        dword ptr ds:[44802Ch]
00447FF9 DD 1D 68 C7 54 00    fstp        qword ptr ds:[54C768h]
00447FFF 9B                   wait
00448000 C7 05 78 C7 54 00 FF mov         dword ptr ds:[54C778h],0FFFFFFFFh
0044800A E8 7D FA FF FF       call        00447A8C
0044800F E8 88 F4 FF FF       call        0044749C
00448014 33 C0                xor         eax,eax   <----- Piu' di zero ? Ok
                                                                proseguiamo!
00448016 5A                   pop         edx
00448017 59                   pop         ecx
00448018 59                   pop         ecx
00448019 64 89 10             mov         dword ptr fs:[eax],edx
0044801C 68 29 80 44 00       push        448029h
00448021 C3                   ret
.
.

mi scuso se ho riportato questa valanga di codice macchina, ma siamo arrivati al
cuore della routine di check. Il succo di tutto il check sta in quel 'SUB DWORD
PTR DS:[54C77Ch],1', ovvero nel decremento di una variabile. l'istruzione
successiva 'JAE 00448014' fa in modo che  il programma salti a 00448014 se la
variabile vale zero o piu' di zero (JAE = Jump if Above or Equal). Che mi venga
un accidente se quelli non sono i giorni rimasti! Se sono rimasti 0 giorni di
prova e ne tolgo uno la variabile contiene un numero negativo e la procedura non
salta. Tutto qui. La tentazione è davvero forte e visto che comunque il
breakpoint ce lo siamo già marcato, tantovale fare la prova. Eseguiamo fino al
salto condizionato e simuliamolo settando l' EIP appunto a 00448014. Se la
nostra teoria fosse esatta il programma non dovrebbe + mostrare la messagebox ma
proseguire indisturbato.
Se proviamo a lasciar andare il programma (F5 sotto VC++) in realtà le cose non
sono così semplici. La messagebox NON viene piu' visualizzata, viene
visualizzata la splashform di presentazione giusta, ma nonostante cio' il
programma esce. Evidentemente ci sono altri checks :-(
A questo punto la disperazione potrebbe avervi colto. "Ma come, un onesto
Reverse Engeneer si da' così tanto da fare e poi il risultato è tutto qui?
BWaaaaah" (segue pianto disperato).
In realta' questo crack presenta altre insidie, ed inoltre ha diverse possibili
'soluzioni' a seconda del livello di eleganza che si è in grado di raggiungere.
Visto che in fondo questo è un tutorial e che se foste in grado di fare tutto da
soli molto ma molto probabilmente non stareste perdendo tempo con il mio
scritto, vi dico brevemente come si arriva ad una soluzione (la piu' immediata e
meno elegante, quella di cui un purista non dovrebbe mai accontentarsi).
Riprendete in mano il debugger e settate un nuovo breakpoint proprio sotto la
prima chiamata in questo punto esatto :

.
.
005488DD 8B EC                mov         ebp,esp
005488DF 83 C4 EC             add         esp,0ECh
005488E2 B8 04 84 54 00       mov         eax,548404h
005488E7 E8 34 DF EB FF       call        00406820
005488EC A1 8C B5 54 00       mov         eax,[0054B58C]  <-- Breakpoint qui
005488F1 8B 00                mov         eax,dword ptr [eax]
.
.

ora attivate entrambi i breakpoint trovati, questo appena inserito e quello che
interrompe il programma all' inizio della prima routine di check, ovvero
all'indirizzo 00447FD4. Con questi due BP impostati lanciate il programma (F5),
eseguite la routine di check fino al salto condizionato visto prima (il 'JAE
00448014'), saltate direttamente a 00448014 e date nuovamente F5. A questo punto
vi troverete proprio sotto la chiamata iniziale, nel corpo principale del
programma.
Se stepperete il programma (F10) da qui troverete che il programma stesso si
chiuderà eseguendo una malefica 'CALL 00403C14', e che la stessa chiamata è
ripetuta con lo stesso schema per ben quattro volte prima dell'inizio della
routine del programma :

.
.
005488F8 84 C0                test        al,al
005488FA 74 07                je          00548903
005488FC 33 C0                xor         eax,eax
005488FE E8 11 B3 EB FF       call        00403C14  <--<<< Call Malefica
00548903 E8 7C F8 EF FF       call        00448184
.
.
00548942 9E                   sahf
00548943 76 07                jbe         0054894C
00548945 33 C0                xor         eax,eax
00548947 E8 C8 B2 EB FF       call        00403C14  <--<<< Anche qui !
0054894C A1 78 B9 54 00       mov         eax,[0054B978]
.
.
00548969 84 C0                test        al,al
0054896B 75 07                jne         00548974
0054896D 33 C0                xor         eax,eax
0054896F E8 A0 B2 EB FF       call        00403C14  <--<<< E qui !
00548974 8B 0D 18 BA 54 00    mov         ecx,dword ptr ds:[54BA18h]
.
.
005489AB 9E                   sahf
005489AC 76 07                jbe         005489B5
005489AE 33 C0                xor         eax,eax
005489B0 E8 5F B2 EB FF       call        00403C14 <--<<< E pure qui !
005489B5 A1 78 B9 54 00       mov         eax,[0054B978]
.
.

Ok, riassumendo il lavoro fatto fino ad ora (uff...) per sperare di usare 'sto
benedetto programma oltre i 30 giorni del trial period dobbiamo modificare
*cinque* istruzioni di salto condizionato in altrettanti salti incondizionati
ovvero Jxx -> JMP.
Se volete provare con un hexeditor a cercare nel file eseguibile la sequenza di
bytes < 83 2D 7C C7 54 00 01 [73] 26 > e sostituire '73' con 'EB' (JAE con JMP
in poche parole) e poi tutte le varie sequenze dei 4 salti condizionati e
sostituite l'istruzione di salto incondizionato JMP (per inciso :
< 84 C0 [74] 07 33 C0 E8 11 > con 'EB' al posto di '74' ,
< 9E [76] 07 33 C0 E8 C8 > 'EB' al posto di '76',
< 84 C0 [75] 07 33 C0 E8 A0 > 'EB' al posto di '75' e infine
< 9E [76] 07 33 C0 E8 5F > 'EB' al posto di '76' ) e salvare il tutto, potrete
vedere che con somma gioia il programma FUNZIONA! La splash form e la prima
schermata riportano 0 giorni rimasti, ma uno potrebbe dire 'Ecchissenefrega' e
tanti saluti. Noi NO.
Questa come dicevo è una soluzione perfettamente funzionante, relativamente poco
invasiva (5 bytes modificati su un file di oltre 2Mb) ma decisamente poco
elegante. Dato che siamo/siete qui per IMPARARE, cerchiamo di andare oltre
questa prima modifica.
Prima di tutto sarebbe il caso di esaminare cosa fa la chiamata 'CALL 00403C14'
che fa terminare il programma. Vi riporto il codice :

.
.
00403C14 89 05 30 C0 54 00    mov         dword ptr ds:[54C030h],eax
00403C1A E9 DD FE FF FF       jmp         00403AFC
00403C1F C3                   ret
.
.

beh, che dire... invece che modificare le istruzioni di salto per fare in modo
che non venga eseguita questa chiamata si potrebbe + elegantemente (ed anche +
utilmente, vedi il mio tutorial sul dongle) inserire come prima istruzione un
bel 'RET', visto che comunque le istruzioni di salto condizionato viste in
precedenza puntano tutte all'istruzione successiva a 'CALL 00403C14'. Piazzando
un 'RET' all' indirizzo 00403C14 non bisogna dimenticarsi di noppare i bytes in
modo da riempire la lunghezza dell' istruzione fino a quella successiva (il NOP
è rappresentato dal valore 90h).
Anche questa soluzione è perfettamente funzionale, e ci evita di modificare i 4
salti condizionati modificando solamente una istruzione nella stessa CALL da
loro eseguita.
Puo' bastare? Per usare il programma si, certo. Per voi NO.
Ecco allora la parte del crack lasciata aperta :

                 * LA PARTE DEL CRACK LASCIATA APERTA *
                         (bel titolino vero ?:-)

Ok,si diceva che se lo scopo fosse stato un crack fine a se stesso ce ne sarebbe
stato già d' avanzo. Siccome noi siamo gente ONESTISSIMA e reversiamo i
programmi al solo scopo di imparare a debuggare i nostri (cazzo quasi quasi ci
credo pure io tanto che lo dico bene...) è d' obbligo estendere i nostri
orizzonti oltre le barriere del conosciuto.
Voglio infatti lasciare in sospeso alcune migliorie, parte delle quali le ho
sperimentate personalmente, mentre altre mi sono venute in mente quando oramai
avevo chiuso l' R-FILE (Reverse File :-) e non avevo voglia di rifare tutto
daccapo. Di conseguenza alcune modifiche sono semplici da fare, altre mediamente
difficili, altre di difficoltà sconosciuta a priori. Non vi dico niente in
anticipo altrimenti sareste tentati di provare solo quelle già sperimentate da
me. Sappiatemi dire ovviamente...

UNO) Eliminare completamente la form di apertura che ci annoia con quel suo
     assurdo tasto ESCI (bahh!!);

UNObis) Eliminare di conseguenza la stessa form all'uscita del prg.

DUE) Modificare il programma in modo da continuare a sembrare shareware con
     un tot di giorni mancanti alla scadenza anche dopo che il prg  ha cessato
     di funzionare;

TRE) Modificare il programma per fare in modo che il programma appaia    
     registrato a nome di qkuno (che ne so, RingZer0 Inc. :-) e si comporti di
     conseguenza (il codice lo prevede già, basta attivarlo);

QUATTRO) In realtà la chiamata che noi eliminiamo con il RET ('CALL 00403C14')
         ha anche altre funzioni e non viene eseguita sempre tutte e 4 le volte
         che compare : tracciatela e modificate il programma in modo che essa
         non faccia terminare il programma (ovviamente in maniera piu' elegante
         che con il RET iniziale!.

Alcune tracce : Prima di tutto non vi ho fatto aprire WDasm32 tanto per giocare,
di conseguenza utilizzatelo perchè puo' tornare molto utile per questa parte;
Il ciclo 'CALL EAX' che vi ho detto di analizzare con cura non finisce dopo il
check ma fa altre cose;
Non sempre c'è una sola variabile per tenere traccia di un dato.

Fine :-)

(C) 1999 T3X  for RingZer0
  _________        ____  ___                                                   
  ########@ a##gg  \##B ,##M                                                   
  ########@,#####@  M##C&##"  <------------------------=< [ EFnet or IRCnet ]          
     ##@   &#" Q##  `#####N                                                    
     ##@       Q#B   7###B     GReEtZ GoEs oUt To: Tutti i D4RkSiDErz per le
     ##@      g#B"    ###C  kose ke abbiamo saputo fare, ai bros M|kky, Layne,
     ##@      ###@   ,###@       Slay, Hija, EvilBUU, GOLDRAKE, a Maddevil,    
     ##@       \##a  &####a     Eko, R4|k3r, Net-Call e tutti quelli ke non         
     ##@   ##  ]##J /##@##W            ho voglia di menzionare :-)
     ##@   Q#g_d##  @##"Q##g      Ovviamente a tutti koloro ke tengono in
     ##@   `####B" Z##N `##B,         piedi il progetto RingZer0      
            `"""               
      E' assolutamente VIETATO riprodurre questo testo usando fusilli
  Barilla per comporre le lettere nonchè utilizzare le informazioni quivi
         contenute per fare colpo sulla compagna di banco strafiga
                          [tanto non funziona(mai)].