在搜索到class文件后,得到字节码数据,然后将它解析为class文件结构
Class文件结构
分析class文件结构,可以发现它主要是由以下部分组成:
- 魔数、版本号、访问标志、类索引、父类索引
- 常量池
- 字段表、方法表
- 属性表
据此建立class文件结构体,然后将字节码数据解析为结构体。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type ClassFile struct { minorVersion uint16 majorVersion uint16
constantPool ConstantPool
accessFlags uint16 thisClass uint16 superClass uint16
interfaces []uint16
fields []*MemberInfo methods []*MemberInfo
attributes []AttributeInfo }
|
解析Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| func Parse(classData []byte) (cf *ClassFile, err error) { defer func() { if r := recover(); r != nil { var ok bool err, ok = r.(error) if !ok { err = fmt.Errorf("%v", r) } } }()
cr := &ClassReader{classData} cf = &ClassFile{}
cf.read(cr) return }
func (self *ClassFile) read(reader *ClassReader) { self.readAndCheckMagic(reader) self.readAndCheckVersion(reader) self.constantPool = readConstantPool(reader)
self.accessFlags = reader.readUint16() self.thisClass = reader.readUint16() self.superClass = reader.readUint16() self.interfaces = reader.readUint16s()
self.fields = readMembers(reader, self.constantPool) self.methods = readMembers(reader, self.constantPool) self.attributes = readAttributes(reader, self.constantPool) }
|
模拟tryCatch,然后读取数据,根据class文件的字节码顺序,进行读取,将数据存储在classfile中。
分析常量池
常量池,是存储常量(Constant_Info)的表,读取常量池大小,然后读取常量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| type ConstantPool []ConstantInfo
func readConstantPool(reader *ClassReader) ConstantPool { cpCount := int(reader.readUint16()) cp := make([]ConstantInfo, cpCount)
for i := 1; i < cpCount; i++ { cp[i] = readConstantInfo(reader, cp) switch cp[i].(type) { case *ConstantLongInfo, *ConstantDoubleInfo: i++ } }
return cp }
|
读取常量部分,采用不同类型,进行不同操作,生成不同常量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| type ConstantInfo interface { readInfo(reader *ClassReader) }
func readConstantInfo(reader *ClassReader, cp ConstantPool) ConstantInfo { tag := reader.readUint8() c := newConstantInfo(tag, cp) c.readInfo(reader) return c }
func newConstantInfo(tag uint8, cp ConstantPool) ConstantInfo { switch tag { case CONSTANT_Integer: return &ConstantIntegerInfo{} case CONSTANT_Float: return &ConstantFloatInfo{} case CONSTANT_Long: return &ConstantLongInfo{} case CONSTANT_Double: return &ConstantDoubleInfo{} case CONSTANT_Utf8: return &ConstantUtf8Info{} case CONSTANT_String: return &ConstantStringInfo{cp: cp} case CONSTANT_Class: return &ConstantClassInfo{cp: cp} case COMSTANT_Fieldref: return &ConstantFieldrefInfo{ConstantMemberrefInfo{cp: cp}} case CONSTANT_Methodref: return &ConstantMethodrefInfo{ConstantMemberrefInfo{cp: cp}} case CONSTANT_InterfaceMethodref: return &ConstantInterfaceMethodrefInfo{ConstantMemberrefInfo{cp: cp}} case CONSTANT_NameAndType: return &ConstantNameAndTypeInfo{} default: panic("java.lang.ClassFormatError: constant pool tag!") } }
|
对于不同的常量处理过程,详见这里
解析字段和方法表
由于字段表和方法表采用相同的结构,所以采用相同的方法进行解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| type MemberInfo struct { cp ConstantPool accessFlags uint16 nameIndex uint16 descriptorIndex uint16 attributes []AttributeInfo }
func readMembers(reader *ClassReader, cp ConstantPool) []*MemberInfo { memberCount := reader.readUint16() members := make([]*MemberInfo, memberCount) for i := range members { members[i] = readMember(reader, cp) } return members }
func readMember(reader *ClassReader, cp ConstantPool) *MemberInfo { return &MemberInfo{ cp: cp, accessFlags: reader.readUint16(), nameIndex: reader.readUint16(), descriptorIndex: reader.readUint16(), attributes: readAttributes(reader, cp), } }
|
解析属性表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
type AttributeInfo interface { readInfo(reader *ClassReader) }
func readAttributes(reader *ClassReader, cp ConstantPool) []AttributeInfo { attributesCount := reader.readUint16() attributes := make([]AttributeInfo, attributesCount) for i := range attributes { attributes[i] = readAttribute(reader, cp) } return attributes }
func readAttribute(reader *ClassReader, cp ConstantPool) AttributeInfo { attrNameIndex := reader.readUint16() attrName := cp.getUtf8(attrNameIndex) attrLen := reader.readUint32() attrInfo := newAttributeInfo(attrName, attrLen, cp) attrInfo.readInfo(reader) return attrInfo }
func newAttributeInfo(attrName string, attrLen uint32, cp ConstantPool) AttributeInfo { switch attrName {
case "Deprecated": return &DeprecatedAttribute{} case "Synthetic": return &SyntheticAttribute{}
case "SourceFile": return &SourceFileAttribute{cp: cp} case "LineNumberTable": return &LineNumberTableAttribute{} case "LocalVariableTable": return &LocalVariableTableAttribute{}
case "ConstantValue": return &ConstantValueAttribute{} case "Code": return &CodeAttribute{cp: cp} case "Exceptions": return &ExceptionsAttribute{}
default: return &UnparsedAttribute{attrName, attrLen, nil} } }
|
首先读取属性表,然后再读每一个属性,根据属性名,创建属性信息,然后读取属性信息。
具体属性信息也详见这里
结束
解析class文件部分,基本就是使用reader,读取固定长度的字节,获取相应的信息,并组成不同的信息结构,这里就不再赘述了。