• java agent技术的注入利用与避坑点


    什么是Java agent技术?

    Java代理(Java agent)是一种Java技术,它允许开发人员在运行时以某种方式修改或增强Java应用程序的行为。Java代理通过在Java虚拟机(JVM)启动时以"代理"(agent)的形式加载到JVM中,以监视、修改或甚至完全改变目标应用程序的行为。

    Java agent 可以做什么?

    1. 1. 安全监控和审计:

      通过Java代理,可以在应用程序中注入代码以监视其行为并记录关键事件。这可以用于安全审计目的,以确保应用程序不受到恶意行为或违规操作的影响。
    2. 2. 安全验证和授权:

      Java代理可以拦截对受保护资源的访问,并执行安全验证和授权操作。通过代理,可以实现访问控制策略,确保只有经过授权的用户或系统可以访问特定资源。
    3. 3. 安全加固:

      通过Java代理,可以对应用程序进行安全加固,例如实时检测和防御攻击,包括代码注入、SQL注入、跨站点脚本攻击等。代理可以拦截请求,并根据安全策略进行处理,从而提高应用程序的安全性。
    4. 4. 加密和解密:

      Java代理可以用于实现端到端的数据加密和解密,保护敏感数据在传输过程中的安全性。代理可以拦截数据流,对数据进行加密或解密操作,以确保数据在传输过程中不会被窃取或篡改。
    5. 5. 安全日志记录:

      Java代理可以用于记录应用程序的安全日志,包括用户操作、异常事件、安全警报等。通过代理,可以将安全日志发送到中央日志服务器进行集中管理和分析,以便及时发现和应对安全威胁。

    静态Agent使用

    创建Maven项目,写一个类PreMainTraceAgent,使用Maven编译并打成jar包。

    1. package com.example;
    2. import java.lang.instrument.ClassFileTransformer;
    3. import java.lang.instrument.IllegalClassFormatException;
    4. import java.lang.instrument.Instrumentation;
    5. import java.security.ProtectionDomain;
    6. public class PreMainTraceAgent {
    7. public static void premain(String agentArgs, Instrumentation inst) {
    8. System.out.println("agentArgs : " + agentArgs);
    9. inst.addTransformer(new DefineTransformer(), true);
    10. }
    11. static class DefineTransformer implements ClassFileTransformer {
    12. static int counts=0;
    13. @Override
    14. public byte[] transform(
    15. ClassLoader loader,
    16. String className,
    17. Class classBeingRedefined,
    18. ProtectionDomain protectionDomain,
    19. byte[] classfileBuffer
    20. throws IllegalClassFormatException {
    21. System.out.println("premain load Class:" + className);
    22. System.out.println("filter "+(counts++)+" class");
    23. return classfileBuffer;
    24. }
    25. }
    26. }

    打成jar包之后我们要注意META-INF目录下的MSNIFEST.MF文件,MANIFEST.MF 文件是 Java 归档文件(如 JAR 文件)的一部分,用于描述归档文件的元数据信息和配置。它通常位于归档文件的根目录下。

    图片

    图片

    一些常见的属性我们需要了解

    1. 1. Manifest-Version: 描述了 MANIFEST.MF 文件的版本。

    2. 2. Created-By: 描述了创建该归档文件的工具名称和版本。

    3. 3. Main-Class: 描述了可执行 JAR 文件的入口类(Main类),当您执行 JAR

      文件时,Java虚拟机会自动寻找并执行该类中的main方法。
    4. 4. Class-Path: 描述了归档文件中包含的依赖项 JAR 文件的路径,以便 Java

      虚拟机在运行时能够找到并加载这些依赖项。

    在构建和部署 Java 应用程序时,MANIFEST.MF 文件可以帮助指定各种元数据信息,使得应用程序可以更好地被管理和执行。例如,当您创建一个可执行的 JAR 文件时,通过指定 Main-Class 属性,可以告诉 Java 虚拟机该 JAR 文件的入口点是哪个类。

    另外创建一个项目,写一个主函数,内容随意,配置虚拟机选项。这里-javaagent:后面跟上上面项目jar包的绝对路径

    图片

    运行结果如图:

    图片

    可以看到premain方法中的代码成功的执行在了Main函数之前。这种使用premain方法在Main函数前执行的也被成为静态agent

    动态Agent使用

    首先是被代理部分(单独的项目)

    1. package com.example;
    2. import java.lang.instrument.ClassFileTransformer;
    3. import java.lang.instrument.IllegalClassFormatException;
    4. import java.lang.instrument.Instrumentation;
    5. import java.security.ProtectionDomain;
    6. public class AgentMain {
    7. public static void agentmain(String agentArgs, Instrumentation
    8. instrumentation) {
    9. instrumentation.addTransformer(new MyTransformer(),true);
    10. }
    11. public static class MyTransformer implements ClassFileTransformer {
    12. static int count = 0;
    13. @Override
    14. public byte[] transform(
    15. ClassLoader loader,
    16. String className,
    17. Class classBeingRedefined,
    18. ProtectionDomain protectionDomain,
    19. byte[] classfileBuffer) throws IllegalClassFormatException {
    20. System.out.println("hello world");//这里就是我们能看到的输出。
    21. return classfileBuffer;
    22. }
    23. }
    24. }

    接下来就是使用Maven打成jar包

    默认情况下META-INFMANIFEST.MF文件中有这些内容

    Manifest-Version: 1.0

    Created-By: Maven JAR Plugin 3.3.0

    Build-Jdk-Spec: 11

    但是这些是不够的,我们需要指出被代理的类。

    Manifest-Version: 1.0

    Agent-Class: com.example.AgentMain

    Can-Redefine-Classes: true

    Can-Retransform-Classes: true

    1. 1. Agent-Class:指定了代理的入口类。这个属性告诉 Java

      虚拟机代理应该从哪个类的  premain 或  agentmain 方法开始执行。 premain 方法用于静态代理(在 JVM 启动时加载),而  agentmain 方法用于动态代理(在 JVM 运行时加载)。代理的入口类必须包含其中一个方法。
    2. 2. Can-Redefine-Classes:指定了代理是否可以重新定义类。如果设置为

      true,代理将允许重新定义已经加载的类,这意味着你可以修改已经加载的类的字节码。这对于某些代理操作,如热代码替换,非常有用。
    3. 3. Can-Retransform-Classes:指定了代理是否可以重新转换类。如果设置为

      true,代理将允许重新转换已经加载的类,这意味着你可以多次修改已经加载的类的字节码。这对于一些特定的代理操作也是非常有用的,如 AOP(面向切面编程)。

    因为是动态加载所以我们不需要在虚拟机启动选项中指定jar包的路径。

    接下来写主程序的测试类

    1. package org.example;
    2. import com.sun.tools.attach.VirtualMachine;
    3. import java.io.File;
    4. import java.lang.management.ManagementFactory;
    5. public class TestMain {
    6. public static void main(String[] args) {
    7. String agentJarPath =
    8. "C:Users86186DesktopstudyJavauntitledtargetuntitled-1.0-SNAPSHOT.jar";
    9. File agentJarFile = new File(agentJarPath);
    10. if (!agentJarFile.exists()) {
    11. System.err.println("Agent JAR file not found.");
    12. return;
    13. }
    14. String name = ManagementFactory.getRuntimeMXBean().getName();
    15. String pid = name.split("@")[0];
    16. if (pid == null) {
    17. System.err.println("Unable to find process ID.");
    18. return;
    19. }
    20. String targetClassName = "AgentMain";
    21. try {
    22. VirtualMachine vm = VirtualMachine.attach(pid);
    23. vm.loadAgent(agentJarPath,targetClassName);
    24. vm.detach();
    25. catch (Exception e) {
    26. e.printStackTrace();
    27. }
    28. }
    29. }

    这里在获取进程号的时候会因为版本的不同而出现错误,java9以下默认是正常的,java9以上会出现报错,我们需要在虚拟机启动参数中加上-Djdk.attach.allowAttachSelf=true。

    图片

    运行结果:

    图片

    为什么结果中有多个helloworld

    这里有讲一下为什么我们在代码中之用了一次sout,但是在结果中却出现了多个helloworld。

    MyTransformer类中的transform方法中的输出语句只会在类被加载时执行一次,但是它会对每个类文件调用一次。由于一个类可能会由多个ClassLoader加载,或者同一个ClassLoader可能会加载多次,因此会导致多次输出。

    这种情况通常在Java应用程序中使用了多个ClassLoader时发生,例如Web应用程序中的热部署或者OSGi环境中。每次类被加载,transform方法都会被调用一次,因此会看到多次输出。

    我们可以修改一下代码做测试,这里我在每个helloworld后添加了被加载类的名字

    图片

    修改后的输出结果:

    图片

    实战示例:修改目标虚拟机中执行的程序

    第一步

    首先我们写出我们正在执行的程序:循环打印helloworld。

    1. package org.example;
    2. import static java.lang.Thread.sleep;
    3. public class Main {
    4. public static void main(String[] args) throws InterruptedException {
    5. while(true) {
    6. hello();
    7. sleep(1500);
    8. }
    9. }
    10. public static void hello(){
    11. System.out.println("Hello World!");
    12. }
    13. }

    图片

    第二步

    准备我们的agentmain和ClassFileTransformer实现类。

    1. package com.example;
    2. import java.lang.instrument.ClassFileTransformer;
    3. import java.lang.instrument.IllegalClassFormatException;
    4. import java.lang.instrument.Instrumentation;
    5. import java.lang.instrument.UnmodifiableClassException;
    6. import java.security.ProtectionDomain;
    7. public class AgentMain {
    8. public static void agentmain(String agentArgs, Instrumentation
    9. instrumentation) throws UnmodifiableClassException {
    10. Class [] classes = instrumentation.getAllLoadedClasses();
    11. //获取目标JVM加载的全部类
    12. for(Class cls : classes){
    13. if (cls.getName().equals("org.example.Main")){
    14. instrumentation.addTransformer(new HackTransform(),true);
    15. instrumentation.retransformClasses(cls);
    16. }
    17. // System.out.println(cls.getName());
    18. }
    19. }
    20. }
    21. package com.example;
    22. import javassist.ClassClassPath;
    23. import javassist.ClassPool;
    24. import javassist.CtClass;
    25. import javassist.CtMethod;
    26. import java.io.IOException;
    27. import java.lang.instrument.ClassFileTransformer;
    28. import java.lang.instrument.IllegalClassFormatException;
    29. import java.security.ProtectionDomain;
    30. public class HackTransform implements ClassFileTransformer {
    31. @Override
    32. public byte[] transform(ClassLoader loader, String className,
    33. Class classBeingRedefined, ProtectionDomain protectionDomain,
    34. byte[] classfileBuffer) throws IllegalClassFormatException {
    35. if (className.equals("org/example/Main")) {
    36. try {
    37. System.out.println(className);
    38. ClassPool classPool = ClassPool.getDefault();
    39. if (classBeingRedefined != null) {
    40. ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
    41. classPool.insertClassPath(ccp);
    42. }
    43. CtClass ctClass = classPool.get("org.example.Main");
    44. System.out.println(ctClass);
    45. CtMethod ctMethod = ctClass.getDeclaredMethod("hello");
    46. //设置方法体
    47. String body = "{System.out.println("[+]Hacker!!");}";
    48. ctMethod.setBody(body);
    49. ctClass.defrost();
    50. return ctClass.toBytecode();
    51. catch (Exception e) {
    52. e.printStackTrace();
    53. }
    54. }
    55. return null;
    56. }
    57. }

    第三步

    把第二步中的两个类打成jar包。并修改其中MANIFEST.MF中的内容。

    图片

    MANIFEST.MF中的内容

    Manifest-Version: 1.0Agent-Class: com.example.AgentMainCan-Redefine-Classes: trueCan-Retransform-Classes: true

    第四步

    写我们的注入代码

    1. package org.example;
    2. import com.sun.tools.attach.*;
    3. import java.io.IOException;
    4. import java.util.List;
    5. public class inject {
    6. public static void main(String[] args) throws IOException,
    7. AttachNotSupportedException, AgentLoadException,
    8. AgentInitializationException {
    9. //调用VirtualMachine.list()获取正在运行的JVM列表
    10. List list = VirtualMachine.list();
    11. for (VirtualMachineDescriptor vmd : list) {
    12. System.out.println(vmd.displayName());
    13. if (vmd.displayName().equals("org.example.Main")) {
    14. //连接指定JVM
    15. VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
    16. String agentJarPath =
    17. "C:Users86186DesktopstudyJavauntitledtargetuntitled-1.0-SNAPSHOT.jar";
    18. //加载Agent
    19. virtualMachine.loadAgent(agentJarPath,"com.example.AgentMain");
    20. //断开JVM连接
    21. virtualMachine.detach();
    22. }
    23. }
    24. }
    25. }

    第五步

    执行即可(先运行主java程序,后运行注入程序)

    图片

    图片

    图片

  • 相关阅读:
    微信支付APIv3版,平台证书可视化下载工具
    在亚马逊购买产品时怎么选择自动收货方式
    Vue2.0开发之——loader处理图片问题(08)
    FunAudioLLM SenseVoice语音转录(ASR)与CosyVoice语音合成(TTS)及语音克隆使用案例;webui可视化页面操作使用
    PGL图学习之图神经网络ERNIESage、UniMP进阶模型[系列八]
    Ovalbumin-tyrosine 鸡卵白蛋白修饰络氨酸,tyrosine-PEG-OVA 络氨酸-聚乙二醇-卵清蛋白
    携手华为使能全场景创新,夯实算力底座,麒麟信安受邀参加华为全联接大会2023
    Drone-Yolo:一种高效的无人机图像目标检测神经网络方法
    pysot-master-train.py 运行记录
    Socket编程实验
  • 原文地址:https://blog.csdn.net/YJ_12340/article/details/136630601