Begeben Sie sich auf eine Reise durch die Welt von Java Bytecode? Dieser Artikel behandelt alles, was Sie wissen müssen, um loszulegen.
Im Jahr 1995 gründete Sun Microsystems, die Erfinder der Java-Programmierung Sprache, stellte eine kühne Behauptung auf. Sie sagten, dass man mit Java „einmal schreiben und überall ausführen“ könne. Das bedeutete, dass die kompilierten Binärdateien auf jeder Systemarchitektur ausgeführt werden könnten, was C nicht konnte und bis heute ein zentraler Mandant beim Schreiben von Java bleibt.
Um diese plattformübergreifende Fähigkeit zu erreichen, verwendet Java ein einzigartiger Ansatz beim Kompilieren. Anstatt vom Quellcode direkt in Maschinencode überzugehen (was für jede Systemarchitektur spezifisch wäre), kompiliert Java seine Programme in eine Zwischenform, die als Bytecode bekannt ist. Bytecode ist eine Reihe von Anweisungen, die weder an eine bestimmte Maschinensprache gebunden noch von einer bestimmten Hardwarearchitektur abhängig sind. Diese Abstraktion ist der Schlüssel zur Portabilität von Java.
Das Programm, das Java-Bytecode-Anweisungen interpretiert und ausführt, wird Java Virtual Machine (JVM) genannt. Die JVM übersetzt jede Bytecode-Anweisung in den Maschinencode, der für die jeweilige Systemarchitektur, auf der sie ausgeführt wird, nativ ist. Dieser Prozess, der oft als „Just-in-Time“-Kompilierung (JIT) bezeichnet wird, ermöglicht die möglichst effiziente Ausführung von Java-Bytecode auf jeder beliebigen Plattform.
Bytecode isn Es ist jedoch nicht nur für die JVM nützlich. Da der Bytecode einer Java-Klasse für Reverse Engineering, Leistungsoptimierung, Sicherheitsforschung und andere statische Analysefunktionen hilfreich ist, wird das JDK mit Dienstprogrammen ausgeliefert, die Ihnen und mir bei der Überprüfung helfen.
Um einen Blick auf ein Beispiel zu werfen Bytecode, betrachten Sie die folgenden zwei Methoden aus „java.lang.Boolean“, „booleanValue“ und „valueOf(boolean)“, die den primitiven Typ „boolean“ jeweils entpacken und einpacken:
public boolean booleanValue() { return value; } public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }
Verwenden Sie die ` Mit dem Befehl „javap“, der im JDK enthalten ist, können wir den Bytecode für jeden sehen. Sie können dies tun, indem Sie „javap“ mit dem Befehl „-c“ und dem vollständig qualifizierten Namen der Klasse ausführen, etwa so:
javap -c java.lang.Boolean
Das Ergebnis ist der Bytecode für alle öffentlichen Methoden in „ java.lang.Boolean`. Hier habe ich nur den Bytecode für „booleanValue“ und „valueOf(boolean)“ kopiert:
public boolean booleanValue(); code: 0: aload_0 1: getfield #7 // Field value:Z 4: ireturn public static java.lang.Boolean valueOf(boolean); Code: 0: iload_0 1: ifeq 10 4: getstatic #27 // Field TRUE:Ljava/lang/Boolean; 7: goto 13 10: getstatic #31 // Field FALSE:Ljava/lang/Boolean; 13: areturn
Auf den ersten Blick ist es eine völlig neue Sprache zum Erlernen. Es wird jedoch schnell klar, wenn Sie lernen, was die einzelnen Anweisungen bewirken und dass Java mit einem Stack arbeitet.
Nehmen Sie zum Beispiel die drei Bytecode-Anweisungen für „booleanValue“:
„aload_n“ bedeutet, einen Verweis auf eine lokale Variable auf dem Stapel zu platzieren. In einer Klasseninstanz bezieht sich „aload_0“ auf „this“.
„getfield“ bedeutet, die Mitgliedsvariable aus „this“ (dem unteren Element auf dem Stapel) zu lesen und dort zu platzieren Wert auf den Stapel
`#7` bezieht sich auf den Index der Referenz im Konstantenpool
`// Feldwert:Z` sagt Was sich „#7“ bezieht, ist ein Feld mit dem Namen „value“ vom Typ „boolean“ (Z)
„ireturn“ bedeutet, einen primitiven Wert einzufügen vom Stapel entfernen und zurückgeben
Kurz gesagt, diese drei Anweisungen suchen das „Wert“-Feld der Instanz und geben es zurück.
Nehmen Sie als zweites Beispiel a Schauen Sie sich die nächste Methode an, `valueOf(boolean)`:
`iload_n` bedeutet, eine primitive lokale Variable auf dem Stapel zu platzieren. „iload_0“ bezieht sich auf den ersten Methodenparameter (da der erste Methodenparameter ein Grundelement ist)
`ifeq n` bedeutet, den Wert vom Stapel zu entfernen und zu prüfen, ob er wahr ist; Wenn ja, fahren Sie mit der nächsten Zeile fort, andernfalls springen Sie zu Zeile `n`
`getstatic #n` bedeutet, ein statisches Mitglied auf den Stapel zu lesen
„#27“ bezieht sich auf den Index des statischen Mitglieds im Konstantenpool
`// Field TRUE:Ljava/lang/Boolean` sagt uns, worauf sich „#27“ bezieht , ein statisches Mitglied namens „TRUE“ vom Typ „Boolean“
„goto n“ bedeutet, dass jetzt zu Zeile „n“ im Bytecode gesprungen wird
„areturn“ bedeutet, dass eine Referenz vom Stapel entfernt und zurückgegeben wird
Mit anderen Worten, diese Anweisungen besagen, dass der erste Methodenparameter übernommen werden soll, wenn er wahr ist , dann `Boolean.TRUE` zurückgeben; Andernfalls geben Sie „Boolean.FALSE“ zurück.
Ich habe bereits erwähnt, dass dies für Reverse Engineering, Leistungsoptimierung und Sicherheitsforschung hilfreich sein kann. Lassen Sie uns diese jetzt näher erläutern.
Bei der Arbeit mit Bibliotheken von Drittanbietern oder Closed-Source-Komponenten wird die Bytecode-Analyse zu einem leistungsstarken Werkzeug. Das Dekompilieren von Bytecode kann einen Einblick in das Innenleben dieser Bibliotheken geben und bei der Integration, Fehlerbehebung und Gewährleistung der Kompatibilität helfen.
In Situationen, in denen Sie auf proprietären oder Closed-Source-Java-Code stoßen, kann das Lesen von Bytecode die einzig mögliche Lösung sein Möglichkeit, seine Funktionalität zu verstehen. Durch die Bytecode-Analyse können Sie das Verhalten von Closed-Source-Anwendungen zurückentwickeln und verstehen und so die Interoperabilität oder Anpassung erleichtern.
Um ein reales Beispiel zu nennen: Ich habe kürzlich versucht, ein Tool zur Paketverwirrungsanalyse eines Drittanbieters in unser Ci-System zu integrieren. Leider war der Anbieter ein Closed-Source-Anbieter und verfügte nur über Dokumentation für den Zugriff auf die Bibliothek über seine proprietäre Benutzeroberfläche. Durch die Analyse des Bytecodes konnte ich die erwarteten Ein- und Ausgaben der zugrunde liegenden Analyse-Engine rückentwickeln.
Das obige ist der detaillierte Inhalt vonWie man Java-Bytecode mit Spaß und Gewinn liest. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!