• java类加载器-(2)


    一、工作目标

    在服务端执行临时代码。

    实现的过程中要解决的问题:

    (1)如何编译提交到服务器的java代码?

    ans:提交字节码,其实也可以提交.java文件。

    (2)如何执行编译后的java代码 ?

    ans: 让加载器加载这个类生成的class对象,再反射调用类的方法。

    (3) 如何收集java的执行结果?

    ans: 这里在输出的时候对System.out的符号引用进行了修改,目前先不管。

    二、主要实现类

    • 类加载器
    package com.company.cp9;
    
    /**
     * Created by lsj on 2015/9/7.
     * 只有外部显式调用loadByte时才用defineClass
     * 虚拟机调用 时还是使用loadClass进行类加载
     */
    public class HotSwapClassLoader  extends ClassLoader{
        public HotSwapClassLoader(){
            super(HotSwapClassLoader.class.getClassLoader());
        }
    
        public Class loadByte (byte [] classByte){
            return defineClass(null, classByte,0,classByte.length) ;
        }
    }
    
    • JavaClassExecuter实现
    package com.company.cp9;
    
    import java.lang.reflect.Method;
    
    /**
     * JavaClass执行字节码工具
     */
    public class JavaClassExecuter {
    
        /**
         * 执行外部传过来的代表一个Java类的Byte数组<br>
         * 将输入类的byte数组中代表java.lang.System的CONSTANT_Utf8_info常量修改为劫持后的HackSystem类
         * 执行方法为该类的static main(String[] args)方法,输出结果为该类向System.out/err输出的信息
         * @param classByte 代表一个Java类的Byte数组
         * @return 执行结果
         */
        public static String execute(byte[] classByte) {
            HackSystem.clearBuffer();
            ClassModifier cm = new ClassModifier(classByte);
            byte[] modiBytes = cm.modifyUTF8Constant("java/lang/System", "com/company/cp9/HackSystem");
            HotSwapClassLoader loader = new HotSwapClassLoader();
            //上面的只是在修改class文件中的常量池部分,可以不修改
            Class clazz = loader.loadByte(modiBytes);
            //不修改常量池
            //Class clazz = loader.loadByte(classByte) ;
            try {
                //直接使用loadClass加载,加载的是.java文件
                //Class clazz = loader.loadClass("com.company.cp9.TestClass");
                Object object = clazz.newInstance();
                /**
                 *param 1:方法名  param 2: 这个方法的参数类型
                 */
                Method method = clazz.getMethod("main", new Class[]{String[].class});
                /**
                 *param1: the Object where the method comes from
                 *param2: the args used for the method
                 */
                method.invoke(object, new String[] { null});
            } catch (Throwable e) {
                e.printStackTrace(HackSystem.out);
            }
            return HackSystem.getBufferString();
        }
    }
    
    • 验证
    package com.company.cp9;
    
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    /**
     * Created by lsj on 2015/9/9.
     * 加载TestClass.class字节码到JavaClassExecuter中
     */
    public class TestMain {
        public static void main(String [] args){
            System.out.println("begin");
            try {
                //注意要是class字节码
                InputStream is = new FileInputStream("E:/sourcecode/IDEA/JVMStudy/TestClass.class") ;
                byte [] b = new byte[is.available()] ;
                is.read(b) ;
                is.close();
                System.out.println(JavaClassExecuter.execute(b));
    
            }catch (Exception e ){
                e.printStackTrace();
            }
    
        }
    }
    

    这里我们将TestClass.class文件放在对应的目录下面了。  

    对应的TestClass.java为

    package com.company.cp9;
    
    /**
     * Created by lsj on 2015/9/7.
     */
    public class TestClass {
        public void main(String [] args ){
            System.out.println("electronics");
        }
    }
    

    三、修改class文件的相关类

    • 被修改的为HackSystem
    package com.company.cp9;
    
    import java.io.InputStream;
    import java.io.* ;
    
    /**
     * 为JavaClass劫持java.lang.System提供支持
     * 除了out和err外,其余的都直接转发给System处理
     *
     * @author zzm
     */
    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 currentTimeMillis() {
            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);
        }
    
        // 下面所有的方法都与java.lang.System的名称一样
        // 实现都是字节转调System的对应方法
        // 因版面原因,省略了其他方法
    }
    

    工具类

    package com.company.cp9;
    
    /**
     * Bytes数组处理工具
     * @author
     */
    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;
        }
    }
    

    另一个

    package com.company.cp9;
    
    /**
     * 修改Class文件,暂时只提供修改常量池常量的功能
     * @author zzm
     */
    public class ClassModifier {
    
        /**
         * Class文件中常量池的起始偏移
         */
        private static final int CONSTANT_POOL_COUNT_INDEX = 8;
    
        /**
         * CONSTANT_Utf8_info常量的tag标志
         */
        private static final int CONSTANT_Utf8_info = 1;
    
        /**
         * 常量池中11种常量所占的长度,CONSTANT_Utf8_info型常量除外,因为它不是定长的
         */
        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;
        }
    
        /**
         * 修改常量池中CONSTANT_Utf8_info常量的内容
         * @param oldStr 修改前的字符串
         * @param newStr 修改后的字符串
         * @return 修改结果
         */
        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;
        }
    
        /**
         * 获取常量池中常量的数量
         * @return 常量池数量
         */
        public int getConstantPoolCount() {
            return ByteUtils.bytes2Int(classByte, CONSTANT_POOL_COUNT_INDEX, u2);
        }
    }
    

      

      

  • 相关阅读:
    POJ 1306.Combinations
    HDU 5640.King's Cake
    HDU 1072.Nightmare
    HDU 2717.Catch That Cow
    HDU 1372.Knight Moves
    HDU 1548.A strange lift
    AOJ 802.运输宝物
    AOJ 794.西瓜理发记(二)
    AOJ 793.西瓜理发记(一)
    AOJ 789.买酒
  • 原文地址:https://www.cnblogs.com/chuiyuan/p/4796337.html
Copyright © 2020-2023  润新知