JVM学习笔记2 --纯手写读取class文件常量池内容并输出

JVM的class文件定义有严格的规范,当虚拟机读取class文件的二进制数据时,JVM虽然不会管该二进制数据是从java编译期编译而来还是其他例如groovy或者kotlin等等编译而来,只要符号jvm定义的规范,就能够读取,也就能在JVM虚拟机上运行

直接上代码,可能有些粗糙,不喜勿喷哈各位大神:

两个类,没细化处理,一个主类,一个配置类,本人IDEA+JDK1.8运行

1、主类HexConvertString.java

public class HexConvertString {

    private static volatile LinkedHashMap constant_pool = new LinkedHashMap<>(10);//保存常量池解析出来的数据

    public static void main(String[] args) {
        FileInputStream fin = null;//用来读取class文件的输入流
        StringWriter sw = new StringWriter();//保存转化后的十六进制的String流
        try {
            //本人本地的测试路径
            fin = new FileInputStream(new File("X:\\Users\\csfxy\\Desktop\\MagnumMain.class"));
            int len = 1;
            byte[] temp = new byte[len];
            //进行16进制的读取转化
            for (; (fin.read(temp, 0, len)) != -1; ) {
                if (temp[0] > 0xf && temp[0] <= 0xff) {//如果是在16到272之间,证明有两位数了,不需要前面加0
                    sw.write(Integer.toHexString(temp[0]));
                } else if (temp[0] >= 0 && temp[0] <= 0xf) {//对于只有1位的16进制数前边补“0”
                    sw.write("0" + Integer.toHexString(temp[0]));
                } else { //对于int<0的位转化为16进制的特殊处理,因为Java没有Unsigned int,所以这个int可能为负数
                    sw.write(Integer.toHexString(temp[0]).substring(6));
                }
            }
            String readResult = sw.toString();//输出转化后的十六进制
            //解析class 文件16进制
            String access_flag = "";//类的访问修饰符
            if (readResult.startsWith("cafebabe")) {
                StringBuffer sb = new StringBuffer(readResult.substring(8));
                //解析版本
                long code = hexParseLong(sb.substring(0, ConstantParam.ITEM * 2)) + hexParseLong(sb.substring(ConstantParam.ITEM * 2, ConstantParam.ITEM * 4));
                System.out.println("该class文件字节码由" + ConstantParam.JdkVersion.getName(code) + "编译");
                sb = new StringBuffer(sb.substring(ConstantParam.ITEM * 4));
                // System.out.println(sb.toString());
                //解析常量池个数
                int CONSTANT_POOL_COUNT = (int) (hexParseLong(sb.substring(0, ConstantParam.ITEM * 2)) - 1);
                System.out.println("constant_pool_count:" + CONSTANT_POOL_COUNT);
                sb = new StringBuffer(sb.substring(ConstantParam.ITEM * 2));
                //解析常量
                for (int i = 1; i <= CONSTANT_POOL_COUNT; i++) {
                    String desc = ConstantParam.ConstantPoolContrast.getDesc(hexParseLong(sb.substring(0, ConstantParam.ITEM)));//获取配置的描述信息
                    //常量池已经读取完毕了
                    if ("".equals(desc)) {
                        System.out.println("常量池读取完毕了");
                        break;
                    }
                    sb = new StringBuffer(sb.substring(ConstantParam.ITEM));
                    String[] subCount = desc.split(",");
                    int cacheLength = Integer.parseInt(subCount[1]);
                    if (desc.contains("Utf8")) {
                        long length = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));//需要读取随后的多少个byte字节
                        sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
                        String value = hexParseString(sb.substring(0, (int) (ConstantParam.ITEM * length)));
                        constant_pool.put("#" + i + " = " + subCount[0], value);
                        sb = new StringBuffer(sb.substring((int) (ConstantParam.ITEM * length)));
                    }
                    if (desc.contains("Integer") || desc.contains("Long")) {
                        long value = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));
                        constant_pool.put("#" + i + " = " + subCount[0], value + "");
                        sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
                    }
                    if (desc.contains("Float") || desc.contains("Double")) {
                        long ca_i = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));
                        float value = Double.doubleToLongBits(ca_i);
                        constant_pool.put("#" + i + " = " + subCount[0], value + "");
                        sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
                    }
                    if (desc.contains("Class") || desc.contains("String")) {
                        long ca_i = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));
                        constant_pool.put("#" + i + " = " + subCount[0], "#" + ca_i);
                        sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
                    }
                    if (desc.startsWith("Field") || desc.startsWith("Method") || desc.startsWith("Interface") || desc.startsWith("Name")) {
                        long index_1 = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));
                        sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
                        long index_2 = hexParseLong(sb.substring(0, ConstantParam.ITEM * Integer.parseInt(subCount[2])));
                        constant_pool.put("#" + i + " = " + subCount[0], "#" + index_1 + ",#" + index_2);
                        sb = new StringBuffer(sb.substring(ConstantParam.ITEM * Integer.valueOf(subCount[2])));
                    }
                }
                //解析类的访问修饰符,紧跟着常量池后四位标示该类的修饰符
                char[] access_flags = sb.substring(0, 4).toCharArray();
                char a_1 = access_flags[0];
                char a_2 = access_flags[1];
                char a_3 = access_flags[2];
                char a_4 = access_flags[3];
                switch (a_1) {
                    case '1':
                        access_flag += "ACC_SYNTHETIC  ";
                        break;
                    case '2':
                        access_flag += "ACC_ANNOTATION  ";
                        break;
                    case '4':
                        access_flag += "ACC_ENUM ";
                        break;
                }
                switch (a_2) {
                    case '2':
                        access_flag += "ACC_INTERFACE  ";
                        break;
                    case '4':
                        access_flag += "ACC_ABSTRACT  ";
                        break;
                }
                switch (a_3) {
                    case '1':
                        access_flag += "ACC_FINAL  ";
                        break;
                    case '2':
                        access_flag += "ACC_SUPER  ";
                        break;
                }
                switch (a_4) {
                    case '1':
                        access_flag += "ACC_PUBLIC  ";
                        break;
                    case '2':
                        access_flag += "ACC_PRIVATE  ";
                        break;
                }
                System.out.println("该类的访问修饰符是:" + access_flag);
                //读取类名

            } else {
                System.out.println("错误!该class文件被篡改");
            }
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("D:/test.txt")), "utf-8"));
            bw.write("该类的修饰符是:" + access_flag);
            bw.newLine();
            constant_pool.forEach((key, value) ->
            {
                try {
                    bw.write(key + "  ");
                    bw.write(value);
                    bw.newLine();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            bw.flush();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != fin) {
                try {
                    fin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 将传入的String字符串和该字符串对应的二进制表现形式,转为long输出
     *
     * @param hex
     * @return
     */
    private static long hexParseLong(String hex) {
        char[] chars = hex.toCharArray();
        long number = 0;
        for (int i = chars.length - 1; i >= 0; i--) {
            number = number + Long.parseLong(String.valueOf(chars[i]), ConstantParam.RADIS) * (int) Math.pow(ConstantParam.RADIS, chars.length - 1 - i);
        }
        return number;
    }

    /**
     * 将传入的String字符串和该字符串对应的二进制表现形式,转为String输出
     *
     * @param hex
     * @return
     */
    private static String hexParseString(String hex) throws UnsupportedEncodingException {
        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (0xff & Integer.parseInt(hex.substring(2 * i, 2 * (i + 1)), ConstantParam.RADIS));
        }
        return new String(bytes, "UTF-8");
    }
}

 

2、配置类ConstantParam.java

public class ConstantParam {

    public static final int ITEM = 2;//一个字节占2位,用来进行截取识别
    public static final int RADIS = 16;//进制数

    //JDK版本比照
    public enum JdkVersion{
        JDK1_1("JDK1.1",45),JDK1_2("JDK1.2",46),JDK1_3("JDK1.3",47),JDK1_4("JDK1.4",48),JDK5("JDK5",49),JDK6("JDK6",50),JDK7("JDK7",51),JDK8("JDK8",52);
        private String name;
        private long code;

        public static String getName( long code){
            for (JdkVersion value : JdkVersion.values()) {
                if (code == value.code ){
                    return value.name;
                }
            }
            return null;
        }

        private JdkVersion(String name,int code){
            this.name = name;
            this.code = code;
        }

    }
    //常量池数据类型结构对照
    public enum ConstantPoolContrast{
        U1(1,"Utf8,2"),//名称,需要读取多少个byte,随后在需要读取多少个byte
        U3(3,"Integer,4"),
        U4(4,"Float,4"),
        U5(5,"Long,8"),
        U6(6,"Double,8"),
        U7(7,"Class,2"),
        U8(8,"String,2"),
        U9(9,"Fieldref,2,2"),
        U10(10,"Methodref,2,2"),
        U11(11,"InterfaceMethodref,2,2"),
        U12(12,"NameAndType,2,2");
        private int index;
        private String desc;
        private ConstantPoolContrast( int index,String desc ){
            this.desc = desc;
            this.index = index;
        }
        public static String getDesc( long index ){
            for (ConstantPoolContrast value : ConstantPoolContrast.values()) {
                if (value.index == index ){
                    return value.desc;
                }
            }
            return "";
        }
    }
}

可使用java命令解析  javap -verbose ./Test.class 进行结果对照,当然,可能超大类会有误差,粗版本,学习交流,记录下来

至于代码中为什么要这样处理,请看我的另外一篇文章解释https://blog.csdn.net/qq_22597325/article/details/92133375

你可能感兴趣的:(JVM学习)