博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM-Class文件结构
阅读量:5815 次
发布时间:2019-06-18

本文共 18708 字,大约阅读时间需要 62 分钟。

hot3.png

这周的分享准备时间太短了,总结比较潦草,还望见谅!

学习jvm的class文件结构基础的就不说了,参照书上的内容简单了解下,感觉写个例子更能加深对class文件结构的了解,于是写了两个例子:

1、替换方法的调用,和书上的那个例子差不多,自己改了场景实践了一下,主要实现思路就是修改常量池中原class的字面值,比较简单。

2、BeanCopy的实现,自己实现的工具类,相当的容易读懂(你懂的),只是为了实践而已,鲁棒性自然很差了(很多地方写死了,比如说是有int类型的字段的Bean才可以复制,Bean的字段名字只能是一个英文字母,都是为了方便哈~~谅解)。主要实现思路是增加常量池常量,修改常量池常量数,修改code区域的jvm指令,修改code的length,最后生成class。

一、方法替换

【工具类】

package jvm;public class ByteUtil {    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 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 String byte2String(byte[] b, int start, int len) {        return new String(b, start, len);    }    public static byte[] string2Bytes(String str) {        return str.getBytes();    }    public static byte[] byteReplace(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;    }    public static void main(String[] args) {        byte[] res = int2Bytes(29, 2);        System.out.println(res);    }}
【TestA】
package jvm;public class TestA {    public static int fuc() {        return 1;    }}
【TestB】
package jvm;public class TestB {    public static int fuc() {        return 2;    }}
【具体的字节码解析和替换的实现类】
package jvm;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, -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;    }    public int getConstantPoolCount() {        return ByteUtil.byte2Int(classByte, CONSTANT_POOL_COUNT_INDEX, u2);    }    public byte[] modify(String oldStr, String newStr) {        int cpc = getConstantPoolCount();        int offset = CONSTANT_POOL_COUNT_INDEX + u2;        for (int i = 0; i < cpc; i++) {            int tag = ByteUtil.byte2Int(classByte, offset, u1);            if (tag == CONSTANT_UTF8_INFO) {                int len = ByteUtil.byte2Int(classByte, offset + u1, u2);                offset += (u1 + u2);                String str = ByteUtil.byte2String(classByte, offset, len);                if (str.equalsIgnoreCase(oldStr)) {                    byte[] strBytes = ByteUtil.string2Bytes(newStr);                    byte[] strLen = ByteUtil.int2Bytes(newStr.length(), u2);                    classByte = ByteUtil.byteReplace(classByte, offset - u2, u2, strLen);                    classByte = ByteUtil.byteReplace(classByte, offset, len, strBytes);                    return classByte;                } else {                    offset += len;                }            } else {                offset += CONSTANT_ITEM_LENGTH[tag];            }        }        return classByte;    }}
【Class的生成】
package jvm;public class TestClassLoader extends ClassLoader {    public TestClassLoader(){        super(TestClassLoader.class.getClassLoader());    }    public Class loadByte(byte[] classByte) {        return defineClass(null, classByte, 0, classByte.length);    }}
【Client实现】
package jvm;import java.io.FileInputStream;import java.io.InputStream;import java.lang.reflect.Method;import java.util.Date;public class TestClass {    private static final int m = staticInc();    private Date             curr = new Date();    public int inc() {        return m + 1;    }    public static int staticInc() {        return TestA.fuc();    }    public static void main(String[] args) throws Exception {        InputStream is = new FileInputStream("/Users/apple/workspace/CommonTest/bin/jvm/TestClass.class");        byte[] b = new byte[is.available()];        is.read(b);        is.close();        ClassModifier cm = new ClassModifier(b);        byte[] newBytes = cm.modify("jvm/TestA", "jvm/TestB");        TestClassLoader loader = new TestClassLoader();        Class clazz = loader.loadByte(newBytes);        Method method = clazz.getMethod("staticInc");        System.out.println((Integer) method.invoke(null));    }}

二、BeanCopy的实现

【测试Bean,其实可有可无】

package jvm;public class Bean {    int    i;    int    j;    public int getI() {        return i;    }    public void setI(int i) {        this.i = i;    }    public int getJ() {        return j;    }    public void setJ(int j) {        this.j = j;    }}
【模型类,不可能自己从零开始吧,所以基于这玩意改的字节码】
package jvm;public class BeanCopierModel {    public static Object copy(Object bean) {        return null;    }}

【实现类,比较臭,但是可读性还是可以,那些偏移量不需要关注,主要是流程】 

package jvm;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.InputStream;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;public class BeanCopyClient {    private static final int CODE_LENGTH   = 15;    private static final int CODE_PER_STEP = 8;    private static final int CONSTANT_COUNT_OFF          = 8;    private static final int CONSTANT_NAME_AND_TYPE_INIT = 9;    private static final int CONSTANT_COUNT              = 19;    private static final int CODE_BEGIN                  = 358;    private static final int CODE_LEN                    = 10;    private static final int CONSTANT_END                = 270;    private static final int METHOD_LEN                  = 14;    private static final int ATTRIBUTE_OFF               = 371;    private static final int ATTRIBUTE_LEN               = 31;    private static final int U1                          = 1;    private static final int U2                          = 2;    private static final int U4                          = 4;    String                   className;    List
fields = new ArrayList
(); Map
constantIndex = new HashMap
(); private void init(){ //准备测试数据 // 测试数据 fields.add("I"); fields.add("J"); className = "jvm/Bean"; } private byte[] modifyConstantCount(byte[] b) { return ByteUtil.byteReplace(b, CONSTANT_COUNT_OFF, 2, ByteUtil.int2Bytes(constantNum(), U2)); } private byte[] modifyCodeLen(byte[] b) { int len = codeLen(); return ByteUtil.byteReplace(b, CODE_BEGIN - 4, 4, ByteUtil.int2Bytes(len, U4)); } private byte[] generateConstant() { int len = coutLen(); int index = CONSTANT_COUNT; byte[] b = new byte[len]; int offset = 0; // class 的utf8-info byte[] tag = ByteUtil.int2Bytes(1, U1); byte[] length = ByteUtil.int2Bytes(className.length(), U2); byte[] bytes = ByteUtil.string2Bytes(className); System.arraycopy(tag, 0, b, offset, U1); offset = offset + U1; System.arraycopy(length, 0, b, offset, U2); offset = offset + U2; System.arraycopy(bytes, 0, b, offset, className.length()); offset = offset + className.length(); constantIndex.put("utf8_class", ++index); // class info System.arraycopy(ByteUtil.int2Bytes(7, U1), 0, b, offset, U1); offset = offset + U1; int class_index = constantIndex.get("utf8_class"); System.arraycopy(ByteUtil.int2Bytes(class_index, U2), 0, b, offset, U2); offset = offset + U2; constantIndex.put("class_info", ++index); // get set 的utf8-info for (String field : fields) { String set = "set" + field; String get = "get" + field; byte[] tag_ = ByteUtil.int2Bytes(1, U1); byte[] length_ = ByteUtil.int2Bytes(field.length() + 3, U2); byte[] bytes_ = ByteUtil.string2Bytes(set); byte[] bytes__ = ByteUtil.string2Bytes(get); System.arraycopy(tag_, 0, b, offset, U1); offset = offset + U1; System.arraycopy(length_, 0, b, offset, U2); offset = offset + U2; System.arraycopy(bytes_, 0, b, offset, field.length() + 3); offset = offset + field.length() + 3; constantIndex.put("utf8_set" + field, ++index); System.arraycopy(tag_, 0, b, offset, U1); offset = offset + U1; System.arraycopy(length_, 0, b, offset, U2); offset = offset + U2; System.arraycopy(bytes__, 0, b, offset, field.length() + 3); offset = offset + field.length() + 3; constantIndex.put("utf8_get" + field, ++index); } // get set的描述字段 byte[] tag_setDesc = ByteUtil.int2Bytes(1, U1); byte[] length_setDesc = ByteUtil.int2Bytes(4, U2); byte[] bytes_setDesc = ByteUtil.string2Bytes("(I)V"); System.arraycopy(tag_setDesc, 0, b, offset, U1); offset = offset + U1; System.arraycopy(length_setDesc, 0, b, offset, U2); offset = offset + U2; System.arraycopy(bytes_setDesc, 0, b, offset, 4); offset = offset + 4; constantIndex.put("utf8_setDesc", ++index); byte[] tag_getDesc = ByteUtil.int2Bytes(1, U1); byte[] length_getDesc = ByteUtil.int2Bytes(3, U2); byte[] bytes_getDesc = ByteUtil.string2Bytes("()I"); System.arraycopy(tag_getDesc, 0, b, offset, U1); offset = offset + U1; System.arraycopy(length_getDesc, 0, b, offset, U2); offset = offset + U2; System.arraycopy(bytes_getDesc, 0, b, offset, 3); offset = offset + 3; constantIndex.put("utf8_getDesc", ++index); // get set 的NameType for (String field : fields) { int field_name_index = constantIndex.get("utf8_set"+field); int field_desc_index = constantIndex.get("utf8_setDesc"); System.arraycopy(ByteUtil.int2Bytes(12, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(field_name_index, U2), 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(field_desc_index, U2), 0, b, offset, U2); offset = offset + U2; constantIndex.put("name_type_set" + field, ++index); int field_name_index_ = constantIndex.get("utf8_get" + field); int field_desc_index_ = constantIndex.get("utf8_getDesc"); System.arraycopy(ByteUtil.int2Bytes(12, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(field_name_index_, U2), 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(field_desc_index_, U2), 0, b, offset, U2); offset = offset + U2; constantIndex.put("name_type_get" + field, ++index); } // get set method int class_info_index = constantIndex.get("class_info"); byte[] tag_method = ByteUtil.int2Bytes(10, U1); byte[] class_ref = ByteUtil.int2Bytes(class_info_index, U2); for (String field : fields) { int name_type_set = constantIndex.get("name_type_set" + field); System.arraycopy(tag_method, 0, b, offset, U1); offset = offset + U1; System.arraycopy(class_ref, 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(name_type_set, U2), 0, b, offset, U2); offset = offset + U2; constantIndex.put("method_set" + field, ++index); int name_type_get = constantIndex.get("name_type_get" + field); System.arraycopy(tag_method, 0, b, offset, U1); offset = offset + U1; System.arraycopy(class_ref, 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(name_type_get, U2), 0, b, offset, U2); offset = offset + U2; constantIndex.put("method_get" + field, ++index); } // init method System.arraycopy(tag_method, 0, b, offset, U1); offset = offset + U1; System.arraycopy(class_ref, 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(CONSTANT_NAME_AND_TYPE_INIT, U2), 0, b, offset, U2); offset = offset + U2; constantIndex.put("method_init", ++index); return b; } private byte[] generateCode() { int len = 8 + CODE_LENGTH + CODE_PER_STEP * fields.size(); int offset = 0; byte[] b = new byte[len]; System.arraycopy(ByteUtil.int2Bytes(2, U2), 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(3, U2), 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(CODE_LENGTH + CODE_PER_STEP * fields.size(), U4), 0, b, offset, U4); offset = offset + U4; // 代码体 int class_info_index = constantIndex.get("class_info"); int init_index = constantIndex.get("method_init"); System.arraycopy(ByteUtil.int2Bytes(0x2A, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(0xC0, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(class_info_index, U2), 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(0x4C, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(0xBB, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(class_info_index, U2), 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(0x59, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(0xB7, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(init_index, U2), 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(0x4D, U1), 0, b, offset, U1); offset = offset + U1; for (String field : fields) { int get_index = constantIndex.get("method_get" + field); int set_index = constantIndex.get("method_set" + field); System.arraycopy(ByteUtil.int2Bytes(0x2C, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(0x2B, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(0xB6, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(get_index, U2), 0, b, offset, U2); offset = offset + U2; System.arraycopy(ByteUtil.int2Bytes(0xB6, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(set_index, U2), 0, b, offset, U2); offset = offset + U2; } System.arraycopy(ByteUtil.int2Bytes(0x2C, U1), 0, b, offset, U1); offset = offset + U1; System.arraycopy(ByteUtil.int2Bytes(0xB0, U1), 0, b, offset, U1); offset = offset + U1; return b; } private byte[] replace(byte[] o, byte[] constant, byte[] code) { byte[] step1 = ByteUtil.byteReplace(o, CODE_BEGIN, CODE_LEN, code); byte[] step2 = ByteUtil.byteReplace(step1, CONSTANT_END + 1, 0, constant); return step2; } private int coutLen() { // class 的utf8-info int utf_class = className.length() + 3; // get set 的utf8-info int utf_getSet = fields.size() * 2 * 7; // get set的描述字段 int utf_getSetDesc = 6 + 7; // get set 的NameType int name_type = fields.size() * 2 * 5; // class info int class_info = 3; // init method int method_init = 5; // get set method int getSet_method = fields.size() * 2 * 5; return utf_class + utf_getSet + utf_getSetDesc + name_type + class_info + method_init + getSet_method; } private int constantNum() { return fields.size() * 6 + 5 + CONSTANT_COUNT + 1; } private int codeLen() { return CODE_LENGTH + fields.size() * CODE_PER_STEP - 2 + METHOD_LEN; } private byte[] delAttribute(byte[] b) { return ByteUtil.byteReplace(b, ATTRIBUTE_OFF, ATTRIBUTE_LEN, new byte[] { 0 }); } private byte[] byteModify(byte[] b) { byte[] midRes = modifyCodeLen(b); byte[] finalRes = modifyConstantCount(midRes); return finalRes; } public static void main(String[] args) throws Exception { String name = "/Users/apple/workspace/CommonTest/bin/jvm/BeanCopierModel.class"; BeanCopyClient bc = new BeanCopyClient(); // 准备些测试数据 bc.init(); InputStream is = new FileInputStream(name); byte[] a = new byte[is.available()]; is.read(a); is.close(); // 删掉Code区块里面没用的属性,只是为了学习,可以不删 byte[] b = bc.delAttribute(a); // 修改一些count值 byte[] bm = bc.byteModify(b); // 开始做替换 byte[] constant = bc.generateConstant(); byte[] code = bc.generateCode(); byte[] res = bc.replace(bm, constant, code); // 打印出来反编译看看而已 FileOutputStream out = new FileOutputStream("/Users/apple/Downloads/BeanCopierModel.class"); out.write(res); out.close(); TestClassLoader loader = new TestClassLoader(); Class clazz = loader.loadByte(res); Method method = clazz.getMethod("copy", Object.class); Bean bean = new Bean(); bean.setI(2); bean.setJ(3); Bean newBean = (Bean) method.invoke(null, bean); System.out.println(newBean != bean && newBean.getI() == 2); }}
【最后补充下生成的字节码的长相】
package jvm;public class BeanCopier {    public static Object copy(Object bean) {        Bean plah = (Bean) bean;        Bean newBean = new Bean();        newBean.setI(plah.getI());        newBean.setJ(plah.getJ());        return newBean;    }}

转载于:https://my.oschina.net/tryUcatchUfinallyU/blog/140994

你可能感兴趣的文章
一些常见的js编程题
查看>>
ORA-01012: not logged on
查看>>
IntelliJ IDEA 快捷键和设置
查看>>
nodejs中function*、yield和Promise的示例
查看>>
对《技术人员,你拿什么拯救你的生活----温水煮青蛙》的一点看法
查看>>
达拉草201771010105《面向对象程序设计(java)》第十一周学习总结
查看>>
android加载网络gif图片
查看>>
bzoj 3027 [Ceoi2004]Sweet——生成函数
查看>>
dede调用第一张大图,非缩略图
查看>>
「golang」nginx支持grpc
查看>>
regulator
查看>>
python 函数之walk
查看>>
关于面试
查看>>
I.MX6 U-boot GPIO hacking
查看>>
Linux likely unlikely
查看>>
[WinCE | VS2008 | Solution] VS2008 building WinCE projects taking a long time
查看>>
Hadoop3.0.3-SYNTH运行
查看>>
TYPE=MyISAM 与 ENGINE=MyISAM 的区别(摘要版)
查看>>
java求两个字符A和B最大的相同字符串
查看>>
POJ 1416:Shredding Company
查看>>