Как извлечь код класса IL из загруженной сборки и сохранить на диск? - PullRequest
0 голосов
/ 02 апреля 2011

Как мне извлечь код IL для классов, которые генерируются во время выполнения с помощью отражения, чтобы я мог сохранить его на диск? Если вообще возможно. У меня нет контроля над кодом, который генерирует эти классы.

В конце концов, я хотел бы загрузить этот код IL с диска в другую сборку.

Я знаю, что могу сериализовать / десериализовать классы, но я хочу использовать чисто IL-код. Я не обеспокоен последствиями для безопасности.

Запуск Mono 2.10.1

Ответы [ 2 ]

1 голос
/ 02 апреля 2011

Или еще лучше, используйте Mono.Cecil.

Это позволит вам получать отдельные инструкции, даже манипулируя ими и разбирая их (с добавлением монокомпилятора ).

Обратите внимание, что декомпилятор находится в процессе разработки (в прошлый раз, когда я проверял, он не полностью поддерживает лямбда-выражения и блоки исключений Visual Basic), но вы можете получить довольно декомпилированный вывод в C # довольно легко, если вы не нажмете эти граничные условия. Кроме того, работа продвинулась с тех пор.

Mono Cecil, в общем, давайте также напишем IL для новой сборки, которую вы затем сможете загрузить в свой домен приложения, если хотите поиграть с передовым краем.

Обновление Я решил попробовать это. К сожалению, я думаю, что нашел проблему, с которой вы столкнулись. Оказывается, что нет никакого способа получить байты IL для сгенерированного типа , если только сборка не была записана где-то, откуда вы можете загрузить ее.

Я предполагал, что вы можете просто получить биты посредством отражения (поскольку классы поддерживают требуемые методы), однако связанные методы просто вызывают исключение The invoked member is not supported in a dynamic module. при вызове. Вы можете попробовать это с приведенным ниже кодом, но вкратце я предполагаю, что это означает, что этого не произойдет , если вы не захотите f * ck с Marshal::GetFunctionPointerForDelegate(). Вам нужно будет выполнить двоичный вывод инструкций и вручную разобрать их как коды операций IL. Там будут драконы.

Фрагмент кода:

using System;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Reflection.Emit;
using System.Reflection;

namespace REFLECT
{
    class Program
    {
        private static Type EmitType()
        {
            var dyn = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Emitted"), AssemblyBuilderAccess.RunAndSave);
            var mod = dyn.DefineDynamicModule("Emitted", "Emitted.dll");
            var typ = mod.DefineType("EmittedNS.EmittedType", System.Reflection.TypeAttributes.Public);
            var mth = typ.DefineMethod("SuperSecretEncryption", System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static, typeof(String), new [] {typeof(String)});

            var il = mth.GetILGenerator();
            il.EmitWriteLine("Emit was here");
            il.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);    
            il.Emit(System.Reflection.Emit.OpCodes.Ret);
            var result = typ.CreateType();
            dyn.Save("Emitted.dll");
            return result;
        }

        private static Type TestEmit()
        {
            var result = EmitType();
            var instance = Activator.CreateInstance(result);
            var encrypted = instance.GetType().GetMethod("SuperSecretEncryption").Invoke(null, new [] { "Hello world" });
            Console.WriteLine(encrypted); // This works happily, print "Emit was here" first

            return result;
        }

        public static void Main (string[] args)
        {
            Type emitted = TestEmit();

              // CRASH HERE: even if the assembly was actually for SaveAndRun _and_ it 
              // has actually been saved, there seems to be no way to get at the image
              // directly:
            var ass = AssemblyFactory.GetAssembly(emitted.Assembly.GetFiles(false)[0]);

              // the rest was intended as mockup on how to isolate the interesting bits
              // but I didn't get much chance to test that :)
            var types = ass.Modules.Cast<ModuleDefinition>().SelectMany(m => m.Types.Cast<TypeDefinition>()).ToList();
            var typ = types.FirstOrDefault(t => t.Name == emitted.Name);

            var operands = typ.Methods.Cast<MethodDefinition>()
                .SelectMany(m => m.Body.Instructions.Cast<Instruction>())
                .Select(i => i.Operand);

            var requiredTypes = operands.OfType<TypeReference>()
                .Concat(operands.OfType<MethodReference>().Select(mr => mr.DeclaringType))
                .Select(tr => tr.Resolve()).OfType<TypeDefinition>()
                .Distinct();
            var requiredAssemblies = requiredTypes
                .Select(tr => tr.Module).OfType<ModuleDefinition>()
                .Select(md => md.Assembly.Name as AssemblyNameReference);

            foreach (var t in types.Except(requiredTypes))
                ass.MainModule.Types.Remove(t);

            foreach (var unused in ass.MainModule
                     .AssemblyReferences.Cast<AssemblyNameReference>().ToList()
                     .Except(requiredAssemblies))
                ass.MainModule.AssemblyReferences.Remove(unused);

            AssemblyFactory.SaveAssembly(ass, "/tmp/TestCecil.dll");
        }
    }
}
0 голосов
/ 02 апреля 2011

Если все, что вы хотите, это IL для вашего User класса, у вас уже есть.Именно в dll вы скомпилировали его.

Из вашей другой сборки вы можете загрузить dll с классом User динамически и использовать его через отражение.

ОБНОВЛЕНИЕ :

Если у вас есть динамический класс, созданный с помощью Reflection.Emit, у вас есть AssemblyBuilder, который можно использовать для сохранения это на диск.

Если ваш динамический тип был создан вместо Mono.Cecil, у вас есть AssemblyDefinition, который вы можете сохранить на диск с помощью myAssemblyDefinition.Write("MyAssembly.dll") (в Mono.Cecil 0.9).

...