Javassist学习(二)

0x01 动态生成类

import javassist.*;
import java.util.Scanner;

public class JavassistTest {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        Scanner scanner = new Scanner(System.in);
        // 获取类名
        System.out.print("输入类的名字: ");
        String classname = scanner.nextLine();
        CtClass ctClass = pool.makeClass(classname);
        
        //"public void Hello() { System.out.println(\"Hello, World!\"); }"
        //注意转义

        // 获取方法
        System.out.print("输入类的方法: ");
        String classmethod = scanner.nextLine();
        CtMethod method = CtNewMethod.make(classmethod, ctClass);
        ctClass.addMethod(method);
        //将类加载到JVM
        Class<?> clazz = ctClass.toClass();
        Object instance = clazz.getDeclaredConstructor().newInstance();
        //获取并调用方法
        System.out.print("输入方法名: ");
        String classmethod_name = scanner.nextLine();

        clazz.getMethod(classmethod_name).invoke(instance);
    }
}

0x02 加载已有类

import javassist.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.io.IOUtils;

import java.util.Base64;
import java.util.Scanner;

public class webshelltest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {

        Scanner scanner = new Scanner(System.in);
        // 获取命令
        System.out.print("输入执行的命令: ");
        String command = scanner.nextLine();
        

        /*byte[] rtn = new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101};//java.lang.Runtime
        //String javaruntime = "java.lang.Runtime";
        String run_base64 = Base64.getEncoder().encodeToString(rtn);
        String run = new String(Base64.getDecoder().decode(run_base64));
        byte[] b2 = new byte[]{101, 120, 101, 99}; //exec
        String cm = new String(b2);*/

        String run = "java.lang.Runtime"; // 可以替换为实际的类名
        String cm = "exec"; // exec 方法名

        try {
            // 动态加载类
            Class<?> shellClass = Class.forName(run);//加载已有类
            Constructor<?> declaredConstructor = shellClass.getDeclaredConstructor();//获取构造器
            declaredConstructor.setAccessible(true);//确保构造器可以访问
            Object o = declaredConstructor.newInstance();//实例化

            // 获取方法
            Method exec = shellClass.getMethod(cm, String.class);
            Process process = (Process) exec.invoke(o, command);

            // 获取进程输出流
            InputStream inputStream = process.getInputStream();
            String output = IOUtils.toString(inputStream, "gbk");

            // 输出命令结果
            System.out.println(output);
        } catch (Exception e) {
            e.printStackTrace(); // 异常处理
        }
    }
}

PS. exec 方法入参是 单个String []类型,但是实际的入参类型可能多个是 String[](例如ping 1.1.1.1),这里重写了exec方法的签名,或许还可以通过拆分String,再强制类型转换为object。

实际在测试的过程中,有这几个问题:
1、如果是为了绕过waf,是不是应该尝试传参的时候使用base64或者其他的方式加密一下,执行的时候再解密
2、如果是采用这种方式,如何绕过rasp或者edr之类的?感觉绕过这两个靠加解密是不行的。
3、实际环境不太可能有javassist

上一篇
下一篇