2014-11-21 14 views
5

Chciałbym analizować zespoły .Net, aby były niezależne od języka C#, VB.NET lub cokolwiek innego.
Znam Roslyn i NRefactory, ale wydają się działać tylko na poziomie kodu źródłowego C#?
Istnieje również projekt "Common Compiler Infrastructure: Code Model and AST API" na CodePlex, który twierdzi, że "obsługuje hierarchiczny model obiektów, który reprezentuje bloki kodu w niezorganizowanej strukturze językowej", które brzmią dokładnie dla tego, czego szukam.
Jednak nie mogę znaleźć żadnej użytecznej dokumentacji lub kodu, który faktycznie to robi.
Każda rada, jak to zarchiwizować?
Czy Mono.Cecil może coś robi?Pobierz AST z zestawu .Net bez kodu źródłowego (kod IL)

Odpowiedz

0

O ile mi wiadomo, nie można budować AST z binarnego (bez źródeł), ponieważ AST jest generowany przez parser jako część procesu kompilacji ze źródeł. Mono.Cecil nie pomoże, ponieważ można modyfikować tylko opkody/metadane, a nie analizować zespołu.

Ale ponieważ jest. NET można zrzucić IL kodu z biblioteki dll za pomocą ildasm. Następnie możesz przekazać wygenerowane źródła do dowolnego parsera z podłączonym słownikiem CIL i uzyskać AST z parsera. Problem polega na tym, że o ile wiem, istnieje tylko jedna publicznie dostępna gramatyka CIL dla parsera, więc tak naprawdę nie masz wyboru. ECMA-355 jest wystarczająco duży, więc nie jest dobrym pomysłem napisanie własnej gramatyki. Więc mogę ci zaproponować tylko jedno rozwiązanie:

  1. Przepisz zestaw do ildasm.exe, aby uzyskać CIL.
  2. Następnie przechodzą CIL do ANTLR v3 parsera z this CIL gramatyki okablowany (zauważ, że to trochę przestarzałe - gramatyka utworzony w 2004 roku, a najnowsza specyfikacja CIL jest 2006, ale CIL naprawdę nie zmieni się znacznie)
  3. Po tym można swobodnie dostęp AST generowane przez ANTLR

pamiętać, że nie trzeba ANTLR v3 v4, ponieważ gramatyka napisana dla 3 wersji, a to prawie niemożliwe, aby przenieść go do V4 bez dobrej znajomości składni antlr.

Ponadto można spróbować spojrzeć na nowe Microsoft ryujit źródeł kompilatora na github (część CoreCLR) - ja nie wiem, że to pomaga, ale w teorii Musi zawiera CIL gramatycznych i parsera implementacje ponieważ działa z Kod CIL. Ale jest napisany w CPP, ma ogromną bazę kodu i brak dokumentacji, ponieważ jest w fazie rozwoju, więc może być łatwiej utknąć z ANTLR.

+0

Jeśli chcesz po prostu uzyskać IL, to podejście jest zbyt zawiłe. Korzystanie z Cecil będzie znacznie prostsze. – svick

0

Jeśli potraktujesz plik binarny .net jako strumień bajtów, powinieneś być w stanie "parsować" to dobrze.

Po prostu piszesz gramatykę, której tokeny są w zasadzie bajtami. Z pewnością możesz zbudować klasyczny lexer/parser z prawie każdym zestawem narzędzi lexer/parser, definiując lexer'a do czytania pojedynczych bajtów jako tokenów.

Następnie można zbudować AST za pomocą standardowych maszyn budujących AST dla silnika analizującego (samodzielnie dla YACC, automatycznie z ANTLR4).

Oczywiście odkryjesz, że "parsowanie" to za mało; nadal będziesz musiał budować tabele symboli i przeprowadzać analizy i analizy przepływu danych, jeśli zamierzasz przeprowadzić poważną analizę odpowiedniego kodu. Zobacz mój esej na temat LifeAfterParsing.

Będziesz także musiał wziąć pod uwagę "wyodrębnione" funkcje, które zapewniają kluczowe środowisko uruchomieniowe dla poszczególnych języków programowania, które faktycznie wygenerowały kod CIL. A to spowoduje, że twoje analizatory będą zależne od języka. Tak, nadal udostępniasz część analizy, która działa na ogólne CIL.

+0

Format pliku binarnego .NET nie kwalifikuje się jako wolny od kontekstu (lub kontekstowy), więc ani YACC, ani ANTLR nie będą w stanie wygenerować analizatora składni. –

+0

Gdzie jest to naruszenie c-free lub c-sensitive? –

+0

1) Lokalizacja różnych struktur w module jest określona za pomocą RVA (względny adres wirtualny), znalezienie przesunięcia w pliku wymaga mapowania za pomocą tabeli przekrojów. 2) Liczba wierszy dla każdej tabeli pojawia się przed każdym z nich, te liczby mogą mieć wpływ na określone szerokości kolumn. 3) Większość bibliotek DLL, które badałem, umieszcza metadane po ciałach funkcyjnych (choć nie jest to wymagane), więc jeśli czytasz je w trybie forward (np. Z wygenerowanym parserem), nie będziesz wiedział kiedy napotkano ciało metody długo po tym, jak je minął. –

2

Możesz to zrobić i jest też jeden (choć malutki) example of this w źródle ILSpy.

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

CCI Kod modelu jest gdzieś pomiędzy disassembler IL i pełne C# dekompilator: daje kod pewną strukturę (np if instrukcji i wyrażeń), ale zawiera również pewne operacje niskiego poziomu stosu jak push i pop.

CCI zawiera próbkę, która pokazuje: PeToText.

Na przykład, aby uzyskać kod modelu dla pierwszej metody typu Program (w globalnej przestrzeni nazw), można użyć kodu:

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

Aby wykazać, jeśli dekompilować wyżej kod i pokazać go za pomocą PeToText, masz zamiar dostać:

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

Godne uwagi są te wszystkie push, pop i dup oświadczenia i warunki buforowania lambda.

Powiązane problemy