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包。

package com.example;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;public class PreMainTraceAgent {public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("agentArgs : " + agentArgs);
inst.addTransformer(new DefineTransformer(), true);
}static class DefineTransformer implements ClassFileTransformer {
static int counts=0;
@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer
) throws IllegalClassFormatException {
System.out.println("premain load Class:" + className);
System.out.println("filter "+(counts++)+" class");
return classfileBuffer;
}
}
}

打成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使用

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

package com.example;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;public class AgentMain {
public static void agentmain(String agentArgs, Instrumentation
instrumentation) {
instrumentation.addTransformer(new MyTransformer(),true);}
public static class MyTransformer implements ClassFileTransformer {
static int count = 0;@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("hello world");//这里就是我们能看到的输出。
return classfileBuffer;
}
}
}

接下来就是使用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包的路径。

接下来写主程序的测试类

package org.example;import com.sun.tools.attach.VirtualMachine;import java.io.File;
import java.lang.management.ManagementFactory;public class TestMain {
public static void main(String[] args) {
String agentJarPath =
"C:Users86186DesktopstudyJavauntitledtargetuntitled-1.0-SNAPSHOT.jar";
File agentJarFile = new File(agentJarPath);
if (!agentJarFile.exists()) {
System.err.println("Agent JAR file not found.");
return;
}
String name = ManagementFactory.getRuntimeMXBean().getName();
String pid = name.split("@")[0];if (pid == null) {
System.err.println("Unable to find process ID.");
return;
}
String targetClassName = "AgentMain";
try {
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent(agentJarPath,targetClassName);
vm.detach();
} catch (Exception e) {
e.printStackTrace();
}
}}

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

图片

运行结果:

图片

为什么结果中有多个helloworld

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

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

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

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

图片

修改后的输出结果:

图片

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

第一步

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

package org.example;import static java.lang.Thread.sleep;public class Main {
public static void main(String[] args) throws InterruptedException {
while(true) {
hello();
sleep(1500);
}
}
public static void hello(){
System.out.println("Hello World!");
}
}

图片

第二步

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

package com.example;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;public class AgentMain {
public static void agentmain(String agentArgs, Instrumentation
instrumentation) throws UnmodifiableClassException {
Class [] classes = instrumentation.getAllLoadedClasses();//获取目标JVM加载的全部类
for(Class cls : classes){if (cls.getName().equals("org.example.Main")){instrumentation.addTransformer(new HackTransform(),true);
instrumentation.retransformClasses(cls);
}
// System.out.println(cls.getName());
}}}package com.example;import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;public class HackTransform implements ClassFileTransformer {@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (className.equals("org/example/Main")) {
try {
System.out.println(className);ClassPool classPool = ClassPool.getDefault();if (classBeingRedefined != null) {
ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
classPool.insertClassPath(ccp);
}CtClass ctClass = classPool.get("org.example.Main");
System.out.println(ctClass);CtMethod ctMethod = ctClass.getDeclaredMethod("hello");//设置方法体
String body = "{System.out.println("[+]Hacker!!");}";
ctMethod.setBody(body);
ctClass.defrost();return ctClass.toBytecode();} catch (Exception e) {
e.printStackTrace();
}
}return null;
}
}

第三步

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

图片

MANIFEST.MF中的内容

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

第四步

写我们的注入代码

package org.example;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<VirtualMachineDescriptor> list = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : list) {
System.out.println(vmd.displayName());if (vmd.displayName().equals("org.example.Main")) {//连接指定JVM
VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
String agentJarPath =
"C:Users86186DesktopstudyJavauntitledtargetuntitled-1.0-SNAPSHOT.jar";
//加载Agent
virtualMachine.loadAgent(agentJarPath,"com.example.AgentMain");
//断开JVM连接
virtualMachine.detach();
}}}
}

第五步

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

图片

图片

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/737903.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CPU飙升和OOM排查思路

CPU使用率高和OOM&#xff08;Out of Memory&#xff09;异常是两个不同但可能相互关联的问题。下面分别对这两个问题的排查流程进行详细说明&#xff1a; CPU使用率高排查流程 实时监控与初步定位&#xff1a; 使用系统工具&#xff0c;如Linux中的top命令查看整体CPU使用情况…

分发平台如何支持热更

随着移动应用程序和游戏的迅猛发展&#xff0c;用户对于获得最新功能和修复bug的期望也越来越高。为了满足这一需求&#xff0c;现代的分发平台越来越注重在应用程序或游戏发布后能够支持热更新的功能。热更新是指通过网络直接获取更新并应用到用户设备上&#xff0c;而无需重新…

云原生周刊:Helm Charts 深入探究 | 2024.3.11

开源项目推荐 Glasskube Glasskube 提供了一个用于 Kubernetes 的缺失的包管理器。它具有图形用户界面(GUI)和命令行界面(CLI)。Glasskube 包是具备依赖感知、GitOps 准备和可以通过中央公共包仓库自动更新的特性。 imgpkg imgpkg&#xff08;发音为&#xff1a;"imag…

WPF 该线程是用不接受参数的 ThreadStart 委托创建的。

创建无参数线程是无法发去传递参数的&#xff0c;需要把 《 thread.Start(“张三”); 》改为《 thread.Start(); 》 把参数去掉就可以了。 public RegisterWindow(){InitializeComponent();//无参数线程Thread thread new Thread(pageLoad);thread.IsBackground true;//thr…

ubuntu 23开机界面美化教程

效果 方法 GRUB开机界面美化 从上述网站中&#xff0c;查找GRUB Themes分类&#xff0c;并下载GRUB主题包&#xff08;tar.gz格式&#xff09;&#xff0c;如CyberSynchro.tar.gz&#xff1b; 解压下载得到的压缩包&#xff0c;得到CyberSynchro&#xff1b; 将CyberSynchro…

leetcode 热题 100_轮转数组

题解一&#xff1a; 新数组存储&#xff1a;另外用一个数组存储移动后的结果&#xff0c;再复制回原数组。 class Solution {public void rotate(int[] nums, int k) {int[] result new int[nums.length];for (int i 0; i < nums.length; i) {result[(i k) % nums.lengt…

llama factory 是如何加载数据集 通过对数据集加载的代码的理解编写自定义数据集训练代码

第一层从训练代码追踪到以下代码 def get_dataset(tokenizer: "PreTrainedTokenizer",model_args: "ModelArguments",data_args: "DataArguments",training_args: "Seq2SeqTrainingArguments",stage: Literal["pt", "…

Python自动化测试之Python简介及环境安装配置

经过持续的"内卷"&#xff0c;编程变成测试工程师不可或缺的一项能力&#xff0c;掌握了一门编程语言,使你在面试过程中更有竞争力&#xff0c;是升值加薪的利器。 一、Python发展史 Python 是由 Guido van Rossum 在八十年代末和九十年代初&#xff0c;在荷兰国家数…

springboot同时接收json数据和 MultipartFile

首先测试接口发送方式。。。。。注意发送结构&#xff01; 后端接收RequestPart SaCheckPermission("system:records:add")Log(title "【用药纪录】", businessType BusinessType.INSERT)RepeatSubmit()PostMapping()public R<Void> add( RequestP…

大模型GPU监控之nvitop

背景 在进行大模型训练的时候&#xff0c;往往需要用到多张GPU卡&#xff0c;如何实现多卡的管理和监控&#xff0c;这是一个比较好的话题&#xff0c;下面介绍一个小工具。 安装nvitop pip install nvitop nvitop -m full 监控界面

抖音视频提取gif怎么做?分分钟帮你生成gif

通过将视频转换成gif动图的方式能够方便的在各种平台上分享、传播。相较于视频文件&#xff0c;gif动图的体积更小&#xff0c;传播起来更方便&#xff0c;能够吸引大众的注意力。下面&#xff0c;就来给大家分享一个gif图片制作&#xff08;https://www.gif.cn/&#xff09;的…

如何隐藏前端项目编译后的.map文件

需求来源&#xff1a; 1、不想让用户在客户端看到源码。 2、应对安全扫描&#xff0c;给客户交付的项目.map被扫描出风险&#xff0c;确保交付。 在Web开发中&#xff0c;通常会使用Source Map文件&#xff08;.map文件&#xff09;来将压缩后的JavaScript或CSS代码映射回原始…

分布式架构下 网络通信的底层实现原理(三)

阻塞通信 Java中经常会使用Scoket套接字来实现网通信&#xff0c; 举个栗子&#xff1a; import java.io.*; import java.net.ServerSocket; import java.net.Socket;public class testSocket {public static void main(String[] args) throws IOException {final int DEFAU…

web自动化测试框架都是有哪些?

Web自动化测试框架主要有以下几种&#xff1a; 1.Selenium&#xff1a;轻量级的Web自动化测试框架&#xff0c;支持多种Web浏览器和语言的集成。Selenium提供了一个IDE来录制和运行自动化测试脚本&#xff0c;还提供了WebDriver&#xff0c;可以通过编程语言编写自动化测试脚本…

【鸿蒙 HarmonyOS 4.0】Web组件

一、介绍 页面加载是Web组件的基本功能。根据页面加载数据来源可以分为三种常用场景&#xff0c;包括加载网络页面、加载本地页面、加载HTML格式的富文本数据。 二、加载网页 2.1、加载在线网页 Web组件的使用非常简单&#xff0c;只需要在Page目录下的ArkTS文件中创建一个…

5款好用的AI办公软件,一键轻松制作PPT、视频,提升工作效率!

众所周知&#xff0c;AI 人工智能技术已渗透到生活的方方面面&#xff0c;无论是很多人早已用上的智能音箱、语音助手&#xff0c;还是新近诞生的各种 AI 软件工具&#xff0c;背后都离不开 AI 人工智能技术的加持。 对于各类新生的 AI 软件工具&#xff0c;人们很容易「选边站…

Go的安装

一. 下载地址 Go官方下载地址&#xff1a;https://golang.org/dl/ Go中文网&#xff1a;https://go.p2hp.com/go.dev/dl/ 根据不同系统下载不同的包。 二. 配置GOPATH GOPATH是一个环境变量&#xff0c;用来表明你写的go项目的存放路径。 GOPATH路径最好只设置一个&#xff0…

部署 Web 项目到Linux上

目录 环境配置 构建项目 ​编辑 数据准备 运行并查看日志 杀掉进程 把程序安装到生产环境上, 这个过程称为 "部署"&#xff0c;也叫 "上线"。一旦程序部署成功, 那么这个程序就能被外网中千千万万的普通用户访问到。 环境配置 程序配置文件修改 实…

讲解Python 3的10种基本语法

在学习Python编程语言时&#xff0c;掌握基本语法是至关重要的。本文将通过10个范例详细讲解Python 3的基本语法&#xff0c;帮助读者更好地理解和掌握这门流行的编程语言。 1. 变量和数据类型 Python是一种动态类型语言&#xff0c;不需要事先声明变量的数据类型。例如&…

网络套接字1

网络套接字1 &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;Linux &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容讲解了udp的Linux环境下的使用&#xff0c…