Categories
.NET Software Libre

Cecil: Examina y modifica ensamblados .NET al vuelo

Uso de Mono Cecil para la modificación (agregar, modificar y quitar código) de ensamblados .NET.

Cecil es una librería creada por Jean-Baptiste Evain que permite examinar y modificar los ensamblados .NET de manera sencilla, a su vez estos cambios pueden sobreescribir el original o ser almacenados en un ensamblado nuevo.

Veamos dos ejemplos sencillos que muestran como funciona Cecil:

  1. Convertir una aplicación de consola a una aplicación Windows.- Si compilamos el siguiente código como una aplicación de consola, al ejecutarse mostrará 2 ventanas (una para la consola y otra para el formulario creado):

    csharp:

    // console.cs
    using System;
    using System.Windows.Forms;

    class Program
    {
        static void Main(string[] args)
        {
            Form frm = new Form();
            frm.Height = 100;
            frm.Width = 100;
            frm.ShowDialog();
        }
    }

    Para hacer la conversión usando Cecil, lo único que se tiene que hacer es:

    csharp:

    using Mono.Cecil;
    using Mono.Cecil.Cil;

    class Program
    {
        static void Main(string[] args)
        {
            AssemblyDefinition assembly = AssemblyFactory.GetAssembly("consola.exe");
            assembly.Kind = AssemblyKind.Windows;
            AssemblyFactory.SaveAssembly(assembly, "windows.exe");
        }
    }

    Si ejecutamos windows.exe, esta vez sólo se mostrará el formulario creado.

  2. Cambiar el espacio de nombres y modificadores de acceso: En este ejemplo se va a cambiar el espacio de nombres de las clases y los modificadores de acceso de un ensamblado denominado ClassLibrary1.dll

    csharp:

    namespace ClassLibrary1.Utility
    {
        public enum HashMethod
        {
            MD5,
            SHA1
        }

        internal class Security
        {
            public static string MD5(string password)
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
        }
       
        class Validation
        {
            public static bool IsEmail(string email)
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
            private void IsUrl(string url)
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
            private void IsIp(string ip)
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
        }
    }

    Para hacer realizar los cambios en el ensamblado de arriba:

    csharp:

    using Mono.Cecil;
    using Mono.Cecil.Cil;

    namespace CecilDemo
    {
        class Program
        { 
            static void Main(string[] args)
            {
                string assemblyName = "ClassLibrary1.dll";

                /* Cargar el ensamblado */
                AssemblyDefinition assembly = AssemblyFactory.GetAssembly(assemblyName);

                /* Iterar sobre los tipos del módulo principal */
                foreach (TypeDefinition type in assembly.MainModule.Types)
                {
                    /* Cambiar el espacio de nombres y el
                     * nivel de acceso a todas las clases
                     */

                    if ("<Module>" != type.Name)
                    {
                        type.Namespace = type.Namespace.Replace("ClassLibrary1", "Buayacorp");
                        type.Attributes |= TypeAttributes.Public;                   
                    }
                   
                    /* Hacer que todos los métodos de la clase Validation
                     * sean públicos y miembros de clase (static).
                     */

                    if ("Validation" == type.Name)
                    {
                        foreach (MethodDefinition method in type.Methods)
                        {
                            /* Eliminar el atributo 'private' de cada uno de los métodos */
                            if ((method.Attributes & MethodAttributes.Private) == MethodAttributes.Private)
                            {
                                method.Attributes ^= MethodAttributes.Private;
                            }

                            method.Attributes |= MethodAttributes.Public | MethodAttributes.Static;                       
                        }
                    }
                }
                /* Almacenar el nuevo ensamblado en CustomClassLibrary1.dll */
                AssemblyFactory.SaveAssembly(assembly, "Custom" + assemblyName);
            }
        }
    }

    Si observamos el ensamblado modificado con Reflector, veremos que se realizaron los cambios.

El uso de esta herramienta obviamente va a depender de las necesidades y posibles limitaciones de acceso al código fuente que tengamos, puesto que no sería nada productivo usar Cecil para hacer cosas parecidas a los ejemplos que muestro. 😀

2 replies on “Cecil: Examina y modifica ensamblados .NET al vuelo”

Comments are closed.