Sébastien Lebreton
Modification d'assemblage et injection de code C# / VB.NET à la volée
Reflexil est un plugin pour Reflector permettant la modification des assemblages .NET. Il permet par exemple d'altérer le code IL d'un assemblage ou de travailler à plus haut niveau en injectant directement du code C#/VB dans le corps d'une méthode.
Par Sébastien Lebreton publié le 17/09/2007 à 00:34, lu 6784 fois, 9 pages
 9 | Conclusion et retour sur Mono.Cecil
Reflexil repose intégralement sur Mono.Cecil pour manipuler les différentes entités présentes dans un assemblage.
Il est intéressant de souligner que les assemblages chargés via Mono.Cecil ne sont aucunement chargés dans le runtime, il n'y a donc pas de contraintes liées à la libération de ressources et à l'isolation via des AppDomains par exemple.
En fait Mono.Cecil s'attaque directement au code binaire de l'assemblage. En particulier il n'y a pas de relation entre un System.Type et un Mono.Cecil.TypeDefinition, alors qu'ils matérialisent tous deux la notion de type .NET.
Si nous avions tenté de reproduire par programmation notre premier exemple (l'overload de show), nous aurions pu écrire grâce à Mono.Cecil le code suivant :

using System;

using Mono.Cecil;

using Mono.Cecil.Cil;

 

namespace ReflexilDemo

{

    static class Cecil

    {

        // Retrouve une définition de méthode suivant un type owner, un nom et un nombre de paramètres

        // Note: Méthode de démo uniquement car il n'est pas rigoureux de seulement compter les paramètres

        static MethodDefinition GetMethodDefinition(AssemblyDefinition adef,

                                                    string owner,

                                                    string name,

                                                    int paramcount)

        {

            // On récupère la définition du type

            TypeDefinition tdef = adef.MainModule.Types[owner];

            foreach (MethodDefinition mdef in tdef.Methods)

            {

                // Si le nom et le nombre de paramètres correspondent, c'est bon!

                if ((mdef.Name == name) && (paramcount == mdef.Parameters.Count)) return mdef;

            }

            throw new ArgumentException("Unable to find this method!");

        }

 

        public static void DoPatch()

        {

            // Chargement de l'assembly à patcher

            AssemblyDefinition targetasmdef = AssemblyFactory.GetAssembly("ReflexilDemo.exe");

            // On retrouve la définition de ComputeAndDisplay

            MethodDefinition computedefinition = GetMethodDefinition(targetasmdef,

                                                                    "ReflexilDemo.DemoForm",

                                                                    "ComputeAndDisplay",

                                                                    2);

 

            // On ajoute une instruction pour charger la chaîne sur la pile

            CilWorker worker = computedefinition.Body.CilWorker;

            Instruction insld = worker.Create(OpCodes.Ldstr, "The result is!");

            worker.InsertBefore(computedefinition.Body.Instructions[11], insld);

 

            // On récupère l'instruction d'appel à MessageBox.Show

            Instruction insshow = computedefinition.Body.Instructions[12];

            // Et en particulier la référence à MessageBox.Show

            MethodReference oldshowref = insshow.Operand as MethodReference;

 

            // Puis on charge l'assemblage qui déclare MessageBox.Show (System.Windows.Forms)

            AssemblyNameReference scope = oldshowref.DeclaringType.Scope as AssemblyNameReference;

            // On utilise le resolver car l'assemblage n'est pas dans le répertoire courant

            DefaultAssemblyResolver resolver = new DefaultAssemblyResolver();

            AssemblyDefinition sourceasmdef = resolver.Resolve(scope);

            // On récupère la définition de l'overload qui nous interesse de MessageBox.Show

            MethodDefinition newshowref = GetMethodDefinition(sourceasmdef,

                                                              "System.Windows.Forms.MessageBox",

                                                              "Show",

                                                              2);

 

            // On importe une référence de cet overload dans l'assemblage de travail

            insshow.Operand = computedefinition.DeclaringType.Module.Import(newshowref);

            // On sauvegarde!

            AssemblyFactory.SaveAssembly(targetasmdef, "Reflexil.Manualy.Patched.exe");

        }

    }

}

 
» Démarrer une discussion
 
Discussion démarée par Noham Choulant le 18/04/2008 à 13:53, 2 commentaire(s).