Zoom Icon

Programmiamo in IL

From UIC Archive

Programmiamo in IL

Contents


Programmiamo in IL
Author: Quake2
Email: [email protected]
Website: http://pmode.cjb.net
Date: 27/07/2004 (dd/mm/yyyy)
Level: Slightly hard
Language: Italian Flag Italian.gif
Comments:

Programmazione in IL (come rendere semplici le cose difficili :))



Introduzione

Beh penso che dal titolo si capisce che tratteremo della programmazione in IL (Intermediate Language), ma in modo molto inusuale :)

Ristrutturato il 11/04/2007 da ValerioSoft


Essay

In questo tutorial parleremo di come scrivere un semplice programmino direttamente in IL, e nel contempo sarà anche un tutorial sul C# (o meglio, su .NET), dato che parleremo in modo esteso del namespace System.Reflection, e mostrerò alcune tecniche carine per generare codice a runtime in modo semplicissimo (più semplice di come state immaginando ora! :)). I requisiti necessari per questo tutorial sono:
br />

  • Conoscenza del C#
  • Conoscenza di .NET
  • Un framework compatibile con .NET, quindi o Mono o .NET Framework, il primo lo trovate su http://www.go-mono.com, per il secondo, uhm... indovinate? Sul sito della Microsoft!! :) Vabbe so che siete pigri, ecco il link per il .NET Framework SDK (è GROSSO): .NET Framework SDK, in alternativa potete scaricare il .NET Redistributable Package (un po' più piccolo) da qua: .NET Redistributable Package, però vi avverto che non sono sicuro che ci sia il compilatore, teoricamente dovrebbe esserci, altrimenti le classi che usano System.CodeDom.Compiler non funzionerebbero, comunque in ogni caso, c'è sempre Mono che è abbastanza piccolo.

NOTA: Platform.NET non è supportato, non ho potuto provare il codice su questo ambiente .NET, quindi non vi assicuro che qua funziona, poi c'è Mono che (secondo me) è molto meglio di Platform.NET, quindi usate quello!!! :)

NOTONA (ovvero più importante della NOTA): Ovviamente presuppongo che state usando .NET 1.1, il codice non l'ho provato sulla versione 1.0 ne voglio farlo :)


Tools

Beh ve l'ho detto prima, mi sembrava più una cosa da introduzione, vabbe dai, qua vi do il link diretto pure per Mono:


URL o FTP del programma

Oh, il programma ce lo facciamo noi!


Notizie sul programma

Questo programma usa una super protezione con codice polimorfico decriptato da 40-1 (come le frustate) thread in parallelo che funziona solo su un'architettura smp con 512 processori R12000, gestiti dalla marmotta che confeziona la cioccolata. (Vabbe avete capito che voglio solo riempire un po' di spazio :))


Essay

Beh, iniziamo a fare su serio va. In questo tutorial vedremo come sfruttare i potenti mezzi messi a disposizione dal namespace System.Reflection, per generare un eseguibile (si avete capito bene, un eseguibile intero e funzionante) a runtime, ma senza impazzire con indirizzi,rilocazione, gestione della memoria, ecc... Vi starete chiedendo come sia possibile una cosa del genere, beh è semplice, prima di tutto chiudete ilasm.exe che sicuramente avete aperto :), perché NOI saremo sia l'assemblatore sia il linker :).

Ho deciso di usare il C# come linguaggio, primo perché lo conosco meglio, secondo perché con VB.NET mi impiccio :), comunque il codice dovrebbe essere facilmente convertibile in VB.NET.

Per prima cosa, vediamo un po' di classi importanti dei namespace System.Reflection e System.Reflection.Emit:

  • System.Reflection.Assembly: Permette di gestire un assembly, ovvero un pezzo di codice completamente funzionante che può interagire con altrocodice del CLR (Common Language Runtime)
  • System.Reflection.AssemblyName: Descrive il nome di un assembly in modo univoco, può essere sia un simple name che uno strong name, di cui comunque parlerò dopo.
  • System.Reflection.Emit.AssemblyBuilder: Serve per definire e rappresentare un assembly dinamico
  • System.Reflection.Emit.ConstructorBuilder: Definisce il costruttore di una classe dinamica
  • System.Reflection.Emit.EnumBuilder: Definisce e descrive una enum
  • System.Reflection.Emit.EventBuilder: Permette di definire degli eventi per una classe
  • System.Reflection.Emit.FieldBuilder: Permette di definire i campi di un oggetto, questa classe non può essere istanziata
  • System.Reflection.Emit.ILGenerator: Il cuore :) Permette di generare codice IL
  • System.Reflection.Emit.LocalBuilder: Permette di definire una variabile locale in un metodo o in un costruttore
  • System.Reflection.Emit.MethodBuilder: Permette di definire un metodo in una classe
  • System.Reflection.Emit.ModuleBuilder: Permette di definire un modulo che conterrà l'assembly
  • System.Reflection.Emit.OpCodes: Contiene tutte le istruzioni del linguaggio IL
  • System.Reflection.Emit.ParameterBuilder: Permette di creare o associare dei parametri di un metodo
  • System.Reflection.Emit.PropertyBuilder: Permette di definire le proprietà di una classe
  • System.Reflection.Emit.TypeBuilder: Il cervello :) Permette di costruire classi a runtime


Bene, queste sono alcune delle classi più importanti, alcune le useremo altre no, però è importante conoscerle. Per prima cosa, dobbiamo pensare al programma che vogliamo creare in IL, e se vogliamo fare un eseguibile o una dll, che in .NET sono la stessa identica cosa, l'unica differenza è che un eseguibile ha un entry point, mentre una dll no. Direi che possiamo iniziare con una dll e un semplice programma che ha una funzione che accetta due interi e li somma, stampando nella console la loro somma e ritornandola come valore di ritorno (useremo degli int per semplificare le cose), il cui codice C# è il seguente:

using System; namespace OurMath {

 public class MathOp
 {
   public MathOp()
   {
     Console.WriteLine("Costruttore");
   }
   public int Sum(int a, int b)
   {
     int s = a+b;
     Console.WriteLine("La somma dei numeri è {0}", s);
     return s;
   }
 }

}

Come vedete il codice è semplicissimo, ma stiamo appunto iniziando :) Anche con cose più complicate, il concetto è lo stesso, cambia solo il tempo necessario a scriverlo :). Ok, adesso è tempo di programmare in IL :). Se state usando il Visual Studio.NET create un nuovo progetto Visual C#, e scegliete Console Application, se state usando Mono, prendete un editor di testo e create un nuovo file .cs :), chiamate il progetto, il namespace e la classe come vi pare, e poi nel Main mettete il seguente codice (il codice presenta gia dei commenti, ma dopo averlo riportato darò più spiegazioni):

public void Generate() {

   //crea un nome per l'assembly, abbiamo scelto di usare un simple name invece di uno strong name
   //per motivi di semplicità, la differenza tra single name e strong name è che uno strong name
   //può contenere una chiave pubblica e una firma digitale per motivi di sicurezza, in più uno strong
   //name ha un nome unico garantito (cosa che non accade con i simple name)
   AssemblyName an = new AssemblyName();
   //la versione di questo assembly, mettetene una a caso, io ho scelto 1.0.0.0 :)
   an.Version = new Version(1,0,0,0);
   //il nome dell'assembly, con molta fantasia OurMath
   an.Name = "OurMath";
   //creiamo una classe AssemblyBuilder, che ci permetterà di costruire il nostro assembly
   //tra i parametri specifichiamo l'AssemblyName che vogliamo suare, e il tipo di accesso
   //mettendo AssemblyBuilderAccess.Save possiamo salvare l'assembly su disco una volta creato
   //gli altri tipi di accesso sono AssemblyBuilderAccess.Run che permette di usare l'assembly ma senza
   //salvarlo su disco e AssemblyBuilderAccess.RunAndSave che permette di salvarlo su disco e di
   //usarlo subito
   AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Save);
   //creiamo il ModuleBuilder, che ci permetterà di aggiungere tipi al nostro assembly, il primo
   // parametro è il nome del modulo (deve essere minore di 260 caratteri), il secondo è il nome del
   //file che vogliamo creare
   ModuleBuilder mb = ab.DefineDynamicModule("OurMath", "OurMath.dll");
   //ed eccoci al TypeBuilder :), quello che facciamo è creare una nuova classe,
   //usando il metodo DefineType della classe ModuleBuilder
   //il primo parametro è il nome del tiponel formato namespace.tipo, il secondo sono le proprietà del
   //tipo, in questo caso vogliamo una classe (TypeAttributes.Class) publica (TypeAttributes.Public)
   TypeBuilder tb = mb.DefineType("OurMath.MathOp", TypeAttributes.Class | TypeAttributes.Public);
  
   //questo è un passo obbligatorio, ovvero dobbiamo definire il costruttore della classe
   //il primo parametro indica l'accesso, scegliamo public per poter istanziare la classe, il secondo 
   //parametro indica la calling convention che vogliamo usare (standard in questo caso), e il 
   //terzo indica gli argomenti del costruttore in questo caso siccome non vogliamo niente, 
   //mettiamo new Type[0], ovvero un costruttore senza argomenti se avremmo voluto un costruttore
   //con 2 argomenti, uno di tipo string e uno di tipo int, avremmo fatto:
   //new Type[] { typeof(string), typeof(int) }
   ConstructorBuilder cb = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard,

new Type[0]);

   //ogni metodo (il costruttore è un caso particolare di un metodo) ha un ILGenerator associato ad esso
   //che cos'è un ILGenerator? Semplice, è una classe che permette di generare il codice direttamente in 
   //IL, senza impazzire con indirizzi o altro, ma pensa a tutto la classe, noi dobbiamo solo dirgli
   //cosa generare

ILGenerator ilgen = cb.GetILGenerator();

   //il codice del costruttore come prima cosa DEVE chiamare il costruttore della classe base, ovvero 
   //Object, in C# tutte le classi derivano implicitamente da Object, questo è un passo obbligatorio
   //per chiamare il costruttore di Object, dobbiamo passargli un riferimento a chi lo sta chiamando
   //dovete sapere che in C#, come in C++, il primo argomento di qualsiasi funzione è sempre 
   //implicitamente un riferimento all'oggetto che sta chiamando quella funzione (in altre parole 
   //è il puntatore this, che in C# è un riferimento)
   //l'istruzione IL per pushare un argomento nello stack è Ldarg_x, dove x è l'argomento da pushare,
   //siccome vogliamo pushare this, facciamo Ldarg_0, usando il metodo Emit di ILGenerator,
   //e passandogli come argomento l'opcode da generare, OpCodes è una classe che contiene tutti
   //gli opcode del linguaggio IL
   ilgen.Emit(OpCodes.Ldarg_0);
   //ora siccome vogliamo chiamare il costruttore, generiamo l'opcode Call, seguito dalla funzione 
   //che vogliamo chiamare per prendere la funzione da chiamare, tramite il metodo GetConstructor
   //della classe System.Type(che viene ritornata dall'operatore typeof(oggetto)),
   //prendiamo un riferimento ad un oggetto ConstructorInfo, che è appunto ciò che ritorna 
   //GetConstructor il costruttore che scegliamo è quello senza argomenti, che tra l'altro è l'unico 
   //presente nella classe Object
   ilgen.Emit(OpCodes.Call, typeof(Object).GetConstructor(new Type[0]));
   //qui usiamo un trucchetto :), non generiamo codice IL a mano, ma tramite ILGenerator ci facciamo
   //generare il codice necessario ad eseguire una call la System.Console.WriteLine,
   //se non dobbiamo passare argomenti alla funzione, questo è il modo più semplice per farlo, 
   //più avanti comunque vedremo come passare argomenti a System.Console.WriteLine
   ilgen.EmitWriteLine("Costruttore");
   //il lavoro del costruttore è finito, quindi generiamo l'opcode Ret, che esce dalla funzione e torna
   //a chi l'ha chiamata da notare che se è presente qualcosa sullo stack, Ret prende quel valore e lo 
   //ritorna alla funzione chiamante
   ilgen.Emit(OpCodes.Ret);
   //ora creiamo un metodo, come potete vedere, invece di usare ConstructorBuilder, usiamo MethodBuilder
   //,questo perché ora vogliamo creare un metodo e non un costruttore, il primo parametro è il nome del 
   //terzo rappresenta la metodo, in questo caso Sum, il secondo il tipo di accesso, anche in questo 
   //caso Public, il calling convention da utiilzzare, sempre standard, il quarto e il quinto 
   //definiscono rispettivamente il valore di ritorno e gli argomenti del metodo, siccome 
   //avevamo definito Sum come: int Sum(int a, int b), il valore di ritorno è un int (typeof(int)) 
   //e gli argomenti sono due int, siccome la funzione richiede un array di Type,
   //lo generiamo con new Type[] {typeof(int), typeof(int)}
   MethodBuilder sumMethod = tb.DefineMethod("Sum", MethodAttributes.Public, CallingConventions.Standard 
                                               ,typeof(int),new Type[] {typeof(int), typeof(int)});
   //prendiamo la classe ILGenerator di questo metodo
   ilgen = sumMethod.GetILGenerator();
   //tramite il metodo DeclareLocal di ILGenerator, definiamo una variabile locale, ovvero una variabile
   //che verrà allocata nello stack frame di questa funzione, il tipo che abbiamo scelto è int,
   //infatti specifichiamo typeof(int)
   ilgen.DeclareLocal(typeof(int));
   //da come avevamo definito Sum, faceva la somma dei suoi argomenti (int a ed int b),
   //quindi quello che facciamo con queste istruzioni è generare l'opcode per Ldarg_1 e Ldarg_2,
   //che rispettivamente pushano il primo ed il secondo argomento nello stack
   //come penso avrete capito, si parte da 1 perché Ldarg_0 è per il riferimento this, che stavolta non 
   //ci serve
   ilgen.Emit(OpCodes.Ldarg_1);
   ilgen.Emit(OpCodes.Ldarg_2);
   //tramite l'istruzione Add aggiungiamo i due valori presenti sullo stack, quello che fa l'istruzione
   //Add è prendere i due valori nello stack, fare un pop di entrambi, sommarli, e poi pushare 
   //nello stack il risultato della loro somma
   //ci sono due cose da notare, la prima che Add non fa controlli di overflow (Add_Ovf si), la 
   //seconda è che Add fa la somma di due interi con segno, per fare la somma di due interi senza segno 
   //bisogna usare Add_Ovf_Un
   ilgen.Emit(OpCodes.Add);
   //come ho detto, Add pusha il risultato nello stack, quindi con Stloc_0, prendiamo il risultato 
   //dallo stack e lo mettiamo nella prima variabile locale definita
   ilgen.Emit(OpCodes.Stloc_0);
   //la stringa che stamperà il risultato della somma
   String resStr = "La somma dei numeri è {0}";
   //i parametri che passeremo alla funzione WriteLine, qui bisogna stare attenti,
   //infatti come sapete WriteLine accetta un numero di parametri variabile ed è qui che dobbiamo
   //scegliere quanti parametri passare, in questo caso ne passiamo uno string, e uno object
   //perché object e non int? Semplice, WriteLine non può sapere cosa gli passiamo, dato che nella 
   //stringa noi diciamo solo dove mettere l'argomento tramite {0}, {1}, ecc..., ma non gli diciamo 
   //cosa mettere, al contrario di ciò che avviene con la printf del C ad esempio dove gli diciamo 
   //cosa mettere, quindi la WriteLine definisce la lista di argomenti come un array di tipo object 
   //(se prende più di 2 parametri, il primo di tipo string)
   //o come un singolo object (se prende solo due parametri, di cui il primo sempre di tipo string, 
   //come in questo caso) prendendo un array (o un singolo elemento) di tipo object, siccome tutte 
   //le classi del C# derivano da object, possiamo passare alla WriteLine qualsiasi classe
   Type[] wlParams = new Type[] {typeof(string), typeof(object)};
   //prendiamo le informazioni necessarie sul metodo WriteLine di System.Console, sempre tramite 
   //l'operatore typeof passando wlParams come secondo argomento, diciam alla funzione che 
   //stiamo scegliendo l'overload WriteLine(string format, object arg0)
   MethodInfo wlMInfo = typeof(Console).GetMethod("WriteLine", wlParams);
   //iniziamo a passare gli argomenti sullo stack, siccome il primo argomento di WriteLine è una stringa
   //,pushamo sullo stack la stringa resStr, tramite l'istruzione Ldstr
   ilgen.Emit(OpCodes.Ldstr, resStr);
   //pushamo nello stack la prima variabile locale, risultato della somma degli argomenti del metodo
   ilgen.Emit(OpCodes.Ldloc_0);
   //facciamo un boxing della variabile locale, questo perché nello stack noi non abbiamo un riferimento
   //, ma un valore a WriteLine dobbiamo passare un riferimento, quindi tramite il boxing convertiamo 
   //una variabile che contiene un valore, in un riferimento, il secondo argomento dice il tipo 
   //della variabile che vogliamo convertire l'istruzione Box prende il valore corrente nello stack, 
   //lo tira fuori, lo converte, e pusha nello stack il nuovo valore così ora al posto della 
   //variabile locale, avremo un oggetto che rappresenta un riferimento a questa variabile
   ilgen.Emit(OpCodes.Box, typeof(int));
   //tramite il metodo EmitCall, diciamo a ILGenerator di generare l'opcode necessario per chiamare 
   //la nostra funzione il terzo parametro indica gli argomenti opzionali da passare, per una funzione 
   //vararg, ma in questo caso non ce ne sono quindi specifichiamo null
   ilgen.EmitCall(OpCodes.Call, wlMInfo, null);
   //pushamo il risultato della somma nello stack (la variabile locale NON è stata modificata 
   //dall'operazione di boxing)
   ilgen.Emit(OpCodes.Ldloc_0);
   //e ritorniamo alla funzione chiamante, come ho detto qualche riga più sopra,
   //se Ret trova un valore nello stack lo prende e lo ritorna alla funzione chiamante
   ilgen.Emit(OpCodes.Ret);
   //ci siamo!!! CreateType() crea la classe che abbiamo generato in modo definitivo
   tb.CreateType();
   //salviamo il file su disco, dandogli il nome OurMath.dll
   ab.Save("OurMath.dll");

}


Ok, ora mette quella funzione nel file C# che avete creato, e chiamatela dal Main, dopo aver eseguito il tutto (se usate Mono, fate: mono file.exe), otterrete nella stessa cartella del file .exe un file .dll, che sarà appunto il nostro modulo. Ora per testare questo modulo potete (se usate il visual studio), fare Project->Add Reference, scegliere la dll e poi utilizzarla come se fosse una classe normale del namespace. Però non c'è niente di dinamico in tutto ciò, quindi quello che faremo è caricare l'assembly a runtime :)
Il codice necessario è il seguente:

public void LoadAndExec() {

   //carichiamo l'assembly con il metodo LoadFrom, che permette di specificare il file dal quale 
   //caricarlo
   Assembly a = Assembly.LoadFrom("OurMath.dll");
   //prendiamo un riferimento alla classe MathOp, che sarebbe la classe che vogliamo istanziare, come 
   //sempre bisogna specificare il namespace oltre al nome della classe
   Type MathOp = a.GetType("OurMath.MathOp");
   //a questo punto prendiamo il metodo che vogliamo creare, usando la classe MethodInfo, come al solito 
   //usiamo il metodo GetMethod di type per prendere il metodo desiderato
   MethodInfo Sum = MathOp.GetMethod("Sum");
   //istanziamo la classe MathOp tramite CreateInstance, questa funzione ritorna un Object, che 
   //dobbiamo usare quando chiamiamo il metodo
   Object obj = Activator.CreateInstance(MathOp);
     //e infatti tramite il metodo Invoke di MethodInfo chiamiamo il metodo Sum, notate che come primo 
   //oggetto viene passata l'istanza della quale vogliamo che venga chiamato il metodo, bisogna fare un 
   //cast ad int, perché Invoke ritorna un Object che dobbiamo unboxare per poterlo usare, anche se 
   //dovendolo passare a WriteLine, avremmo potuto passarlo così com'è, considerando che
   //poi bisogna fare il box di nuovo, ansi, vi consiglio di evitare di far fare troppi box-unbox 
   //ai vostri programmi, quando potete evitatelo :)
   int res = (int)Sum.Invoke(obj, new object[] {4, 5});
   //stampiamo il risultato ottenuto dal metodo Sum di MathOp
   Console.WriteLine("{0}", res);

}


Come potete vedere, il codice è di una semplicità assurda, ma vi starete chiedendo a cosa può servire creare eseguibili a runtime, beh la risposta è semplice, il namespace System.Reflection.Emit è stato creato apposta per permettere agli sviluppatori di creare il loro compilatore per .NET, rendendo questo ambiente di una flessibilità estrema, praticamente è possibile fare qualsiasi cosa.

Ok questo tutorial è praticamente finito (so che è stato corto, ma forse ne scriverò un altro più lungo e con cose ancora più carine, sempre riguardanti i namespace System.Reflection e System.Reflection.Emit), come ultima cosa però, voglio discutere un po' il modo in cui vengono trattati le variabili locali e gli argomenti delle funzioni in IL. Dunque, dando un occhiata alla classe System.Reflection.Emit.OpCodes, noteremo i seguenti opcode per gestire le variabili locali (tra parentesi c'è l'opcode effettivamente generato da ILGenerator):

Ldloc (ldloc) Ldloca (ldloca) Ldloca_S (ldloca.s) Ldloc_0 (ldloc.0) Ldloc_1 (ldloc.1) Ldloc_2 (ldloc.2) Ldloc_3 (ldloc.3) Ldloc_S (ldloc.s)

Perché tanti opcode per fare la stessa cosa? Beh per rendere le cose più semplici al programma, ad esempio perché usare lo stesso opcode se tipo nel programma ci sono solo 2 variabili? Ricordiamoci che stiamo su una virtual machine, su un processore codificare un opcode di 3 byte o 10 byte non cambia niente, su una vm può infierire, per questo esistono diversi tipi di opcode a seconda di quello che bisogna fare, ad esempio Ldloc_0, Ldloc_1, Ldloc_2 ed Ldloc_3 servono per caricare sullo stack le primo 4 variabili locali, e se ne abbiamo più di 4? Allora c'è Ldloca_S, che permette di caricare fino a 255 variabili, usando un intero a 8bit come indice, se ce ne sono più di 255? Beh allora si usa Ldloc, che permette un massimo di 65535 (da 0 a 65534) variabili locali, ma ve ne serviranno mai così tante? Se come ad ogni essere umano al massimo ve ne serviranno 10-15, verrà usato l'opcode Ldloc_S (ldloc.s), che è più efficiente da decodificare, ovviamente Ldloc_0, Ldloc_1, Ldloc_2 ed Ldloc_3 sono ancora più efficienti, quindi USATE quelli quando dovete caricare le prime variabili, ricordatevi sempre che state su una vm, per quanto ci sia il JIT o altro a velocizzare le cose, è una vm, non complicategli il lavoro, gia di per se complicato :). Gli opcode Ldloca e Ldloca_S fanno la stessa cosa ma caricano l'indirizzo della variabile locale.
Il discorso è analogo per gli opcode relativi agli argomenti di una funzione, infatti anche la abbiamo Ldarg_0 Ldarg_1, Ldarg_2, Ldarg_3 ecc... .
Ovviamente il discorso vale pure per i rispettivi Stloc, Stloc_0, Stloc_1, ecc... (no, non vale per Starg_0, Starg_1, ecc... perché non esistono :), ma abbiamo solo Starg ed Starg_S, una cosa importante, in caso di una funzione vararg, Starg_S può essere usata solo per gli argomenti "fissi", non per la parte variabile).

Beh, per ora è tutto, alla prossima!! (che spero ci sarà :))



Note finali

Beh spero che questo tutorial vi sia piaciuto, forse un po' troppo corto, ma odio i tutorial troppo lunghi (vi starete chiedendo perché quello su safedisc2 è enorme, ma sono due argomenti diversi, qua si parla di programmazione :)), come come ho detto durante il tutorial, forse ne scriverò qualcun altro sempre su IL, System.Reflection e System.Reflection.Emit .
Comunque, spero che questo tutorial vi abbia fatto comprendere l'ENORME potenza di .NET, se non l'avete compreso, beh rileggetevi il tutorial 4-5 volte :), e poi trovate un ambiente in cui potete fare qualcosa del genere in modo così semplice :) (sto scherzando, non è una sfida eh :))

Passiamo ai ringraziamenti e saluti di rito :)
Ringraziamenti:
Microsoft: per aver creato .NET, un capolavoro assoluto, forse la cosa migliore che hanno fatto negli ultimi anni :) (molti diranno l'unica cosa buona che ha fatto negli ultimi anni :))

Mono: per aver permesso la diffusione di .NET su piattaforme diverse da Windows. Winamp: per aver suonato ininterrottamente dalla mattina alla sera le canzoni che ascolto senza lamentarsi

I gruppi che ascolto: per creare la migliore musica esistente nell'universo, quindi un ringraziamento a: Iron Maiden, Iced Earth, Manowar, Virgin Steele, Helloween, Opeth, My Dying Bride, Agalloch, Dio, Dark Tranquillity, Running Wild, Atheist, Cynic, Death e basta sennò que poi dice che facevo prima ad incollare la track list del winamp :) Be Inc. : per aver creato il miglior sistema operativo (BeOS) della storia, peccato sia finita male :(

Haiku (OpenBeOS): per lo sforzo che stanno facendo per ricreare BeOS, avanti così!!!! :)

Il mio monitor: perché dopo 10 anni onorevoli di servizio ancora riesce a reggere una risoluzione di 1280x1024x32 a 60hz senza esplodere (beh più o meno), e i miei occhi che ancora lo sopportano :)

Edgar Allan Poe: beh, il mio preferito :)

H.P. Lovecraft: per non avermi fatto dormire la notte con i suoi racconti :), soprattutto Dagon e The Nameless City

Frank Herbert: per avere creato il più bel romanzo della storia

Veniamo ai saluti:
#gameprog-ita: in particolare Rhymes che mi ha fatto conoscere .NET e Python, ovvero due capolavori degli anni '90-'00, elevator2 che risponde sempre alle mie insistenti domande sul 3D, Lexiw per avermi fatto conoscere gli Agalloch, e tutti gli altri :)

#crack-it: beh se mi metto a scrivere tutti quanti qua non finisco più, saluto tutti quanti in particolare que che pubblicherà questo tutorial :)

#asm: tutti quanti pure qua :)

#programmazione: come sopra :)

#beos: tutti chi? siamo in 2 :)

#c#: ecco il tutorial che avevo promesso :)


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.