Execute .NET IL code in C#

2020-06-08 03:01发布

问题:

Is there any way to execute an array of IL codes in C# like shell codes in C/C++?

I want to create a method, convert it to IL code, obfuscate it and store in an array of bytes and finally want to execute it decrypt the array content and execute IL code.

For example this is my C# code:

 static int MyMethod(string A, int B)
 {
    B += 10;
    if (A.Equals("A"))
        B = 0;
    return B;
 }

Now I convert it to IL code :

 private static int MyMethod(string A, int B)
  {
    locals: int local_0,
            bool local_1

    /* 0000025C 00             */ nop
    /* 0000025D 03             */ ldarg_1 // int B
    /* 0000025E 1F 0A          */ ldc_i4_s 10
    /* 00000260 58             */ add
    /* 00000261 10 01          */ starg_s arg_1 // int B
    /* 00000263 02             */ ldarg_0 // string A
    /* 00000264 72 01 00 00 70 */ ldstr "A"
    /* 00000269 6F 11 00 00 0A */ callvirt System.String::Equals(string) // returns bool
    /* 0000026E 16             */ ldc_i4_0
    /* 0000026F FE 01          */ ceq
    /* 00000271 0B             */ stloc_1 // bool local_1
    /* 00000272 07             */ ldloc_1 // bool local_1
    /* 00000273 2D 03          */ brtrue_s loc_28
    /* 00000275 16             */ ldc_i4_0
    /* 00000276 10 01          */ starg_s arg_1 // int B
loc_28:
    /* 00000278 03             */ ldarg_1 // int B
    /* 00000279 0A             */ stloc_0 // int local_0
    /* 0000027A 2B 00          */ br_s loc_32
loc_32:
    /* 0000027C 06             */ ldloc_0 // int local_0
    /* 0000027D 2A             */ ret
  }

And finally this is a byte array :

private byte[] ilcode = 
{
   0x00, 0x03, 0x1F, 0x0A, 0x58, 0x10, 0x01, 0x02, 0x72, 0x01, 0x00, 0x00, 0x70, 0x6F, 0x11, 0x00, 0x0, 0x0A, 0x16,
   0xFE, 0x01, 0x0B, 0x07, 0x2D, 0x03, 0x16, 0x10, 0x01, 0x03, 0x0A, 0x2B, 0x00, 0x06, 0x2A
};

回答1:

You will need to use the infrastructure in the System.Reflection.Emit namespace. Specifically, you should look at the docs for MethodBuilder.CreateMethodBody which takes an array of bytes representing MSIL instructions. There is a full example there, but below is short snippet of its use. Here, I will create a delegate to the dynamic method as well.

I will note that this is only supported in a very limited way, which is called out in the docs:

This is currently not fully supported. The user cannot supply the location of token fix ups and exception handlers.

The issue is that metadata tokens used in IL to reference types, methods, string literals, etc are resolved at the module level. Thus IL is not completely portable in the sense that you can't take arbitrary, raw IL from a method in one module and just drop it into another method in another module. You need the proper metadata tokens in the new module. However, if you know that your IL contains no metadata tokens you can do it, but this severely limits what you can do with this. (HT: svick, Simon Svensson)

class Program
{
    static void Main(string[] args)
    {
        // opcodes for pushing two arguments to the stack, adding, and returning the result.
        byte[] ilcodes = { 0x02, 0x03, 0x58, 0x2A };
        var method = CreateFromILBytes(ilcodes);
        Console.WriteLine(method(2, 3));
    }

    private static Func<int, int, int> CreateFromILBytes(byte[] bytes)
    {
        var asmName = new AssemblyName("DynamicAssembly");
        var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
        var module = asmBuilder.DefineDynamicModule("DynamicModule");
        var typeBuilder = module.DefineType("DynamicType");
        var method = typeBuilder.DefineMethod("DynamicMethod", 
            MethodAttributes.Public | MethodAttributes.Static, 
            typeof(int), 
            new[] { typeof(int), typeof(int) });
        method.CreateMethodBody(bytes, bytes.Length);
        var type = typeBuilder.CreateType();
        return (Func<int, int, int>)type.GetMethod("DynamicMethod").CreateDelegate(typeof(Func<int, int, int>));
    }
}

Note the use of the RunAndSave option. This will save the dynamic assembly to disk in a temporary location. It may be more desirable to use RunAndCollect which will generate the assembly in memory only and allow it to be collected later when all references to it are dead. However, there are some caveats to so called collectible assemblies, detailed here.



标签: c# .net il