Skip to content
← Alle Beiträge
· Delta1 Labs Glass.NETDekompilierungAnleitung.NET

Unbekannten .NET-Fremdcode mit Glass.NET verstehen

Sie hängen von einer DLL ab, für die Sie keinen Quellcode haben — ein verwaistes Paket, eine interne Bibliothek, deren Autor weg ist, ein Anbieter-SDK mit dünner Doku — und müssen wissen, wie sie wirklich funktioniert. Hier ist ein konkreter Untersuchungsablauf in Glass.NET, dem kostenlosen, rein statischen .NET-Decompiler, der das inspizierte Assembly nie ausführt.

Früher oder später lehnt sich jedes .NET-Projekt an eine DLL an, die niemand erklären kann. Das Paket wurde vor drei Jahren aufgegeben, und sein Repository ist verschwunden. Die interne Hilfsbibliothek schrieb jemand, der die Firma längst verlassen hat. Das SDK des Anbieters kommt mit einer Doku, die genau dort aufhört, wo Ihre Frage beginnt. Das Verhalten ist falsch, oder überraschend, oder schlicht undokumentiert — und das Einzige, was es klären würde, der Quellcode, ist das Einzige, was Sie nicht haben.

Sie brauchen ihn nicht. Ein kompiliertes .NET-Assembly trägt eine nahezu vollständige, wiederherstellbare Beschreibung dessen, was es tut: IL plus reiche Metadaten, die jeden Typ, jede Methode und jedes Feld benennen. Glass.NET liest das und rekonstruiert lesbares C#, und macht aus „ich habe keine Ahnung, was diese Abhängigkeit tut” ein „lass mich die Methode lesen und es herausfinden”. Es ist kostenlos und streng statisch — es liest Metadaten und IL und rekonstruiert C#, und führt das inspizierte Assembly nie aus. Dieser letzte Punkt zählt hier am meisten: Sie können Glass auf eine Abhängigkeit richten, der Sie nicht vertrauen, weil nichts in der DLL je läuft.

Machen wir es konkret. Nehmen wir an, Sie hängen von einem verwaisten NuGet-Paket ab — nennen wir es Acme.Resilience — das Ihre ausgehenden HTTP-Aufrufe in eine Wiederholungsrichtlinie hüllt. Zeitweise hängt ein Aufruf, der schnell hätte scheitern sollen, fast eine Minute; ein anderer, der hätte wiederholen sollen, gibt sofort auf. Die Doku ist ein Absatz in einer toten README. Sie müssen wissen, was RetryPolicy.ExecuteAsync tatsächlich tut: wie es seinen Backoff berechnet, welche Ausnahmen es als wiederholbar behandelt und ob es je von sich aus mit dem Netzwerk spricht. So finden Sie es heraus.

Legen Sie das Assembly auf die Platte und öffnen Sie es

Finden Sie zuerst die DLL. Für eine NuGet-Abhängigkeit liegt sie im globalen Paket-Cache — %USERPROFILE%\.nuget\packages\<id>\<version>\lib\<tfm>\ unter Windows, ~/.nuget/packages/... sonst. Für eine interne Bibliothek oder eine Anbieterlieferung liegt sie dort, wo Ihr Build sie hinkopiert hat, meist in bin/.

Öffnen Sie sie in Glass.NET: Ziehen Sie die Datei auf das Fenster oder Datei ▸ Assembly öffnen. Glass lädt die Metadaten und listet das Assembly in einem Baum. Sie brauchen weder ein passendes PDB noch den originalen Quellcode — eine schlichte verwaltete .dll genügt, und Glass rekonstruiert das C# ohne Symbole (mehr dazu in ohne PDB-Symbole dekompilieren). Und weil Öffnen, Lesen und Exportieren allesamt statisch sind, ist dieser erste Schritt bereits sicher: Ein Paket, das Sie zögern würden auszuführen, lässt sich bedenkenlos öffnen, weil Glass es nie ausführt.

Orientieren Sie sich, bevor Sie lesen

Widerstehen Sie dem Drang, direkt in eine Methode zu springen. Nehmen Sie sich dreißig Sekunden, um zuerst die Form der Sache zu erfassen. Klappen Sie das Assembly in seinen Baum aus Namespaces ▸ Typen ▸ Member auf — er spiegelt die Struktur wider, die der Compiler aufgezeichnet hat, sodass Sie sofort sehen, ob Sie es mit drei ordentlichen Typen oder einem Wust von hundert zu tun haben. Für Acme.Resilience würden Sie eine kleine öffentliche Oberfläche erwarten — RetryPolicy, vielleicht ein RetryOptions, einen Ausnahmetyp oder zwei — über einer Handvoll interner Helfer.

Die Assembly-Informationsansicht ist das andere Orientierungswerkzeug, und sie beantwortet eine Frage, die Sie früh geklärt haben wollen: Was kann dieses Ding überhaupt tun? Sie zeigt das Zielframework, die Produktattribute und — der nützliche Teil — die referenzierten Assemblys. Wenn ein „Wiederholungshelfer” System.Net.Http referenziert, ist das erwartbar; wenn er System.Diagnostics.Process referenziert oder etwas, das in einer Backoff-Bibliothek nichts zu suchen hat, haben Sie etwas gelernt, bevor Sie eine einzige Zeile gelesen haben. Der Expositionsbericht von Glass ist hier ein guter Begleiter: Lassen Sie ihn laufen, um einzuschätzen, wie lesbar das Assembly ist. Eine gewöhnliche Open-Source-artige Bibliothek sollte sich klar lesen; kommt sie stark obfuskiert zurück, ist das selbst ein Signal, dem man nachgehen sollte. (Das ist die Sicht von der anderen Seite des Schwester-Obfuskators Nebula.NET, dessen Code-Virtualisierung ein Pro-Feature ist — aber eine gewöhnliche Abhängigkeit hat keinen Grund, so auszusehen.)

Dekompilieren Sie den Einstiegspunkt

Gehen Sie nun zu dem Code, den Sie tatsächlich aufrufen. Der schnellste Weg ist Gehe zu allem (Ctrl+T), das unscharfe Schnellöffnen über jeden Typ und Member: Tippen Sie ExecuteAsync und springen Sie direkt dorthin, ohne den Baum zu durchscrollen. Klicken Sie den Member an, und Glass dekompiliert ihn bei Bedarf zu C#.

Lesen Sie ExecuteAsync als den Einstiegspunkt, und lesen Sie ihn fokussiert — Sie versuchen nicht, die ganze Bibliothek zu verstehen, nur den Weg, den Ihr Aufruf durch sie nimmt. Sie sehen die Wiederholungsschleife, den Aufruf dessen, was auch immer die Verzögerung berechnet, das Prädikat, das entscheidet, ob eine Ausnahme wiederholbar ist, und wo die eigentliche Arbeit aufgerufen wird. Das allein beantwortet oft die Frage. Aber die interessante Logik wohnt meist eine Ebene tiefer, in den Helfern, die diese Methode aufruft — also folgen Sie ihnen.

Folgen Sie den Aufrufen, um den Kontrollfluss zu verstehen

Hier verdient sich ein navigationsorientierter Decompiler seinen Platz, denn unbekannten Code zu verstehen besteht vor allem darin, Aufrufen zu folgen, nicht eine Methode von oben nach unten zu lesen.

  • Gehe zu Definition (F12 oder Ctrl+Klick) auf den Aufruf des Verzögerungshelfers bringt Sie direkt hinein. Jetzt können Sie genau lesen, wie der Backoff berechnet wird — und ob jenes Hängen, das Sie gesehen haben, eine exponentielle Verzögerung ohne Obergrenze ist, sodass eine späte Wiederholung konstruktionsbedingt 60 Sekunden wartet.
  • Alle Verweise suchen (Shift+F12) auf dem Feld RetryOptions.MaxAttempts, das in das Ergebnisfenster strömt, zeigt überall, wo das Limit gelesen wird — so bestätigen Sie, ob der Fall „gibt sofort auf” ein Standardwert von 1 ist, der an einer unerwarteten Stelle wortwörtlich befolgt wird.
  • Aufruf- und Typhierarchie lassen Sie die Struktur auf- und abwärts durchlaufen — wer die Methode aufruft, in der Sie sind, wovon ein gegebener Typ erbt oder was er implementiert — weit schneller, als aus Namen zu raten.

Verweise werden aus dem eigenen Syntaxbaum des Decompilers aufgelöst, sodass F12 auf dem richtigen Symbol landet und nicht auf einer Textübereinstimmung. Der Navigationsverlauf und ein Brotkrümelpfad aus Namespace ▸ Typ ▸ Member halten Sie beim Bewegen orientiert, sodass Sie denselben Weg zurückgehen können.

Steigen Sie zur IL hinab, wo das C# mehrdeutig ist

Eine async-Methode wie ExecuteAsync wird aus einer vom Compiler generierten Zustandsmaschine rekonstruiert. Der Decompiler erkennt dieses Lowering und gibt Ihnen meist sauberes async/await-C# zurück — aber „meist” ist der Haken. Wenn sich das rekonstruierte C# seltsam liest oder Sie nicht genau unterscheiden können, wo eine Ausnahme gefangen wird und wo die erwartete Verzögerung in der Zustandsmaschine sitzt, schalten Sie denselben Member auf die IL-Ansicht. Die IL ist das, was tatsächlich läuft, also ist sie die Grundwahrheit; sie zu lesen ist der Schiedsrichter, wenn das C# mehrdeutig ist. Sie müssen nicht in der IL wohnen — greifen Sie bei der Handvoll Methoden dazu, bei denen die übergeordnete Ansicht ein Detail abrundet, auf das es ankommt. (Die IL lesen geht durch, was Sie da vor sich haben, und wie man eine .NET-DLL zu C# dekompiliert deckt den breiteren Arbeitsablauf ab.)

Exportieren Sie es in ein Projekt für einen gründlichen Blick

Wenn Sie mehr tun wollen als lesen — das ganze Assembly mit grep nach jedem HttpClient, Task.Delay oder Process.Start durchsuchen, einen Analyzer darauf richten, anmerken, was Sie herausgefunden haben, oder die Bibliothek als dauerhafte Referenz auf der Platte behalten — exportieren Sie es in ein baubares Projekt. In der Oberfläche ist das Datei ▸ In Projekt exportieren; aus einem Skript ist es der Unterbefehl glass export:

glass export Acme.Resilience.dll --output ./Acme.Resilience.Source

Das schreibt eine .csproj plus die rekonstruierten .cs-Dateien und die extrahierten Ressourcen — ein Projekt, das Ihre Toolchain versteht, kein Abladen. Eine kleine Bibliothek wie diese kompiliert meist direkt wieder; eine große oder ungewöhnliche braucht vielleicht einen kurzen manuellen Durchgang für ungelöste Verweise oder exotischen, vom Compiler generierten Code. So oder so haben Sie nun den Quellcode auf der Platte, um ihn in Ihrer eigenen IDE zu erkunden, genau als hätte ihn der Autor ausgeliefert — und, auch hier, der Export hat die Binärdatei nie ausgeführt, um ihn zu erzeugen. Die ausführliche Betrachtung dazu steht in eine DLL zurück in ein baubares Projekt verwandeln.

Der Punkt

Eine unbekannte Abhängigkeit ist keine Blackbox. Öffnen Sie sie, orientieren Sie sich mit dem Typbaum und der Assembly-Information, dekompilieren Sie den Einstiegspunkt, folgen Sie den Aufrufen, um den echten Kontrollfluss zu sehen, steigen Sie zur IL hinab, wo das C# mehrdeutig ist, und exportieren Sie in ein Projekt, wenn Sie es auf der Platte wollen. An einem Nachmittag können Sie genau wissen, was Acme.Resilience mit Ihren HTTP-Aufrufen tut — den ungedeckelten Backoff, das übereifrige Aufgeben, was auch immer es sich herausstellt zu sein — statt aus dem Verhalten zu raten. Und weil Glass.NET kostenlos und streng statisch ist, haben Sie das alles getan, ohne eine einzige Zeile Code auszuführen, dem Sie nicht vertrauten. Laden Sie Glass.NET herunter, öffnen Sie die DLL, die Sie verwirrt, und lesen Sie sie selbst.

Nebula.NET testen

Härten Sie Ihren .NET-Code in wenigen Minuten — starten Sie mit der kostenlosen Edition.