Buscar..


Modificando clases con agentes.

En primer lugar, asegúrese de que el agente que se está utilizando tenga los siguientes atributos en el archivo Manifest.mf:

Can-Redefine-Classes: true
Can-Retransform-Classes: true

Iniciar un agente java permitirá que el agente acceda a la clase de Instrumentación. Con Instrumentation puede llamar a addTransformer (transformador ClassFileTransformer) . ClassFileTransformers te permitirá reescribir los bytes de las clases. La clase tiene un solo método que suministra el ClassLoader que carga la clase, el nombre de la clase, una instancia de java.lang.Class, es ProtectionDomain y, por último, los bytes de la propia clase.

Se parece a esto:

byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
          ProtectionDomain protectionDomain, byte[] classfileBuffer)

Modificar una clase puramente de bytes puede llevar siglos. Para remediar esto, hay bibliotecas que se pueden usar para convertir los bytes de clase en algo más utilizable.

En este ejemplo, usaré ASM, pero otras alternativas como Javassist y BCEL tienen características similares.

ClassNode getNode(byte[] bytes) {
    // Create a ClassReader that will parse the byte array into a ClassNode
    ClassReader cr = new ClassReader(bytes);
    ClassNode cn = new ClassNode();
    try {
        // This populates the ClassNode
        cr.accept(cn, ClassReader.EXPAND_FRAMES);
        cr = null;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return cn;
}

Desde aquí se pueden hacer cambios al objeto ClassNode. Esto hace que cambiar el acceso al campo / método sea increíblemente fácil. Además, con la API del árbol de ASM, la modificación del código de bytes de los métodos es muy sencilla.

Una vez que las ediciones hayan finalizado, puede volver a convertir el ClassNode en bytes con el siguiente método y devolverlos en el método de transformación :

public static byte[] getNodeBytes(ClassNode cn, boolean useMaxs) {
    ClassWriter cw = new ClassWriter(useMaxs ? ClassWriter.COMPUTE_MAXS : ClassWriter.COMPUTE_FRAMES);
    cn.accept(cw);
    byte[] b = cw.toByteArray();
    return b;
}

Agregar un agente en tiempo de ejecución

Los agentes se pueden agregar a una JVM en tiempo de ejecución. Para cargar un agente, deberá usar VirtualMachine.attatch (Id . De cadena) de Attach API. A continuación, puede cargar un jar de agente compilado con el siguiente método:

public static void loadAgent(String agentPath) {
    String vmName = ManagementFactory.getRuntimeMXBean().getName();
    int index = vmName.indexOf('@');
    String pid = vmName.substring(0, index);
    try {
        File agentFile = new File(agentPath);
        VirtualMachine vm = VirtualMachine.attach(pid);
        vm.loadAgent(agentFile.getAbsolutePath(), "");
        VirtualMachine.attach(vm.id());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Esto no llamará premain ((String agentArgs, Instrumentation inst) en el agente cargado, sino que llamará a agentmain (String agentArgs, Instrumentation inst) . Esto requiere que Agent-Class esté configurado en el agente Manifest.mf.

Configuración de un agente básico

La clase Premain contendrá el método "premain (String agentArgs Instrumentation inst)"

Aquí hay un ejemplo:

import java.lang.instrument.Instrumentation;

public class PremainExample {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println(agentArgs);
    }
}

Cuando esté compilado en un archivo jar, abra el Manifiesto y asegúrese de que tenga el atributo de clase principal.

Aquí hay un ejemplo:

Premain-Class: PremainExample

Para usar el agente con otro programa java "myProgram", debe definir el agente en los argumentos de JVM:

java -javaagent:PremainAgent.jar -jar myProgram.jar


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow