ILSpy,如何解决依赖关系?

发布于 2024-12-23 14:59:11 字数 1665 浏览 1 评论 0原文

我想使用 ILSpy 反汇编整个 .NET 程序集。

我使用此代码作为基础:
http://skysigal.xact-solutions.com/Blog /tabid/427/entryid/2488/Default.aspx

它工作正常,就在我有一个引用 Npgsql.dll (或任何其他非 gac 程序集),然后我得到 AssemblyResolutionException。

无法解析程序集:'Npgsql,Version=2.0.11.92,Culture=neutral,PublicKeyToken=5d8b90d52f46fda7'

我知道如何获取引用的程序集,但如何将它们添加到 ast ?

    // SqlWebAdmin.Models.Decompiler.DecompileAssembly("xy.dll");
    public static string DecompileAssembly(string pathToAssembly)
    {
        //Assembly assembly = Assembly.LoadFrom(pathToAssembly);
        System.Reflection.Assembly assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(pathToAssembly);
        //assembly.GetReferencedAssemblies();

        //assembly.GetReferencedAssemblies(assembly);
        Mono.Cecil.AssemblyDefinition assemblyDefinition =
            Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly);



        ICSharpCode.Decompiler.Ast.AstBuilder astBuilder = new ICSharpCode.Decompiler.Ast.AstBuilder(new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
        astBuilder.AddAssembly(assemblyDefinition);


        //new Helpers.RemoveCompilerAttribute().Run(decompiler.CompilationUnit);
        using (System.IO.StringWriter output = new System.IO.StringWriter())
        {
            astBuilder.GenerateCode(new ICSharpCode.Decompiler.PlainTextOutput(output));
            string result = output.ToString();
            return result;
        }

        return "";
    } // End Function DecompileAssembly

I want to disassemble an entire .NET assembly with ILSpy.

I used this code as base:
http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/2488/Default.aspx

And it works fine, just when I have an assembly that references Npgsql.dll (or any other non-gac assembly), then I get an AssemblyResolutionException.

Failed to resolve assembly: 'Npgsql, Version=2.0.11.92, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7'

I know how I can get the referenced assemblies, but how can I add them to ast ?

    // SqlWebAdmin.Models.Decompiler.DecompileAssembly("xy.dll");
    public static string DecompileAssembly(string pathToAssembly)
    {
        //Assembly assembly = Assembly.LoadFrom(pathToAssembly);
        System.Reflection.Assembly assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(pathToAssembly);
        //assembly.GetReferencedAssemblies();

        //assembly.GetReferencedAssemblies(assembly);
        Mono.Cecil.AssemblyDefinition assemblyDefinition =
            Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly);



        ICSharpCode.Decompiler.Ast.AstBuilder astBuilder = new ICSharpCode.Decompiler.Ast.AstBuilder(new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
        astBuilder.AddAssembly(assemblyDefinition);


        //new Helpers.RemoveCompilerAttribute().Run(decompiler.CompilationUnit);
        using (System.IO.StringWriter output = new System.IO.StringWriter())
        {
            astBuilder.GenerateCode(new ICSharpCode.Decompiler.PlainTextOutput(output));
            string result = output.ToString();
            return result;
        }

        return "";
    } // End Function DecompileAssembly

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

贵在坚持 2024-12-30 14:59:11

您需要告诉 ILSpy 正在使用的底层元数据读取器 Cecil,您的程序集在哪里。您可以这样写:

var resolver = new DefaultAssemblyResolver();
resolver.AddSearchDirectory("path/to/my/assemblies");

var parameters = new ReaderParameters
{
    AssemblyResolver = resolver,
};

var assembly = AssemblyDefinition.ReadAssembly(pathToAssembly, parameters);

这是告诉 Cecil 在何处解析引用的程序集的最自然方式。这样,您可以删除使用 System.Reflection 加载程序集的行,并且仅使用 ILSpy 堆栈。

You need to tell Cecil, the underlying metadata reader that ILSpy is using, where your assemblies are. You can write:

var resolver = new DefaultAssemblyResolver();
resolver.AddSearchDirectory("path/to/my/assemblies");

var parameters = new ReaderParameters
{
    AssemblyResolver = resolver,
};

var assembly = AssemblyDefinition.ReadAssembly(pathToAssembly, parameters);

This is the most natural way to tell Cecil where to resolve referenced assemblies. This way you can remove the line where you load the assembly using System.Reflection, and only use the ILSpy stack.

遮云壑 2024-12-30 14:59:11

这是改进的@Nayan 答案。如果您想忽略丢失的程序集,请复制此类:

using Mono.Cecil;

public class IgnoringExceptionsAssemblyResolver : DefaultAssemblyResolver
{
    public override AssemblyDefinition Resolve(AssemblyNameReference name)
    {
        try
        {
            return base.Resolve(name);
        }
        catch
        {
            return null;
        }
    }
}

并像这样使用它:

var assembly = AssemblyDefinition.ReadAssembly(path, new ReaderParameters() {
    AssemblyResolver = new IgnoringExceptionsAssemblyResolver()
});

This is improved @Nayan answer. If you want to ignore missing assemblies, copy this class:

using Mono.Cecil;

public class IgnoringExceptionsAssemblyResolver : DefaultAssemblyResolver
{
    public override AssemblyDefinition Resolve(AssemblyNameReference name)
    {
        try
        {
            return base.Resolve(name);
        }
        catch
        {
            return null;
        }
    }
}

and use it like that:

var assembly = AssemblyDefinition.ReadAssembly(path, new ReaderParameters() {
    AssemblyResolver = new IgnoringExceptionsAssemblyResolver()
});
分开我的手 2024-12-30 14:59:11

除了 JB Evain 的建议之外,此代码将有助于避免异常。您所要做的就是在解析器中处理异常。

我承认这不是最好的方法。但它适用于这种情况:“如果我在不存在引用的程序集的系统上反编译 DLL,则反编译会失败(有例外)。至少,我希望看到反编译代码,无论是什么已解决。”

using System;
using System.Collections.Generic;
using Mono.Cecil;

public class MyAssemblyResolver : BaseAssemblyResolver
{
    private readonly IDictionary<string, AssemblyDefinition> cache;
    public MyAssemblyResolver()
    {
        this.cache = new Dictionary<string, AssemblyDefinition>(StringComparer.Ordinal);
    }
    public override AssemblyDefinition Resolve(AssemblyNameReference name)
    {
        if (name == null)
            throw new ArgumentNullException("name");
        AssemblyDefinition assemblyDefinition = null;
        if (this.cache.TryGetValue(name.FullName, out assemblyDefinition))
            return assemblyDefinition;
        try  //< -------- My addition to the code.
        {
            assemblyDefinition = base.Resolve(name);
            this.cache[name.FullName] = assemblyDefinition;
        }
        catch { } //< -------- My addition to the code.
        return assemblyDefinition;
    }
    protected void RegisterAssembly(AssemblyDefinition assembly)
    {
        if (assembly == null)
            throw new ArgumentNullException("assembly");
        string fullName = assembly.Name.FullName;
        if (this.cache.ContainsKey(fullName))
            return;
        this.cache[fullName] = assembly;
    }
}

并像这样使用它:

var rp = new Mono.Cecil.ReaderParameters() { AssemblyResolver = new MyAssemblyResolver() };
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp);
var astBuilder = new ICSharpCode.Decompiler.Ast.AstBuilder(
     new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
astBuilder.AddAssembly(assemblyDefinition);

我实际上希望看到反编译器的增强功能:它当前忽略该用户的 ReaderParameters集,在DefaultAssemblyResolver 类。

用法:

var rp = new Mono.Cecil.ReaderParameters();
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp);

当前 DefaultAssemblyResolver 代码:

public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
    if (name == null)
    {
        throw new ArgumentNullException("name");
    }
    AssemblyDefinition assemblyDefinition;
    if (this.cache.TryGetValue(name.FullName, out assemblyDefinition))
    {
        return assemblyDefinition;
    }
    assemblyDefinition = base.Resolve(name); // <---------
// Is the `ReaderParameters` object set by user, used to resolve in `base` class?

    this.cache[name.FullName] = assemblyDefinition;
    return assemblyDefinition;
}

In addition to what JB Evain suggested, this code will help in avoiding the exception. All you have to do is handle the exception in resolver.

Not the best way, I admit. But it works for this scenario: "If I am decompiling a DLL on a system where the referred assemblies are not present, the decompilation fails (with exception.) At least, i would like to see the decompile code, for whatever has been resolved."

using System;
using System.Collections.Generic;
using Mono.Cecil;

public class MyAssemblyResolver : BaseAssemblyResolver
{
    private readonly IDictionary<string, AssemblyDefinition> cache;
    public MyAssemblyResolver()
    {
        this.cache = new Dictionary<string, AssemblyDefinition>(StringComparer.Ordinal);
    }
    public override AssemblyDefinition Resolve(AssemblyNameReference name)
    {
        if (name == null)
            throw new ArgumentNullException("name");
        AssemblyDefinition assemblyDefinition = null;
        if (this.cache.TryGetValue(name.FullName, out assemblyDefinition))
            return assemblyDefinition;
        try  //< -------- My addition to the code.
        {
            assemblyDefinition = base.Resolve(name);
            this.cache[name.FullName] = assemblyDefinition;
        }
        catch { } //< -------- My addition to the code.
        return assemblyDefinition;
    }
    protected void RegisterAssembly(AssemblyDefinition assembly)
    {
        if (assembly == null)
            throw new ArgumentNullException("assembly");
        string fullName = assembly.Name.FullName;
        if (this.cache.ContainsKey(fullName))
            return;
        this.cache[fullName] = assembly;
    }
}

And use it like this:

var rp = new Mono.Cecil.ReaderParameters() { AssemblyResolver = new MyAssemblyResolver() };
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp);
var astBuilder = new ICSharpCode.Decompiler.Ast.AstBuilder(
     new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
astBuilder.AddAssembly(assemblyDefinition);

I would actually like to see an enhancement in the decompiler: it currently ignores the ReaderParameters that user sets, in DefaultAssemblyResolver class.

Usage:

var rp = new Mono.Cecil.ReaderParameters();
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp);

Current DefaultAssemblyResolver code:

public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
    if (name == null)
    {
        throw new ArgumentNullException("name");
    }
    AssemblyDefinition assemblyDefinition;
    if (this.cache.TryGetValue(name.FullName, out assemblyDefinition))
    {
        return assemblyDefinition;
    }
    assemblyDefinition = base.Resolve(name); // <---------
// Is the `ReaderParameters` object set by user, used to resolve in `base` class?

    this.cache[name.FullName] = assemblyDefinition;
    return assemblyDefinition;
}
旧街凉风 2024-12-30 14:59:11

根据 Mono.Cecil 源代码,我猜测您可能可以使用 Mono.Cecil.DefaultAssemblyResolver 类来处理此问题。

而不是这个代码:

Mono.Cecil.AssemblyDefinition assemblyDefinition =
    Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly);

尝试这个:

Mono.Cecil.AssemblyDefinition assemblyDefinition =
    new Mono.Cecil.DefaultAssemblyResolver().Resolve(System.Reflection.AssemblyName.GetAssemblyName(pathToAssembly).ToString());

编辑

虽然我最初的建议可能有效也可能无效(我从未这样做过,所以不能保证),但您可能想研究一下Mono。 Mono.Addins 项目 中的 Addins.CecilReflector.dll 程序集可帮助缓解此类问题。它也基于 Mono.Cecil (就像 ILSpy 一样),因此即使 Mono.Addins 是一个可扩展性库的一般前提不能满足您的需求,它也可能包含一些用于您的目的或至少可以学习的代码。

Based on the Mono.Cecil source, I would guess that you could probably handle this using the Mono.Cecil.DefaultAssemblyResolver class.

Instead of this code:

Mono.Cecil.AssemblyDefinition assemblyDefinition =
    Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly);

try this:

Mono.Cecil.AssemblyDefinition assemblyDefinition =
    new Mono.Cecil.DefaultAssemblyResolver().Resolve(System.Reflection.AssemblyName.GetAssemblyName(pathToAssembly).ToString());

EDIT

While my original suggestion may or may not work (I've never done it, so no guarantees), you may want to look into the Mono.Addins.CecilReflector.dll assembly from the Mono.Addins project to help mitigate these sort of problems. It is also based on Mono.Cecil (just as ILSpy is) so even though the general premise that Mono.Addins is an extensibility library doesn't meet your needs it may contain some code use for your purposes or at least learn from.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文