Java agent技术的注入利用与避坑点
什么是Java agent技术?
Java代理(Java
agent)是一种Java技术,它允许开发人员在运行时以某种方式修改或增强Java应用程序的行为。Java代理通过在Java虚拟机(JVM)启动时以"代理"(agent)的形式加载到JVM中,以监视、修改或甚至完全改变目标应用程序的行为。
Java agent 可以做什么?
-
安全监控和审计:
通过Java代理,可以在应用程序中注入代码以监视其行为并记录关键事件。这可以用于安全审计目的,以确保应用程序不受到恶意行为或违规操作的影响。
-
安全验证和授权:
Java代理可以拦截对受保护资源的访问,并执行安全验证和授权操作。通过代理,可以实现访问控制策略,确保只有经过授权的用户或系统可以访问特定资源。
-
安全加固:
通过Java代理,可以对应用程序进行安全加固,例如实时检测和防御攻击,包括代码注入、SQL注入、跨站点脚本攻击等。代理可以拦截请求,并根据安全策略进行处理,从而提高应用程序的安全性。
-
加密和解密:
Java代理可以用于实现端到端的数据加密和解密,保护敏感数据在传输过程中的安全性。代理可以拦截数据流,对数据进行加密或解密操作,以确保数据在传输过程中不会被窃取或篡改。
-
安全日志记录:
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
文件)的一部分,用于描述归档文件的元数据信息和配置。它通常位于归档文件的根目录下。
一些常见的属性我们需要了解
-
Manifest-Version: 描述了 MANIFEST.MF 文件的版本。
-
Created-By: 描述了创建该归档文件的工具名称和版本。
-
Main-Class: 描述了可执行 JAR 文件的入口类(Main类),当您执行 JAR
文件时,Java虚拟机会自动寻找并执行该类中的main方法。
-
Class-Path: 描述了归档文件中包含的依赖项 JAR 文件的路径,以便 Java
虚拟机在运行时能够找到并加载这些依赖项。
在构建和部署 Java 应用程序时,MANIFEST.MF
文件可以帮助指定各种元数据信息,使得应用程序可以更好地被管理和执行。例如,当您创建一个可执行的
JAR 文件时,通过指定 Main-Class 属性,可以告诉 Java 虚拟机该 JAR
文件的入口点是哪个类。
另外创建一个项目,写一个主函数,内容随意,配置虚拟机选项。这里-javaagent:后面跟上上面项目jar包的绝对路径。
运行结果如图:
可以看到premain方法中的代码成功的执行在了Main函数之前。这种使用premain方法在Main函数前执行的也被成为静态agent
帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)
动态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
-
Agent-Class:指定了代理的入口类。这个属性告诉 Java
虚拟机代理应该从哪个类的 premain 或 agentmain
方法开始执行。premain 方法用于静态代理(在 JVM
启动时加载),而 agentmain 方法用于动态代理(在 JVM
运行时加载)。代理的入口类必须包含其中一个方法。 -
Can-Redefine-Classes:指定了代理是否可以重新定义类。如果设置为
true,代理将允许重新定义已经加载的类,这意味着你可以修改已经加载的类的字节码。这对于某些代理操作,如热代码替换,非常有用。
-
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程序,后运行注入程序)
相关文章:

Java agent技术的注入利用与避坑点
什么是Java agent技术? Java代理(Java agent)是一种Java技术,它允许开发人员在运行时以某种方式修改或增强Java应用程序的行为。Java代理通过在Java虚拟机(JVM)启动时以"代理"(agent…...
Linux每日练习
第一部分 1.打开桌面的主文件夹,在图片文件夹下新建一个名为111的文件夹,在视频文件夹下创建一个名为222的文件夹 [rootxcz7 desk]# mkdir -p ./pic/111 [rootxcz7 desk]# mkdir -p ./video/2222.在桌面打开终端,先切换到根目录下ÿ…...

【Python】6. 基础语法(4) -- 列表+元组+字典篇
列表和元组 列表是什么, 元组是什么 编程中, 经常需要使用变量, 来保存/表示数据. 如果代码中需要表示的数据个数比较少, 我们直接创建多个变量即可. num1 10 num2 20 num3 30 ......但是有的时候, 代码中需要表示的数据特别多, 甚至也不知道要表示多少个数据. 这个时候,…...

【C++庖丁解牛】C++内存管理 | new和delete的使用以及使用原理
📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 1. C/C内存分布2. C语…...
go调用 c++中数组指针相关
要在Go语言中调用C编译的DLL(动态链接库)并传递数组,你需要遵循以下步骤: 编写C代码:首先,你需要有一个C的DLL,它提供了你想要在Go中调用的函数。为了确保Go可以调用它,你需要使用C…...

NTFS Disk by Omi NTFS for mac v1.1.4中文版
NTFS Disk by Omi NTFS for Mac:NTFS文件系统的无缝桥梁 软件下载:NTFS Disk by Omi NTFS for mac v1.1.4中文版 🌐 跨平台访问,文件无阻 NTFS Disk by Omi NTFS for Mac 为您的Mac提供了对NTFS文件系统的无缝访问。无论您是在Win…...

Arduino应用开发——使用GUI-Guider制作LVGL UI并导入ESP32运行
Arduino应用开发——使用GUI-Guider制作LVGL UI并导入ESP32运行 目录 Arduino应用开发——使用GUI-Guider制作LVGL UI并导入ESP32运行前言1 使用GUI-Guider设计UI1.1 创建工程1.2 设计UI 2 ESP工程导入UI2.1 移植LVGL2.2 移植UI文件2.3 调用UI文件2.4 烧录测试 结束语 前言 GU…...

前端WebRTC局域网1V1视频通话
基本概念 WebRTC(Web Real-Time Communications) 网络实时通讯,它允许网络应用或者站点,在不借助中间媒介的情况下,建立点对点(Peer-to-Peer)的连接,实现视频流和音频流或者其他任…...
设计模式之构建者模式
构建者模式(Builder) 定义 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示 使用场景 主要角色 产品 Product建造者接口 Builder具体的建造者 Concrete Builder指挥者 Director:组织构建过程 示例代码 Data p…...

【PCIe 链路训练】之均衡(equalization)
1、概述 这篇文章简单介绍一下PCIE phy的均衡原理和过程,USB phy,ethernet phy这些高速的串行serdes也有相同或者相似的结构。可以不用太关注其中的细节,等到debug的时候可以查询协议,但是需要了解这个故事讲的大概内容。整个equalization过程是controller和phy一起配合完成…...
P1059 [NOIP2006 普及组] 明明的随机数
题目描述 明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了 N 个 1 到 1000 之间的随机整数 (N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着…...
【每日一问】Cookie、Session 和 Token 有什么区别?
Cookie、Session 和 Token 通常都是用来保存用户登录信息的技术,但三者有很大的区别,简单来说 Cookie 适用于简单的状态管理,Session 适用于需要保护用户敏感信息的场景,而 Token 适用于状态无关的身份验证和授权。 具体来说&…...

智能合约语言(eDSL)—— proc_macro实现合约init函数
我们通过属性宏来实现合约的init函数,call函数其实和init是类似的; GitHub - XuHugo/xwasm 构建属性宏,要在cargo.toml里面设置一些参数,这是必须的。一般来说,过程宏必须是一个库,或者作为工程的子库&…...

如何使用 ArcGIS Pro 制作三维地形图
伴随硬件性能的提高和软件算法的优化,三维地图的应用场景会越来越多,这里为大家介绍一下在ArcGIS Pro怎么制作三维地形图,希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的DEM和影像数据,除了DEM和影像数据…...

服务器配置禁止IP直接访问,只允许域名访问
联网信息系统需设置只允许通过域名访问,禁止使用IP地址直接访问,建议同时采用云防护技术隐藏系统真实IP地址且只允许云防护节点IP访问服务器,提升网络安全防护能力。 一、Nginx 修改配置文件nginx.conf,在server段里插入正则表达式…...
#14vue3生成表单并跳转到外部地址的方式
1、背景 后端返回的json数据中包含一个json数组,此数组中是目标跳转地址所需要的form表单的数据。 2、跳转前的页面 const goto () > {finish.value true;request.post(/xxx/yyy,{zzz: zzz.value}).then(res > {const url res.data.submitUrlconst params…...

航测管家:智能化革新航测作业流程
在信息时代的浪潮中,倾斜摄影作为一种高效的航测技术,对于城市规划、土地管理、环境监测等领域的重要性日益凸显。然而,航测作业中的数据管理和设备操作复杂性一直是行业面临的挑战。深圳赛尔智控推出的赛尔航测管家,凭借其智能化…...

XXE-XML实体注入漏洞
目录 1.xml基础 1.1什么是xml 1.2xml文档结构 1.3 什么是DTD 1.4 什么是实体 1.5 什么是外部实体 2.xxe漏洞 2.1xxe漏洞基本介绍 2.2xxe漏洞的危害 经典漏洞案例分析 3.xxe漏洞挖掘和利用 3.1. 识别潜在的XML入口 3.2. 检查XML处理逻辑 3.3. 构造试探Payload 常…...

数据结构从入门到精通——栈
栈 前言一、栈1.1栈的概念及结构1.2栈的实现1.3栈的面试题 二、栈的具体实现代码栈的初始化栈的销毁入栈出栈返回栈顶元素返回栈中的元素个数检测是否为空Stack.hStack.ctest.c 前言 栈,作为一种后进先出(LIFO)的数据结构,在计算…...

webhook详解
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 webhook简介 在当今高度连接的网络世界中,没有什么可以孤立地发挥最佳作用。完成一项任务(几乎)总是需要多个实体的参与。电子商务应用程序需要与支付系统通信,支付…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...