从.Net程序集中获取AST,无需源代码(IL代码)

时间:2014-11-21 12:02:21

标签: abstract-syntax-tree il roslyn mono.cecil nrefactory

我想分析.Net程序集与C#,VB.NET或其他任何语言无关。
我知道Roslyn和NRefactory,但他们似乎只在C#源代码级别上工作? 还有" Common Compiler Infrastructure: Code Model and AST API" CodePlex上的项目声称"支持分层对象模型,该模型代表与语言无关的结构化形式的代码块。这听起来正是我想要的。
但是,我无法找到实际执行此操作的任何有用文档或代码。
有什么建议如何存档?
Mono.Cecil可以做点什么吗?

4 个答案:

答案 0 :(得分:2)

你可以这样做,ILSpy的来源中也有一个(虽然很小)example of this

var assembly = AssemblyDefinition.ReadAssembly("path/to/assembly.dll");
var astBuilder = new AstBuilder(new DecompilerContext(assembly.MainModule));
decompiler.AddAssembly(assembly);
astBuilder.SyntaxTree...

答案 1 :(得分:1)

CCI代码模型介于IL反汇编程序和完整的C#反编译器之间:它为您的代码提供了一些结构(例如if语句和表达式),但它还包含一些低级别的堆栈操作,如{{1} }和push

CCI包含一个示例:PeToText

例如,要获取pop类型的第一个方法(在全局命名空间中)的代码模型,您可以使用如下代码:

Program

为了演示,如果你反编译上面的代码并使用PeToText显示它,你就会得到:

string fileName = "whatever.exe";

using (var host = new PeReader.DefaultHost())
{
    var module = (IModule)host.LoadUnitFrom(fileName);
    var type = (ITypeDefinition)module.UnitNamespaceRoot.Members
        .Single(m => m.Name.Value == "Program");
    var method = (IMethodDefinition)type.Members.First();
    var methodBody = new SourceMethodBody(method.Body, host, null, null);
}

值得注意的是所有Microsoft.Cci.ITypeDefinition local_3; Microsoft.Cci.ILToCodeModel.SourceMethodBody local_5; string local_0 = "C:\\code\\tmp\\nuget tmp 2015\\bin\\Debug\\nuget tmp 2015.exe"; Microsoft.Cci.PeReader.DefaultHost local_1 = new Microsoft.Cci.PeReader.DefaultHost(); try { push (Microsoft.Cci.IModule)local_1.LoadUnitFrom(local_0).UnitNamespaceRoot.Members; push Program.<>c.<>9__0_0; if (dup == default(System.Func<Microsoft.Cci.INamespaceMember, bool>)) { pop; push Program.<>c.<>9.<Main0>b__0_0; Program.<>c.<>9__0_0 = dup; } local_3 = (Microsoft.Cci.ITypeDefinition)System.Linq.Enumerable.Single<Microsoft.Cci.INamespaceMember>(pop, pop); local_5 = new Microsoft.Cci.ILToCodeModel.SourceMethodBody((Microsoft.Cci.IMethodDefinition)System.Linq.Enumerable.First<Microsoft.Cci.ITypeDefinitionMember>(local_3.Members).Body, local_1, (Microsoft.Cci.ISourceLocationProvider)null, (Microsoft.Cci.ILocalScopeProvider)null, 0); } finally { if (local_1 != default(Microsoft.Cci.PeReader.DefaultHost)) { local_1.Dispose(); } } pushpop语句以及lambda缓存条件。

答案 2 :(得分:0)

据我所知,从二进制文件(没有源代码)构建AST是不可能的,因为AST本身是由解析器生成的,作为源代码编译过程的一部分。 Mono.Cecil没有帮助,因为你只能用它们修改操作码/元数据,而不是分析汇编。

但是因为它是.NET,你可以在ildasm的帮助下从dll转储IL代码。然后,您可以将生成的源传递给任何解析器,并连接CIL字典并从解析器获取AST。问题是,据我所知,解析器只有一个公开可用的CIL语法,所以你真的没有选择。 ECMA-355足够大,所以编写自己的语法是个坏主意。 所以我建议你只有一个解决方案:

  1. 将程序集传递给ildasm.exe以获取CIL。
  2. 然后将CIL传递给ANTLR v3解析器,并将this CIL语法连接起来(注意它有点过时了 - 2004年创建的语法和最新的CIL规范是2006年,但CIL并没有真正改变为多)
  3. 之后您可以自由访问ANTLR生成的AST
  4. 请注意,您需要ANTLR v3而不是v4,因为为第3版编写了语法,并且在不了解ANTLR语法的情况下几乎不可能将其移植到v4。

    此外,您可以尝试在github(CoreCLR的一部分)中查看新的Microsoft ryujit 编译器源代码 - 我不确定它是否有帮助,但理论上它必须包含CIL语法和解析器实现因为它适用于CIL代码。但它是用CPP编写的,具有庞大的代码库和缺乏文档,因为它处于活跃的开发阶段,因此使用ANTLR可能更容易。

答案 3 :(得分:0)

如果将.net二进制文件视为字节流,则应该能够“解析”它。

你只需编写一个语法,其标记基本上是字节。您当然可以通过定义词法分析器将单个字节作为标记来构建几乎任何词法分析器/解析器工具的经典词法分析器/解析器。

然后,您可以使用标准AST构建机制为解析引擎构建AST(对于YACC,您自己使用ANTLR4自行构建)。

当然,你会发现“解析”是不够的;如果要对相应的代码进行认真分析,您仍需要构建符号表,并执行控制和数据流分析。请参阅我关于LifeAfterParsing的文章。

您还可能必须考虑“杰出”函数,这些函数为实际生成CIL代码的特定编程语言提供关键运行时功能。这些将使您的分析仪依赖于语言。是的,您仍然可以分享适用于通用CIL的分析部分。