ReadyToRun-(R2R-)Assemblies dekompilieren: warum vorkompilierter nativer Code sauber dekompiliert
ReadyToRun-Images tragen vorkompilierten nativen Code — und dekompilieren dennoch so leicht zu C# wie jede Assembly, weil das IL weiterhin vorhanden ist. Hier steht, was R2R tatsächlich speichert, wie man es erkennt und warum ein Dekompilierer die native Seite ignoriert.
Wenn man Ihnen je eine „vorkompilierte“ .NET-Assembly gereicht und Sie angenommen haben, ein Dekompilierer würde daran scheitern, ist ReadyToRun die angenehme Überraschung. R2R ist die Art, wie .NET schnell startenden Code ausliefert — die Laufzeitbibliotheken selbst sind R2R, und PublishReadyToRun=true aktiviert es für Ihre Apps und Container-Images — und es klingt, als müsste es die Dekompilierung vereiteln, weil es AOT-kompilierten nativen Code enthält. Tut es nicht. Eine R2R-Assembly dekompiliert zu sauberem C#, weil das Einzige, was R2R nie verwirft, das IL ist. Zu verstehen, warum, macht eine ganze Kategorie von „Kann ich das lesen?“-Fragen leicht beantwortbar.
Was ein R2R-Image tatsächlich enthält
Eine normale .NET-Assembly ist Metadaten + IL, beim ersten Lauf vom JIT in nativen Code übersetzt. Ein R2R-Image wird von crossgen2 erzeugt, das diesen JIT im Voraus ausführt und den resultierenden nativen Code in der Assembly speichert, damit die Laufzeit den JIT beim Start überspringen kann. Entscheidend: Es speichert den nativen Code zusätzlich zu, nicht anstelle der ursprünglichen Metadaten und des IL.
Die PE-Datei enthält also drei hier wichtige Dinge: die ECMA-335-Metadatentabellen (Typen, Methoden, Signaturen — unberührt), die IL-Methodenrümpfe (ebenfalls vorhanden) und einen ReadyToRun-Abschnitt mit dem vorkompilierten nativen Code plus den Tabellen, die die Laufzeit zu dessen Nutzung braucht. Ein ReadyToRunHeader, über das Verzeichnis für verwaltet-nativen Header des CLI-Headers erreichbar, verbindet alles und kennzeichnet das Image als R2R.
Warum der Dekompilierer die native Seite ignoriert
Ein Dekompilierer wie Glass ist ein IL-zu-C#-Rekonstrukteur: Er durchläuft die Metadatentabellen, um Typen und Signaturen wiederherzustellen, und liest dann den IL-Rumpf jeder Methode und hebt ihn zurück nach C#. R2Rs nativer Code liegt in einem separaten Abschnitt mit eigenen Nachschlagetabellen, und der Dekompilierer zieht ihn schlicht nicht heran: Das IL ist die maßgebliche, portable Definition jeder Methode, und es liegt noch genau dort. Deshalb ist das Öffnen eines R2R-Images aus Sicht des Dekompilierers nicht vom Öffnen einer reinen JIT-Assembly zu unterscheiden: dieselben Metadaten, dasselbe IL, dasselbe C# als Ausgabe.
Darum können Sie die .NET-Laufzeitbibliotheken (die als R2R ausgeliefert werden), eine PublishReadyToRun-App oder ein crossgen-Container-Image ohne besondere Behandlung dekompilieren. Die Sorge „es ist vorkompiliert“ betrifft die nativen Bytes, und die liest ein Dekompilierer nicht.
R2R erkennen, und die eine echte Ausnahme
Um zu bestätigen, dass eine Assembly R2R ist, suchen Sie den ReadyToRun-Header (Werkzeuge kennzeichnen ihn als „ReadyToRun“ und zeigen oft eine R2R-Versionsnummer) oder kennen Sie einfach die Herkunft: crossgen2 / PublishReadyToRun. Dass das IL vorhanden ist, ist die Garantie, dass Sie es dekompilieren können.
Die Ausnahme, die verwirrt, ist NativeAOT, das ähnlich klingt, aber der umgekehrte Fall ist. NativeAOT kompiliert die gesamte App zu einer eigenständigen nativen ausführbaren Datei ohne IL und ohne verwaltete Metadaten zum Wiederherstellen — es gibt nichts, das ein IL-Dekompilierer lesen könnte. Der Schnelltest: Kann ein Werkzeug die Datei überhaupt als .NET-Assembly laden, ist sie verwaltet (normales IL oder R2R) und dekompiliert; ist sie nur eine native Binärdatei, ist es NativeAOT, und Sie haben eine Aufgabe für einen nativen Disassembler, nicht für die Dekompilierung. (Wie sich das auf Ihr Bedrohungsmodell als Anbieter auswirkt, steht in .NET-Code vor Dekompilierung schützen.)
Mit Glass umsetzen
Glass öffnet ReadyToRun-Assemblies transparent — es liest das intakte IL und rekonstruiert C#, wobei es den nativen R2R-Abschnitt ignoriert, sodass die Laufzeitbibliotheken, ein crossgen-Dienst oder eine PublishReadyToRun-App wie jede andere Assembly lesbar sind. Es ist derselbe Ablauf wie eine DLL zu C# dekompilieren; R2R stellt sich einfach nicht in den Weg.
Glass ist kostenlos: Richten Sie es auf ein R2R-Image und sehen Sie das IL als C# zurückkommen.
Nebula.NET testen
Härten Sie Ihren .NET-Code in wenigen Minuten — starten Sie mit der kostenlosen Edition.