简要解析class文件,理解class结构

简要解析class文件,理解class结构_第1张图片

class文件的结构如下。u1,u2,u3,u4分别表示多少字节。java文件,读4字节,可以得到magic,这是一个数,如果读到该数,说明读的是class文件。再读2字节,会读到小版本号。 依次继续。 可以读到魔数,小版本号,大版本号,常量池中常量个数,常量池信息,类的访问标识符,当前类在常量池的索引,父类在常量池的索引,接口个数,接口的索引,属性个数,属性信息,方法个数,方法信息,类的attribute个数,类的attribute.

其中常量池的结构如下:
constant_pool  cp_info

–CONSTANT_Utf8    1  UTF-8编码的Unicode字符串
–CONSTANT_Integer    3  int类型的字面值
–CONSTANT_Float    4  float类型的字面值
–CONSTANT_Long    5  long类型的字面值
–CONSTANT_Double    6  double类型的字面值
–CONSTANT_Class    7  对一个类或接口的符号引用
–CONSTANT_String    8  String类型字面值的引用
–CONSTANT_Fieldref    9  对一个字段的符号引用
–CONSTANT_Methodref  10  对一个类中方法的符号引用
–CONSTANT_InterfaceMethodref  11  对一个接口中方法的符号引用
–CONSTANT_NameAndType  12  对一个字段或方法的部分符号引用

先读一个字节,是常量结构的tag, tag的值和上面的表对应,就能知道该常量是什么类型,进而进一步解析。需要注意的是,第0个常量是系统保留的,并没有正真的常量,我们从索引为1的开始解析。另外double和long常量超出了常量项的保存能力,所以当索引为i的常量解析为double或者long的时候,接下来要解析索引为i+2的常量,而不是i+1的常量。
常量的机构见代码。

解析出常量之后需要将常量池保存下来,接下来解析其他信息时要用。

接下来是类的访问标识符,是通过将16位各个位上置为1来表示是否有修饰符的。
简要解析class文件,理解class结构_第2张图片

待会解析到方法和属性的时候,也会有访问修饰符的解析,方法类似,但规则稍有不同。

接下来解析当前的类。我们读到的2字节,实际上是指向常量池的索引。根据索引去常量池里需找,会发现常量项的类型是CONSTANT_Class,而这个结构又指向了常量池里的另一个常量CONSTANT_UTF8, 这是一个类似java/lang/Object的utf8序列。当我们解析方法名,字段名,方法描述等信息的时候,过程和这个是类似的。

接下来用同样的方法可以解析出父类,所实现的接口等信息。

之后开始解析属性和方法,都是先读出有多少属性及方法,依次解析。看看属性和方法的结构

简要解析class文件,理解class结构_第3张图片

简要解析class文件,理解class结构_第4张图片

他们结构类似。其中attribute_info是最复杂的信息。 attribute_info是一类复杂的信息,比如方法内的代码,异常处理代码的偏移量、是否被废弃(deprecated),对应源代码的行数,源文件名、局部变量表等等有用的信息。attribute_info不是只有方法和属性结构有,类本身也会有。每一种attribute_info都有自己的格式,attribute_info结构可以嵌套,解析起来比较麻烦,我这里就省略了。。。如果都解析出来,可以做反编译了。

在属性和方法的结构中access_flags是访问标识符,和class的有点像,但是解析的时候是不一样的。name_index是指向常量池的索引。descriptor_index也指向常量池,是对类和方法的描述。描述为了节省空间,都是简写的
简要解析class文件,理解class结构_第5张图片

通过一些代码,我们就可以解析class文件,得到一些类的信息,这里不解析attribute_info.

public class ClassAnalyser {
    private static Cons_info[] cons = null;
    //public static String myClass = "D:Worker.class";
    public static void main(String[] args) {
        DataInputStream input = null;
        try {
            input = new DataInputStream(new BufferedInputStream(
                    new FileInputStream(myClass)));
            // 获取文件大小
            FileSize(new DataInputStream(new BufferedInputStream(
                    new FileInputStream(myClass))));
            // 解析数据
            analyze(input);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("解析失败!");
        } finally {
            try {
                input.close();
            } catch (Exception e) {
            }
        }
    }

    public static void FileSize(DataInputStream input) throws IOException {
        int length = 0; // 长度,字节,B
        double kblength = 0.0D; // 长度,千字节,KB
        while (input.read() != -1)
            ++length;
        kblength = length / 1024.0D;
        System.out.println("文件大小(字节):" + length + "\n文件大小(KB):" + kblength);
        input.close();
    }

    public static void analyze(DataInputStream input) throws IOException {
        // 读取魔数
        int magic = u4(input);
        if (magic == 0xCAFEBABE) // -
            System.out.println("魔数 = 0xCAFEBABE -----标准class文件");
        else {
            System.out.println("无效的魔数,不是.class文件");
            return;
        }
        // 读取版本号
        int minor_ver = u2(input);
        int major_ver = u2(input);
        System.out.println("版本号 :大版本- " + major_ver + "  小版本-" + minor_ver);
        // 读取常量池表中表项的个数
        short cons_count = u2(input);
        System.out.println("常量池大小 = " + cons_count);
        cons = new Cons_info[cons_count];
        // 读取每个常量
        //第一个系统保留,不读
        for (int i = 1; i < cons_count; ++i){
            i = analyzeConstant(input, i);
        }
        short access_flags = u2(input);
        analyzeAccessFlag(access_flags);
        // 读取类或者接口
        short this_class_index = u2(input);
        short super_class_index = u2(input);
        System.out.println("当前类的索引为:" + this_class_index);
        System.out.println("类名为:"+cons[cons[this_class_index].n16].utf8);
        System.out.println("父类的索引为:" + super_class_index);
        System.out.println("类名为:"+cons[cons[super_class_index].n16].utf8);
        short inteCes_count = u2(input);
        System.out.println("实现的接口数量为:" + inteCes_count);
        for (int i = 1; i <= inteCes_count; i++) {
            short inteCe_index = u2(input);
            System.out.println("接口第 " + i + "个的索引为:"+ inteCe_index);
            System.out.println("接口名为:"+cons[cons[inteCe_index].n16].utf8);
        }
        short fields_count = u2(input);
        System.out.println("属性的个数为:"+fields_count+"个");
        for(int i=0;i<fields_count;i++){
            readField(input,i);
        }
        short method_count = u2(input);
        System.out.println("方法的个数为:"+method_count+"个");
        for(int i=0;i<method_count;i++){
            readMethod(input,i);
        }
    }

    /**
     * 读方法 
     * @param input
     * @param i
     * @throws IOException 
     */
    private static void readMethod(DataInputStream input, int i) throws IOException {
        System.out.println("-----------------------方法"+i);
        short access = u2(input);
        analyzeAccessFlagMethod(access);
        short name_index = u2(input);
        System.out.println("方法名为:"+cons[name_index].utf8);
        short desc_index = u2(input);
        System.out.println("描述符为:"+cons[desc_index].utf8);
        short attr_count = u2(input);
        for(int j=0;j<attr_count;j++){
            readAttribute(input,j);
        }
    }

    /**
     * 读属性
     * @param input
     * @param i
     * @throws IOException 
     */
    private static void readField(DataInputStream input, int i) throws IOException {
        System.out.println("-----------------------属性"+i);
        short access = u2(input);
        analyzeAccessFlagField(access);
        short name_index = u2(input);
        System.out.println("属性名为:"+cons[name_index].utf8);
        short desc_index = u2(input);
        System.out.println("描述符为:"+cons[desc_index].utf8);
        short attr_count = u2(input);
        for(int j=0;j<attr_count;j++){
            readAttribute(input,j);
        }
        
    }
    /**
     * 读attribute 暂时不做任何事情
     * @param input
     * @param j
     * @throws IOException
     */
    private static void readAttribute(DataInputStream input,int j) throws IOException{
        u2(input);
        int length = u4(input);
        byte[] bytes = new byte[length];
        input.readFully(bytes);
    }

    public static byte u1(DataInputStream input) throws IOException {
        return input.readByte();
    }

    public static Short u2(DataInputStream input) throws IOException {
        return input.readShort();
    }

    public static int u4(DataInputStream input) throws IOException {
        return input.readInt();
    }

    public static long u8(DataInputStream input) throws IOException {
        return input.readLong();
    }
    /**
     * 类访问权限
     * @param i
     */
    private static void analyzeAccessFlag(short i){
        System.out.println("类的访问权限:");
        String desc = toBinary(i);
        if('1' == desc.charAt(15)){
            System.out.print("Public ");
        }
        if('1' == desc.charAt(14)){
            System.out.print("final ");
        }
        if('1' == desc.charAt(13)){
            System.out.print("ACC_super ");
        }
        if('1' == desc.charAt(12)){
            System.out.print("interface ");
        }
        if('1' == desc.charAt(11)){
            System.out.print("Abstract ");
        }
        if('1' == desc.charAt(10)){
            System.out.print("synthetic ");
        }
        if('1' == desc.charAt(9)){
            System.out.print("annotation ");
        }
        if('1' == desc.charAt(8)){
            System.out.print("enum ");
        }
        System.out.println();
    }

    /**
     * 属性访问权限
     * @param i
     */
    private static void analyzeAccessFlagField(short i){
        String desc = toBinary(i);
        if('1' == desc.charAt(15)){
            System.out.print("public ");
        }
        if('1' == desc.charAt(14)){
            System.out.print("private ");
        }
        if('1' == desc.charAt(13)){
            System.out.print("potected ");
        }
        if('1' == desc.charAt(12)){
            System.out.print("static ");
        }
        if('1' == desc.charAt(11)){
            System.out.print("final ");
        }
        if('1' == desc.charAt(10)){
            System.out.print("volatile ");
        }
        if('1' == desc.charAt(9)){
            System.out.print("transient ");
        }
        if('1' == desc.charAt(8)){
            System.out.print("synthetic ");
        }
        if('1' == desc.charAt(7)){
            System.out.print("enum ");
        }
        System.out.println();
    }
    /**
     * 方法权限
     * @param i
     */
    private static void analyzeAccessFlagMethod(short i){
        String desc = toBinary(i);
        if('1' == desc.charAt(15)){
            System.out.print("public ");
        }
        if('1' == desc.charAt(14)){
            System.out.print("private ");
        }
        if('1' == desc.charAt(13)){
            System.out.print("potected ");
        }
        if('1' == desc.charAt(12)){
            System.out.print("static ");
        }
        if('1' == desc.charAt(11)){
            System.out.print("final ");
        }
        if('1' == desc.charAt(10)){
            System.out.print("sychronized ");
        }
        if('1' == desc.charAt(9)){
            System.out.print("bridge ");
        }
        if('1' == desc.charAt(8)){
            System.out.print("varargs ");
        }
        if('1' == desc.charAt(7)){
            System.out.print("native ");
        }
        if('1' == desc.charAt(6)){
            System.out.print("abstract ");
        }
        if('1' == desc.charAt(5)){
            System.out.print("strictfp ");
        }
        if('1' == desc.charAt(4)){
            System.out.print("synthetic ");
        }
        System.out.println();
    }
    /**
     * 解析常量
     * @param input
     * @param index
     * @throws IOException
     */
    public static int analyzeConstant(DataInputStream input, int index)
            throws IOException {
        // 用于读:
        // byte n8;
        short n16 = 0;
        short n162 = 0;
        int n32 = 0;
        long n64 = 0l;
        float f = 0f;
        double d = 0d;
        byte[] buffer;
        byte tag = input.readByte(); // 读取数据类型标签
        System.out.println("第"+index+"个常量");
        switch (tag) {
        case 1: // utf-8 string
            System.out.println("常量类型=utf8");
            n16 = u2(input);
            System.out.println(" length = " + n16);
            buffer = new byte[n16];
            input.readFully(buffer); // 数组读满才返回
            System.out.println(" value = " + new String(buffer));
            cons[index] = new Cons_info(n16,n16,0,0,0,0,new String(buffer));
            break;
        case 3: // integer
            System.out.println("常量类型=Integer"); 
            n32 = u4(input);
            System.out.println(" value = " + n32);
            cons[index] = new Cons_info(n16,n16,n32,n64,f,d,"");
            break;
        case 4: // float
            System.out.println("常量类型=Float");
            f = u4(input);
            System.out.println(" value = " + f);
            cons[index] = new Cons_info(n16,n16,n32,n64,f,d,"");
            break;
        case 5: // long
            System.out.println("常量类型=Long");
            //n64 = u8(input);
            n64 = input.readLong();
            System.out.println(" value = " + n64);
            cons[index] = new Cons_info(n16,n16,n32,n64,f,d,"");
            index ++;
            break;
        case 6: // double
            System.out.println("常量类型=Double");
            //d = u8(input);
            d = input.readDouble();
            System.out.println(" value = " + d);
            cons[index] = new Cons_info(n16,n16,n32,n64,f,d,"");
            index ++;
            break;
        case 7: //Class_info
            System.out.println("常量类型=Class"); // 类或者接口的 索引
            n16 = u2(input);
            System.out.println(" 索引 = " + n16 );
            cons[index] = new Cons_info(n16,n16,n32,n64,f,d,"");
            break;
        case 8: // String_info
            System.out.println("常量类型=String"); // 字符串索引
            n16 = u2(input);
            System.out.println(" 索引 = " + n16);
            cons[index] = new Cons_info(n16,n16,n32,n64,f,d,"");
            break;
        case 9://Fieldref_info
            System.out.println("常量类型=字段引用");
            n16 = u2(input);
            System.out.println("类名索引 = "+n16);
            n162 = u2(input);
            System.out.println("名称和类型 = "+n162);
            cons[index] = new Cons_info(n16,n162,n32,n64,f,d,"");
            break;
        case 10: //Methodref_info
            System.out.println("常量类型=类中方法引用");
            n16 = u2(input);
            System.out.println(" 索引 = " + n16);
            n162 = u2(input);
            System.out.println("名称和类型 = " + n162);
            cons[index] = new Cons_info(n16,n162,n32,n64,f,d,"");
            break;
        case 11: //InterfaceMethodref_info
            System.out.println("常量类型=接口中的方法引用");
            n16 = u2(input);
            System.out.println(" 索引 = " + n16 );
            n162 = u2(input);
            System.out.println("名称和类型 = " + n162 );
            cons[index] = new Cons_info(n16,n162,n32,n64,f,d,"");
            break;
        case 12: //NameAndType_info
            System.out.println("常量类型=字段和方法的名称以及类型的符号引用 ");
            n16 = u2(input);
            System.out.println(" 索引 = " + n16 );
            n162 = u2(input);
            System.out.println(" 描述符 = " + n162);
            cons[index] = new Cons_info(n16,n162,n32,n64,f,d,"");
            break;
        default:
            throw new RuntimeException("unknown constant pool flag: " + tag);
        } 
        System.out.println("-----------------------");
        return index;
    }

    /**
     * 补足16位
     * @param i
     * @return
     */
    private static String toBinary(short i){
        String a = Integer.toBinaryString(i);
        while(a.length()<16){
            a = "0"+a;
        }
        return a;
    }
    
    
}

/**
 * 常量
 */
class Cons_info{
    short n16;
    short n162;
    int n32;
    long n64;
    float f;
    double d;
    String utf8;
    public Cons_info(short n16, short n162, int n32, long n64, float f,
            double d, String utf8) {
        super();
        this.n16 = n16;
        this.n162 = n162;
        this.n32 = n32;
        this.n64 = n64;
        this.f = f;
        this.d = d;
        this.utf8 = utf8;
    }
}


你可能感兴趣的:(java,Class,反编译)