2014-11-21 69 views
5

我想分析.Net程序集與C#,VB.NET或其他語言無關。
我知道Roslyn和NRefactory,但他們似乎只能在C#源代碼級別上工作?
CodePlex上還有一個「Common Compiler Infrastructure: Code Model and AST API」項目,聲稱它「支持以獨立於語言的結構化形式表示代碼塊的分層對象模型」,該聲音完全符合我的需求。
但是,我無法找到任何有用的文檔或代碼,實際上是這樣做的。
任何意見如何歸檔?
Can Mono.Cecil可能會做些什麼?從沒有源代碼的.Net程序集(IL代碼)獲取AST

回答

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. 之後,你可以自由地訪問AST由ANTLR

請注意,您將需要ANTLR v3的不V4,因爲語法的第3版本寫的,這是幾乎不可能將它移植到無ANTLR語法的良好的知識v4的產生。

你也可以嘗試尋找到新的Microsoft ryujit編譯器源在github上(CoreCLR的一部分) - 我不知道它的幫助,但在理論上它必須包含CIL的語法和解析器實現,因爲它的工作原理CIL代碼。但是它是用CPP編寫的,由於它處於積極的開發階段,因此代碼量巨大,缺乏文檔,因此可能更容易陷入ANTLR。

+0

如果你只想獲得IL,這種方法太複雜了。使用塞西爾會簡單得多。 – svick 2015-05-16 13:26:54

0

如果您將.net二進制文件視爲一個字節流,您應該能夠「解析」它就好了。

您只需編寫一個語法標記本質上是字節的語法。通過定義詞法分析器來讀取單個字節作爲標記,您當然可以使用幾乎任何詞法分析器/分析器工具來構建經典的詞法分析器/分析器。

然後,您可以使用用於解析引擎的標準AST構建機器(對YACC自動使用ANTLR4自動構建AST)來構建AST。

你會發現,當然,「解析」是不夠的;如果您要對相應的代碼進行認真分析,您仍然需要構建符號表,並執行控制和數據流分析。查看我在LifeAfterParsing上的散文。

您還可能需要考慮爲特定編程語言提供關鍵運行時功能的「區分」功能,這些編程語言實際上會生成CIL代碼。這些將使您的分析儀依賴於語言。是的,你仍然可以分享對通用CIL有效的分析部分。

+0

.NET二進制文件格式沒有限定爲上下文無關(或對上下文敏感),因此YACC和ANTLR都不能爲其生成解析器。 – 2015-02-22 20:56:34

+0

它在哪裏違反c-free或c-sensitive? – 2015-02-22 22:21:29

+0

1)使用RVA(相對虛擬地址)指定模塊內各種結構的位置,找到文件內的偏移量需要通過節表進行映射。 2)每個表的行數出現在其中的任何一個之前,這些計數會影響某些列的寬度。 3)我已經檢查過的大多數dll都將元數據放置在函數體之後(儘管這不是必需的),所以如果您僅以前向方式(例如使用生成的解析器)讀取它,則不會知道何時你已經遇到了一個方法體,直到你通過它爲止。 – 2015-02-23 11:13:58

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

CCI的代碼模型是某處IL反彙編器和完整的C#反編譯工具之間:它使你的代碼中的一些結構(例如if語句和表達式),但它也包含像pushpop一些低級別的堆棧操作。

CCI包含一個顯示此項的示例:PeToText

例如,要獲取代碼模型的Program類型的第一個方法(在全局命名空間),你可以使用這樣的代碼:

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

爲了證明,如果你反編譯以上代碼,並顯示它使用PeToText,你會得到:

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

值得注意的是所有這些pushpopdup語句和lambda緩存條件。