自己动手实现远程执行功能(深入理解java虚拟机)

 2019-12-22 11:07  阅读(1285)
文章分类:JVM

书中的案例自己实现一遍,过程如下文

具体是在eclipse中实现,整体截图如下:

2019120001576\_1.png

1.ByteUtils的代码如下:

package org.jvm;
    /**
     * Bytes数组处理工具
     */
    public class ByteUtils {
        public static int bytes2Int(byte[] b, int start, int len) {
            int sum = 0;
            int end = start + len;
            for(int i = start; i < end; i++){
                int n = ((int) b[i]) & 0xff;
                n <<= (--len) * 8;
                sum = n + sum;
            }
            return sum;
        }

        public static byte[] int2Bytes(int value, int len) {
            byte[] b = new byte[len];
            for(int i = 0; i < len; i++){
                b[len - i - 1] = (byte) ((value >> 8 * i) & 0xff);
            }
            return b;
        }

        public static String bytes2String(byte[] b, int start, int len) {
            return new String(b, start, len);
        }

        public static byte[] string2Bytes(String str) {
            return str.getBytes();
        }

        public static byte[] bytesReplace(byte[] originalBytes, int offset, int len,
                byte[] replaceBytes) {
            byte[] newBytes = new byte[originalBytes.length + (replaceBytes.length - len)];
            System.arraycopy(originalBytes, 0, newBytes, 0, offset);
            System.arraycopy(replaceBytes, 0, newBytes, offset, replaceBytes.length);
            System.arraycopy(originalBytes, offset + len, newBytes, offset + replaceBytes.length, originalBytes.length - offset - len);
            return newBytes;
        }
    }

2.ClassModifier代码如下:

package org.jvm;
    /**
     * 修改Class文件,暂时只提供修改常量池常量的功能
     */
    public class ClassModifier {
        private static final int CONSTANT_POOL_COUNT_INDEX = 8;
        private static final int CONSTANT_Utf8_info = 1;
        private static final int[] CONSTANT_ITEM_LENGTH = {-1,-1,-1,5,5,9,9,3,3,5,5,5,5};
        private static final int u1 = 1;
        private static final int u2 = 2;
        private byte[] classByte;

        public ClassModifier(byte[] classByte){
            this.classByte = classByte;
        }

        public byte[] modifyUTF8Constant(String oldStr, String newStr){
            int cpc = getConstantPoolCount();
            int offset = CONSTANT_POOL_COUNT_INDEX + u2;
            for(int i = 0; i < cpc; i++){
                int tag = ByteUtils.bytes2Int(classByte,offset, u1);
                if(tag == CONSTANT_Utf8_info){
                    int len = ByteUtils.bytes2Int(classByte, offset + u1, u2);
                    offset += (u1 + u2);
                    String str = ByteUtils.bytes2String(classByte, offset, len);
                    if(str.equalsIgnoreCase(oldStr)){
                        byte[] strBytes = ByteUtils.string2Bytes(newStr);
                        byte[] strLen = ByteUtils.int2Bytes(newStr.length(),u2);
                        classByte = ByteUtils.bytesReplace(classByte, offset - u2, u2, strLen);
                        classByte = ByteUtils.bytesReplace(classByte, offset, len, strBytes);
                        return classByte;
                    }else{
                        offset += len;
                    }
                }else{
                    offset += CONSTANT_ITEM_LENGTH[tag];
                }
            }
            return classByte;
        }

        public int getConstantPoolCount(){
            return ByteUtils.bytes2Int(classByte, CONSTANT_POOL_COUNT_INDEX, u2);
        }
    }

3.HackSystem代码如下

package org.jvm;

    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    import java.io.PrintStream;

    /**
     * 为JavaClass劫持java.lang.System提供支持
     * 除了out和err外,其余的都直接转发给System处理
     */
    public class HackSystem {
        public final static InputStream in = System.in;
        private static ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        public final static PrintStream out = new PrintStream(buffer);
        public final static PrintStream err = out;

        public static String getBufferString(){
            return buffer.toString();
        }

        public static void clearBuffer(){
            buffer.reset();
        }
        public static void setSecurityManager(final SecurityManager s){
            System.setSecurityManager(s);
        }
        public static SecurityManager getSecurityManager(){
            return System.getSecurityManager();
        }
        public static long currentTimeMills(){
            return System.currentTimeMillis();
        }
        public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length){
            System.arraycopy(src, srcPos, dest, destPos, length);
        }
        public static int identityHashCode(Object x){
            return System.identityHashCode(x);
        }
    }

4.HotSwapClassLoader代码如下

package org.jvm;
    /**
     * 为了多次载入执行类而加入的加载器
     * 把defineClass方法开放出来,只有外部显式调用的时候才会使用到loadByte方法
     * 由虚拟机调用时,仍然按照原有的双亲委派规则使用loadClass方法进行类加载
     */
    public class HotSwapClassLoader extends ClassLoader{

        public HotSwapClassLoader() {
            super(HotSwapClassLoader.class.getClassLoader());
        }
        public Class<?> loadByte(byte[] classByte){
            return defineClass(null,classByte,0,classByte.length);
        }
    }

5.JavaClassExecuter代码如下

package org.jvm;
    /**
     * 为了多次载入执行类而加入的加载器
     * 把defineClass方法开放出来,只有外部显式调用的时候才会使用到loadByte方法
     * 由虚拟机调用时,仍然按照原有的双亲委派规则使用loadClass方法进行类加载
     */
    public class HotSwapClassLoader extends ClassLoader{

        public HotSwapClassLoader() {
            super(HotSwapClassLoader.class.getClassLoader());
        }
        public Class<?> loadByte(byte[] classByte){
            return defineClass(null,classByte,0,classByte.length);
        }
    }

6.test.jsp代码如下

<%@ page import="java.lang.*" %>
    <%@ page import="java.io.*" %>
    <%@ page import="org.jvm.*" %>

    <%
        InputStream is = new FileInputStream("c:/TestClass.class");
        byte[] b = new byte[is.available()];
        is.read(b);
        is.close();

        out.println("<textarea style='width:1000;heigth=800'>");
        out.println(JavaClassExecuter.execute(b));
        out.println("</textarea>");
    %>

7.测试类放到了c盘根目录下,如下

2019120001576\_2.png

其中TestClass代码如下

/**
     * 测试类
     */
    public class TestClass {
        public static void main(String[] args) {
            System.out.println("-----this is test class out println----");
            System.out.println("hahhahahaha");
        }
    }

以管理员身份打开cmd窗口,如下:

2019120001576\_3.png

javac编译TestClass文件,如下:

2019120001576\_4.png

最后运行tomcat,如下:

2019120001576\_5.png

访问结果如下:

2019120001576\_6.png

当直接修改TestClass.java文件,不关闭tomcat,直接运行localhost:8080/classLoaderTest/test.jsp,结果随之而变。

点赞(1)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 自己动手实现远程执行功能(深入理解java虚拟机)

相关推荐