今天看啥  ›  专栏  ›  StefanJi

使用 Java 实现解析 JVM 字节码结构

StefanJi  · 掘金  ·  · 2020-01-19 02:32
阅读 30

使用 Java 实现解析 JVM 字节码结构

JVM 字节码的存储格式 docs.oracle.com/javase/spec…

/**
 * <pre>
 * ClassFile {
 *     u4             magic;
 *     u2             minor_version;
 *     u2             major_version;
 *     u2             constant_pool_count;
 *     cp_info        constant_pool[constant_pool_count-1];
 *     u2             access_flags;
 *     u2             this_class;
 *     u2             super_class;
 *     u2             interfaces_count;
 *     u2             interfaces[interfaces_count];
 *     u2             fields_count;
 *     field_info     fields[fields_count];
 *     u2             methods_count;
 *     method_info    methods[methods_count];
 *     u2             attributes_count;
 *     attribute_info attributes[attributes_count];
 * }
 * </pre>
 */
public final class BytecodeParser {

    /**
    * 按照字节码的存储格式依次解析每个部分
    */ 
    public void parse(final byte[] bytes) {
        MagicNumber magicNumber = new MagicNumber(0, bytes);
        magicNumber.parse();

        Version version = new Version(magicNumber.end(), bytes);
        version.parse();

        ConstantPool constantPool = new ConstantPool(version.end(), bytes);
        constantPool.parse();

        AccessFlags accessFlags = new AccessFlags(constantPool.end(), bytes);
        accessFlags.parse();

        ThisClass thisClass = new ThisClass(accessFlags.end(), bytes);
        thisClass.parse();

        SuperClass superClass = new SuperClass(thisClass.end(), bytes);
        superClass.parse();

        Interfaces interfaces = new Interfaces(superClass.end(), bytes);
        interfaces.parse();

        //TODO 字段表及之后的属性解析待完善
        Fields fields = new Fields(interfaces.end(), bytes);
        fields.parse();
        System.out.println(fields);
    }

    private static final String path = "xxx/BytecodeParser.class"; // 编译之后的字节码文件路径

    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream(new File(path));
            int count = fis.available();
            byte[] buff = new byte[count];
            int read = fis.read(buff);
            if (read != count) {
                return;
            }
            new BytecodeParser().parse(buff);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class Utils {

    /**
     * 获取占4字节的数
     */
    static int read4Number(byte[] bytes, int offset) {
        return ((bytes[offset] & 0xFF) << 24) | ((bytes[offset + 1] & 0xFF) << 16) | ((bytes[offset + 2] & 0xFF) << 8) | ((bytes[offset + 3] & 0xFF));
    }

    /**
     * 获取占2字节的数, 为了避免 java 中只有 signed short 越界出现显示了负数, 所以返回 int
     */
    static int read2Number(byte[] bytes, int offset) {
        return ((bytes[offset] & 0xFF) << 8) | (bytes[offset + 1] & 0xFF);
    }

    static int readUnsignedByte(byte[] bytes, int offset) {
        return (bytes[offset] & 0xFF);
    }

    /**
     * 获得占8字节的数
     */
    static long read8Number(byte[] bytes, int offset) {
        return (
                ((long) bytes[offset] & 0xFF) << 56) |
                (((long) bytes[offset + 1] & 0xFF) << 48) |
                (((long) bytes[offset + 2] & 0xFF) << 40) |
                (((long) bytes[offset + 3] & 0xFF) << 32) |
                (((long) bytes[offset + 3] & 0xFF) << 24) |
                (((long) bytes[offset + 3] & 0xFF) << 16) |
                (((long) bytes[offset + 3] & 0xFF) << 8) |
                (((long) bytes[offset + 3] & 0xFF));
    }
}

abstract class Section {
    final int start;
    final byte[] bytes;

    public Section(int start, byte[] bytes) {
        this.start = start;
        this.bytes = bytes;
    }

    public final int start() {
        return start;
    }

    public final int end() {
        return start + size();
    }

    abstract int size();

    abstract public void parse();
}

class MagicNumber extends Section {

    public MagicNumber(int start, byte[] bytes) {
        super(start, bytes);
    }

    @Override
    int size() {
        return 4;
    }

    @Override
    public void parse() {
        System.out.println("MagicNumber: 0x"
                + Integer.toHexString(Utils.readUnsignedByte(bytes, start)).toUpperCase()
                + Integer.toHexString(Utils.readUnsignedByte(bytes, start + 1)).toUpperCase()
                + Integer.toHexString(Utils.readUnsignedByte(bytes, start + 2)).toUpperCase()
                + Integer.toHexString(Utils.readUnsignedByte(bytes, start + 3)).toUpperCase()
        );
    }
}

class Version extends Section {

    public Version(int start, byte[] bytes) {
        super(start, bytes);
    }

    @Override
    int size() {
        return 4;
    }

    @Override
    public void parse() {
        int minorVersion = Utils.read2Number(bytes, start);
        int majorVersion = Utils.read2Number(bytes, start + 2);
        System.out.println("Version: " + majorVersion + "." + minorVersion);
    }
}

class ConstantPool extends Section {

    private int poolSize = 2;
    private ArrayList<ConstantItem> mConstantItems = new ArrayList<>();

    public List<ConstantItem> getConstantItems() {
        return mConstantItems;
    }

    public ConstantPool(int start, byte[] bytes) {
        super(start, bytes);
    }

    @Override
    int size() {
        return poolSize;
    }

    @Override
    public void parse() {
        int poolCount = Utils.read2Number(bytes, start);
        System.out.println("Constant Pool Count: " + poolCount);
        // 遍历常量表的每一项常量
        int offset = 2;
        for (short i = 1; i <= poolCount - 1; i++) {
            int tag = Utils.readUnsignedByte(bytes, start + offset);
            ConstantItem constantItem = null;
            // 找到匹配的常量
            for (ConstantItem item : ConstantItem.values()) {
                if (item.tag == tag) {
                    constantItem = item;
                    break;
                }
            }
            if (constantItem == null) {
                System.err.println("Not found ConstantItem for " + tag);
                return;
            }
            constantItem.parse(bytes, start + offset);
            System.out.println("#" + i + " " + constantItem);
            offset += constantItem.size();
            poolSize += constantItem.size();
            mConstantItems.add(constantItem);
        }
    }

    /**
     * 常量项
     * 每个常量项都有 u1 的 tag, 表示其类型
     */
    enum ConstantItem {
        UTF8(1) {
            private int length;
            private String value;

            @Override
            protected int contentSize() {
                return length /* 字符串占用的字节数 */ + 2 /* u2 的字符串长度 length*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                length = Utils.read2Number(bytes, start + 1);
                value = new String(bytes, start + 3, length);
            }

            @Override
            public String toString() {
                return "Utf8{" +
                        "length=" + length +
                        ", value='" + value + '\'' +
                        '}';
            }
        },
        INTEGER(3) {
            int value;

            @Override
            protected int contentSize() {
                return 4 /*u4 值*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                value = Utils.read4Number(bytes, start + 1);
            }

            @Override
            public String toString() {
                return "Integer{" +
                        "value=" + value +
                        '}';
            }
        },
        FLOAT(4) {
            float value;

            @Override
            protected int contentSize() {
                return 4 /* 同 INTEGER 的注释 */;
            }

            @Override
            void parse(byte[] bytes, int start) {
                value = Utils.read4Number(bytes, start + 1);
            }

            @Override
            public String toString() {
                return "Float{" +
                        "value=" + value +
                        '}';
            }
        },
        LONG(5) {
            long value;

            @Override
            protected int contentSize() {
                return 8 /* u8 值*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                value = Utils.read8Number(bytes, start);
            }

            @Override
            public String toString() {
                return "Long{" +
                        "value=" + value +
                        '}';
            }
        },
        DOUBLE(6) {
            double value;

            @Override
            protected int contentSize() {
                return 8 /*u8 值*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                value = Utils.read8Number(bytes, start + 1);
            }

            @Override
            public String toString() {
                return "Double{" +
                        "value=" + value +
                        '}';
            }
        },
        CLASS(7) {
            int index;

            @Override
            protected int contentSize() {
                return 2 /*u2 常量池索引*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                index = Utils.read2Number(bytes, start + 1);
            }

            @Override
            public String toString() {
                return "Class{" +
                        "index=" + index +
                        '}';
            }
        },
        STRING(8) {
            int index;

            @Override
            protected int contentSize() {
                return 2/*u2 常量池索引*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                index = Utils.read2Number(bytes, start + 1);
            }

            @Override
            public String toString() {
                return "String{" +
                        "index=" + index +
                        '}';
            }
        },
        FIELD_REF(9) {
            int classInfoIndex,nameAndTypeIndex;

            @Override
            protected int contentSize() {
                return 2/*u2 类信息常量池索引*/ + 2/*u2 名字和类型常量池索引*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                classInfoIndex = Utils.read2Number(bytes, start + 1);
                nameAndTypeIndex = Utils.read2Number(bytes, start + 3);
            }

            @Override
            public String toString() {
                return "FileRef{" +
                        "classInfoIndex=" + classInfoIndex +
                        ", nameAndTypeIndex=" + nameAndTypeIndex +
                        '}';
            }
        },
        METHOD_REF(10) {
            int classInfoIndex,nameAndTypeIndex;

            @Override
            protected int contentSize() {
                return 4 /*同 FIELD_REF*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                classInfoIndex = Utils.read2Number(bytes, start + 1);
                nameAndTypeIndex = Utils.read2Number(bytes, start + 3);
            }

            @Override
            public String toString() {
                return "MethodRef{" +
                        "classInfoIndex=" + classInfoIndex +
                        ", nameAndTypeIndex=" + nameAndTypeIndex +
                        '}';
            }
        },
        Interface_Method_Ref(11) {
            int classInfoIndex,nameAndTypeIndex;

            @Override
            protected int contentSize() {
                return 4/*同FIELD_REF*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                classInfoIndex = Utils.read2Number(bytes, start + 1);
                nameAndTypeIndex = Utils.read2Number(bytes, start + 3);
            }

            @Override
            public String toString() {
                return "InterfaceMethodRef{" +
                        "classInfoIndex=" + classInfoIndex +
                        ", nameAndTypeIndex=" + nameAndTypeIndex +
                        '}';
            }
        },
        NAME_AND_TYPE(12) {
            int nameIndex,descriptorIndex;

            @Override
            protected int contentSize() {
                return 2/*u2 名称在常量池的索引*/ + 2/*u2 描述符在常量池的索引*/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                nameIndex = Utils.read2Number(bytes, start + 1);
                descriptorIndex = Utils.read2Number(bytes, start + 3);
            }

            @Override
            public String toString() {
                return "NameAndType{" +
                        "nameIndex=" + nameIndex +
                        ", descriptorIndex=" + descriptorIndex +
                        '}';
            }
        },
        Method_Handle(15) {

            int referenceKind; //unsigned byte
            int referenceIndex;

            @Override
            protected int contentSize() {
                return 1/**/ + 2/**/;
            }

            @Override
            void parse(byte[] bytes, int start) {
                referenceKind = Utils.readUnsignedByte(bytes, start + 1);
                referenceIndex = Utils.read2Number(bytes, start + 2);
            }

            @Override
            public String toString() {
                return "MethodHandle{" +
                        "referenceKind=" + referenceKind +
                        ", referenceIndex=" + referenceIndex +
                        '}';
            }
        },
        Method_Type(16) {
            int descriptorIndex;

            @Override
            protected int contentSize() {
                return 2;
            }

            @Override
            void parse(byte[] bytes, int start) {
                descriptorIndex = Utils.read2Number(bytes, start + 1);
            }

            @Override
            public String toString() {
                return "MethodType{" +
                        "descriptorIndex=" + descriptorIndex +
                        '}';
            }
        },
        Invoke_Dynamic(18) {
            int bootstrapAttrIndex,nameAndTypeIndex;

            @Override
            protected int contentSize() {
                return 4;
            }

            @Override
            void parse(byte[] bytes, int start) {
                bootstrapAttrIndex = Utils.read2Number(bytes, start + 1);
                nameAndTypeIndex = Utils.read2Number(bytes, start + 3);
            }

            @Override
            public String toString() {
                return "InvokeDynamic{" +
                        "bootstrapAttrIndex=" + bootstrapAttrIndex +
                        ", nameAndTypeIndex=" + nameAndTypeIndex +
                        '}';
            }
        };

        public final int tag; // unsigned byte

        ConstantItem(int tag) {
            this.tag = tag;
        }

        public int size() {
            return 1/*u1 的 tag*/ + contentSize() /* 不同常量占用的字节数*/;
        }

        protected abstract int contentSize();

        abstract void parse(byte[] bytes, int start);
    }
}

class AccessFlags extends Section {

    private static final int PUBLIC = 0x0001;    // 0000 0000 0000 0001
    private static final int FINAL = 0x0010;     // 0000 0000 0001 0000
    private static final int SUPER = 0x0020;     // 0000 0000 0010 0000
    private static final int INTERFACE = 0x0200; // 0000 0010 0000 0000
    private static final int ABSTRACT = 0x0400;  // 0000 0100 0000 0000
    private static final int SYNTHETIC = 0x1000; // 0001 0000 0000 0000
    private static final int ANNOTATION = 0x2000;// 0010 0000 0000 0000
    private static final int ENUM = 0x4000;      // 0100 0000 0000 0000

    private int accessFlags;

    public AccessFlags(int start, byte[] bytes) {
        super(start, bytes);
    }

    @Override
    int size() {
        return 2;
    }

    @Override
    public void parse() {
        accessFlags = Utils.read2Number(bytes, start);
        System.out.println("AccessFlags: " + printAccess(accessFlags));
    }

    private static String printAccess(int access) {
        ArrayList<String> accessFlags = new ArrayList<>();
        if ((access & PUBLIC) == PUBLIC) {
            accessFlags.add("public");
        }
        if ((access & FINAL) == FINAL) {
            accessFlags.add("final");
        }
        if ((access & SUPER) == SUPER) {
            accessFlags.add("super");
        }
        if ((access & INTERFACE) == INTERFACE) {
            accessFlags.add("interface");
        }
        if ((access & ABSTRACT) == ABSTRACT) {
            accessFlags.add("abstract");
        }
        if ((access & SYNTHETIC) == SYNTHETIC) {
            accessFlags.add("synthetic");
        }
        if ((access & ANNOTATION) == ANNOTATION) {
            accessFlags.add("annotation");
        }
        if ((access & ENUM) == ENUM) {
            accessFlags.add("enum");
        }
        StringBuilder sb = new StringBuilder();
        accessFlags.forEach(s -> sb.append(s).append(","));
        return sb.toString();
    }
}

class ThisClass extends Section {

    private int classInfoIndex;

    public ThisClass(int start, byte[] bytes) {
        super(start, bytes);
    }

    @Override
    int size() {
        return 2;
    }

    @Override
    public void parse() {
        classInfoIndex = Utils.read2Number(bytes, start);
        System.out.println("This class: " + classInfoIndex);
    }
}

class SuperClass extends Section {

    private int classInfoIndex;

    public SuperClass(int start, byte[] bytes) {
        super(start, bytes);
    }

    @Override
    int size() {
        return 2;
    }

    @Override
    public void parse() {
        classInfoIndex = Utils.read2Number(bytes, start);
        System.out.println("Super class: " + classInfoIndex);
    }
}

class Interfaces extends Section {

    private int contentSize = 0;
    private ArrayList<Integer> indexs = new ArrayList<>();

    public Interfaces(int start, byte[] bytes) {
        super(start, bytes);
    }

    @Override
    int size() {
        return contentSize + 2;
    }

    @Override
    public void parse() {
        int interfaceCount = Utils.read2Number(bytes, start);
        System.out.println("Interface count: " + interfaceCount);
        int offset = 2;
        for (short i = 0; i < interfaceCount; i++) {
            int index = Utils.read2Number(bytes, start + offset);
            indexs.add(index);
            contentSize += 2;
        }
        System.out.println("Interface Indexes: " + indexs);
    }
}

/**
 * <pre>
 * field_info {
 *     u2             access_flags;
 *     u2             name_index;
 *     u2             descriptor_index;
 *     u2             attributes_count;
 *     attribute_info attributes[attributes_count];
 * }
 * </pre>
 */
class Fields extends Section {

    private int fieldsSize = 0;

    int filedsCount;
    int accessFlag;
    int nameIndex;
    int descriptorIndex;
    int attributesCount;
    private ArrayList<AttributeInfo> mAttributeInfos = new ArrayList<>();

    public Fields(int start, byte[] bytes) {
        super(start, bytes);
    }

    @Override
    int size() {
        return fieldsSize + 2;
    }

    @Override
    public void parse() {
        int offset = start;
        filedsCount = Utils.read2Number(bytes, offset);
        offset += 2;
        accessFlag = Utils.read2Number(bytes, offset);
        offset += 2;
        nameIndex = Utils.read2Number(bytes, offset);
        offset += 2;
        descriptorIndex = Utils.read2Number(bytes, offset);
        offset += 2;
        attributesCount = Utils.read2Number(bytes, offset);
        offset += 2;
        for (int i = 0; i < attributesCount; i++) {
            AttributeInfo attributeInfo = new AttributeInfo();
            attributeInfo.parse(bytes, offset);
            mAttributeInfos.add(attributeInfo);
            offset += attributeInfo.size();
        }
    }

    @Override
    public String toString() {
        return "Fields{" +
                "fieldsSize=" + fieldsSize +
                ", filedsCount=" + filedsCount +
                ", accessFlag=" + accessFlag +
                ", nameIndex=" + nameIndex +
                ", descriptorIndex=" + descriptorIndex +
                ", attributesCount=" + attributesCount +
                ", mAttributeInfos=" + mAttributeInfos +
                '}';
    }
}

/**
 * <pre>
 * attribute_info {
 *     u2 attribute_name_index;
 *     u4 attribute_length;
 *     u1 info[attribute_length];
 * }
 * </pre>
 */
class AttributeInfo {
    int nameIndex;  //u2
    int length; //u4
    ArrayList<Info> mInfos = new ArrayList<>();

    public void parse(byte[] bytes, int offset) {
        nameIndex = Utils.read2Number(bytes, offset);
        length = Utils.read4Number(bytes, offset + 2);
        for (int i = 0; i < length; i++) {

        }
    }

    public int size() {
        return 2 + 4;
    }

    enum Info {
        Code("Code") {
            @Override
            public void parse(byte[] bytes, int offset) {

            }

            @Override
            public int size() {
                return 0;
            }
        },
        ConstantValue("ConstantValue") {
            int nameIndex; //u2
            int attributeLength; //u4
            int constantValueIndex; //u2

            @Override
            public void parse(byte[] bytes, int offset) {
                nameIndex = Utils.read2Number(bytes, offset);
                attributeLength = Utils.read4Number(bytes, offset + 2);
                constantValueIndex = Utils.read2Number(bytes, offset + 2);
            }

            @Override
            public int size() {
                return 2 + 4 + 2;
            }

            @Override
            public String toString() {
                return "ConstantValue{" +
                        "nameIndex=" + nameIndex +
                        ", attributeLength=" + attributeLength +
                        ", constantValueIndex=" + constantValueIndex +
                        '}';
            }
        };

        private final String name;

        Info(String name) {
            this.name = name;
        }

        abstract public void parse(byte[] bytes, int offset);

        abstract public int size();
    }

    @Override
    public String toString() {
        return "AttributeInfo{" +
                "nameIndex=" + nameIndex +
                ", length=" + length +
                '}';
    }
}
复制代码



原文地址:访问原文地址
快照地址: 访问文章快照