猿问

反编译.NET Core中的匿名方法IL

我有这个方法


public void Add(Action<Cursor> action)

{

    // Decompile `action` to C#

}

在方法内部,我想将内部的匿名方法反编译action 为 C# 代码,或 C# 代码的 AST 表示形式。


我尝试使用Mono.Ceciland ICSharpCode.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?

}


GCT1015
浏览 309回答 1
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(源),它通过搜索文件系统中的公共文件夹来完成工作(它接收一个框架版本以知道在哪里搜索)。我不喜欢它,因为它假设太多并且需要太多信息(我并不总是知道运行时的框架或其他部分),因此我创建了自己的解析器,它遍历程序集图(从调用程序集开始)。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);    }}我不知道这是否是适合所有用例的最佳方法,但它对我来说非常有效,至少到目前为止是这样。
随时随地看视频慕课网APP
我要回答