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


     1、功能类

       功能类共有五,分别是:

    package org.jvm;
    
    import java.io.*;
    
    /**
     * 对字节数组操作的工具类
     */
    public class ByteUtils {
        public static int byte2Int(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[] origialBytes,int offset,int len,byte[] replaceBytes){
            byte[] newBytes=new byte[origialBytes.length+(replaceBytes.length-len)];
            System.arraycopy(origialBytes,0,newBytes,0,offset);
            System.arraycopy(replaceBytes,0,newBytes,offset,replaceBytes.length);
            System.arraycopy(origialBytes,offset+len,newBytes,offset+replaceBytes.length,origialBytes.length-offset-len);
            return newBytes;
        }
    }
     
    package org.jvm;
    
    import java.io.*;
    
    /**
     * 对测试类class文件的字节数组执行替换,将oldStr替换成newStr
     */
    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,5,-1,5,9,9,3,3,5,5,5,5};
        private final int u1=1;
        private final int u2=2;
        private byte[] classByte;
        public ClassModifier(byte[] classByte){
            this.classByte=classByte;
        }
        public byte[] modiftyUTF8Constant(String oldStr,String newStr){
            int cpc=getConstantPoolCount();
            int offset=CONSTANT_POOL_COUNT_INDEX+u2;
            for(int i =0;i<cpc;i++){
                //取出CONSTANT_UTF8_info中标志部分
                int tag= ByteUtils.byte2Int(classByte, offset, u1);
                //判断是否为CONSTANT_UTF8_info数据类型
                if(tag==CONSTANT_UTF8_info){
                    //取出CONSTANT_UTF8_info中字符串的长度
                    int len=ByteUtils.byte2Int(classByte,offset+u1,u2);
                    offset+=(u1+u2);
                    //取出CONSTANT_UTF8_info中的字符串部分
                    String str=ByteUtils.bytes2String(classByte,offset,len);
                    //通过字符串部分比较是否为需要修改的CONSTANT_UTF8_info
                    if(str.equalsIgnoreCase(oldStr)){
                        //将新字符串的值打散成字节数组
                        byte[] strBytes=ByteUtils.string2Bytes(newStr);
                        //将表示字符串长度值的两个字节分别以16进制的形式装在字节数组中
                        byte[] strLen=ByteUtils.int2Bytes(newStr.length(),u2);
                        //将CONSTANT_UTF8_info中表示length部分进行替换
                        classByte=ByteUtils.bytesReplace(classByte,offset-u2,u2,strLen);
                        //将CONSTANT_UTF8_info中字符串部分进行替换
                        classByte=ByteUtils.bytesReplace(classByte,offset,len,strBytes);
                        return classByte;
                    //如不是需要修改的CONSTANT_UTF8_info,则跳过这个类型,接着循环
                    }else {
                        offset+=len;
                    }
                //如果不是CONSTANT_UTF8_info数据类型,根据tag跳转CONSTANT_ITEM_LENGTH中定义的字节数
                }else {
                    offset+=CONSTANT_ITEM_LENGTH[tag];
                }
            }
            return classByte;
        }
        public int getConstantPoolCount(){
    package org.jvm;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    import java.io.PrintStream;
    
    /**
     * 用于替换System的输出,将测试类中每次System.out的内容输出到字节数组流中,最后一次性输出到页面
     */
    public class HackSystem {
        public final static InputStream in=System.in;
        private static ByteArrayOutputStream buffer=new ByteArrayOutputStream();
        public static final PrintStream out=new PrintStream(buffer);
        public static final PrintStream err=out;
        public static  String getBuffer(){
            return buffer.toString();
        }
        public static void clearBuffer(){
            buffer.reset();
        }
    }
    package org.jvm;
    
    /**
     * 测试类的类加载器,通过字节数组的方式进行加载
     */
    public class HotSwapClassloader  extends ClassLoader{
        public HotSwapClassloader(){
            super(HotSwapClassloader.class.getClassLoader());
        }
        public Class loadByte(byte[] classByte){
            return defineClass(null,classByte,0,classByte.length);
        }
    
    }
    package org.jvm;
    
    import java.lang.reflect.Method;
    
    /**
     * 执行类,通过反射调用测试类中的main方法,最后取出HackSystem中字节数组流中的数据进行返回
     */
    public class JavaClassExecuter {
        public static String executer(byte[] classByte) throws NoSuchMethodException {
         HackSystem.clearBuffer(); ClassModifier classModifier
    =new ClassModifier(classByte); byte[] modiByte=classModifier.modiftyUTF8Constant("java/lang/System","org/jvm/HackSystem"); HotSwapClassloader loader=new HotSwapClassloader(); Class cs=loader.loadByte(modiByte); try { Method method=cs.getMethod("main", new Class[]{String[].class}); method.invoke(null,new String []{null}); }catch (Throwable throwable){ throwable.printStackTrace(HackSystem.out); } return HackSystem.getBuffer(); } }

    2、测试类

      

    package org.jvm;
    
    /**
     * 测试类,在此类中打印想要在页面看到的内容,System.out输出的内容会存在HackSystem的字节数组输出流中
     */
    public class TestClass {
        public static void main(String[] args) {
            System.out.println("-----this is test class out println----");
        }
    }

    3、jsp页面

      test.jsp

    <%@ page import="java.lang.*" %>
    <%@ page import="java.io.*" %>
    <%@ page import="org.jvm.*" %>
    <%
    InputStream inputStream=new FileInputStream("/opt/TestClass.class");
                byte[] b=new byte[inputStream.available()];
                inputStream.read(b);
                inputStream.close();
                out.println(JavaClassExecuter.executer(b));
    %>

    使用方法:

      1、将 ByteUtils ClassModifier HackSystem HotSwapClassloader JavaClassExecuter TestClass 这六个.java文件上传到服务器通过javac进行编译成.class 文件

      2、将编译好的TestClass放在/opt目录中

      3、在tomcat的项目位置的WEB-INF/classes/中新建org/jvm文件夹,再将编译好的 ByteUtils ClassModifier HackSystem HotSwapClassloader JavaClassExecuter 放在WEB-INF/classes/org/jvm中

      4、将test.jsp放在项目中能访问到的位置,如项目的根路径中

      5、在浏览器中访问jsp页面即可,如http://192.168.3.235:8080/test.jsp即可看到页面中会输出

      -----this is test class out println----

      

  • 相关阅读:
    Shell脚本
    数据结构 栈 java 自带的数据结构
    桃夭
    得道多助,失道寡助
    采薇
    离骚
    两小儿辩日
    鱼我所欲也
    生于忧患,死于安乐
    曹刿论战
  • 原文地址:https://www.cnblogs.com/luobiao320/p/7651486.html
Copyright © 2020-2023  润新知