2014-10-11 4 views
17

Есть ли способ выполнить массив IL-кодов в C#, таких как коды оболочки в C/C++?Выполнить код .NET IL в C#

Я хочу создать метод, преобразовать его в 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 
}; 
+2

Невозможно выполнить IL так, как вы ожидаете, но вы можете использовать отражение для загрузки и вызова любых методов в нем динамически. – Thangadurai

+3

Я думаю, что ваша самая большая проблема - это токены метаданных. Например, токен для 'string.Equals()' ('11 00 00 0A' в вашем коде) не всегда будет одинаковым. – svick

+1

@svick Знаки метаданных беспорядок это большое время, поскольку они, похоже, хранятся на уровне модуля (по крайней мере, это то, что я вижу из моих быстрых тестов). То же самое касается таких строк, как строковые литералы. –

ответ

21

Вы необходимо будет использовать инфраструктуру в пространстве имен System.Reflection.Emit. В частности, вы должны посмотреть на docs для MethodBuilder.CreateMethodBody, который принимает массив байтов, представляющий инструкции MSIL. Здесь есть полный пример, но ниже приведен короткий фрагмент его использования. Здесь я также создам делегат для динамического метода.

Замечу, что это поддерживается только в очень ограниченном объеме, который выкрикнул в документации:

Это в настоящее время не поддерживается полностью. Пользователь не может предоставить местоположение исправлений фишек и обработчиков исключений.

Проблема заключается в том, что токены метаданных, используемые в IL для ссылочных типов, методов, строковых литералов и т. Д., Разрешаются на уровне модуля. Таким образом, IL не полностью переносится в том смысле, что вы не можете взять произвольный, необработанный IL из метода в одном модуле и просто отбросить его в другой метод в другом модуле. В новом модуле нужны жетоны метаданных. Однако, если вы знаете, что ваш IL не содержит токенов метаданных, вы можете это сделать, но это сильно ограничивает то, что вы можете сделать с этим. (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>)); 
    } 
} 

Обратите внимание на использование опции RunAndSave. Это позволит сохранить динамическую сборку на диск во временном месте. Может быть более желательно использовать RunAndCollect, который будет генерировать сборку только в памяти и позволяет собирать ее позже, когда все ссылки на нее мертвы. Тем не менее, есть некоторые предостережения для так называемых коллекционных сборок, подробно here.

+0

Приятно это знать. Я должен начать читать еще несколько книг! – Thangadurai

+1

Этот пример (с байтовым массивом) работает, поскольку коды операций ('ldarg.0',' ldarg.1', 'add',' ret') не используют токены метаданных. – sisve

+0

@SimonSvensson. Верно, я расширил это в своем ответе. –

Смежные вопросы