Javassist:如何将代理添加到类路径?
Javassist: How to add agent to classpath?
我有一个动态加载到 运行 Java 应用程序的代理程序,它在附加时打开一个简单的 Swing JFrame。它还允许将新行附加到该 JFrame 内的 TextArea。
我的目标是改变某些方法在加载代理的应用程序中的工作方式。
public class MyAgent {
public static void agentmain(String args, Instrumentation instrumentation) {
UI.openWindow();
UI.addMessage("Agent loaded: %s", args);
instrumentation.addTransformer(new MyTransformer());
instrumentation.redefineClasses(new ClassDefinition(Class.forName("app.TargetClass"), ...));
}
}
UI window 在另一个可从代理访问的 class 中进行管理。它成功打开 window 并在加载代理时附加一条文本消息。
public class UI {
private static SwingWindow swingWindow;
public static void addMessage(String format, Object... args) {
System.out.println("UI: " + String.format(format, args));
swingWindow.appendToTextArea(format, args);
}
public static void openWindow() {
try {
SwingUtilities.invokeAndWait(() -> swingWindow = new SwingWindow());
}
catch (Exception e) {}
}
}
我正在使用 Javassist 在我的转换器中生成字节码。
public class MyTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, ..., byte[] classBuffer) {
if (className.equals("app/TargetClass")) {
UI.addMessage("Now transforming the class that I need!");
try {
ClassPool classPool = ClassPool.getDefault();
CtClass targetClass = classPool.get("app.TargetClass");
CtMethod targetMethod = targetClass.getDeclaredMethod("importantMethod");
targetMethod.insertBefore("me.domain.agent.ui.UI.addMessage(\"Hello from Javassist!\")");
byte[] byteCode = targetClass.toBytecode();
targetClass.detach();
return byteCode;
}
catch (Exception e) {
UI.addMessage("Couldn't transform the class I needed.");
}
}
return classBuffer;
}
}
找到目标 class,但字节码未编译:
UI: Failed transforming class app.TargetClass: [source error] no such class: me$domain.agent.ui.UI
然而,UIclass在代理里面:
agent.jar
├── META-INF
└── me.domain.agent
├── ui
│ └── UI.class
├── MyTransformer.class
└── Agent.class
我试过将代理的 ClassLoader 添加到 Javassist 的 ClassPool:
classPool.insertClassPath(new LoaderClassPath(Agent.class.getClassLoader()));
但是没有用。如何将对代理 UI 的调用添加到字节码中?
我决定使用 ASM 从字节码调用我的代理 UI。找到 class.
没有问题
以下是基于 ASM 的 class 变压器的外观:
public class MyTransformer implements ClassFileTransformer {
public void transformClass(ClassNode classNode) {
MethodNode methodNode = findMethodNodeOfClass(classNode, "importantMethod", "()V");
if (methodNode == null) {
throw new TransformerException("app.TargetClass#importantMethod not found");
}
AbstractInsnNode firstInsn = findFirstInstruction(methodNode);
if (firstInsn == null) {
throw new TransformerException("No instructions in app.TargetClass#importantMethod");
}
InsnList insnList = new InsnList();
insnList.add(new LdcInsnNode("Hello from ASM!"));
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(UI.class), "addMessage", "(Ljava/lang/String;)V"));
methodNode.instructions.insertBefore(firstInsn, insnList);
}
@Override
public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] byteCode) {
if (className.equals("app/TargetClass")) {
try {
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(byteCode);
classReader.accept(classNode, 0);
this.transformClass(classNode);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(classWriter);
return classWriter.toByteArray();
}
catch (Exception e) {
e.printStackTrace();
}
}
return byteCode;
}
}
这将在 app.TargetClass#importantMethod
的第一条非标签指令之前插入静态调用 UI.addMessage("Hello from ASM!")
。
我有一个动态加载到 运行 Java 应用程序的代理程序,它在附加时打开一个简单的 Swing JFrame。它还允许将新行附加到该 JFrame 内的 TextArea。
我的目标是改变某些方法在加载代理的应用程序中的工作方式。
public class MyAgent {
public static void agentmain(String args, Instrumentation instrumentation) {
UI.openWindow();
UI.addMessage("Agent loaded: %s", args);
instrumentation.addTransformer(new MyTransformer());
instrumentation.redefineClasses(new ClassDefinition(Class.forName("app.TargetClass"), ...));
}
}
UI window 在另一个可从代理访问的 class 中进行管理。它成功打开 window 并在加载代理时附加一条文本消息。
public class UI {
private static SwingWindow swingWindow;
public static void addMessage(String format, Object... args) {
System.out.println("UI: " + String.format(format, args));
swingWindow.appendToTextArea(format, args);
}
public static void openWindow() {
try {
SwingUtilities.invokeAndWait(() -> swingWindow = new SwingWindow());
}
catch (Exception e) {}
}
}
我正在使用 Javassist 在我的转换器中生成字节码。
public class MyTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, ..., byte[] classBuffer) {
if (className.equals("app/TargetClass")) {
UI.addMessage("Now transforming the class that I need!");
try {
ClassPool classPool = ClassPool.getDefault();
CtClass targetClass = classPool.get("app.TargetClass");
CtMethod targetMethod = targetClass.getDeclaredMethod("importantMethod");
targetMethod.insertBefore("me.domain.agent.ui.UI.addMessage(\"Hello from Javassist!\")");
byte[] byteCode = targetClass.toBytecode();
targetClass.detach();
return byteCode;
}
catch (Exception e) {
UI.addMessage("Couldn't transform the class I needed.");
}
}
return classBuffer;
}
}
找到目标 class,但字节码未编译:
UI: Failed transforming class app.TargetClass: [source error] no such class: me$domain.agent.ui.UI
然而,UIclass在代理里面:
agent.jar
├── META-INF
└── me.domain.agent
├── ui
│ └── UI.class
├── MyTransformer.class
└── Agent.class
我试过将代理的 ClassLoader 添加到 Javassist 的 ClassPool:
classPool.insertClassPath(new LoaderClassPath(Agent.class.getClassLoader()));
但是没有用。如何将对代理 UI 的调用添加到字节码中?
我决定使用 ASM 从字节码调用我的代理 UI。找到 class.
没有问题以下是基于 ASM 的 class 变压器的外观:
public class MyTransformer implements ClassFileTransformer {
public void transformClass(ClassNode classNode) {
MethodNode methodNode = findMethodNodeOfClass(classNode, "importantMethod", "()V");
if (methodNode == null) {
throw new TransformerException("app.TargetClass#importantMethod not found");
}
AbstractInsnNode firstInsn = findFirstInstruction(methodNode);
if (firstInsn == null) {
throw new TransformerException("No instructions in app.TargetClass#importantMethod");
}
InsnList insnList = new InsnList();
insnList.add(new LdcInsnNode("Hello from ASM!"));
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(UI.class), "addMessage", "(Ljava/lang/String;)V"));
methodNode.instructions.insertBefore(firstInsn, insnList);
}
@Override
public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] byteCode) {
if (className.equals("app/TargetClass")) {
try {
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(byteCode);
classReader.accept(classNode, 0);
this.transformClass(classNode);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(classWriter);
return classWriter.toByteArray();
}
catch (Exception e) {
e.printStackTrace();
}
}
return byteCode;
}
}
这将在 app.TargetClass#importantMethod
的第一条非标签指令之前插入静态调用 UI.addMessage("Hello from ASM!")
。