偷天换日,用 JavaAgent 欺骗你的 JVM

Posted ImportNew

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了偷天换日,用 JavaAgent 欺骗你的 JVM相关的知识,希望对你有一定的参考价值。

System. System. System. 在运行过程中出现异常,那么也会导致主程序的启动失败。我们对上面例子中  System. System. RuntimeException( System. System. System. 和主程序了,这里需要借助 com.sun.tools.attach 包下的 VirtualMachine 工具类。需要注意该类不是 JVM 标准规范,是由 Sun 公司自己实现的,使用前需要引入依赖:

VirtualMachine VirtualMachine vm= VirtualMachine.attach( vm.loadAgent( (Exception e) e.printStackTrace(); 中的代码:

premain(agentArgs)premain(agentArgs, Instrumentation inst)

agentmain(agentArgs)agentmain(agentArgs, Instrumentation inst)


System. System. Fruit().getFruit(); inst.addTransformer(FruitTransformer()); @ ProtectionDomain protectionDomain, (!className. classfileBuffer; String fileName= getClassBytes(fileName); File file = File(fileName); = FileInputStream(file); ByteArrayOutputStream bs = ByteArrayOutputStream()) length = file.length(); n; ((n = bs.write(bytes, bytes; (Exception e) e.printStackTrace(); ClassNotFoundException, UnmodifiableClassException;


...


UnmodifiableClassException, ClassNotFoundException String fileName= ClassDefinition def=ClassDefinition(Fruit.class, FruitTransformer.getClassBytes(fileName)); inst.redefineClasses(ClassDefinition[]def); UnmodifiableClassException;


throws UnmodifiableClassException inst.addTransformer(FruitTransformer(), inst.retransformClasses(Fruit.class); System. canRetransform);


InterruptedException Fruit().getFruit(); TimeUnit.SECONDS.sleep( 代理到主程序中:

Exception VirtualMachine vm = VirtualMachine.attach( vm.loadAgent( inst.addTransformer(LogTransformer()); ProtectionDomain protectionDomain, IllegalClassFormatException (!className.equals( calculate(); (Exception e) e.printStackTrace(); ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.get( CtMethod ctMethod = ctClass.getDeclaredMethod( CtMethod copyMethod = CtNewMethod.ClassMap()); ctMethod.setName( StringBuffer body = StringBuffer( . . . . copyMethod.setBody(body.toString()); ctClass.addMethod(copyMethod); ctClass.toBytecode();(Method method : Fruit.class.getDeclaredMethods()) System. method.invoke(Fruit()); System.out.println("-------");


查看结果,可以看到类中确实已经新增了一个方法:



除此之外,javassist 还有很多其他的功能,例如新建 Class、设置父类、读取和写入字节码等等,大家可以在具体的场景中学习它的用法。


总结


虽然我们在平常的工作中,直接用到 Java Agent 的场景可能并不是很多,但是在热部署、监控、性能分析等工具中,它们可能隐藏在业务系统的角落里,一直在默默发挥着巨大的作用。


本文从 Java Agent 的两种模式入手,手动实现并简要分析了它们的工作流程。虽然在这里只利用它们完成了一些简单的功能,但是不得不说,正是 Java Agent 的出现,让程序的运行不再循规蹈矩,也为我们的代码提供了无限的可能性。


- EOF -

推荐阅读  点击标题可跳转

1、Java 动态代理及 RPC 框架介绍

2、Java Proxy 和 CGLIB 动态代理原理

3、从头捋了一遍 Java 代理机制,收获颇丰


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

点赞和在看就是最大的支持❤️



在 main Jar 中引用 Jar 作为 javaagent 传递给 JVM

【中文标题】在 main Jar 中引用 Jar 作为 javaagent 传递给 JVM【英文标题】:Reference Jar within main Jar to pass as javaagent to JVM 【发布时间】:2018-01-24 08:02:15 【问题描述】:

我需要在运行 jar1 时在我的 JVM args 中添加一个 -javaagent 参数,但引用 jar1 中包含的 jar2。我试过了:

-javaagent:BOOT-INF/lib/jetty-alpn-agent-2.0.0.jar"

没有成功。如何在运行时确定 JAR 在运行系统中的位置?

(这是为了将 jetty-alpn-agent-2.0.0.jar 作为 Java 代理运行,用于 HTTP/2 与 Pushy APN 一起使用)

【问题讨论】:

你找到解决这个问题的方法了吗? 【参考方案1】:

如果你知道类在类路径上,你通常可以这样做:

URL jar = MainClass.class.getProtectionDomain().getCodeSource().getLocation();

Javaagent 总是加载在类路径上,这就是为什么您应该能够引用常规应用程序的主类的原因。

【讨论】:

【参考方案2】:

@indusBull

对我有用的解决方案是在我的 gradle.properties 文件中定义一个名为 libDirectory 的属性,这是 JAR 在运行系统中的位置,然后从我的 Dockerfile 中引用该变量:

gradle.properties

libDirectory = /opt/meanwhileinhell/app/lib

Dockerfile

ENTRYPOINT  ["java",\
            ...
            ...
            "-javaagent:$libDirectory/jetty-alpn-agent.jar",\
            "-jar", "/app.jar"]

【讨论】:

以上是关于偷天换日,用 JavaAgent 欺骗你的 JVM的主要内容,如果未能解决你的问题,请参考以下文章

javaagent

Java Agent (JVM Instrumentation 机制) 极简教程

在 main Jar 中引用 Jar 作为 javaagent 传递给 JVM

java 简单的代码片段,展示如何将javaagent附加到运行JVM进程

字节码基于JavaAgent的全链路监控四-JVM内存与GC信息

大白话JavaAgent