标签:
一、工作目标
在服务端执行临时代码。
实现的过程中要解决的问题:
(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) ; } }
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文件的相关类
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); } }
标签:
原文地址:http://www.cnblogs.com/chuiyuan/p/4796337.html