当前位置: 首页 > 知识库问答 >
问题:

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

陈晟睿
2023-03-14

我有一个代理,我正在动态地加载到一个正在运行的Java应用程序中,它在附加时打开一个简单的Swing JFrame。它还允许将新行添加到JFrame内部的文本区中。

我的目标是改变一些方法在加载代理的应用程序内部的工作方式。

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) {}
    }
}
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的调用添加到字节码中?

共有1个答案

施学
2023-03-14

我决定使用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;
    }
}

这将在app.TargetClass#ImportMethod的第一个非标签指令之前插入静态调用ui.addMessage(“Hello from ASM!”)

 类似资料:
  • 问题内容: 我在上,我尝试了下都无济于事。还尝试将jar移到src目录,但是java文件仍然无法编译。尝试在脚本中简单地导入httpclient。 问题答案: 安装您的JDK并将 其放置 。难道 不是 .jar文件复制到你的JDK的文件夹! 如果您还没有一个IDE,我建议您使用Eclipse或Netbeans这样的IDE。 我将建立一个新项目,创建或导入您的源,并为该项目设置一个类路径。 如果那行

  • 问题内容: 我试图找出为什么找不到使用using安装的我和/或包装器。我认为是因为它没有添加到我的PATH中: 和: 我安装使用PIP和使用,没有任何问题。我尝试重新安装,但这也没有用。我如何知道要添加的路径?只是似乎安装到的路径?那似乎是: 我还找到了本指南,该指南建议: 但是,这并不能帮助我运行。我在Mac OSX 10.7.5(Lion)上。 问题答案: 似乎我自己是几乎所有“简单”安装过程

  • 在Eclipse中,我使用 项目->生成路径->配置生成路径

  • 在EclipseIDE中,我使用 项目 VisualStudioCode中的等价物是什么? 我查看了。定义了一个类路径。将JAR添加到这个类路径(数组)变量似乎没有效果。 本质上,这是VisualStudioJava语言支持add jar的一个重复问题<但是这个问题没有答案。 这是一个非常基本的问题,如果不在微软的文档中或通过谷歌搜索找到解决方案,我真的无法理解。

  • 问题内容: 我正在尝试使用Visual Studio Code,到目前为止,它看起来很棒(轻巧,快速等)。 我正在尝试运行一个使用虚拟环境的Python应用程序,但是还使用了不在我的虚拟环境的站点包中的库。 我知道在中,我可以指定一个设置,该设置已经完成并且指向虚拟环境。 我也知道我可以向添加其他路径,到目前为止,我正在添加外部库。问题是,当我调试时,它失败了,因为没有找到中指定的库。 为此必须使

  • 问题内容: 我想使两个按钮看起来像按钮。只有当我使用actionlink的#ID来应用时,我才能实现此目的。我想为操作链接分配一个类,但是当我使用下面的代码时,出现一个错误,提示我缺少“}”。 这是我正在应用的样式: 这可行,我想我可以将另一个#ID添加到样式中,但想将样式应用于Class。 问题答案: 您必须使用字符,因为class是C#中的关键字。这是MSDN文档的链接