【问题标题】:How to extract class IL code from loaded assembly and save to disk?如何从加载的程序集中提取类 IL 代码并保存到磁盘?
【发布时间】:2011-04-02 15:19:56
【问题描述】:

如何提取运行时通过反射生成的类的 IL 代码,以便将其保存到磁盘?如果可能的话。我无法控制生成这些类的代码。

最后,我想将此 IL 代码从磁盘加载到另一个程序集中。

我知道我可以序列化/反序列化类,但我希望使用纯 IL 代码。我对安全隐患并不在意。

运行 Mono 2.10.1

【问题讨论】:

  • 这是错误的,代码!=数据。
  • 正如 Hans 所说,IL 不会包含用户名和密码。即使序列化对于用户名和密码也是个坏主意。可能通过解释您的实际问题,您可以帮助社区提出更好的解决方案和答案。
  • @Sanjeevakumar 这纯粹是一个例子。我说我想“用运行时计算的值填充它的属性”。我不认为 IL 代码包含数据,我的意思是我想创建一个我刚刚注入的类的实例,并使用反射改变值。总的来说,我希望能够提取任何可能包含任意数量的属性、字段和方法的对象的 IL 代码。
  • 你能说说存储/加载DLL和存储/加载IL的区别吗?
  • @Akash 我已经编辑了我的问题,说明我尝试提取 IL 代码的类可能是通过反射创建的,也可能不是。因此,通过反射创建的类的 IL 代码不会存储在 DLL 中。

标签: serialization reflection mono reflection.emit il


【解决方案1】:

或者更好的是,使用 Mono.Cecil。

它可以让您获得单独的指令,甚至可以操作和反汇编它们(使用mono decompiler addition)。

请注意,反编译器正在开发中(上次我检查它不完全支持 lambda 表达式和 Visual Basic 异常块),但只要你不打,你可以很容易地在 C# 中获得相当反编译的输出这些边界条件。此外,此后工作也取得了进展。

一般来说,Mono Cecil 让您也可以将 IL 写入一个新程序集,然后如果您喜欢玩前沿技术,您可以随后将其加载到您的 appdomain 中。

更新我来试试这个。不幸的是,我想我找到了你遇到的问题。事实证明,似乎没有办法获取生成类型的 IL 字节除非程序集恰好被写出您可以从中加载它的地方。

我假设您可以通过反射获取位(因为类支持所需的方法),但是相关方法只会在调用时引发异常 The invoked member is not supported in a dynamic module.。你可以用下面的代码试试这个,但简而言之,我想这意味着它不会发生,除非你想f*ck with Marshal::GetFunctionPointerForDelegate()。您必须将指令二进制转储并手动将它们反汇编为 IL 操作码。有龙。

代码sn-p:

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

【讨论】:

  • 不幸的是,因为 Mono.Cecil 缺少官方文档,我找不到可以让我将表示特定类的 IL 代码保存到文件的示例。是否允许你将 IL 代码注入到当前的 appdomain 中?
  • Mono 的 repo 包含 cecil-roundtrip.cs,我认为它应该是非常全面的开始工作。我看看能不能弄个小样
  • 更新了坏消息。抱歉 :) 还有一个建议,但我不确定我会去那里
【解决方案2】:

如果您想要的只是 User 类的 IL,那么您已经拥有它。它在您编译成的 dll 中。

从您的其他程序集中,您可以动态地使用Userload the dll,并通过反射use it

更新

如果你有一个使用Reflection.Emit 创建的动态类,你有一个AssemblyBuilder 可以用来将save it 用于磁盘。

如果您的动态类型是使用 Mono.Cecil 创建的,则您有一个 AssemblyDefinition,您可以使用 myAssemblyDefinition.Write("MyAssembly.dll") 将其保存到磁盘(在 Mono.Cecil 0.9 中)。

【讨论】:

    猜你喜欢
    • 2012-06-26
    • 1970-01-01
    • 2017-07-28
    • 2013-02-14
    • 2019-07-22
    • 2019-06-03
    • 1970-01-01
    • 2012-10-13
    • 2020-12-17
    相关资源
    最近更新 更多