Ricerca…


Modifica delle classi con gli agenti

Innanzitutto, assicurati che l'agente in uso abbia i seguenti attributi in Manifest.mf:

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

L'avvio di un agente java consentirà all'agente di accedere alla Strumentazione della classe. Con Instrumentation è possibile chiamare addTransformer (trasformatore ClassFileTransformer) . ClassFileTransformers ti consente di riscrivere i byte delle classi. La classe ha un solo metodo che fornisce ClassLoader che carica la classe, il nome della classe, un'istanza java.lang.Class, è ProtectionDomain e infine i byte della classe stessa.

Sembra questo:

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

Modificare una classe puramente da byte può richiedere anni. Per ovviare a questo ci sono librerie che possono essere utilizzate per convertire i byte di classe in qualcosa di più utilizzabile.

In questo esempio userò ASM, ma altre alternative come Javassist e BCEL hanno caratteristiche simili.

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;
}

Da qui le modifiche possono essere apportate all'oggetto ClassNode. Ciò rende incredibilmente facile cambiare campo / metodo di accesso. Inoltre con ASM's Tree API modificare il bytecode dei metodi è un gioco da ragazzi.

Una volta terminate le modifiche, puoi convertire nuovamente il ClassNode in byte con il seguente metodo e restituirli nel metodo di trasformazione :

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;
}

Aggiunta di un agente in fase di runtime

Agenti possono essere aggiunti a una JVM in fase di runtime. Per caricare un agente è necessario utilizzare VirtualMachine.attatch (ID stringa) di Attach API. È quindi possibile caricare un jar compilato con il seguente metodo:

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);
    }
}

Questo non chiamerà premain ((String agentArgs, Instrumentation inst) nell'agente caricato, ma invece chiamerà agentmain (String agentArgs, Instrumentation inst) . Ciò richiede che Agent-Class sia impostata nell'agente Manifest.mf.

Impostazione di un agente di base

La classe Premain conterrà il metodo "premain (String agentArgs Instrumentation inst)"

Ecco un esempio:

import java.lang.instrument.Instrumentation;

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

Quando viene compilato in un file jar, apri il manifest e assicurati che abbia l'attributo Premain-Class.

Ecco un esempio:

Premain-Class: PremainExample

Per utilizzare l'agente con un altro programma java "myProgram", è necessario definire l'agente negli argomenti JVM:

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


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow