1

How would I go about extracting the IL code for classes that are generated at runtime by reflection so I can save it to disk? If at all possible. I don't have control of the piece of code that generates these classes.

Eventually, I would like to load this IL code from disk into another assembly.

I know I could serialise/deserialise classes but I wish to use purely IL code. I'm not fussed with the security implications.

Running Mono 2.10.1

Steven Ross
  • 229
  • 1
  • 6
  • 2
    This is just wrong, code != data. – Hans Passant Apr 02 '11 at 16:24
  • As Hans commented, IL will not contain username & password. Even serialization is bad idea for username & password. Probably by explaining your actual problem you can help community come up with better solution & answer. – Sanjeevakumar Hiremath Apr 02 '11 at 16:38
  • @Sanjeevakumar It was purely an example. I said I wanted to "populate its properties with runtime calculated values". I didn't think IL code contained data, by this I meant I wanted to create an instance of the class I just injected and alter is values using reflection. Overall, I want to be able to extract the IL code of any object that may contain any amount of properties, fields and methods. – Steven Ross Apr 02 '11 at 16:51
  • Can you mention difference between storing/loading DLL and storing/loading IL? – Akash Kava Apr 02 '11 at 17:05
  • @Akash I have edited my question to state that the classes in which I am trying to extract IL code for may or may not be created by reflection. Hence no IL code for classes created via reflection will be stored in the DLL. – Steven Ross Apr 02 '11 at 17:10
  • @Steven, you said serialization was one option in your original question, that's why it was hard to understand whether u wanted data or code. – Sanjeevakumar Hiremath Apr 02 '11 at 17:50
  • @Steven your edit does not answer my question, also IL will not work without all referenced IL merged in your dll. – Akash Kava Apr 02 '11 at 18:02
  • For some reason I'm still unable to understand this question, can someone edit it to make some sense? – Sanjeevakumar Hiremath Apr 02 '11 at 18:06
  • @Akash @Senjeevakimar @Hans I have edited the question again to try and simplify it. Please accept my apologies for not making myself clear. However, as Akash pointed out, it seems what I want to do is not possible. :( – Steven Ross Apr 02 '11 at 19:19

2 Answers2

1

Or better yet, use Mono.Cecil.

It will allow you to get at the individual instructions, even manipulating them and disassembling them (with the mono decompiler addition).

Note that the decompiler is a work in progress (last time I checked it did not fully support lambda expressions and Visual Basic exception blocks), but you can have pretty decompiled output in C# pretty easily as far as you don't hit these boundary conditions. Also, work has progressed since.

Mono Cecil in general let's you write the IL to a new assembly, as well, which you can then subsequently load into your appdomain if you like to play with bleeding edge.

Update I came round to trying this. Unfortunately I think I found what problem you run into. It turns out there is seems to be no way to get at the IL bytes for a generated type unless the assembly happened to get written out somewhere you can load it from.

I assumed you could just get the bits via reflection (since the classes support the required methods), however the related methods just raise an exception The invoked member is not supported in a dynamic module. on invocation. You can try this with the code below, but in short I suppose it means that it ain't gonna happen unless you want to f*ck with Marshal::GetFunctionPointerForDelegate(). You'd have to binary dump the instructions and manually disassemble them as IL opcodes. There be dragons.

Code snippet:

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");
        }
    }
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Unfortunately because Mono.Cecil lacks official documentation I can't find an example that would allow me to save the IL code that represents a specific class to a file. Does it allow you to inject IL code into the current appdomain? – Steven Ross Apr 02 '11 at 16:15
  • Mono's repo contain [cecil-roundtrip.cs](https://github.com/mono/cecil/blob/master/tools/cecil-roundtrip.cs) which I suppose should be pretty comprehensive to start things. I'll see whether I can knock up a little sample – sehe Apr 02 '11 at 20:28
  • Updated with bad news. Sorry :) One more suggestion, but I'm not sure I would go there – sehe Apr 03 '11 at 00:16
0

If all you want is the IL for your User class, you already have it. It's in the dll that you compiled it to.

From your other assembly, you can load the dll with the User class dynamically and use it through reflection.

UPDATE:

If what you have is a dynamic class created with Reflection.Emit, you have an AssemblyBuilder that you can use to save it to disk.

If your dynamic type was instead created with Mono.Cecil, you have an AssemblyDefinition that you can save to disk with myAssemblyDefinition.Write("MyAssembly.dll") (in Mono.Cecil 0.9).

Jordão
  • 55,340
  • 13
  • 112
  • 144