Java Language
Agenci Java
Szukaj…
Modyfikowanie klas za pomocą agentów
Po pierwsze, upewnij się, że używany agent ma następujące atrybuty w pliku Manifest.mf:
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Uruchomienie agenta Java umożliwi agentowi dostęp do klasy Instrumentation. Za pomocą Instrumentacji można wywołać addTransformer (transformator ClassFileTransformer) . ClassFileTransformers pozwoli Ci przepisać bajty klas. Klasa ma tylko jedną metodę, która dostarcza ClassLoader, który ładuje klasę, nazwę klasy, instancję java.lang.Class, jest to ProtectionDomain, a na koniec bajty samej klasy.
To wygląda tak:
byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
Modyfikowanie klasy wyłącznie z bajtów może trwać wieki. Aby temu zaradzić, istnieją biblioteki, których można użyć do konwersji bajtów klasy na coś bardziej użytecznego.
W tym przykładzie będę używać ASM, ale inne alternatywy, takie jak Javassist i BCEL, mają podobne funkcje.
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;
}
Stąd można dokonać zmian w obiekcie ClassNode. To sprawia, że zmiana dostępu do pola / metody jest niezwykle łatwa. Plus z drzewem API ASM modyfikującym kod bajtowy metod to pestka.
Po zakończeniu edycji możesz przekonwertować ClassNode z powrotem na bajty za pomocą następującej metody i zwrócić je w metodzie transformacji :
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;
}
Dodawanie agenta w czasie wykonywania
Agentów można dodawać do JVM w czasie wykonywania. Aby załadować agenta, musisz użyć VirtualMachine.attatch (Attachment String) Attach API. Następnie można załadować skompilowany słoik agenta za pomocą następującej metody:
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);
}
}
To nie wywoła premain ((String agentArgs, Instrumentation inst) w załadowanym agencie, ale zamiast tego wywoła agentmain (String agentArgs, Instrumentation inst) . Wymaga to ustawienia klasy agenta w Manifest.mf agenta.
Konfigurowanie podstawowego agenta
Klasa Premain będzie zawierać metodę „premain (String agentArgs Instrumentation inst)”
Oto przykład:
import java.lang.instrument.Instrumentation;
public class PremainExample {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println(agentArgs);
}
}
Po skompilowaniu do pliku jar otwórz Manifest i upewnij się, że ma on atrybut Premain-Class.
Oto przykład:
Premain-Class: PremainExample
Aby użyć agenta z innym programem Java „myProgram”, musisz zdefiniować agenta w argumentach JVM:
java -javaagent:PremainAgent.jar -jar myProgram.jar