2014-11-21 15 views
5

Ich möchte .NET-Assemblys analysieren, um sprachunabhängig von C#, VB.NET oder was auch immer zu sein.
Ich kenne Roslyn und NRefactory, aber sie scheinen nur auf C# Quellcode Ebene zu arbeiten?
Es gibt auch das Projekt "Common Compiler Infrastructure: Code Model and AST API" auf CodePlex, das behauptet, "unterstützt ein hierarchisches Objektmodell, das Code-Blöcke in einer sprachunabhängigen strukturierten Form darstellt", die genau nach dem klingen, wonach ich suche.
Ich bin jedoch nicht in der Lage, irgendeine nützliche Dokumentation oder Code zu finden, der dies tatsächlich tut.
Irgendwelche Ratschläge, wie man das archiviert?
Kann Mono.Cecil vielleicht etwas tun?Erhalten Sie AST von .Net-Assembly ohne Quellcode (IL-Code)

Antwort

0

Soweit ich weiß, ist es nicht möglich, AST aus Binär (ohne Quellen) aufzubauen, da AST selbst von Parser als Teil des Kompilierungsprozesses aus Quellen generiert wird. Mono.Cecil wird nicht helfen, weil Sie Opcodes/Metadaten nur mit ihnen ändern können, nicht Assembly analysieren.

Aber da es .NET ist, können Sie IL-Code aus DLL mit Hilfe von Ildasm dump. Dann können Sie generierte Quellen an jeden Parser übergeben, der CIL-Wörterbücher angeschlossen hat und AST vom Parser erhalten. Das Problem ist, dass es, soweit ich weiß, nur eine öffentlich verfügbare CIL-Grammatik für Parser gibt, so dass Sie nicht wirklich eine Wahl haben. Und ECMA-355 ist groß genug, so dass es keine gute Idee ist, eine eigene Grammatik zu schreiben. So kann ich Ihnen nur eine Lösung vorschlagen:

  1. Pass Montage Ildasm.exe CIL zu bekommen.
  2. Dann CIL passieren zu ANTLR v3 Parser mit this CIL Grammatik verdrahtet (beachten Sie, es ist ein bisschen veraltet - Grammatik 2004 und aktuelle CIL-Spezifikation 2006 erstellt, aber CIL ändert sich nicht wirklich viel)
  3. Danach Sie frei AST zugreifen können
  4. von ANTLR generiert

Beachten Sie, dass ANTLR müssen v3 nicht v4, da Grammatik für die 3. Version geschrieben, und es ist kaum möglich, zu portieren, ohne gute Kenntnisse der ANTLR Syntax in v4.

Sie können auch versuchen, in neuen Microsoft ryujit Compiler Quellen bei Github (Teil CoreCLR) zu sehen - ich bin nicht sicher tun, dass es hilft, aber in der Theorie muss es enthält CIL Grammatik und Parser-Implementierungen, da es mit funktioniert CIL-Code. Aber es ist in CPP geschrieben, hat enorme Codebasis und fehlende Dokumentation, da es sich in der aktiven Entwicklungsphase befindet, also ist es vielleicht einfacher, mit ANTLR zu arbeiten.

+0

Wenn Sie nur die IL erhalten möchten, ist dieser Ansatz viel zu kompliziert. Die Verwendung von Cecil wird viel einfacher sein. – svick

0

Wenn Sie die .net-Binärdatei als einen Strom von Bytes behandeln, sollten Sie in der Lage sein, es gut zu "parsen".

Sie schreiben einfach eine Grammatik, deren Token im Wesentlichen Bytes sind. Sie können sicherlich einen klassischen Lexer/Parser mit fast jedem Satz von Lexer/Parser-Tools erstellen, indem Sie den Lexer so definieren, dass einzelne Bytes als Token gelesen werden.

Sie können dann den AST unter Verwendung von AST-Standardmaschinen für die Parsing-Engine erstellen (auf eigene Faust für YACC, automatisch mit ANTLR4).

Was Sie entdecken werden, ist natürlich, dass "Parsing" nicht genug ist; Sie müssen immer noch Symboltabellen erstellen und Kontroll- und Datenflussanalysen durchführen, wenn Sie eine ernsthafte Analyse des entsprechenden Codes durchführen. Siehe meinen Aufsatz über LifeAfterParsing.

Sie müssen wahrscheinlich auch "distinguierte" Funktionen in Betracht ziehen, die den bestimmten Programmiersprachen, die tatsächlich den CIL-Code erzeugt haben, wichtige Laufzeitfunktionen zur Verfügung stellen. Und diese machen Ihre Analysatoren sprachabhängig. Ja, Sie können den Teil der Analyse, der an der generischen CIL funktioniert, noch teilen.

+0

Das .NET-Binärdateiformat gilt nicht als kontextfrei (oder kontextsensitiv), sodass weder YACC noch ANTLR einen Parser dafür generieren können. –

+0

Wo verletzt es c-free oder c-sensitive? –

+0

1) Die Position verschiedener Strukturen innerhalb des Moduls wird mit einer RVA (relative virtuelle Adresse) angegeben. Das Auffinden des Offsets in der Datei erfordert die Zuordnung über die Schnitttabelle. 2) Die Zeilenanzahl für jede Tabelle wird vor jedem von ihnen angezeigt. Diese Anzahl kann sich auf bestimmte Spaltenbreiten auswirken. 3) Die meisten DLLs, die ich untersucht habe, platzieren die Metadaten nach den Funktionskörpern (obwohl dies nicht erforderlich ist). Wenn Sie es also nur vorwärts lesen (z. B. mit einem generierten Parser), wissen Sie nicht wann Sie haben einen Methodenkörper erst lange nach dem Überschreiten gefunden. –

2

Sie können dies tun und es gibt auch eine (wenn auch winzige) example of this in der Quelle von ILSpy.

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

Das CCI-Code-Modell ist irgendwo zwischen einem IL-Disassembler und vollen C# Decompiler: es Ihrem Code eines gewissen Struktur gibt (zum Beispiel if Anweisungen und Ausdrücke), aber es enthält auch einige geringen Stapeloperationen wie push und pop.

CCI enthält ein Beispiel, das dies zeigt: PeToText.

Zum Beispiel Codemodell für das erste Verfahren des Program Typs (im globalen Namespace) zu erhalten, können Sie folgenden Code verwenden könnten:

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

Um zu zeigen, wenn Sie die oben decompile Code und zeigen Sie es PeToText verwenden, Sie gehen zu bekommen:

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

Bemerkenswert sind alle push, pop und dup Aussagen und der Lambda-Caching-Zustand.