Maison > Java > javaDidacticiel > le corps du texte

Comment lire le Bytecode Java pour le plaisir et le profit

PHP中文网
Libérer: 2024-10-22 13:03:13
original
1008 Les gens l'ont consulté

Vous vous lancez dans un voyage à travers le monde du Bytecode Java ? Cet article couvre tout ce que vous devez savoir pour commencer.

Comment lire le Bytecode Java pour le plaisir et le profit

Qu'est-ce que le bytecode ?

En 1995, Sun Microsystems, les créateurs de la programmation Java langue, a fait une affirmation audacieuse. Ils ont dit que Java vous permettrait « d’écrire une fois et de l’exécuter n’importe où ». Cela signifiait que les binaires compilés seraient capables de s'exécuter sur n'importe quelle architecture système, ce que C ne pouvait pas faire et qui reste à ce jour un élément essentiel de l'écriture de Java.

Pour obtenir cette capacité multiplateforme, Java utilise une approche unique lors de la compilation. Au lieu de passer directement du code source au code machine (qui serait spécifique à chaque architecture système), Java compile ses programmes sous une forme intermédiaire appelée bytecode. Le bytecode est un ensemble d'instructions qui n'est ni lié à un langage machine particulier ni dépendant d'une architecture matérielle spécifique. Cette abstraction est la clé de la portabilité de Java.

Le programme qui interprète et exécute les instructions du bytecode Java est appelé machine virtuelle Java (JVM). La JVM traduit chaque instruction de bytecode en code machine natif de l'architecture système particulière sur laquelle elle s'exécute. Ce processus, souvent appelé compilation « juste à temps » (JIT), permet d'exécuter le bytecode Java aussi efficacement que possible sur n'importe quelle plate-forme donnée.

Affichage du bytecode

Le bytecode n'est pas Mais ce n'est pas seulement utile pour la JVM. Étant donné que le bytecode d'une classe Java est utile pour l'ingénierie inverse, l'optimisation des performances, la recherche de sécurité et d'autres fonctions d'analyse statique, le JDK est livré avec des utilitaires pour nous aider, vous et moi, à l'inspecter.

Pour avoir un aperçu d'un exemple de bytecode, considérons les deux méthodes suivantes de `java.lang.Boolean`, `booleanValue` et `valueOf(boolean)` qui respectivement déballer et encadrer le type primitif `boolean` :

public boolean booleanValue() {
    return value;
}

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}
Copier après la connexion

Utilisation du ` javap`, fournie avec le JDK, nous pouvons voir le bytecode de chacun. Vous pouvez le faire en exécutant `javap` avec la commande `-c` et le nom complet de la classe, comme ceci :

javap -c java.lang.Boolean
Copier après la connexion

Le résultat est le bytecode de toutes les méthodes publiques dans ` java.lang.Boolean`. Ici, j'ai copié uniquement le bytecode de `booleanValue` et `valueOf(boolean)` :

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
Copier après la connexion

Dissection du bytecode

À première vue, c'est un tout nouveau langage à apprendre. Cependant, cela devient rapidement simple lorsque vous apprenez ce que fait chaque instruction et que Java fonctionne avec une pile.

Prenons par exemple les trois instructions de bytecode pour `booleanValue` :

  • `aload_n` signifie placer une référence à une variable locale sur la pile. Dans une instance de classe, `aload_0` fait référence à `this`.

  • `getfield` signifie lire la variable membre à partir de `this` (l'élément inférieur de la pile) et la placer valeur sur la pile

    • `#7` fait référence à l'index de la référence dans le pool constant

    • `// Valeur du champ : Z` indique nous à quoi `#7` fait référence, un champ nommé `value` de type `boolean` (Z)

  • `ireturn` signifie afficher une valeur primitive hors de la pile et le renvoyer

Pour faire court, ces trois instructions recherchent le champ `value` de l'instance et le renvoient.

Comme deuxième exemple, prenons un regardez la méthode suivante, `valueOf(boolean)` :

  • `iload_n` signifie placer une variable locale primitive sur la pile. `iload_0` fait référence au premier paramètre de méthode (puisque le premier paramètre de méthode est une primitive)

  • `ifeq    n` signifie retirer la valeur de la pile et voir si elle est vraie ; si c'est le cas, passez à la ligne suivante, sinon passez à la ligne `n`

  • `getstatic #n` signifie lire un membre statique sur la pile

    • `#27` fait référence à l'index du membre statique dans le pool de constantes

    • `// Field TRUE:Ljava/lang/Boolean` nous dit à quoi `#27` fait référence , un membre statique nommé `TRUE` de type `Boolean

  • `goto n` signifie maintenant passer à la ligne `n` dans le bytecode

  • `areturn` signifie extraire une référence de la pile et la renvoyer

En d'autres termes, ces instructions disent de prendre le premier paramètre de méthode, si c'est vrai , puis retournez `Boolean.TRUE` ; sinon, renvoyez `Boolean.FALSE`.

Exploiter l'analyse du bytecode

J'ai mentionné plus tôt que cela peut être utile pour l'ingénierie inverse, l'optimisation des performances et la recherche sur la sécurité. Développons-les maintenant.

Ingénierie inverse

Lorsque vous travaillez avec des bibliothèques tierces ou des composants à source fermée, l'analyse du bytecode devient un outil puissant. La décompilation du bytecode peut donner un aperçu du fonctionnement interne de ces bibliothèques, facilitant l'intégration, le dépannage et garantissant la compatibilité.

Dans les situations où vous rencontrez du code Java propriétaire ou à source fermée, la lecture du bytecode peut être la seule solution possible. manière de comprendre sa fonctionnalité. L'analyse du bytecode vous permet de faire de l'ingénierie inverse et de comprendre le comportement des applications fermées, facilitant ainsi l'interopérabilité ou la personnalisation.

À titre d'exemple concret, j'essayais récemment d'intégrer un outil d'analyse d'enchevêtrement de packages tiers dans notre système Ci. Malheureusement, le fournisseur était de source fermée et ne disposait que d'une documentation expliquant comment accéder à la bibliothèque via son interface utilisateur propriétaire. En analysant le bytecode, j'ai pu procéder à l'ingénierie inverse des entrées et sorties attendues du moteur d'analyse sous-jacent.


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal