c# - 在 C# 中执行 .NET IL 代码

标签 c# .net il

有没有办法像 C/C++ 中的 shell 代码一样在 C# 中执行一组 IL 代码?

我想创建一个方法,将其转换为 IL 代码,对其进行混淆处理并存储在一个字节数组中,最后想要执行它解密数组内容并执行 IL 代码。

例如这是我的 C# 代码:

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

现在我将它转换为 IL 代码:

 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
  }

最后这是一个字节数组:

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
};

最佳答案

您将需要使用 System.Reflection.Emit 命名空间中的基础结构。具体来说,您应该查看 docs对于 MethodBuilder.CreateMethodBody,它采用表示 MSIL 指令的字节数组。那里有一个完整的示例,但下面是其使用的简短片段。在这里,我还将为动态方法创建一个委托(delegate)。

我会注意到,这仅以非常有限的方式受支持,这在文档中被调用:

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

问题是 IL 中用于引用类型、方法、字符串文字等的元数据标记在模块级别解析。因此,从某种意义上说,IL 不是完全可移植的,因为您不能从一个模块中的方法中获取任意原始 IL,然后将其放入另一个模块中的另一种方法中。您需要在新模块中使用适当的元数据标记。但是,如果您知道您的 IL 不包含任何元数据标记,您就可以这样做,但这严重限制了您可以使用它做的事情。 (HT:斯维克,西蒙·斯文森)

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>));
    }
}

注意 RunAndSave 选项的使用。这会将动态程序集保存到磁盘的临时位置。可能更需要使用 RunAndCollect,它只会在内存中生成程序集,并允许稍后在对它的所有引用都失效时收集它。但是,对于所谓的收藏品组件有一些注意事项,详见 here .

关于c# - 在 C# 中执行 .NET IL 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26312026/

相关文章:

c# - 如何检测udpclient何时收到数据?

c# - 如何改进 C# 枚举

C# Linq 与对象的一部分相交/除外

c# - MVVM Light + Unity 还是 Prism?

.net - ildasm,然后是具有相同元数据的 ilasm

c# - 增加 .ashx 的超时

c# - Where vs. foreach with if - 为什么会有不同的结果?

.net - ASP.NET 项目中 TypeScript 文件和代码的结构

c# - 如何使用 OpCodes.Calli 在指向 C++ 对象的指针上调用方法

c# - 某些编译器发出的奇怪 IL 代码