javassist:如何将代理添加到类路径?

xzlaal3s  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(451)

我有一个代理,我正在动态加载到一个正在运行的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窗口在可从代理访问的另一个类中管理。它成功地打开一个窗口,并在加载代理时附加一条文本消息。

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

找到目标类,但字节码不编译:
ui:转换类app.targetclass失败:[源错误]没有此类类:me$domain.agent.ui.ui
但是,ui类位于代理内部:

agent.jar
├── META-INF
└── me.domain.agent
    ├── ui
    │   └── UI.class
    ├── MyTransformer.class
    └── Agent.class

我尝试将代理的类加载器添加到javassist的类池:

classPool.insertClassPath(new LoaderClassPath(Agent.class.getClassLoader()));

但它不起作用。如何将对代理ui的调用添加到字节码中?

68de4m5k

68de4m5k1#

我决定用asm从字节码调用我的代理ui。找班级没有问题。
以下是基于asm的类转换器的外观:

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

这将插入静态调用 UI.addMessage("Hello from ASM!") 第一次无标签指示前 app.TargetClass#importantMethod .

相关问题