Sei sulla pagina 1di 13

Alla scoperta di lARP64Pro

Sommario
Introduzione .............................................................................................................................................................1
lARP64 Pro ................................................................................................................................................................1
Tools .........................................................................................................................................................................1
Codeflow Obfuscation ..............................................................................................................................................2
TLS Callbacks .............................................................................................................................................................3
CRC Checks & Code En/Decryption ..........................................................................................................................4
Antis ..........................................................................................................................................................................5
Anti-Attach Hooks.................................................................................................................................................5
Misc Anti-Debug ...................................................................................................................................................6
VMed Antidbg.......................................................................................................................................................8
Anti-API hooking ...................................................................................................................................................9
API Redirection ...................................................................................................................................................... 10
Simple ................................................................................................................................................................ 10
Advanced ........................................................................................................................................................... 10
Misc tricks .............................................................................................................................................................. 11
Stolen OEP ............................................................................................................................................................. 11
Saluti e ringraziamenti ........................................................................................................................................... 12

Introduzione
Ad alcuni reverser piace giocare insieme ai compagni a guardie e ladri, sfidandosi a colpi di unpackme e
crackme. in questo contesto che nel 2008 lena151, che certo non ha bisogno di presentazioni, ha deciso di
creare la sua creatura pi temuta: lARP64 Pro, un protector per programmi a 64bit rimasto imbattuto per pi di
tre anni.
Nel 2012 lena151, delusa dalla scarsa quantit di tentativi fatti per sbaragliare le protezioni implementate in
lARP64, dichiar interrotto lo sviluppo del software per sempre perch "non c' mai stato un vero bisogno di
mantenersi avanti rispetto alla community", aggiungendo inoltre di aver perso la fede nel reversing.
Spero quindi, con questo paper, di mostrarle che la community tutt'altro che morta e che lo spirito del
reversing vive anche nella nuova generazione, una generazione che lei stessa ha contribuito a fare crescere

lARP64 Pro
"Remark that lARP64Pro is not just like 'any' commercial product but it is merely a 64bit project to
prove it is indeed possible to create uncrackable software. To that extent, we have sent lARP64Pro itself as well
as a software protected by lARP64Pro to the cracking community in september of 2008, requesting to crack
either. Till this day, the cracking community have not succeeded and our conviction is that lARP64Pro will
remain uncracked at least for a significant time. The project has been made available for commercial purposes
and a trial version is freely downloadable from this site, however this does not mean it is sold to everyone or for
every purpose. Yet, contact us in case your x64 software really needs to be super-protected."
Ecco la presentazione di lARP64 cos come la si poteva leggere sul sito ufficiale. Viene promesso un engine di
protezione estremamente potente, forte del fatto che nessun dump funzionante del primo unpackme rilasciato
e nessuna copia crackata di lARP64 stesso siano mai comparsi in rete.
Ma esattamente, che protezioni sono state implementate? Con questa domanda in mente ho tracciato per due
settimane il codice del protector e in questo documento voglio condividere con voi ci che ho scoperto.

Tools

1
2
3

x64_dbg (http://x64dbg.com/);
Multiline Ultimate Assembler (plugin per x64_dbg, usato per assemblare alcuni snippet di codice ASM);
ScyllaHide (plugin per x64_dbg, usato per patchare i byte BeingDebugged1 e NtGlobalFlag2 e la
funzione NtQueryInformationProcess usata da CheckRemoteDebuggerPresent3);
Scylla (https://forum.tuts4you.com/files/file/576-scylla-imports-reconstruction/).

Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 83-84


Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 5-8
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 62-63

Codeflow Obfuscation
Aprendo l'unico unpackme a disposizione della community la prima cosa che si nota la quantit di
codice incomprensibile che ci si trova davanti. Tuttavia, grazie ad un'analisi pi approfondita e con un po' di
tracing si arriva facilmente ad una conclusione: il codice perfettamente lineare e la redirezione avviene per
mezzo di CALL e JCC che fanno saltare in avanti di 1-4 byte l'esecuzione (saltando le istruzioni spazzatura),
sufficienti a far perdere il controllo del programma al debugger la prima volta che un BP viene settato nel
punto sbagliato4.
Nell'immagine si pu vedere il codice prima della pulizia (tracciare all'interno delle prime CALL per capire di
quanto spostino in avanti l'esecuzione e assegnare ad esse una label adeguata semplificher moltissimo il
lavoro).

Ed ecco che noppando i byte spazzatura il codice assume un senso.

Altra caratteristica che concorre nel renderci il tracing difficile il modo in cui vengono effettuate le API calls:
nella quasi totalit dei casi gli indirizzi delle API ritrovati con GetProcAddress sono stati diminuiti di 1 o 2.
Quando servono vengono PUSHati e viene usato un JCC salterino per aggiustare l'indirizzo e finire sull'API
desiderata.

Da qui in avanti mi riferir a questi "redirector" come CALL o JCC salterini per il fatto che incrementano l'indirizzo
presente in [RSP] mentre saltellano qua e la nel codice del protector.

TLS Callbacks
Il primo codice eseguito dal debuggee consiste in 5 TLS Callbacks che si occupano di sistemare gli
indirizzi delle varie CALL salterine al posto giusto e di determinare la presenza di un debugger.
Ecco una breve descrizione di ognuna, nell'ordine di esecuzione:
Procedura 1:
Sistema gli indirizzi dei jump al posto giusto.

Procedura 2:
Calcola il checksum dei primi 270h bytes dall'EP.
Il checksum calcolato in questo modo risulter ovviamente sballato nel caso in cui un debugger abbia messo un
BP in quella posizione.
Il risultato viene poi usato per decryptare dei dati.

Procedura 3:
Preleva il byte BeingDebugged leggendolo dal Process Environment Block (PEB) passando dal Thread
Environment Block (TEB).

Procedura 4:
Preleva il byte BeingDebugged leggendolo dal PEB. Da notare che l'indirizzo al quale viene aggiunto RAX lo
stesso che viene messo in RDI durante la procedura 2.

Procedura 5
Preleva la NtGlobalFlag leggendola dal PEB passando dal TEB.

CRC Checks & Code En/Decryption


Il codice viene controllato abbastanza frequentemente per mezzo di chiamate a procedure che
calcolano il CRC di porzioni fisse o di indirizzo e dimensioni arbitrarie di codice. Quasi mai, per, il risultato del
calcolo del checksum viene verificato in maniera diretta. Molto pi spesso viene semplicemente messo da
parte e controllato pi tardi o, peggio, usato come chiave per decryptare altre porzioni di codice. In caso di
check o decryptazioni fallite le conseguenze possono essere varie, ma in ogni caso ci obbligheranno a riavviare
il debuggee.
Il controllo non avviene solamente sul codice dello stub: una volta decompresso il codice della codesection
viene a sua volta verificato.
Intere regioni dello stub, inoltre, sono racchiuse tra funzioni che ne decryptano il codice, lo eseguono e poi lo
cryptano nuovamente, rendendo impossibile la ricerca diretta di pattern e riferimenti a indirizzi che potrebbero
interessarci.

Antis
Anti-Attach Hooks
La prima cosa che il buon reverser fa, una volta accortosi che far girare il processo sotto debugger sar
un processo molto lungo, tentare di individuare almeno una zona nel codice vicina all'OEP attaccandosi al
processo in esecuzione e cercando pattern familiari o esaminando lo stack. lARP64 cerca di ovviare a questo
problema usando un paio di trucchi: il primo consiste nel posizionare un hook sulla funzione
DbgUiRemoteBreakin - ovvero la funzione che viene chiamata quando un debugger cerca di attaccarsi ad un
processo gi in esecuzione. Per complicarci la vita lARP64 arriva addirittura ad usare direttamente una System
Call!
Fermi tutti, ora mi serve un attimo per parlare di come funzionino le syscalls su Windows a 64bit: per passare
dal codice in ring3 (user mode) al codice in ring0 (kernel mode) viene usato il comando "SYSCALL" (opcode 0x0F
0x05). Le funzioni eseguite in kernel mode sono contenute in ntoskrnl.exe e le loro export non sono disponibili
in user mode, quindi per chiamarle viene usato un ID (si pu pensare a questo ID come all'ordinal di una
normale funzione) che va messo in EAX5; gli argomenti vengono passati secondo la fastcall calling convention
con una sola differenza: il primo argomento va messo in R10 anzich in RCX.6
Ok, torniamo al nostro hook: , all'atto pratico, un BlockInput "fatto in casa" (l'ordinal 1283h corrisponde alla
funzione NtUserSetSystemCursor che chiamata con arg1 == 1 (xor r10, r10; inc r10) blocca gli input provenienti
dalle periferiche attaccate al PC)7. Per fortuna non si tratta di qualcosa di particolarmente grave: gli input non
sono veramente bloccati, bens filtrati e la combinazione Ctrl+Alt+Canc ci permetter di riottenere il controllo
del nostro PC. Il problema ci che viene dopo: effettuata la SYSCALL il controllo passer al protector che, nel
tentativo di mostrare una MessageBox recante la criptica scritta "Unregistered Stack" (questo il
comportamento standard quando uno qualsiasi dei controlli fallisce o qualcosa va storto), crasher
inesorabilmente.

L'aspetto dell'hook una volta che stato posizionato

Non si tratta di un errore di battitura: i 4 byte "pi a sinistra" di RAX non vengono toccati, l'operazione eseguita
sempre MOV EAX, ID
6
Qui ho dovuto semplificare parecchio per non rendere il discorso troppo lungo, per maggiori informazioni sul
funzionamento delle syscalls su Windows vedere "Windows Internals Part 1 6th Edition", di Mark Russinovich, David A.
Solomon e Alex Ionescu, pp. 132 e seguenti
7
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 118-119

Se compare questo messaggio qualcosa andato terribilmente storto

Usare direttamente l'ID delle funzioni del kernel non affatto flessibile come approccio e non supportato da
Microsoft (, anzi, fortemente sconsigliato): le funzioni possono cambiare ordinal non solo tra due sistemi
operativi differenti, ma anche tra service pack differenti dello stesso S.O.
Il secondo trick che il codice ci riserva molto pi semplice: viene scritto un RETN (0xC3) al posto dell'INT3
(0xCC) che si trova all'inizio di DbgBreakPoint (altra funzione chiamata nel momento in cui un debugger si
attacca ad un processo in esecuzione) di fatto impedendo che il Debug Event venga sollevato e catturato dal
debugger, che cos perde il controllo dell'applicazione.

Misc Anti-Debug
La maggior parte di questi sono dei classici:

8
9

NtClose chiamato con un handle non valido8;

Execution timing9 usando RDTSC e una copia "in locale" di GetTickCount (la funzione viene copiata
nella sezione del protector e da quel momento in avanti l'API non viene pi usata, prediligendo
ovviamente la copia fatta). Per individuare possibili modifiche fatte sul valore restituito dalla funzione
locale una volta viene eseguito anche uno SleepEx tra due call a GetTickCount ed il risultato della
sottrazione dei valori restituiti dai GetTickCount viene confrontato con uno fisso: se questo risulta
minore di quello atteso (di poco inferiore al parametro passato a SleepEx) vuol dire che un reverser sta
manomettendo i risultati;

Controllo dei byte BeingDebugged e delle NtFlags, come gi visto nel paragrafo dedicato alle TLS
Callbacks;

CheckRemoteDebuggerPresent;

Cerca file *.nam nella stessa cartella dell'eseguibile usando FindFirstFileA: si tratta dell'estensione di
uno dei file creati da IDA quando gli si d in pasto un programma;

Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 44-48


Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 57-62

GetAsyncKeyState usato per verificare se sono stati premuti i tasti F5, F7, F8 o F9 (comunemente usati
nei debugger per restituire l'esecuzione al programma debuggato in vari modi, dallo step-in alla ripresa
dell'esecuzione del programma).
Curiosit: se viene tenuto premuto uno dei tasti tenuti sotto controllo e si fa partire un programma
lARPato questo penser di essere sotto un debugger e terminer indignato :P

DebugActiveProcess chiamato con dwProcessId == -1. Si tratta di un anti-stepping trick che ho trovato
descritto dal buon @waleedassar in un suo tweet10;

Cicla con FindWindowA attraverso una serie di nomi di finestre e classi che ritiene essere pericolosi11;
nello specifico FindWindowA viene chiamato con i seguenti parametri:

1. ClassName == "TIdaWindow"
2. ClassName == "fasm_win64_AMD64_debugger" e WindowName == "FDBG - Win64 AMD64 Debugger
written in FASM"
3. ClassName == "WinDbgFrameClass";

10
11
12
13
14

CreateFileA con FileName == "\\.\fdbg"12;

Un ciclo a base di CreateToolhelp32Snapshot/Process32First/Process32Next/lstrcmpA 13 per


controllare che in memoria non siano presenti processi che ritiene essere pericolosi, nello specifico:
idag64.exe, windbg.exe, dbgsrv.exe, dsrsvc.exe, fdbg.exe e win64_remotex64.exe;

Usando CsrGetProcessId ottiene il PID del processo csrss.exe, poi tenta di ottenerne l'accesso usando
OpenProcess con dwDesiredAccess == 1f0fffh == PROCESS_ALL_ACCESS14. Se la chiamata ha successo
vuol dire che il processo del programma lARPato fornito del "debug privilege"; questo succede in due
occasioni: stato avviato da un debugger che ha attivato il debug privilege o l'account dal quale
l'eseguibile protetto stato lanciato fa parte del gruppo Administrator e ha tale privilegio attivo;
impossibile distinguere un caso dall'altro ed il processo terminer in entrambi;

EnumWindows che punta ad una funzione che usa GetWindowTextA per controllare che non siano
aperte finestre con titoli propri di applicazioni nefaste, nello specifico: "remotex6", "IDA - " e
"WinDbg";

https://twitter.com/waleedassar/status/257056312494530560
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 100-101
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 48-53
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 65-78
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 41-44

VMed Antidbg
Forse una delle parti pi carine del lARP64: i trick anti-debugging eseguiti usando una VM. Quando
giungiamo al VM handler in un registro presente l'indirizzo di una tabella di opcodes con relativi dati
aggiuntivi (byte che specificano il registro sul quale operare, VA o valori con i quali operare). Questa VM in
particolare ha una struttura molto semplice e un po' atipica: esiste solo un grande handler che esegue compiti
specifici a seconda dello stato interno e degli opcodes da processare15; in pi, la maggior parte delle operazioni
eseguite all'interno di ciascun "pezzo" dell'handler servono per bilanciare e alterare lo stato interno della VM
(Fig. 1), mentre le poche che fanno effettivamente qualcosa di rilevante per il programma (Fig. 2) lavorano
direttamente sui registri e sullo stack del processore reale16, rendendo molto semplice intuire la funzione di
ciascun opcode virtuale e la realizzazione di un software per la devirtualizzazione.
Fig. 1: un tipico blocco di codice che ha il solo scopo di modificare lo stato
interno della VM

Fig. 2: un blocco di codice contenente l'istruzione che ci interessa (MOV


17

RAX, QWORD PTR GS:[30]) .

I trick virtualizzati sono i seguenti:

Lettura del byte BeingDebugged dal PEB passando dal TEB;

Lettura del byte BeingDebugged dal PEB;

Execution timing usando la copia "in locale" di GetTickCount;

NtSetInformationThread con THREADINFOCLASS == ThreadHideFromDebugger18;

NtQuerySystemInformation con SystemInformationClass == SystemKernelDebuggerInformation19;

CheckRemoteDebuggerPresent;

NtClose chiamato con un handle invalido.

I primi tre vengono eseguiti prima di arrivare agli stolen bytes dell'OEP e ogni volta che va risolta un'import
nascosta con l'API Redirection avanzata, mentre gli ultimi quattro vengono eseguiti una sola volta prima di
arrivare ai byte dell'OEP.

15

In implementazioni pi diffuse ogni handler una procedura a s stante


In molte altre implementazioni lo stack e i registri sono anch'essi virtualizzati: lo stack risiede in un'area di memoria
allocata per l'occasione, mentre i registri sono memorizzati come variabili
17
Il mio DeVirtualizer hostato all'indirizzo https://bitbucket.org/SmilingWolf/larp64-devirtualizer insieme alla
documentazione della maggior parte dei virtual opcodes e del formato delle VM Entries
18
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 121-122
19
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 105-106
16

Anti-API hooking
lARP64 controlla i primi n bytes di ogni API che sar usata nell'immediato futuro per controllare che non siano
presenti INT3 (0xCC) o loop infiniti (0xEB 0xFE).
La funzione procede cos: mediante l'uso di un length disassembler determina la lunghezza (in numero di bytes)
dell'istruzione all'indirizzo dato; se l'istruzione ha lunghezza == 1 allora controlla se il byte 0xCC, se l'istruzione
ha lunghezza == 2 controlla se i due bytes sono 0xEB 0xFE; in caso di risposta affermativa ci porta sulla strada
del messaggio di errore, altrimenti la lunghezza trovata viene aggiunta ad un contatore che verr confrontato
con n e aggiunta all'indirizzo da controllare, ciclando in questo modo finch il contatore < n.

Indirizzo dell'API in RDI, n in RBX, contatore in RSI

Viene anche avviato un thread che si occupa esclusivamente di controllare tutte le API in un ciclo infinito.
Curiosit: lARP64 non controlla solo le API che verranno usate di l a poco, ma anche quelle che vengono
caricate durante il setup dell'API redirection semplice e prima di saltarci sopra nell'API redirection avanzata
(vedi sotto): questo perch uno dei precedenti unpackme rilasciati da lena151 (nello specifico, quello protetto
con lARP 2.0 Ultra) stato spacchettato da Quosego usando una DLL modificata perch si bloccasse alla prima
chiamata a HeapCreate (chiamata che in quello specifico unpackme avveniva molto vicino all'OEP).

API Redirection
Simple
Passati i vari trick anti-qualunquecosa lARP64 carica le DLL che serviranno al programma per funzionare
e imposta degli hook al livello della IAT - ovvero mette in un'area di memoria allocata dallo stub del codice
offuscato che salter all'API giusta, poi ne mette l'indirizzo nella IAT del programma (dove invece dovrebbero
stare gli indirizzi delle API). Il codice offuscato relativamente difficile da tracciare a mezzo di scripts et similia,
ma stato commesso un grosso errore: prima di mettere gli indirizzi delle procedure offuscate al loro posto
vengono scritti tutti gli indirizzi giusti (riempiendo la IAT nel modo corretto) e solo DOPO questi vengono
rimpiazzati, rendendo possibile il dump della IAT, che potr poi essere ripristinata quando preferiamo (magari
quando siamo vicini all'OEP ).

Advanced
Seconda tipologia di API redirection usata: alcune CALL [addr] sono state sostituite con un CALL
VMHandler.
Vengono usati un array e due tabelle:
DLLVAs: un array contenente i VA delle DLL usate dal programma;
VMedIT
DWORD RVA
WORD
hashesIndex
WORD
isRETN

HashesTable
DWORD Hash
WORD
DLLVAsIndex
WORD
NLoops1
WORD
NLoops2

Dopo aver eseguito del codice antidebug all'interno della VM si esce da questa per mezzo di un JMP (sempre
virtualizzato) e si atterra su una procedura che:
1. legge l'indirizzo di ritorno della CALL dallo stack, ci sottrae 5 e poi l'imagebase (cos ottiene l'RVA della
CALL);
2. cerca questo indirizzo nella VMedIT (Virtualized Imports Table), che contiene gli RVA delle CALLs gestite
in questo modo;
3. trovata l'entry giusta legge hashesIndex (che contiene l'indice da usare in HashesTable);
4. viene usato l'index trovato per leggere DLLVAsIndex, che funger da indice per DLLVAs;
5. il nome della prima export tra quelle contenute nella Export Table della DLL cos trovata viene passato
alla funzione di hashing ed il risultato confrontato con quello memorizzato nella HashesTable. Se i
due hash coincidono allora l'export della DLL coincide con l'import da risolvere e si passa al punto 6,
altrimenti si calcola l'hash della seconda e si va avanti cos finch non si trova l'hash corrispondente;
6. viene letto l'indirizzo al quale si trova la funzione esportata desiderata;

10

7. se la WORD isRETN all'interno della VMedIT contiene 0x20 allora lo stack viene aggiustato per emulare
la presenza di un RETN dopo la CALL che ci ha fatti giungere all'API redirection, altrimenti questo passo
viene saltato;
8. si esegue un JMP sull'API voluta e l'esecuzione riprende normalmente.
Le WORD NLoops1 e NLoops2 vengono riempite con il numero di iterazioni del ciclo del punto 5 che sono state
necessarie per trovare l'hash giusto; questo pu quindi essere saltato la prossima volta che la stessa import
deve essere risolta velocizzando il processo.

Misc tricks
Una volta che il codice stato decompresso lARP64 ci giocherella un po' per confondere le acque:

gli 0xCC che alcuni compilatori usano per allineare le funzioni in fase di compilazione vengono sostituiti
con byte spazzatura per rendere il codice della codesection meno leggibile;
viene usato un loop per esaminare uno per uno un certo numero di byte della codesection. Ogni volta
che il byte == 0xC3 (RETN) viene eseguita una CALL REG64 (dove REG64 il registro che contiene
l'indirizzo del byte). Penso che lARP64 faccia questo allo scopo di "fregare" alcuni strumenti automatici
per il ritrovamento dell'OEP (una delle tecniche usate da questi programmi consiste infatti nel
controllare quando viene eseguito del codice contenuto nella codesection; appena questo avviene
fermano il programma ed allertano l'utente).

Numero di byte da controllare in RCX, indirizzo del byte in RDI

Stolen OEP
Nella fase di packing lARP64 ruba i byte dell'OEP mettendoli nella sua sezione e cancellandoli dalla codesection
seguendo nel mentre anche i JMP (come quelli presenti vicino all'OEP delle applicazioni compilate con MS
Visual C++). Le istruzioni rubate vengono eseguite dopo che tutti i trick antidebug sono stati usati, poi il
protector passa il controllo all'applicazione. Contrariamente a quanto succede con altri protector per il codice
dell'OEP non viene offuscato, virtualizzato o modificato in alcun modo.

11

Per riconoscere i byte dell'OEP serve un po' di occhio: si tratta di quelle istruzioni circondate da codice che
lavora con variabili contenute nella sezione del protector.

Le istruzioni evidenziate sono i nostri stolen bytes

Saluti e ringraziamenti
Whooohooo finito!!! Avete presente il detto "non si pu apprezzare un lavoro finch non lo si svolge
in prima persona"? No? Allora evidentemente me lo sono inventato sul momento :P
L'unico modo per capire quanto lavoro ci sia dietro ogni singolo paragrafo di un paper scriverne uno, quindi
vorrei salutare tutti gli autori passati, presenti e futuri di paper e tutorial perch hanno tutti il mio rispetto.
D'altro canto, questo paper non avrebbe mai visto la luce senza il contributo di alcune persone e gruppi che
quindi voglio ringraziare:
lena151: la "zietta" di un'intera generazione di reverser e l'autrice del packer trattato qui, con il quale mi hai
insegnato molto senza scrivere una riga... se fare il miglior metodo per imparare, direi che con questo hai
colto nel segno persino meglio di quanto avresti mai potuto fare con cento tutorial. Grazie
Mr. eXoDia: forse non lo sai (e se lo sai non lo ripeter comunque mai abbastanza), ma i tuoi tutorial su
Armadillo, le tue ricerche ed il tuo approccio al reversing in generale (oltre che le grandi abilit) mi hanno
sempre colpito tantissimo... se non fosse stato per te e per l'ispirazione che mi hai sempre dato (e per il
supporto fornito ogni volta che mi trovavo bloccato durante i miei primi approcci al protector della Silicon
Realms) a quest'ora non so dove sarei (ma di certo non sarei qui :P)
tonyweb: per il tempo dedicato alla revisione di questo paper anche se di tempo non ne hai. Gente, lui ha il
documento "pre-revisione" e credetemi, grazie ai suoi consigli questo che state leggendo non neanche
lontanamente paragonabile alla copia che gli avevo mandato.
Tuts4you (tutta la community): siete grandiosi tutti quanti. Fra di voi ci sono persone ad un livello che mi posso
solo sognare ed fantastico che esista ancora una community attiva e capace come questa con persone
disposte ad aiutare gli altri. Greets! :D
Tu: gi, tu che non ti sei addormentato a met e che hai continuato a leggere fin qui. Spero che questa mia
produzione ti sia piaciuta e che ti abbia lasciato qualcosa di pi di un vago senso di torpore
12