-
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
-
1
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 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/).
1 Peter Ferrie, The Ultimate Anti-Debugging Reference, pp.
83-84
2 Peter Ferrie, The Ultimate Anti-Debugging Reference, pp.
5-8
3 Peter Ferrie, The Ultimate Anti-Debugging Reference, pp.
62-63
-
2
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.
4 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.
-
3
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.
-
4
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.
-
5
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
5 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
-
6
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:
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;
8 Peter Ferrie, The Ultimate Anti-Debugging Reference, pp.
44-48
9 Peter Ferrie, The Ultimate Anti-Debugging Reference, pp.
57-62
-
7
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";
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";
10
https://twitter.com/waleedassar/status/257056312494530560 11
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 100-101
12
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 48-53
13
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp. 65-78
14
Peter Ferrie, The Ultimate Anti-Debugging Reference, pp.
41-44
-
8
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
RAX, QWORD PTR GS:[30])17
.
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 16
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
-
9
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).
-
10
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
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;
HashesTable
DWORD Hash
WORD DLLVAsIndex
WORD NLoops1
WORD NLoops2
-
11
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.
-
12
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