Поиск…


Изменение классов с помощью агентов

Во-первых, убедитесь, что используемый агент имеет следующие атрибуты в Manifest.mf:

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

Запуск Java-агента позволит агенту получить доступ к классу Instrumentation. С помощью Instrumentation вы можете вызвать addTransformer (трансформатор ClassFileTransformer) . ClassFileTransformers позволит вам переписать байты классов. Класс имеет только один метод, который поставляет ClassLoader, который загружает класс, имя класса, экземпляр java.lang.Class, это ProtectionDomain и, наконец, байты самого класса.

Это выглядит так:

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

Модификация класса исключительно из байтов может занять возраст. Чтобы исправить это, есть библиотеки, которые могут быть использованы для преобразования байтов класса в нечто более пригодное для использования.

В этом примере я буду использовать ASM, но другие альтернативы, такие как Javassist и BCEL, имеют схожие функции.

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

Отсюда можно внести изменения в объект ClassNode. Это позволяет невероятно легко изменять доступ к полям / методам. Плюс с API-интерфейсом ASM, изменяющим байт-код методов, это легкий ветерок.

После завершения редактирования вы можете преобразовать ClassNode обратно в байты со следующим методом и вернуть их в метод преобразования :

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

Добавление агента во время выполнения

Агенты могут быть добавлены в JVM во время выполнения. Чтобы загрузить агента, вам нужно использовать VirtualMachine.attatch ( Attach API) Attach API (String id) . Затем вы можете загрузить скомпилированную фразу агента следующим способом:

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

Это не будет вызывать premain ((String agentArgs, Инструментарий INST) в загруженном агенте, но вместо этого будет вызывать agentmain (String agentArgs, Инструментарий INST). Это требует Агент-класс должны быть установлено в средстве manifest.mf.

Настройка базового агента

Класс Premain будет содержать метод «premain (String agentArgs Instrumentation inst)»

Вот пример:

import java.lang.instrument.Instrumentation;

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

Когда они скомпилированы в файл jar, откройте манифест и убедитесь, что он имеет атрибут Premain-Class.

Вот пример:

Premain-Class: PremainExample

Чтобы использовать агент с другой java-программой «myProgram», вы должны определить агента в аргументах JVM:

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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow