Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert class method into IL and execute it at runtime

I want to convert a method into an IL codes from a class and then execute it by invoke it. The example I am following is from msdn: https://msdn.microsoft.com/en-us/library/system.reflection.emit.methodbuilder.createmethodbody(v=vs.110).aspx.

It shows exactly what I need, my problem is to generate the ILcodes from a class method.

So basically I need to fill the following

byte[] ILcodes = new byte[] {
  0x02,   /* 02h is the opcode for ldarg.0 */
  0x03,   /* 03h is the opcode for ldarg.1 */
  0x58,   /* 58h is the opcode for add     */
  0x2A    /* 2Ah is the opcode for ret     */
};

from a method defined in a class for example:

public class MethodBodyDemo
{ 
    public int Add(int x, int y)
    {
        return x + y;
    }
}

I tried the following call to fill the byte array:

var ILcodes = typeof(MethodBodyDemo).GetMethod("Add").GetMethodBody().GetILAsByteArray();

The following is the example I am creating but it gives an exception: "Common Language Runtime detected an invalid program."

public class MethodBodyDemo
{ 
    public int Add(int x, int y)
    {
        return x + y;
    }
    // This class will demonstrate how to create a method body using 
    // the MethodBuilder.CreateMethodBody(byte[], int) method.
    public static Type BuildDynType()
    {
        Type addType = null;
        AppDomain currentDom = Thread.GetDomain();
        AssemblyName myAsmName = new AssemblyName();
        myAsmName.Name = "MyDynamicAssembly";
        AssemblyBuilder myAsmBldr = currentDom.DefineDynamicAssembly(
                           myAsmName,
                           AssemblyBuilderAccess.RunAndSave);
        // The dynamic assembly space has been created. Next, create a module
        // within it. The type Point will be reflected into this module.
        ModuleBuilder myModuleBldr = myAsmBldr.DefineDynamicModule("MyModule");
        TypeBuilder myTypeBldr = myModuleBldr.DefineType("Adder");
        MethodBuilder myMthdBldr = myTypeBldr.DefineMethod("Add",
                                MethodAttributes.Public |
                                MethodAttributes.Static,
                                typeof(int),
                                new Type[]
                                {typeof(int), typeof(int)});
        // Build the array of Bytes holding the MSIL instructions.
        // byte[] ILcodes = new byte[] {
        //  0x02,   /* 02h is the opcode for ldarg.0 */
        //  0x03,   /* 03h is the opcode for ldarg.1 */
        //  0x58,   /* 58h is the opcode for add     */
        //  0x2A    /* 2Ah is the opcode for ret     */
        // };
        var ILcodes = typeof(MethodBodyDemo).GetMethod("Add").GetMethodBody().GetILAsByteArray();
        myMthdBldr.CreateMethodBody(ILcodes, ILcodes.Length);
        addType = myTypeBldr.CreateType();
        return addType;
    }
    public static void TestExecMethod()
    {
        Type myType = BuildDynType();
        Console.WriteLine("---");
        Console.Write("Enter the first integer to add: ");
        int aVal = Convert.ToInt32(Console.ReadLine());
        Console.Write("Enter the second integer to add: ");
        int bVal = Convert.ToInt32(Console.ReadLine());
        object adderInst = Activator.CreateInstance(myType, new object[0]);
        Console.WriteLine("The value of adding {0} to {1} is: {2}.", aVal, bVal, myType.InvokeMember("Add", BindingFlags.InvokeMethod, null, adderInst, new object[] { aVal, bVal }));
    }
}

Execute by calling:

MethodBodyDemo.TestExecMethod();

Any help please?

like image 939
Clayton Cassar Avatar asked Jun 12 '26 08:06

Clayton Cassar


2 Answers

I am the author of a library that does a similar thing. It reads the opcodes of an existing method, reinterprets them back to generic opcode items that contain i.e. MethodInfo instead of the numbered token and builds a method copy out of these. This works cross-assembly and would work in your case too.

It was designed to monkey patch games but could be modified to transfer the generic opcodes in "clear text" to a different system so that the destination could do a type lookup and thus obtain the correct opcode for the local assembly.

The project is MIT licensed and is called Harmony.

like image 102
Andreas Pardeike Avatar answered Jun 14 '26 22:06

Andreas Pardeike


Build your project in Release mode, and do one of the following:

Remove the MethodAttributes.Static attribute here

MethodBuilder myMthdBldr = myTypeBldr.DefineMethod("Add",
                           MethodAttributes.Public |
                           MethodAttributes.Static,
                           typeof(int),
                           new Type[]
                           {typeof(int), typeof(int)});

Or change this method to be static

public int Add(int x, int y)
{
    return x + y;
}

And this should work for you.

enter image description here

But, I saw in comments that you want it to pass bytes to other system and run it, my answer is just for the example you have in the question. In reality this is not going to work because the reason that Simon Svensson wrote in comment. (Unless all methods are statics and do pure operations (like return 3+4) without any reference to other methods\types\fields.)

You still can (theoretically) do some magic to make it work but it's not practically.

like image 40
Dudi Keleti Avatar answered Jun 14 '26 22:06

Dudi Keleti



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!