DéveloppeurWeb.Com
    DéveloppeurWeb.Com
    • Agile Zone
    • AI Zone
    • Cloud Zone
    • Database Zone
    • DevOps Zone
    • Integration Zone
    • Web Dev Zone
    DéveloppeurWeb.Com
    Home»Uncategorized»Java Bytecode : Voyage au pays des merveilles (partie 3)
    Uncategorized

    Java Bytecode : Voyage au pays des merveilles (partie 3)

    mars 6, 2023
    Java Bytecode : Voyage au pays des merveilles (partie 3)
    Share
    Facebook Twitter Pinterest Reddit WhatsApp Email

    Notre article précédent a décompressé le bytecode plus en détail et en a discuté ConstantPool. Aujourd’hui, je vais passer par plusieurs ressources pour travailler avec elle maintenant.

    Le bytecode Java est la représentation intermédiaire du code Java de la machine virtuelle Java (JVM). Bien que le bytecode Java ne soit pas censé être lisible par l’homme, il peut être modifié et manipulé pour plusieurs raisons. Cet article examine les outils et méthodes utilisés pour modifier et utiliser le bytecode Java.

    La modification du bytecode Java est souvent effectuée pour ajouter de nouvelles fonctionnalités à un programme Java qui existe déjà. Cela peut être fait avec un injecteur de bytecode, un outil qui vous permet d’ajouter du bytecode à un fichier de classe Java déjà compilé. Les injecteurs de bytecode sont souvent utilisés pour enregistrer ou déboguer des informations et pour autoriser les mises à jour telles que les tests A/B ou les indicateurs de fonctionnalité pendant l’exécution du programme.

    À partir de Java Assist est l’un des outils qui peut être utilisé pour injecter du bytecode. Regardez la classe suivante.

    package ca.bazlur;
    
    public class Greetings {
    
      public void sayHello(String name) {
        System.out.println("Hello " + name + "!");
      }
    }

    Disons que nous avons cette classe et que nous voudrions y ajouter une méthode, mais via une manipulation de bytecode.

    package ca.bazlur;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    
    public class BytecodeInjector {
    
      public static void main(String[] args) throws IOException {
    
        try (var resource = BytecodeInjector.class.getResourceAsStream("Greetings.class")) {
          final var classBytes = resource.readAllBytes();
    
          // Create a ClassPool and import the original class
          ClassPool classPool = ClassPool.getDefault();
          CtClass ctClass = classPool.makeClass(new java.io.ByteArrayInputStream(classBytes));
    
          // Create a new method and add it to the class
          CtMethod newMethod = CtMethod.make("""
                public void printHelloWorld() {
                  System.out.println("Hello, world!");
                }
              """, ctClass);
          ctClass.addMethod(newMethod);
    
          // Write the modified class back to a byte array
          byte[] modifiedClassBytes = ctClass.toBytecode();
    
          // Load the modified class bytes into the JVM
          MyClassLoader classLoader = new MyClassLoader();
          Class<?> modifiedClass = classLoader.defineClass("ca.bazlur.Greetings", modifiedClassBytes);
    
          // Invoke the new method on an instance of the modified class
          Object obj = modifiedClass.newInstance();
          Method method = modifiedClass.getMethod("printHelloWorld");
          method.invoke(obj);
        } catch (CannotCompileException | InvocationTargetException | InstantiationException |
                 IllegalAccessException | NoSuchMethodException e) {
          throw new RuntimeException(e);
        }
      }
    }

    Dans ce code, nous avions une classe appelée Greetings. Nous voulions ajouter une nouvelle méthode. Pour ce faire, nous avons dû lire la classe d’origine dans un tableau d’octets, l’importer dans un ClassPool, puis modifiez-le en ajoutant une nouvelle méthode. Ensuite, la classe modifiée est réécrite dans un tableau d’octets et chargée dans la JVM à l’aide d’un ClassLoader. Enfin, la nouvelle méthode est invoquée sur une instance de la classe modifiée.

    package ca.bazlur;
    
    public class MyClassLoader extends ClassLoader {
      public Class<?> defineClass(String name, byte[] bytes) {
        return super.defineClass(name, bytes, 0, bytes.length);
      }
    }

    Si nous exécutons la classe ci-dessus, nous verrons que la fonctionnalité a été ajoutée à la Greetings classe et également exécuté. Il imprimera :

    Hello, world!

    Il existe également un programme connu sous le nom de « Byte Buddy », et nous pouvons l’utiliser pour faire une chose similaire.

    Supposons que nous voulons savoir combien de temps une méthode prend pour s’exécuter. Nous pouvons utiliser l’instrumentation de bytecode. L’instrumentation bytecode est une autre méthode pour modifier le bytecode Java. Cela se fait en utilisant une bibliothèque ou un outil pour modifier le bytecode d’une classe Java avant que la JVM ne la charge. Cela peut être utile pour ajouter la surveillance des performances ou le profilage de code à une application.

    Utilisons Byte Buddy pour créer un agent simple qui instrumentera chaque classe et calculera le temps nécessaire à l’exécution de chaque méthode.

    package ca.bazlur;
    
    import java.lang.instrument.Instrumentation;
    import net.bytebuddy.agent.builder.AgentBuilder;
    import net.bytebuddy.asm.Advice;
    import net.bytebuddy.matcher.ElementMatchers;
    
    public class MyAgent {
    
      public static void premain(String agentArgs, Instrumentation inst) {
        new AgentBuilder.Default()
            .type(ElementMatchers.any())
            .transform((builder, typeDescription, classLoader, module) -> builder
                .method(ElementMatchers.any())
                .intercept(Advice.to(TimerAdvice.class)))
            .installOn(inst);
      }
    }

    Le TimerAdvice la classe est ici :

    package ca.bazlur;
    
    import net.bytebuddy.asm.Advice;
    
    public class TimerAdvice {
    
      @Advice.OnMethodEnter
      static long invokeBeforeEachMethod(
          @Advice.Origin String method) {
        System.out.println("Entering to invoke : " + method);
        return System.currentTimeMillis();
      }
    
      @Advice.OnMethodExit
      static void invokeWhileExitingEachMethod(@Advice.Origin String method,
          @Advice.Enter long startTime) {
        System.out.println(
            "Method " + method + " took " + (System.currentTimeMillis() - startTime) + "ms");
      }
    }

    Le code source complet est disponible dans le tutoriel Byte Code sur GitHub.

    Une fois que nous l’avons construit et généré un jar, nous pouvons l’utiliser dans la CLI en exécutant la commande suivante :

    java -javaagent:myagent-1.0-SNAPSHOT.jar MyAwesomeJavaProgram

    Le MyAwesomeJavaProgram ressemble à ça:

    public class MyAwesomeJavaProgram {
      public static void main(String[] args) {
        System.out.println(doCalculation());
      }
    
      public static int doCalculation() {
        int result = 0;
        for (int i = 0; i < 100000000; i++) {
          result += i;
        }
        return result;
      }
    }

    Une fois que nous l’avons exécuté dans la CLI, nous obtiendrons la sortie comme suit :

    Entering to invoke : public static void MyAwesomeJavaProgram.main(java.lang.String[])
    Entering to invoke : public static int MyAwesomeJavaProgram.doCalculation()
    Method public static int MyAwesomeJavaProgram.doCalculation() took 41ms
    887459712
    Method public static void MyAwesomeJavaProgram.main(java.lang.String[]) took 45ms

    Voici quelques bibliothèques pour manipuler le bytecode Java :

    1. ASM : un cadre de manipulation de bytecode Java rapide, petit et efficace
    2. BCEL : une bibliothèque pour manipuler le bytecode Java dans le projet Apache Commons
    3. Javassist : une bibliothèque de manipulation de bytecode pour Java
    4. Byte Buddy : une bibliothèque pour générer et modifier le bytecode Java
    5. CFR : un décompilateur de bytecode pour Java, écrit en Java

    Des modifications peuvent être apportées au bytecode Java pour l’obscurcissement et d’autres raisons. « L’obscurcissement » est le processus qui rend le code plus difficile à comprendre et à comprendre comment il fonctionne. Cela peut être bénéfique pour empêcher l’utilisation illégale d’un programme ou pour protéger la propriété intellectuelle.

    Pour le rendre plus difficile à comprendre, le bytecode Java peut être masqué en modifiant les noms des classes et des méthodes ou en ajoutant du code supplémentaire.

    Il existe différents outils pour obscurcir le code Java :

    1. ProGuard: Il s’agit d’un programme gratuit et open-source pour optimiser et masquer le code Java. Il peut se débarrasser du code inutile, accélérer le code et modifier les noms des classes, des champs et des fonctions pour les rendre plus difficiles à comprendre.
    2. DashO: Un programme commercial d’obscurcissement et d’optimisation qui inclut l’obscurcissement du flux de contrôle, le cryptage des chaînes et le filigrane
    3. Zelix KlassMaître: Un programme payant qui fournit des fonctionnalités étendues d’obscurcissement et de protection, telles que l’obscurcissement du flux de contrôle, le chiffrement de chaînes et le changement de nom de classe et de membre
    4. Porteur: un produit commercial qui fournit des fonctionnalités étendues d’obscurcissement et de sécurité telles que l’obscurcissement du flux de contrôle, le chiffrement de chaîne et le changement de nom de classe et de membre.
    5. yGarde: Un outil open source pour optimiser et masquer le code Java, il peut se débarrasser du code inutile, accélérer le code et modifier les noms des classes, des champs et des fonctions pour les rendre plus difficiles à comprendre.

    En conclusion, le bytecode Java peut être mis à jour et contrôlé pour de nombreuses raisons, comme l’ajout de nouvelles fonctionnalités, l’instrumentation du code pour la surveillance des performances ou le profilage, ou l’obscurcissement du code pour protéger la propriété intellectuelle.

    Mais même si ces méthodes peuvent parfois fonctionner, elles doivent être utilisées correctement et conformément aux conditions de toute demande de licence.

    Share. Facebook Twitter Pinterest LinkedIn WhatsApp Reddit Email
    Add A Comment

    Leave A Reply Cancel Reply

    Catégories

    • Politique de cookies
    • Politique de confidentialité
    • CONTACT
    • Politique du DMCA
    • CONDITIONS D’UTILISATION
    • Avertissement
    © 2023 DéveloppeurWeb.Com.

    Type above and press Enter to search. Press Esc to cancel.