2009-03-22 19 views
16

Warum ist es so einfach, .NET-IL-Code in Quellcode zu dekompilieren, im Vergleich zu nativen x86-Binärdateien zu dekompilieren? (Reflektor erzeugt die meiste Zeit ziemlich guten Quellcode, während das Dekompilieren der Ausgabe eines C++ - Compilers fast unmöglich ist.)Warum ist es so einfach, .NET IL Code zu dekompilieren?

Ist das so, weil IL viele Metadaten enthält? Oder liegt es daran, dass IL eine höhere Abstraktion ist als x86-Anweisungen? Ich recherchierte und fand die folgenden zwei nützlichen Artikel, aber keiner von ihnen beantwortet meine Frage.

+0

Es gibt (oder waren) ziemlich gute Decompiler für C/C++, mit Bibliotheks-Plugins für verschiedene Versionen von Watcom, Borland, Microsoft und anderen gängigen Compilern. Z.B. IDA. Das macht jedoch nicht das, was Sie falsch sagen, CLI * ist * eine abstraktere, höhere, aber sauberere Laufzeitumgebung als x86. –

+0

IDA ist kein Decompiler, obwohl das Unternehmen, das es erstellt, einen Decompiler namens hex-rays erstellt. Die Qualität der x86-Dekompilierung ist viel niedriger als die Qualität der Dekompilierung von jvm oder msil. –

Antwort

24

Ich denke, Sie haben die wichtigsten Bits bereits.

  • Wie Sie sagen, gibt es mehr Metadaten zur Verfügung. Ich kenne nicht die Details von, was von einem C- oder C++ - Compiler ausgegeben wird, aber ich vermute weit mehr Namen und ähnliche Informationen sind in IL enthalten. Schauen Sie sich einmal an, was der Decompiler zum Beispiel über einen bestimmten Stack-Frame weiß - was den x86 betrifft, wissen Sie nur, wie der Stack verwendet wird; in IL wissen Sie, was der Inhalt des Stapels (oder zumindest der Typ - nicht die semantische Bedeutung!)
  • Wieder, wie Sie bereits erwähnt haben, ist IL eine höhere Abstraktion als x86. x86 hat keine Ahnung, was eine Methode oder ein Funktionsaufruf ist, oder ein Ereignis oder eine Eigenschaft usw. IL hat alle diese Informationen noch in sich.
  • In der Regel C und C++ - Compiler optimieren viel stärker als (sagen wir) den C# -Compiler. Dies liegt daran, dass der C# -Compiler annimmt, dass der Großteil der Optimierung später noch ausgeführt werden kann - durch das JIT.In gewisser Hinsicht macht es Sinn, dass der C# -Compiler nicht versucht, viel Optimierung zu machen, da es verschiedene Informationsbits gibt, die dem JIT, aber nicht dem C# -Compiler zur Verfügung stehen. Optimierter Code ist schwerer zu dekompilieren, weil er weit davon entfernt ist, eine natürliche Darstellung des ursprünglichen Quellcodes zu sein.
  • IL wurde entwickelt, um JIT kompiliert zu werden; x86 wurde entwickelt, um nativ ausgeführt zu werden (freilich über Mikrocode). Die Informationen, die der JIT-Compiler benötigt, sind ähnlich denen, die ein Decompiler haben möchte, also hat ein Decompiler eine einfachere Zeit mit IL. In mancher Hinsicht ist dies wirklich nur eine Neuformulierung des zweiten Punktes.
+4

Bonus Grund: IL muss nachweislich typsicher sein, was die Arten der verfügbaren Optimierungen einschränkt, sonst kann der Verifier nicht sagen "Yup, dieser Code bricht keine Regeln. Ich lasse ihn laufen." –

4

C# und IL one-to-one fast kartieren. (Dies ist weniger mit einigen neueren C# 3.0-Features.) Die Nähe der Zuordnung (und das Fehlen eines Optimierers im C# -Compiler) macht die Dinge so "reversibel".

9

Es gibt eine Reihe von Dingen, die das Reverse Engineering sehr einfach machen.

  • Typ Informationen. Das ist massiv. Im x86-Assembler müssen Sie die Arten von Variablen basierend auf ihrer Verwendung ableiten.

  • Struktur. Informationen zur Struktur der Anwendung sind in Il-Disassemblies verfügbar. In Kombination mit Typinformationen erhalten Sie so eine erstaunliche Menge an Daten. Du arbeitest an diesem Punkt ziemlich hoch (relativ zum x86-Assembler). Im nativen Assembler müssen Sie die Strukturlayouts (und sogar die Tatsache, dass es sich um Strukturen handelt) basierend auf der Verwendung der Daten ableiten. Nicht unmöglich, aber viel zeitaufwendiger.

  • Namen. Die Namen der Dinge zu kennen, kann nützlich sein.

Diese Dinge, kombiniert, bedeutet, dass Sie eine Menge Daten über die ausführbare Datei haben. Il arbeitet grundsätzlich auf einer Ebene, die viel näher an der Quelle liegt als ein Compiler mit nativem Code. Je höher der Bytecode arbeitet, desto einfacher ist das Reverse Engineering.

3

Erweiterung Brians richtige Antwort

Wenn Sie alle IL denken leicht rückübersetzbar ist, schlage ich vor, eine nicht-triviale F # Programm zu schreiben und versuchen, diesen Code zu dekompilieren. F # führt eine Menge Code-Transformationen durch und hat daher eine sehr schlechte Abbildung von der tatsächlich emittierten IL und der ursprünglichen Codebasis. Meiner Meinung nach ist es wesentlich schwieriger, den dekompilierten F # -Code zu betrachten und das ursprüngliche Programm zurückzurufen als für C# oder VB.Net.

Verwandte Themen