第9章+虚拟机执行子系统+9.2.3 字节码生成技术与动态代理的实现

本文介绍字节码生成技术与动态代理的实现,通过代码示例展示如何在方法执行前添加额外逻辑,同时提供了远程执行功能的实战演示,包括自定义类加载器、类文件修改工具等关键组件的实现。

目录

 

9.2.3 字节码生成技术与动态代理的实现

9.3 实战:自己动手实现远程执行功能

9.3.3 实现

9.3.4 验证


9.2.3 字节码生成技术与动态代理的实现

代码清单9-1, 动态代理的简单示例。原始的逻辑是打印一句“hello world”,代理的逻辑是在原始类的方法执行前打印一句“welcome”

/*
 * 代码清单9-1, 动态代理的简单示例。原始的逻辑是打印一句“hello world”,代理的逻辑是在 
 * 原始类的方法执行前打印一句“welcome”
 */
package cn.chapter9;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyTest {
	
	interface IHello{
		void sayHello();
	}
	
	static class Hello implements IHello{
		@Override
		public void sayHello(){
			System.out.println("hello world");
		}
	}
	
	static class DynamicProxy implements InvocationHandler{
		Object originallObj;
		
		Object bind(Object originalObj){
			this.originallObj = originalObj;
			return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this);
		}
		
		@Override
		public Object invoke(Object proxy, Method method, Object [] args)throws Throwable{
			System.out.println("welcome");
			return method.invoke(originallObj, args);
		}
	}
	
	public static void main(String[] args) {
		IHello hello = (IHello) new DynamicProxy().bind(new Hello());
		hello.sayHello();

	}
}

运行结果:

welcome
hello world

不知什么原因,磁盘中没有生成“$Proxy0.class”,无法进行代码清单9-2的程序

9.3 实战:自己动手实现远程执行功能

9.3.3 实现

代码清单9-3, HotSwapClassLoader的实现

/*
 * 代码清单9-3, HotSwapClassLoader的实现
 * 为了多次载入执行类而加入的加载器<br>
 * 把defineClass方法开放出来,只有外部显示调用的是偶才会使用到loadByte方法
 * 由虚拟机调用时,仍然按照原有的双亲委派规则使用loadClass方法进行类加载
 */
package cn.chapter9;

public class HotSwapClassLoader extends ClassLoader{

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

代码清单9-4, ClassModifier的实现

/*
 * 代码清单9-4, ClassModifier的实现
 * 修改Class文件,暂时只提供修改常量池常量的功能
 */
package cn.chapter9;

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_ELNGTH = {-1, -1, 5, -1, 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.bytesReplace(classByte, offset, len, strBytes);
					classByte = ByteUtils.bytesReplace(classByte, offset-u2, u2, strLen);
					classByte = ByteUtils.bytesReplace(classByte, offset, len, strBytes);
					return classByte;
				}else{
					offset += len;
				}
			}else{
				offset += CONSTANT_ITEM_ELNGTH[tag];
			}
		}
		return classByte;
	}
	
	/*
	 * 获取常量池中常量的数量
	 * @return 常量池数量
	 */
	public int getConstantPoolCount(){
		return ByteUtils.bytes2Int(classByte, CONSTANT_POOL_COUNT_INDEX, u2);
	}

}

代码清单9-5, ByteUtils的实现

/*
 * 代码清单9-5, ByteUtils的实现
 * Bytes数组处理工具
 */
package cn.chapter9;

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

}

代码清单9-6, HackSystem的实现

/*
 * 代码清单9-6, HackSystem的实现
 * 为了JavaClass劫持java.lang.System提供支持
 * 除了out和err外,其余的都直接转发给System处理
 */
package cn.chapter9;

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

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 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的对应方法
	//因版面原因,省略了其他方法
}

代码清单9-7, JavaClassExecutor的实现

/*
 * 代码清单9-7, JavaClassExecutor的实现
 * JavaClass执行工具
 */
package cn.chapter9;

import java.lang.reflect.Method;

public class JavaClassExecutor {
	
	/*
	 * 执行外部传送过来的代表一个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", "cn/chapter9/HackSystem");
		HotSwapClassLoader loader = new HotSwapClassLoader();
		Class clazz = loader.loadByte(modiBytes);
		try{
			Method method = clazz.getMethod("main", new Class[]{String[].class});
			method.invoke(null, new String[] {null});
		}catch(Throwable e){
			e.printStackTrace(HackSystem.out);
		}
		return HackSystem.getBufferString();
	}

}

9.3.4 验证

/*
 * 测试类
 */

package cn.chapter9;

public class TestClass {

	public static void main(String[] args) {
		System.out.println("hello world");

	}
}

Test.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>

<%@ page import = "java.lang.*" %>
<%@ page import = "java.io.*" %>
<%@ page import = "cn.chapter9.*" %>
<%
	InputStream is = new FileInputStream("c:/TestClass.class");
	byte [] b = new byte[is.available()];
	is.read(b);
	is.close();
	
	out.println("<textarea style = 'width:1000;height=800'>");
	out.println(JavaClassExecutor.execute(b));
	out.println("</textarea>")
 %>

</body>
</html>

书上说实在“服务器上运行”,我没有,所以这个验证我没有做起来,结果怎样不知道,以后再说。。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值