前言
动态编程这个概念是相对于静态编程而言的,两者的区别简而言之,在静态编程中,类型检查是在编译时完成的,而动态编程中类型检查是在运行时完成的。所谓动态编程就是绕过编译过程在运行时进行操作的技术
在反射,动态代理中都能感受到动态编程的影子
Javassist
是一个开源的分析、编辑和创建Java字节码的类库,Java 字节码存储在称为类文件的二进制文件中。每个类文件包含一个 Java 类或接口。
可以利用的点就在于使用Javassist
时我们可以像写Java代码一样直接插入Java代码片段,让我们不再需要关注Java底层的字节码的和栈操作,仅需要学会如何使用Javassist
的API即可实现字节码编辑,类似于可以达到任意代码执行的效果。
Javassist使用
最主要的类有以下几个
ClassPool
ClassPool
:一个基于哈希表(Hashtable
)实现的CtClass
对象容器,其中键名是类名称,值是表示该类的CtClass
对象(Hashtable
和Hashmap
类似都是实现``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,都是采用Runtime
,ProcessBuilder
这两个类去进行构造,执行命令。按照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