【问题标题】:Decompiling Anonymous Method IL in .NET Core在 .NET Core 中反编译匿名方法 IL
【发布时间】:2019-07-27 11:16:09
【问题描述】:

我有这个方法

public void Add(Action<Cursor> action)
{
    // Decompile `action` to C#
}

在方法里面我想把action里面的匿名方法反编译成C#代码,或者C#代码的AST表示。

我尝试使用Mono.CecilICSharpCode.Decompiler(来自ILSpy 存储库),但我找不到反编译匿名方法的方法,因为这两个库都要求您从物理位置加载程序集并搜索通过类型图的方法,看起来很复杂,尤其是在寻找匿名方法的时候。

有没有办法使用方法的IL Byte数组,并将其反编译为C#?

public void Add(Action<Cursor> action)
{
    var il = action.Method.GetMethodBody().GetILAsByteArray();
    var ast = decompiler.DecompileIL(il); // Does such decompiler exist?
}

【问题讨论】:

    标签: c# .net-core decompiling


    【解决方案1】:

    使用ICSharpCode.Decompiler 库(来自ILSpy 存储库),我能够反编译一个lambda 方法。

    这样

    public void Add(Action<Cursor> action)
    {
        // Get the assembly in which the lambda resides
        var asm = Assembly.GetCallingAssembly();
        var file = asm.Location;
    
        // Create a resolver (a callback for resolving referenced assemblies during decompilation)
        var resolver = new CustomAssemblyResolver(asm);
        // Create an instance of decompiler
        var decompiler = new CSharpDecompiler(file, resolver, new DecompilerSettings());
    
        // Get an "EntityHandle" instance for the lambda's underlying method
        var method = MetadataTokenHelpers.TryAsEntityHandle(action.Method.MetadataToken); 
    
        var ast = decompiler.Decompile(new List<EntityHandle>() { method.Value });
    }
    

    正如我所提到的,您为 CSharpDecompiler 实例提供了一个解析器 (IAssemblyResolver),其工作是在反编译过程中加载引用的程序集。

    ICSharpCode.Decompiler 有一个UniversalAssemblyResolver (source) 它通过搜索文件系统中的公共文件夹来完成这项工作(它接收一个框架版本来知道在哪里搜索)。我不喜欢它,因为它假设太多并且需要太多信息(我并不总是知道框架或运行时的其他部分),所以我创建了自己的解析器来遍历程序集图(从调用程序集)。

    class CustomAssemblyResolver : IAssemblyResolver
    {
        private Dictionary<string, Assembly> _references;
    
        public CustomAssemblyResolver(Assembly asm)
        {
            _references = new Dictionary<string, Assembly>();
            var stack = new Stack<Assembly>();
            stack.Push(asm);
    
            while (stack.Count > 0)
            {
                var top = stack.Pop();
                if (_references.ContainsKey(top.FullName)) continue;
    
                _references[top.FullName] = top;
    
                var refs = top.GetReferencedAssemblies();
                if (refs != null && refs.Length > 0)
                {
                    foreach (var r in refs)
                    {
                        stack.Push(Assembly.Load(r));
                    }
                }
            }
    
            ;
        }
    
        public PEFile Resolve(IAssemblyReference reference)
        {
            var asm = _references[reference.FullName];
            var file = asm.Location;
            return new PEFile(file, new FileStream(file, FileMode.Open, FileAccess.Read), PEStreamOptions.Default, MetadataReaderOptions.Default);
        }
    
        public PEFile ResolveModule(PEFile mainModule, string moduleName)
        {
            var baseDir = Path.GetDirectoryName(mainModule.FileName);
            var moduleFileName = Path.Combine(baseDir, moduleName);
            if (!File.Exists(moduleFileName))
            {
                throw new Exception($"Module {moduleName} could not be found");
            }
            return new PEFile(moduleFileName, new FileStream(moduleFileName, FileMode.Open, FileAccess.Read), PEStreamOptions.Default, MetadataReaderOptions.Default);
        }
    }
    

    我不知道这是否是所有用例的最佳方法,但它对我来说非常有用,至少到目前为止。

    【讨论】:

    • 可能不是最好的方法,但是当我需要从基于元数据令牌的文件中获取任意函数的 AST 时,这也对我有帮助。在这种情况下,我不需要实现自定义解析器。
    • @Plutoberth 就我而言,我正在使用外部库和 NuGet 包,我需要获取它们的元数据。不过可能是更好的方法。也许我可以对我解决的问题更有选择性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-22
    • 2014-07-12
    • 2023-03-25
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 2011-02-05
    相关资源
    最近更新 更多