java 手動實現遠程執行功能(深刻理解java虛擬機)

 一、功能類java

   功能類共有五,分別是:數組

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(); } }

 

 

二、測試類瀏覽器

  

package org.jvm;

/**
 * 測試類,在此類中打印想要在頁面看到的內容,System.out輸出的內容會存在HackSystem的字節數組輸出流中
 */
public class TestClass {
    public static void main(String[] args) {
        System.out.println("-----this is test class out println----");
    }
}

 

 

三、jsp頁面tomcat

  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));
%>

 

 

使用方法:jvm

  一、將 ByteUtils ClassModifier HackSystem HotSwapClassloader JavaClassExecuter TestClass 這六個.java文件上傳到服務器經過javac進行編譯成.class 文件jsp

  二、將編譯好的TestClass放在/opt目錄中工具

  三、在tomcat的項目位置的WEB-INF/classes/中新建org/jvm文件夾,再將編譯好的 ByteUtils ClassModifier HackSystem HotSwapClassloader JavaClassExecuter 放在WEB-INF/classes/org/jvm中測試

  四、將test.jsp放在項目中能訪問到的位置,如項目的根路徑中this

  五、在瀏覽器中訪問jsp頁面便可,如http://192.168.3.235:8080/test.jsp便可看到頁面中會輸出

  -----this is test class out println----
相關文章
相關標籤/搜索