Java Language
Java 에이전트
수색…
에이전트로 클래스 수정하기
먼저, 사용중인 에이전트가 Manifest.mf에 다음 속성을 가지고 있는지 확인하십시오.
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Java 에이전트를 시작하면 에이전트가 Instrumentation 클래스에 액세스하게됩니다. Instrumentation을 사용하면 addTransformer (ClassFileTransformer transformer)를 호출 할 수 있습니다. 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 객체를 변경할 수 있습니다. 따라서 필드 / 메소드 액세스를 매우 쉽게 변경할 수 있습니다. 또한 메소드의 바이트 코드를 수정하는 ASM의 Tree API가 추가되었습니다.
편집이 끝나면 다음 메소드로 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에 추가 할 수 있습니다. 에이전트를로드하려면 Attach API의 VirtualMachine.attatch (String id) 를 사용해야합니다. 다음 방법으로 컴파일 된 에이전트 jar를로드 할 수 있습니다.
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, Instrumentation inst) 을 호출하지 않고 대신 agentmain (String agentArgs, Instrumentation 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 파일로 컴파일되면 Manifest를 열고 Premain-Class 특성을 갖도록하십시오.
다음은 그 예입니다.
Premain-Class: PremainExample
다른 Java 프로그램 "myProgram"과 함께 에이전트를 사용하려면 JVM 인수에 에이전트를 정의해야합니다.
java -javaagent:PremainAgent.jar -jar myProgram.jar